1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.view.Display.DEFAULT_DISPLAY;
20 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
21 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
22 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
23 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
24 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
25 import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
26 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
27 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
28 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
29 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
30 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
31 import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION;
32 import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
33 import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED;
34 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT;
35 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
36 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST;
37 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
38 import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
39 
40 import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
41 import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
42 
43 import static com.google.common.truth.Truth.assertThat;
44 
45 import static org.mockito.Mockito.doReturn;
46 import static org.mockito.Mockito.mock;
47 import static org.mockito.Mockito.when;
48 import static org.testng.Assert.assertThrows;
49 
50 import static java.util.stream.Collectors.toList;
51 
52 import android.app.ActivityOptions;
53 import android.content.res.Resources;
54 import android.os.Binder;
55 import android.os.Bundle;
56 import android.os.IBinder;
57 import android.platform.test.annotations.Presubmit;
58 import android.view.SurfaceControl;
59 import android.window.WindowContainerToken;
60 
61 import com.google.android.collect.Lists;
62 
63 import org.hamcrest.CustomTypeSafeMatcher;
64 import org.hamcrest.Description;
65 import org.hamcrest.Matcher;
66 import org.junit.Before;
67 import org.junit.Rule;
68 import org.junit.Test;
69 
70 import java.util.ArrayList;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.List;
74 import java.util.Map;
75 import java.util.Set;
76 import java.util.function.Consumer;
77 import java.util.function.Function;
78 import java.util.stream.Collectors;
79 
80 /**
81  * Build/Install/Run:
82  *  atest WmTests:DisplayAreaPolicyBuilderTest
83  */
84 @Presubmit
85 public class DisplayAreaPolicyBuilderTest {
86     private static final String KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID =
87             "android.test.launchTaskDisplayAreaFeatureId";
88 
89     @Rule
90     public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule();
91 
92     private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy();
93     private WindowManagerService mWms;
94     private RootDisplayArea mRoot;
95     private DisplayArea.Tokens mImeContainer;
96     private DisplayContent mDisplayContent;
97     private TaskDisplayArea mDefaultTaskDisplayArea;
98     private List<TaskDisplayArea> mTaskDisplayAreaList;
99     private RootDisplayArea mGroupRoot1;
100     private RootDisplayArea mGroupRoot2;
101     private TaskDisplayArea mTda1;
102     private TaskDisplayArea mTda2;
103 
104     @Before
setup()105     public void setup() {
106         mWms = mSystemServices.getWindowManagerService();
107         mRoot = new SurfacelessDisplayAreaRoot(mWms);
108         mImeContainer = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "ImeContainer");
109         mDisplayContent = mock(DisplayContent.class);
110         doReturn(true).when(mDisplayContent).isTrusted();
111         doReturn(DEFAULT_DISPLAY).when(mDisplayContent).getDisplayId();
112         mDisplayContent.isDefaultDisplay = true;
113         mDefaultTaskDisplayArea = new TaskDisplayArea(mDisplayContent, mWms, "Tasks",
114                 FEATURE_DEFAULT_TASK_CONTAINER);
115         mTaskDisplayAreaList = new ArrayList<>();
116         mTaskDisplayAreaList.add(mDefaultTaskDisplayArea);
117         mGroupRoot1 = new SurfacelessDisplayAreaRoot(mWms, "group1", FEATURE_VENDOR_FIRST + 1);
118         mGroupRoot2 = new SurfacelessDisplayAreaRoot(mWms, "group2", FEATURE_VENDOR_FIRST + 2);
119         mTda1 = new TaskDisplayArea(mDisplayContent, mWms, "tda1", FEATURE_VENDOR_FIRST + 3);
120         mTda2 = new TaskDisplayArea(mDisplayContent, mWms, "tda2", FEATURE_VENDOR_FIRST + 4);
121     }
122 
123     @Test
testBuilder()124     public void testBuilder() {
125         final Feature foo;
126         final Feature bar;
127         DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
128                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
129                         .addFeature(foo = new Feature.Builder(mPolicy, "Foo",
130                                 FEATURE_VENDOR_FIRST)
131                                 .upTo(TYPE_STATUS_BAR)
132                                 .and(TYPE_NAVIGATION_BAR)
133                                 .build())
134                         .addFeature(bar = new Feature.Builder(mPolicy, "Bar",
135                                 FEATURE_VENDOR_FIRST + 1)
136                                 .all()
137                                 .except(TYPE_STATUS_BAR)
138                                 .build())
139                         .setImeContainer(mImeContainer)
140                         .setTaskDisplayAreas(mTaskDisplayAreaList);
141         DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
142                 .setRootHierarchy(rootHierarchy)
143                 .build(mWms);
144 
145         assertThat(policy.getDisplayAreas(foo.getId())).isNotEmpty();
146         assertThat(policy.getDisplayAreas(bar.getId())).isNotEmpty();
147 
148         Matcher<WindowContainer> fooDescendantMatcher = descendantOfOneOf(
149                 policy.getDisplayAreas(foo.getId()));
150         Matcher<WindowContainer> barDescendantMatcher = descendantOfOneOf(
151                 policy.getDisplayAreas(bar.getId()));
152 
153         // There is a DA of TYPE_STATUS_BAR below foo, but not below bar
154         assertThat(fooDescendantMatcher.matches(
155                 policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)))).isTrue();
156         assertThat(barDescendantMatcher.matches(
157                 policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)))).isFalse();
158 
159         // The TDA is below both foo and bar.
160         assertThat(fooDescendantMatcher.matches(mDefaultTaskDisplayArea)).isTrue();
161         assertThat(barDescendantMatcher.matches(mDefaultTaskDisplayArea)).isTrue();
162 
163         // The IME is below both foo and bar.
164         assertThat(fooDescendantMatcher.matches(mImeContainer)).isTrue();
165         assertThat(barDescendantMatcher.matches(mImeContainer)).isTrue();
166         assertThat(policy.findAreaForToken(tokenOfType(TYPE_INPUT_METHOD)))
167                 .isEqualTo(mImeContainer);
168         assertThat(policy.findAreaForToken(tokenOfType(TYPE_INPUT_METHOD_DIALOG)))
169                 .isEqualTo(mImeContainer);
170 
171         List<DisplayArea<?>> actualOrder = collectLeafAreas(mRoot);
172         Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, mImeContainer,
173                 mDefaultTaskDisplayArea);
174         actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList());
175 
176         Map<DisplayArea<?>, Integer> expectedByMinLayer = mapValues(zSets,
177                 v -> v.stream().min(Integer::compareTo).get());
178         Map<DisplayArea<?>, Integer> expectedByMaxLayer = mapValues(zSets,
179                 v -> v.stream().max(Integer::compareTo).get());
180 
181         // Make sure the DAs' order is the same as their layer order.
182         assertMatchLayerOrder(actualOrder, expectedByMinLayer);
183         assertMatchLayerOrder(actualOrder, expectedByMaxLayer);
184     }
185 
186     @Test
testBuilder_defaultPolicy_hasOneHandedFeature()187     public void testBuilder_defaultPolicy_hasOneHandedFeature() {
188         final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
189                 resourcesWithProvider(""));
190         final DisplayAreaPolicyBuilder.Result defaultPolicy =
191                 (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
192                         mRoot, mImeContainer);
193         if (mDisplayContent.isDefaultDisplay) {
194             final List<Feature> features = defaultPolicy.getFeatures();
195             boolean hasOneHandedFeature = false;
196             for (Feature feature : features) {
197                 hasOneHandedFeature |= feature.getId() == FEATURE_ONE_HANDED;
198             }
199 
200             assertThat(hasOneHandedFeature).isTrue();
201         }
202     }
203 
204     @Test
testBuilder_defaultPolicy_hasWindowedMagnificationFeature()205     public void testBuilder_defaultPolicy_hasWindowedMagnificationFeature() {
206         final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
207                 resourcesWithProvider(""));
208         final DisplayAreaPolicyBuilder.Result defaultPolicy =
209                 (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
210                         mRoot, mImeContainer);
211         final List<Feature> features = defaultPolicy.getFeatures();
212         boolean hasWindowedMagnificationFeature = false;
213         for (Feature feature : features) {
214             hasWindowedMagnificationFeature |= feature.getId() == FEATURE_WINDOWED_MAGNIFICATION;
215         }
216 
217         assertThat(hasWindowedMagnificationFeature).isTrue();
218     }
219 
220     @Test
testBuilder_defaultPolicy_hasFullscreenMagnificationFeature()221     public void testBuilder_defaultPolicy_hasFullscreenMagnificationFeature() {
222         final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
223                 resourcesWithProvider(""));
224         final DisplayAreaPolicyBuilder.Result defaultPolicy =
225                 (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
226                         mRoot, mImeContainer);
227         final List<Feature> features = defaultPolicy.getFeatures();
228         boolean hasFullscreenMagnificationFeature = false;
229         for (Feature feature : features) {
230             hasFullscreenMagnificationFeature |=
231                     feature.getId() == FEATURE_FULLSCREEN_MAGNIFICATION;
232         }
233 
234         assertThat(hasFullscreenMagnificationFeature).isTrue();
235     }
236 
237     @Test
testBuilder_defaultPolicy_hasImePlaceholderFeature()238     public void testBuilder_defaultPolicy_hasImePlaceholderFeature() {
239         final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources(
240                 resourcesWithProvider(""));
241         final DisplayAreaPolicyBuilder.Result defaultPolicy =
242                 (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent,
243                         mRoot, mImeContainer);
244         final List<Feature> features = defaultPolicy.getFeatures();
245         boolean hasImePlaceholderFeature = false;
246         for (Feature feature : features) {
247             hasImePlaceholderFeature |= feature.getId() == FEATURE_IME_PLACEHOLDER;
248         }
249 
250         assertThat(hasImePlaceholderFeature).isTrue();
251     }
252 
253     @Test
testBuilder_createCustomizedDisplayAreaForFeature()254     public void testBuilder_createCustomizedDisplayAreaForFeature() {
255         final Feature dimmable;
256         final Feature other;
257         DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy =
258                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
259                         .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable",
260                                 FEATURE_VENDOR_FIRST)
261                                 .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
262                                 .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
263                                 .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
264                                 .build())
265                         .addFeature(other = new Feature.Builder(mPolicy, "Other",
266                                 FEATURE_VENDOR_FIRST + 1)
267                                 .all()
268                                 .build())
269                         .setImeContainer(mImeContainer)
270                         .setTaskDisplayAreas(mTaskDisplayAreaList);
271         DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
272                 .setRootHierarchy(rootHierarchy)
273                 .build(mWms);
274 
275         List<DisplayArea<? extends WindowContainer>> dimmableDAs =
276                 policy.getDisplayAreas(dimmable.getId());
277         List<DisplayArea<? extends WindowContainer>> otherDAs =
278                 policy.getDisplayAreas(other.getId());
279         assertThat(dimmableDAs).hasSize(1);
280         assertThat(dimmableDAs.get(0)).isInstanceOf(DisplayArea.Dimmable.class);
281         for (DisplayArea otherDA : otherDAs) {
282             assertThat(otherDA).isNotInstanceOf(DisplayArea.Dimmable.class);
283         }
284     }
285 
286     @Test
testBuilder_singleRoot_validateSettings()287     public void testBuilder_singleRoot_validateSettings() {
288         final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder();
289 
290         // Root must be set.
291         assertThrows(IllegalStateException.class, () -> builder.build(mWms));
292 
293         // IME must be set.
294         builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
295                 .setTaskDisplayAreas(mTaskDisplayAreaList));
296 
297         assertThrows(IllegalStateException.class, () -> builder.build(mWms));
298 
299         // Default TaskDisplayArea must be set.
300         builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
301                 .setImeContainer(mImeContainer)
302                 .setTaskDisplayAreas(Lists.newArrayList(
303                         new TaskDisplayArea(mDisplayContent, mWms, "testTda",
304                                 FEATURE_VENDOR_FIRST + 1))));
305 
306         assertThrows(IllegalStateException.class, () -> builder.build(mWms));
307 
308         // No exception
309         builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
310                 .setImeContainer(mImeContainer)
311                 .setTaskDisplayAreas(mTaskDisplayAreaList));
312 
313         builder.build(mWms);
314     }
315 
316     @Test
testBuilder_displayAreaGroup_validateSettings()317     public void testBuilder_displayAreaGroup_validateSettings() {
318         final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder();
319 
320         // IME must be set to one of the roots.
321         builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
322         builder1.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
323                 mGroupRoot1)
324                 .setTaskDisplayAreas(mTaskDisplayAreaList));
325 
326         assertThrows(IllegalStateException.class, () -> builder1.build(mWms));
327 
328         // Default TaskDisplayArea must be set.
329         final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder();
330         builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
331         builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
332                 mGroupRoot1)
333                 .setImeContainer(mImeContainer)
334                 .setTaskDisplayAreas(Lists.newArrayList(mTda1)));
335 
336         assertThrows(IllegalStateException.class, () -> builder2.build(mWms));
337 
338         // Each DisplayAreaGroup must have at least one TaskDisplayArea.
339         final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder();
340         builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
341         builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
342                 mGroupRoot1)
343                 .setImeContainer(mImeContainer)
344                 .setTaskDisplayAreas(mTaskDisplayAreaList));
345         builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
346                 mGroupRoot2));
347 
348         assertThrows(IllegalStateException.class, () -> builder3.build(mWms));
349 
350         // No exception
351         final DisplayAreaPolicyBuilder builder4 = new DisplayAreaPolicyBuilder();
352         builder4.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot));
353         builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
354                 mGroupRoot1)
355                 .setImeContainer(mImeContainer)
356                 .setTaskDisplayAreas(mTaskDisplayAreaList));
357         builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
358                 mGroupRoot2)
359                 .setTaskDisplayAreas(Lists.newArrayList(mTda1)));
360 
361         builder4.build(mWms);
362     }
363 
364     @Test
testBuilder_rootHasUniqueId()365     public void testBuilder_rootHasUniqueId() {
366         // Root must have different id from all roots.
367         final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder();
368         builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
369                 .setImeContainer(mImeContainer)
370                 .setTaskDisplayAreas(mTaskDisplayAreaList));
371         final RootDisplayArea groupRoot1 = new SurfacelessDisplayAreaRoot(mWms, "group1",
372                 mRoot.mFeatureId);
373         builder1.addDisplayAreaGroupHierarchy(
374                 new DisplayAreaPolicyBuilder.HierarchyBuilder(groupRoot1)
375                         .setTaskDisplayAreas(Lists.newArrayList(mTda1)));
376 
377         assertThrows(IllegalStateException.class, () -> builder1.build(mWms));
378 
379         // Root must have different id from all TDAs.
380         final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder();
381         builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
382                 .setImeContainer(mImeContainer)
383                 .setTaskDisplayAreas(Lists.newArrayList(
384                         mDefaultTaskDisplayArea,
385                         new TaskDisplayArea(mDisplayContent, mWms, "testTda",
386                                 mRoot.mFeatureId))));
387 
388         assertThrows(IllegalStateException.class, () -> builder2.build(mWms));
389 
390         // Root must have different id from all features.
391         final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder();
392         builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
393                 .setImeContainer(mImeContainer)
394                 .setTaskDisplayAreas(mTaskDisplayAreaList)
395                 .addFeature(new Feature.Builder(mPolicy, "testFeature", mRoot.mFeatureId)
396                         .all()
397                         .build()));
398 
399         assertThrows(IllegalStateException.class, () -> builder3.build(mWms));
400     }
401 
402     @Test
testBuilder_taskDisplayAreaHasUniqueId()403     public void testBuilder_taskDisplayAreaHasUniqueId() {
404         // TDA must have different id from all TDAs.
405         final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder();
406         final List<TaskDisplayArea> tdaList = Lists.newArrayList(
407                 mDefaultTaskDisplayArea,
408                 mTda1,
409                 new TaskDisplayArea(mDisplayContent, mWms, "tda2", mTda1.mFeatureId));
410         builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
411                 .setImeContainer(mImeContainer)
412                 .setTaskDisplayAreas(tdaList));
413 
414         assertThrows(IllegalStateException.class, () -> builder.build(mWms));
415 
416         // TDA must have different id from all features.
417         final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder();
418         builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
419                 .setImeContainer(mImeContainer)
420                 .setTaskDisplayAreas(Lists.newArrayList(
421                         mDefaultTaskDisplayArea,
422                         mTda1))
423                 .addFeature(new Feature.Builder(mPolicy, "testFeature", mTda1.mFeatureId)
424                         .all()
425                         .build()));
426 
427         assertThrows(IllegalStateException.class, () -> builder2.build(mWms));
428     }
429 
430     @Test
testBuilder_featureHasUniqueId()431     public void testBuilder_featureHasUniqueId() {
432         // Feature must have different id from features below the same root.
433         final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder();
434         builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
435                 .setImeContainer(mImeContainer)
436                 .setTaskDisplayAreas(mTaskDisplayAreaList)
437                 .addFeature(new Feature.Builder(mPolicy, "feature1", FEATURE_VENDOR_FIRST + 10)
438                         .all()
439                         .build())
440                 .addFeature(new Feature.Builder(mPolicy, "feature2", FEATURE_VENDOR_FIRST + 10)
441                         .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
442                         .build()));
443 
444         assertThrows(IllegalStateException.class, () -> builder.build(mWms));
445 
446         // Features below different root can have the same id.
447         final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder();
448         builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
449                 .setImeContainer(mImeContainer)
450                 .setTaskDisplayAreas(mTaskDisplayAreaList)
451                 .addFeature(new Feature.Builder(mPolicy, "feature1", FEATURE_VENDOR_FIRST + 10)
452                         .all()
453                         .build()));
454         builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
455                 mGroupRoot1)
456                 .setTaskDisplayAreas(Lists.newArrayList(mTda1))
457                 .addFeature(new Feature.Builder(mPolicy, "feature2", FEATURE_VENDOR_FIRST + 10)
458                         .all()
459                         .build()));
460 
461         builder2.build(mWms);
462     }
463 
464     @Test
testBuilder_idsNotGreaterThanFeatureVendorLast()465     public void testBuilder_idsNotGreaterThanFeatureVendorLast() {
466         // Root id should not be greater than FEATURE_VENDOR_LAST.
467         final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder();
468         final RootDisplayArea root = new SurfacelessDisplayAreaRoot(mWms, "testRoot",
469                 FEATURE_VENDOR_LAST + 1);
470         builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
471                 .setImeContainer(mImeContainer)
472                 .setTaskDisplayAreas(mTaskDisplayAreaList));
473 
474         assertThrows(IllegalStateException.class, () -> builder1.build(mWms));
475 
476         // TDA id should not be greater than FEATURE_VENDOR_LAST.
477         final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder();
478         builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root)
479                 .setImeContainer(mImeContainer)
480                 .setTaskDisplayAreas(Lists.newArrayList(
481                         mDefaultTaskDisplayArea,
482                         new TaskDisplayArea(mDisplayContent, mWms, "testTda",
483                                 FEATURE_VENDOR_LAST + 1))));
484 
485         assertThrows(IllegalStateException.class, () -> builder2.build(mWms));
486 
487         // Feature id should not be greater than FEATURE_VENDOR_LAST.
488         final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder();
489         builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
490                 .setImeContainer(mImeContainer)
491                 .setTaskDisplayAreas(mTaskDisplayAreaList)
492                 .addFeature(new Feature.Builder(mPolicy, "testFeature", FEATURE_VENDOR_LAST + 1)
493                         .all()
494                         .build()));
495 
496         assertThrows(IllegalStateException.class, () -> builder3.build(mWms));
497     }
498 
499     @Test
testBuilder_displayAreaGroup_attachDisplayAreas()500     public void testBuilder_displayAreaGroup_attachDisplayAreas() {
501         final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
502                 .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
503                         .setTaskDisplayAreas(mTaskDisplayAreaList))
504                 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
505                         mGroupRoot1)
506                         .setImeContainer(mImeContainer)
507                         .setTaskDisplayAreas(Lists.newArrayList(mTda1)))
508                 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
509                         mGroupRoot2)
510                         .setTaskDisplayAreas(Lists.newArrayList(mTda2)))
511                 .build(mWms);
512 
513         assertThat(mDefaultTaskDisplayArea.isDescendantOf(mRoot)).isTrue();
514         assertThat(mGroupRoot1.isDescendantOf(mRoot)).isTrue();
515         assertThat(mGroupRoot2.isDescendantOf(mRoot)).isTrue();
516         assertThat(mImeContainer.isDescendantOf(mGroupRoot1)).isTrue();
517         assertThat(mTda1.isDescendantOf(mGroupRoot1)).isTrue();
518         assertThat(mTda2.isDescendantOf(mGroupRoot2)).isTrue();
519         assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot1)).isTrue();
520         assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot2)).isTrue();
521     }
522 
523     @Test
testBuilder_displayAreaGroup_createFeatureOnGroup()524     public void testBuilder_displayAreaGroup_createFeatureOnGroup() {
525         final Feature feature1 = new Feature.Builder(mWms.mPolicy, "feature1",
526                 FEATURE_VENDOR_FIRST + 5)
527                 .all()
528                 .except(TYPE_STATUS_BAR)
529                 .build();
530         final Feature feature2 = new Feature.Builder(mWms.mPolicy, "feature2",
531                 FEATURE_VENDOR_FIRST + 6)
532                 .upTo(TYPE_STATUS_BAR)
533                 .and(TYPE_NAVIGATION_BAR)
534                 .build();
535         final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
536                 .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
537                         .setTaskDisplayAreas(mTaskDisplayAreaList))
538                 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
539                         mGroupRoot1)
540                         .setImeContainer(mImeContainer)
541                         .setTaskDisplayAreas(Lists.newArrayList(mTda1))
542                         .addFeature(feature1))
543                 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
544                         mGroupRoot2)
545                         .setTaskDisplayAreas(Lists.newArrayList(mTda2))
546                         .addFeature(feature2))
547                 .build(mWms);
548 
549         List<DisplayArea<? extends WindowContainer>> feature1DAs =
550                 policy.getDisplayAreas(feature1.getId());
551         List<DisplayArea<? extends WindowContainer>> feature2DAs =
552                 policy.getDisplayAreas(feature2.getId());
553         for (DisplayArea<? extends WindowContainer> da : feature1DAs) {
554             assertThat(da.isDescendantOf(mGroupRoot1)).isTrue();
555         }
556         for (DisplayArea<? extends WindowContainer> da : feature2DAs) {
557             assertThat(da.isDescendantOf(mGroupRoot2)).isTrue();
558         }
559     }
560 
561     @Test
testBuilder_addWindow_selectContainerForWindowFunc_defaultFunc()562     public void testBuilder_addWindow_selectContainerForWindowFunc_defaultFunc() {
563         final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
564                 .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
565                         .setTaskDisplayAreas(mTaskDisplayAreaList))
566                 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
567                         mGroupRoot1)
568                         .setImeContainer(mImeContainer)
569                         .setTaskDisplayAreas(Lists.newArrayList(mTda1)))
570                 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(
571                         mGroupRoot2)
572                         .setTaskDisplayAreas(Lists.newArrayList(mTda2)))
573                 .build(mWms);
574 
575         final WindowToken token = new WindowToken.Builder(mWms, mock(IBinder.class),
576                 TYPE_STATUS_BAR)
577                 .setDisplayContent(mDisplayContent)
578                 .setPersistOnEmpty(true)
579                 .setOwnerCanManageAppTokens(true)
580                 .build();
581 
582         policy.addWindow(token);
583 
584         // By default, window are always added to the root.
585         assertThat(token.isDescendantOf(mRoot)).isTrue();
586         assertThat(token.isDescendantOf(mGroupRoot1)).isFalse();
587         assertThat(token.isDescendantOf(mGroupRoot2)).isFalse();
588 
589         // When the window has options for target root id, attach it to the target root.
590         final Bundle options = new Bundle();
591         options.putInt(KEY_ROOT_DISPLAY_AREA_ID, mGroupRoot2.mFeatureId);
592         final WindowToken token2 = new WindowToken.Builder(mWms, mock(IBinder.class),
593                 TYPE_STATUS_BAR)
594                 .setDisplayContent(mDisplayContent)
595                 .setPersistOnEmpty(true)
596                 .setOwnerCanManageAppTokens(true)
597                 .setOptions(options)
598                 .build();
599         policy.addWindow(token2);
600 
601         assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue();
602     }
603 
604     @Test
testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnType()605     public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnType() {
606         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
607                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
608                         .setImeContainer(mImeContainer)
609                         .setTaskDisplayAreas(Lists.newArrayList(mTda1));
610         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
611                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
612                         .setTaskDisplayAreas(Lists.newArrayList(mTda2));
613         final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
614                 .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
615                         .setTaskDisplayAreas(mTaskDisplayAreaList))
616                 .addDisplayAreaGroupHierarchy(hierarchy1)
617                 .addDisplayAreaGroupHierarchy(hierarchy2)
618                 .setSelectRootForWindowFunc((type, options) -> {
619                     if (type == TYPE_STATUS_BAR) {
620                         return mGroupRoot1;
621                     }
622                     return mGroupRoot2;
623                 })
624                 .build(mWms);
625 
626         final WindowToken token1 = new WindowToken.Builder(mWms, mock(IBinder.class),
627                 TYPE_STATUS_BAR)
628                 .setDisplayContent(mDisplayContent)
629                 .setPersistOnEmpty(true)
630                 .setOwnerCanManageAppTokens(true)
631                 .build();
632         final WindowToken token2 = new WindowToken.Builder(mWms, mock(IBinder.class),
633                 TYPE_WALLPAPER)
634                 .setDisplayContent(mDisplayContent)
635                 .setPersistOnEmpty(true)
636                 .setOwnerCanManageAppTokens(true)
637                 .build();
638         policy.addWindow(token1);
639         policy.addWindow(token2);
640 
641         assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue();
642         assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue();
643     }
644 
645     @Test
testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnOptions()646     public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnOptions() {
647         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
648                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
649                         .setTaskDisplayAreas(mTaskDisplayAreaList);
650         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
651                 new DisplayAreaPolicyBuilder.HierarchyBuilder(
652                         mGroupRoot1)
653                         .setImeContainer(mImeContainer)
654                         .setTaskDisplayAreas(Lists.newArrayList(mTda1));
655         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
656                 new DisplayAreaPolicyBuilder.HierarchyBuilder(
657                         mGroupRoot2)
658                         .setTaskDisplayAreas(Lists.newArrayList(mTda2));
659         final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
660                 .setRootHierarchy(hierarchy0)
661                 .addDisplayAreaGroupHierarchy(hierarchy1)
662                 .addDisplayAreaGroupHierarchy(hierarchy2)
663                 .setSelectRootForWindowFunc((token, options) -> {
664                     if (options == null) {
665                         return mRoot;
666                     }
667                     if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot1.mFeatureId) {
668                         return mGroupRoot1;
669                     }
670                     if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot2.mFeatureId) {
671                         return mGroupRoot2;
672                     }
673                     return mRoot;
674                 })
675                 .build(mWms);
676 
677         final Bundle options1 = new Bundle();
678         options1.putInt("HIERARCHY_ROOT_ID", mGroupRoot1.mFeatureId);
679         final Bundle options2 = new Bundle();
680         options2.putInt("HIERARCHY_ROOT_ID", mGroupRoot2.mFeatureId);
681         final WindowToken token0 = new WindowToken.Builder(mWms, mock(IBinder.class),
682                 TYPE_STATUS_BAR)
683                 .setDisplayContent(mDisplayContent)
684                 .setPersistOnEmpty(true)
685                 .setOwnerCanManageAppTokens(true)
686                 .build();
687         final WindowToken token1 = new WindowToken.Builder(mWms, mock(IBinder.class),
688                 TYPE_STATUS_BAR)
689                 .setDisplayContent(mDisplayContent)
690                 .setPersistOnEmpty(true)
691                 .setOwnerCanManageAppTokens(true)
692                 .setOptions(options1)
693                 .build();
694         final WindowToken token2 = new WindowToken.Builder(mWms, mock(IBinder.class),
695                 TYPE_STATUS_BAR)
696                 .setDisplayContent(mDisplayContent)
697                 .setPersistOnEmpty(true)
698                 .setOwnerCanManageAppTokens(true)
699                 .setOptions(options2)
700                 .build();
701 
702         policy.addWindow(token0);
703         policy.addWindow(token1);
704         policy.addWindow(token2);
705 
706         assertThat(token0.isDescendantOf(mRoot)).isTrue();
707         assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue();
708         assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue();
709     }
710 
711     @Test
testFeatureNotThrowArrayIndexOutOfBoundsException()712     public void testFeatureNotThrowArrayIndexOutOfBoundsException() {
713         final Feature feature1 = new Feature.Builder(mWms.mPolicy, "feature1",
714                 FEATURE_VENDOR_FIRST + 5)
715                 .all()
716                 .except(TYPE_POINTER)
717                 .build();
718     }
719 
720     @Test
testGetTaskDisplayArea_DefaultFunction_NullOptions_ReturnsDefaultTda()721     public void testGetTaskDisplayArea_DefaultFunction_NullOptions_ReturnsDefaultTda() {
722         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
723                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
724                         .setTaskDisplayAreas(mTaskDisplayAreaList);
725         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
726                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
727                         .setImeContainer(mImeContainer)
728                         .setTaskDisplayAreas(Lists.newArrayList(mTda1));
729         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
730                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
731                         .setTaskDisplayAreas(Lists.newArrayList(mTda2));
732         final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
733                 .setRootHierarchy(hierarchy0)
734                 .addDisplayAreaGroupHierarchy(hierarchy1)
735                 .addDisplayAreaGroupHierarchy(hierarchy2)
736                 .build(mWms);
737 
738         final TaskDisplayArea tda = policy.getTaskDisplayArea(null /* options */);
739 
740         assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
741         assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
742     }
743 
744     @Test
testGetTaskDisplayArea_DefaultFunction_NotContainsLunchedTda_ReturnsDefaultTda()745     public void testGetTaskDisplayArea_DefaultFunction_NotContainsLunchedTda_ReturnsDefaultTda() {
746         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
747                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
748                         .setTaskDisplayAreas(mTaskDisplayAreaList);
749         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
750                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
751                         .setImeContainer(mImeContainer)
752                         .setTaskDisplayAreas(Lists.newArrayList(mTda1));
753         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
754                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
755                         .setTaskDisplayAreas(Lists.newArrayList(mTda2));
756         final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
757                 .setRootHierarchy(hierarchy0)
758                 .addDisplayAreaGroupHierarchy(hierarchy1)
759                 .addDisplayAreaGroupHierarchy(hierarchy2)
760                 .build(mWms);
761 
762         final TaskDisplayArea tda = policy.getTaskDisplayArea(new Bundle());
763 
764         assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
765         assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
766     }
767 
768     @Test
testGetTaskDisplayArea_DefaultFunction_InvalidTdaToken_ReturnsDefaultTda()769     public void testGetTaskDisplayArea_DefaultFunction_InvalidTdaToken_ReturnsDefaultTda() {
770         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
771                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
772                         .setTaskDisplayAreas(mTaskDisplayAreaList);
773         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
774                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
775                         .setImeContainer(mImeContainer)
776                         .setTaskDisplayAreas(Lists.newArrayList(mTda1));
777         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
778                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
779                         .setTaskDisplayAreas(Lists.newArrayList(mTda2));
780         final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
781                 .setRootHierarchy(hierarchy0)
782                 .addDisplayAreaGroupHierarchy(hierarchy1)
783                 .addDisplayAreaGroupHierarchy(hierarchy2)
784                 .build(mWms);
785         final ActivityOptions options = ActivityOptions.makeBasic();
786         final WindowContainerToken fakeToken = mRoot.mRemoteToken.toWindowContainerToken();
787         options.setLaunchTaskDisplayArea(fakeToken);
788 
789         final TaskDisplayArea tda = policy.getTaskDisplayArea(options.toBundle());
790 
791         assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
792         assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
793     }
794 
795     @Test(expected = IllegalArgumentException.class)
testGetTaskDisplayArea_DefaultFunction_TdaOnDifferentDisplay_ThrowException()796     public void testGetTaskDisplayArea_DefaultFunction_TdaOnDifferentDisplay_ThrowException() {
797         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
798                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
799                         .setTaskDisplayAreas(mTaskDisplayAreaList);
800         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
801                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
802                         .setImeContainer(mImeContainer)
803                         .setTaskDisplayAreas(Lists.newArrayList(mTda1));
804         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
805                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
806                         .setTaskDisplayAreas(Lists.newArrayList(mTda2));
807         final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
808                 .setRootHierarchy(hierarchy0)
809                 .addDisplayAreaGroupHierarchy(hierarchy1)
810                 .addDisplayAreaGroupHierarchy(hierarchy2)
811                 .build(mWms);
812         final TaskDisplayArea tdaOnSecondaryDisplay = mock(TaskDisplayArea.class);
813         doReturn(DEFAULT_DISPLAY + 1).when(tdaOnSecondaryDisplay).getDisplayId();
814         doReturn(tdaOnSecondaryDisplay).when(tdaOnSecondaryDisplay).asTaskDisplayArea();
815         tdaOnSecondaryDisplay.mRemoteToken = new WindowContainer.RemoteToken(tdaOnSecondaryDisplay);
816 
817         final WindowContainerToken tdaToken = tdaOnSecondaryDisplay.mRemoteToken
818                 .toWindowContainerToken();
819         final ActivityOptions options = ActivityOptions.makeBasic();
820         options.setLaunchTaskDisplayArea(tdaToken);
821         final TaskDisplayArea tda = policy.getTaskDisplayArea(options.toBundle());
822 
823         assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
824         assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
825     }
826 
827     @Test
testGetTaskDisplayArea_DefaultFunction_ContainsTdaToken_ReturnsTda()828     public void testGetTaskDisplayArea_DefaultFunction_ContainsTdaToken_ReturnsTda() {
829         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
830                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
831                         .setTaskDisplayAreas(mTaskDisplayAreaList);
832         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
833                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
834                         .setImeContainer(mImeContainer)
835                         .setTaskDisplayAreas(Lists.newArrayList(mTda1));
836         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
837                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
838                         .setTaskDisplayAreas(Lists.newArrayList(mTda2));
839         final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
840                 .setRootHierarchy(hierarchy0)
841                 .addDisplayAreaGroupHierarchy(hierarchy1)
842                 .addDisplayAreaGroupHierarchy(hierarchy2)
843                 .build(mWms);
844         final ActivityOptions options = ActivityOptions.makeBasic();
845 
846         final WindowContainerToken defaultTdaToken = mDefaultTaskDisplayArea.mRemoteToken
847                 .toWindowContainerToken();
848         options.setLaunchTaskDisplayArea(defaultTdaToken);
849         TaskDisplayArea tda = policy.getTaskDisplayArea(options.toBundle());
850 
851         assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
852         assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
853 
854         final WindowContainerToken tda1Token = mTda1.mRemoteToken.toWindowContainerToken();
855         options.setLaunchTaskDisplayArea(tda1Token);
856         tda = policy.getTaskDisplayArea(options.toBundle());
857 
858         assertThat(tda).isEqualTo(mTda1);
859 
860         final WindowContainerToken tda2Token = mTda2.mRemoteToken.toWindowContainerToken();
861         options.setLaunchTaskDisplayArea(tda2Token);
862         tda = policy.getTaskDisplayArea(options.toBundle());
863 
864         assertThat(tda).isEqualTo(mTda2);
865     }
866 
867     @Test
testBuilder_getTaskDisplayArea_setSelectTaskDisplayAreaFunc()868     public void testBuilder_getTaskDisplayArea_setSelectTaskDisplayAreaFunc() {
869         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 =
870                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)
871                         .setTaskDisplayAreas(mTaskDisplayAreaList);
872         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 =
873                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1)
874                         .setImeContainer(mImeContainer)
875                         .setTaskDisplayAreas(Lists.newArrayList(mTda1));
876         final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 =
877                 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2)
878                         .setTaskDisplayAreas(Lists.newArrayList(mTda2));
879         final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder()
880                 .setRootHierarchy(hierarchy0)
881                 .setSelectTaskDisplayAreaFunc((options) -> {
882                     if (options == null) {
883                         return mDefaultTaskDisplayArea;
884                     }
885                     final int tdaFeatureId =
886                             options.getInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID);
887                     if (tdaFeatureId == mTda1.mFeatureId) {
888                         return mTda1;
889                     }
890                     if (tdaFeatureId == mTda2.mFeatureId) {
891                         return mTda2;
892                     }
893                     return mDefaultTaskDisplayArea;
894                 })
895                 .addDisplayAreaGroupHierarchy(hierarchy1)
896                 .addDisplayAreaGroupHierarchy(hierarchy2)
897                 .build(mWms);
898 
899         TaskDisplayArea tda = policy.getTaskDisplayArea(null /* options */);
900 
901         assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
902         assertThat(tda).isEqualTo(policy.getDefaultTaskDisplayArea());
903 
904         final Bundle options = new Bundle();
905         options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, -1);
906         tda = policy.getTaskDisplayArea(options);
907         assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
908 
909         options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, mDefaultTaskDisplayArea.mFeatureId);
910         tda = policy.getTaskDisplayArea(options);
911         assertThat(tda).isEqualTo(mDefaultTaskDisplayArea);
912 
913         options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, mTda1.mFeatureId);
914         tda = policy.getTaskDisplayArea(options);
915         assertThat(tda).isEqualTo(mTda1);
916 
917         options.putInt(KEY_LAUNCH_TASK_DISPLAY_AREA_FEATURE_ID, mTda2.mFeatureId);
918         tda = policy.getTaskDisplayArea(options);
919         assertThat(tda).isEqualTo(mTda2);
920     }
921 
resourcesWithProvider(String provider)922     private static Resources resourcesWithProvider(String provider) {
923         Resources mock = mock(Resources.class);
924         when(mock.getString(
925                 com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider))
926                 .thenReturn(provider);
927         return mock;
928     }
929 
mapValues(Map<K, V> zSets, Function<V, R> f)930     private <K, V, R> Map<K, R> mapValues(Map<K, V> zSets, Function<V, R> f) {
931         return zSets.entrySet().stream().collect(Collectors.toMap(
932                 Map.Entry::getKey,
933                 e -> f.apply(e.getValue())));
934     }
935 
collectLeafAreas(DisplayArea<?> root)936     private List<DisplayArea<?>> collectLeafAreas(DisplayArea<?> root) {
937         ArrayList<DisplayArea<?>> leafs = new ArrayList<>();
938         traverseLeafAreas(root, leafs::add);
939         return leafs;
940     }
941 
calculateZSets( DisplayAreaPolicyBuilder.Result policy, DisplayArea.Tokens ime, TaskDisplayArea taskDisplayArea)942     private Map<DisplayArea<?>, Set<Integer>> calculateZSets(
943             DisplayAreaPolicyBuilder.Result policy,
944             DisplayArea.Tokens ime,
945             TaskDisplayArea taskDisplayArea) {
946         Map<DisplayArea<?>, Set<Integer>> zSets = new HashMap<>();
947         int[] types = {TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_PRESENTATION,
948                 TYPE_APPLICATION_OVERLAY};
949         for (int type : types) {
950             WindowToken token = tokenOfType(type);
951             recordLayer(policy.findAreaForToken(token), token.getWindowLayerFromType(), zSets);
952         }
953         recordLayer(taskDisplayArea, APPLICATION_LAYER, zSets);
954         recordLayer(ime, mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD), zSets);
955         return zSets;
956     }
957 
recordLayer(DisplayArea<?> area, int layer, Map<DisplayArea<?>, Set<Integer>> zSets)958     private void recordLayer(DisplayArea<?> area, int layer,
959             Map<DisplayArea<?>,  Set<Integer>> zSets) {
960         zSets.computeIfAbsent(area, k -> new HashSet<>()).add(layer);
961     }
962 
descendantOfOneOf(List<? extends WindowContainer> expected)963     private Matcher<WindowContainer> descendantOfOneOf(List<? extends WindowContainer> expected) {
964         return new CustomTypeSafeMatcher<WindowContainer>("descendant of one of " + expected) {
965             @Override
966             protected boolean matchesSafely(WindowContainer actual) {
967                 for (WindowContainer expected : expected) {
968                     WindowContainer candidate = actual;
969                     while (candidate != null && candidate.getParent() != candidate) {
970                         if (candidate.getParent() == expected) {
971                             return true;
972                         }
973                         candidate = candidate.getParent();
974                     }
975                 }
976                 return false;
977             }
978 
979             @Override
980             protected void describeMismatchSafely(WindowContainer item,
981                     Description description) {
982                 description.appendText("was ").appendValue(item);
983                 while (item != null && item.getParent() != item) {
984                     item = item.getParent();
985                     description.appendText(", child of ").appendValue(item);
986                 }
987             }
988         };
989     }
990 
991     private boolean isSibling(WindowContainer da1, WindowContainer da2) {
992         return da1.getParent() != null && da1.getParent() == da2.getParent();
993     }
994 
995     private WindowToken tokenOfType(int type) {
996         return new WindowToken.Builder(mWms, new Binder(), type)
997                 .setDisplayContent(mDisplayContent).build();
998     }
999 
1000     private static void assertMatchLayerOrder(List<DisplayArea<?>> actualOrder,
1001             Map<DisplayArea<?>, Integer> areaToLayerMap) {
1002         for (int i = 0; i < actualOrder.size() - 1; i++) {
1003             DisplayArea<?> curr = actualOrder.get(i);
1004             DisplayArea<?> next = actualOrder.get(i + 1);
1005             assertThat(areaToLayerMap.get(curr)).isLessThan(areaToLayerMap.get(next));
1006         }
1007     }
1008 
1009     private static void traverseLeafAreas(DisplayArea<?> root, Consumer<DisplayArea<?>> consumer) {
1010         boolean leaf = true;
1011         for (int i = 0; i < root.getChildCount(); i++) {
1012             WindowContainer child = root.getChildAt(i);
1013             if (child instanceof DisplayArea<?>) {
1014                 traverseLeafAreas((DisplayArea<?>) child, consumer);
1015                 leaf = false;
1016             }
1017         }
1018         if (leaf) {
1019             consumer.accept(root);
1020         }
1021     }
1022 
1023     static class SurfacelessDisplayAreaRoot extends RootDisplayArea {
1024 
1025         SurfacelessDisplayAreaRoot(WindowManagerService wms) {
1026             this(wms, "SurfacelessDisplayAreaRoot", FEATURE_ROOT);
1027         }
1028 
1029         SurfacelessDisplayAreaRoot(WindowManagerService wms, String name, int featureId) {
1030             super(wms, name, featureId);
1031         }
1032 
1033         @Override
1034         SurfaceControl.Builder makeChildSurface(WindowContainer child) {
1035             return new MockSurfaceControlBuilder();
1036         }
1037     }
1038 
1039 }
1040