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.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
22 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
24 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
25 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
26 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
27 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
28 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
29 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
30 
31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
35 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
36 import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
37 import static com.android.server.wm.DisplayArea.Type.ANY;
38 import static com.android.server.wm.DisplayArea.Type.BELOW_TASKS;
39 import static com.android.server.wm.DisplayArea.Type.checkChild;
40 import static com.android.server.wm.DisplayArea.Type.checkSiblings;
41 import static com.android.server.wm.DisplayArea.Type.typeOf;
42 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
43 import static com.android.server.wm.WindowContainer.POSITION_TOP;
44 import static com.android.server.wm.testing.Assert.assertThrows;
45 
46 import static com.google.common.truth.Truth.assertThat;
47 
48 import static org.junit.Assert.assertEquals;
49 import static org.junit.Assert.assertNull;
50 import static org.mockito.ArgumentMatchers.any;
51 import static org.mockito.ArgumentMatchers.argThat;
52 import static org.mockito.ArgumentMatchers.eq;
53 import static org.mockito.Mockito.mock;
54 import static org.mockito.Mockito.times;
55 import static org.mockito.Mockito.verify;
56 import static org.mockito.Mockito.verifyZeroInteractions;
57 import static org.mockito.Mockito.when;
58 
59 import android.content.pm.ActivityInfo;
60 import android.content.res.Configuration;
61 import android.graphics.Rect;
62 import android.os.Binder;
63 import android.os.IBinder;
64 import android.platform.test.annotations.Presubmit;
65 import android.view.SurfaceControl;
66 import android.view.View;
67 import android.view.WindowManager;
68 import android.window.DisplayAreaInfo;
69 import android.window.IDisplayAreaOrganizer;
70 
71 import com.google.android.collect.Lists;
72 
73 import org.junit.Test;
74 import org.junit.runner.RunWith;
75 
76 import java.util.ArrayList;
77 import java.util.List;
78 import java.util.function.BiFunction;
79 import java.util.function.Consumer;
80 import java.util.function.Function;
81 import java.util.function.Predicate;
82 
83 /**
84  * Tests for the {@link DisplayArea} container.
85  *
86  * Build/Install/Run:
87  *  atest WmTests:DisplayAreaTest
88  */
89 @Presubmit
90 @RunWith(WindowTestRunner.class)
91 public class DisplayAreaTest extends WindowTestsBase {
92 
93     @Test
testDisplayArea_positionChanged_throwsIfIncompatibleChild()94     public void testDisplayArea_positionChanged_throwsIfIncompatibleChild() {
95         DisplayArea<WindowContainer> parent = new DisplayArea<>(mWm, BELOW_TASKS, "Parent");
96         DisplayArea<WindowContainer> child = new DisplayArea<>(mWm, ANY, "Child");
97 
98         assertThrows(IllegalStateException.class, () -> parent.addChild(child, 0));
99     }
100 
101     @Test
testType_typeOf()102     public void testType_typeOf() {
103         assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(mWm, ABOVE_TASKS, "test")));
104         assertEquals(ANY, typeOf(new DisplayArea<>(mWm, ANY, "test")));
105         assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(mWm, BELOW_TASKS, "test")));
106 
107         assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_APPLICATION_OVERLAY)));
108         assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_PRESENTATION)));
109         assertEquals(BELOW_TASKS, typeOf(createWindowToken(TYPE_WALLPAPER)));
110 
111         assertThrows(IllegalArgumentException.class, () -> typeOf(mock(ActivityRecord.class)));
112         assertThrows(IllegalArgumentException.class, () -> typeOf(mock(WindowContainer.class)));
113     }
114 
115     @Test
testType_checkSiblings()116     public void testType_checkSiblings() {
117         checkSiblings(BELOW_TASKS, BELOW_TASKS);
118         checkSiblings(BELOW_TASKS, ANY);
119         checkSiblings(BELOW_TASKS, ABOVE_TASKS);
120         checkSiblings(ANY, ABOVE_TASKS);
121         checkSiblings(ABOVE_TASKS, ABOVE_TASKS);
122         checkSiblings(ANY, ANY);
123 
124         assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, BELOW_TASKS));
125         assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, ANY));
126         assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, BELOW_TASKS));
127     }
128 
129     @Test
testType_checkChild()130     public void testType_checkChild() {
131         checkChild(ANY, ANY);
132         checkChild(ANY, ABOVE_TASKS);
133         checkChild(ANY, BELOW_TASKS);
134         checkChild(ABOVE_TASKS, ABOVE_TASKS);
135         checkChild(BELOW_TASKS, BELOW_TASKS);
136 
137         assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, BELOW_TASKS));
138         assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, ANY));
139         assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ABOVE_TASKS));
140         assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ANY));
141     }
142 
143     @Test
testAsDisplayArea()144     public void testAsDisplayArea() {
145         final WindowContainer windowContainer = new WindowContainer(mWm);
146         final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(mWm, ANY, "DA");
147         final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(null /* displayContent */,
148                 mWm, "TDA", FEATURE_DEFAULT_TASK_CONTAINER);
149 
150         assertThat(windowContainer.asDisplayArea()).isNull();
151         assertThat(displayArea.asDisplayArea()).isEqualTo(displayArea);
152         assertThat(taskDisplayArea.asDisplayArea()).isEqualTo(taskDisplayArea);
153     }
154 
155     @Test
testForAllTaskDisplayAreas_onlyTraversesDisplayAreaOfTypeAny()156     public void testForAllTaskDisplayAreas_onlyTraversesDisplayAreaOfTypeAny() {
157         final RootDisplayArea root =
158                 new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm);
159         final Predicate<TaskDisplayArea> callback0 = tda -> false;
160         final Consumer<TaskDisplayArea> callback1 = tda -> { };
161         final BiFunction<TaskDisplayArea, Integer, Integer> callback2 = (tda, result) -> result;
162         final Function<TaskDisplayArea, TaskDisplayArea> callback3 = tda -> null;
163 
164         // Don't traverse the child if the current DA has type BELOW_TASKS
165         final DisplayArea<WindowContainer> da1 = new DisplayArea<>(mWm, BELOW_TASKS, "DA1");
166         final DisplayArea<WindowContainer> da2 = new DisplayArea<>(mWm, BELOW_TASKS, "DA2");
167         root.addChild(da1, POSITION_BOTTOM);
168         da1.addChild(da2, POSITION_TOP);
169         spyOn(da2);
170 
171         da1.forAllTaskDisplayAreas(callback0);
172         da1.forAllTaskDisplayAreas(callback1);
173         da1.reduceOnAllTaskDisplayAreas(callback2, 0);
174         da1.getItemFromTaskDisplayAreas(callback3);
175 
176         verifyZeroInteractions(da2);
177 
178         // Traverse the child if the current DA has type ANY
179         final DisplayArea<WindowContainer> da3 = new DisplayArea<>(mWm, ANY, "DA3");
180         final DisplayArea<WindowContainer> da4 = new DisplayArea<>(mWm, ANY, "DA4");
181         root.addChild(da3, POSITION_TOP);
182         da3.addChild(da4, POSITION_TOP);
183         spyOn(da4);
184 
185         da3.forAllTaskDisplayAreas(callback0);
186         da3.forAllTaskDisplayAreas(callback1);
187         da3.reduceOnAllTaskDisplayAreas(callback2, 0);
188         da3.getItemFromTaskDisplayAreas(callback3);
189 
190         verify(da4).forAllTaskDisplayAreas(callback0, true /* traverseTopToBottom */);
191         verify(da4).forAllTaskDisplayAreas(callback1, true /* traverseTopToBottom */);
192         verify(da4).reduceOnAllTaskDisplayAreas(callback2, 0 /* initValue */,
193                 true /* traverseTopToBottom */);
194         verify(da4).getItemFromTaskDisplayAreas(
195                 callback3, true /* traverseTopToBottom */);
196 
197         // Don't traverse the child if the current DA has type ABOVE_TASKS
198         final DisplayArea<WindowContainer> da5 = new DisplayArea<>(mWm, ABOVE_TASKS, "DA5");
199         final DisplayArea<WindowContainer> da6 = new DisplayArea<>(mWm, ABOVE_TASKS, "DA6");
200         root.addChild(da5, POSITION_TOP);
201         da5.addChild(da6, POSITION_TOP);
202         spyOn(da6);
203 
204         da5.forAllTaskDisplayAreas(callback0);
205         da5.forAllTaskDisplayAreas(callback1);
206         da5.reduceOnAllTaskDisplayAreas(callback2, 0);
207         da5.getItemFromTaskDisplayAreas(callback3);
208 
209         verifyZeroInteractions(da6);
210     }
211 
212     @Test
testForAllTaskDisplayAreas_appliesOnTaskDisplayAreaInOrder()213     public void testForAllTaskDisplayAreas_appliesOnTaskDisplayAreaInOrder() {
214         final RootDisplayArea root =
215                 new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm);
216         final DisplayArea<DisplayArea> da1 =
217                 new DisplayArea<>(mWm, ANY, "DA1");
218         final DisplayArea<DisplayArea> da2 =
219                 new DisplayArea<>(mWm, ANY, "DA2");
220         final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
221                 mWm, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
222         final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
223                 mWm, "TDA2", FEATURE_VENDOR_FIRST);
224         final TaskDisplayArea tda3 = new TaskDisplayArea(null /* displayContent */,
225                 mWm, "TDA3", FEATURE_VENDOR_FIRST + 1);
226         root.addChild(da1, POSITION_TOP);
227         root.addChild(da2, POSITION_TOP);
228         da1.addChild(tda1, POSITION_TOP);
229         da2.addChild(tda2, POSITION_TOP);
230         da2.addChild(tda3, POSITION_TOP);
231 
232         /*  The hierarchy looks like this
233             Root
234               - DA1
235                 - TDA1 ------ bottom
236               - DA2
237                 - TDA2
238                 - TDA3 ------ top
239          */
240 
241         // Test forAllTaskDisplayAreas(Consumer<TaskDisplayArea>)
242         List<TaskDisplayArea> actualOrder = new ArrayList<>();
243         root.forAllTaskDisplayAreas(tda -> {
244             actualOrder.add(tda);
245         });
246 
247         assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
248 
249         // Test forAllTaskDisplayAreas(Consumer<TaskDisplayArea>, boolean)
250         actualOrder.clear();
251         root.forAllTaskDisplayAreas(tda -> {
252             actualOrder.add(tda);
253         }, false /* traverseTopToBottom */);
254 
255         assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
256 
257         // Test forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean>)
258         actualOrder.clear();
259         root.forAllTaskDisplayAreas(tda -> {
260             actualOrder.add(tda);
261             return false;
262         });
263 
264         assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
265 
266         // Test forAllTaskDisplayAreas(Function<TaskDisplayArea, Boolean>, boolean)
267         actualOrder.clear();
268         root.forAllTaskDisplayAreas(tda -> {
269             actualOrder.add(tda);
270             return false;
271         }, false /* traverseTopToBottom */);
272 
273         assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
274 
275         // Test forAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R>, R)
276         actualOrder.clear();
277         root.reduceOnAllTaskDisplayAreas((tda, result) -> {
278             actualOrder.add(tda);
279             return result;
280         }, 0 /* initValue */);
281 
282         assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
283 
284         // Test forAllTaskDisplayAreas(BiFunction<TaskDisplayArea, R, R>, R, boolean)
285         actualOrder.clear();
286         root.reduceOnAllTaskDisplayAreas((tda, result) -> {
287             actualOrder.add(tda);
288             return result;
289         }, 0 /* initValue */, false /* traverseTopToBottom */);
290 
291         assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
292 
293         // Test <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback)
294         actualOrder.clear();
295         root.getItemFromTaskDisplayAreas(tda -> {
296             actualOrder.add(tda);
297             return null;
298         });
299 
300         assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda3, tda2, tda1));
301 
302         // Test <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, boolean)
303         actualOrder.clear();
304         root.getItemFromTaskDisplayAreas(tda -> {
305             actualOrder.add(tda);
306             return null;
307         }, false /* traverseTopToBottom */);
308 
309         assertThat(actualOrder).isEqualTo(Lists.newArrayList(tda1, tda2, tda3));
310     }
311 
312     @Test
testForAllTaskDisplayAreas_returnsWhenCallbackReturnTrue()313     public void testForAllTaskDisplayAreas_returnsWhenCallbackReturnTrue() {
314         final RootDisplayArea root =
315                 new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm);
316         final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
317                 mWm, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
318         final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
319                 mWm, "TDA2", FEATURE_VENDOR_FIRST);
320         root.addChild(tda1, POSITION_TOP);
321         root.addChild(tda2, POSITION_TOP);
322 
323         /*  The hierarchy looks like this
324             Root
325               - TDA1 ------ bottom
326               - TDA2 ------ top
327          */
328 
329         root.forAllTaskDisplayAreas(tda -> {
330             assertThat(tda).isEqualTo(tda2);
331             return true;
332         });
333 
334         root.forAllTaskDisplayAreas(tda -> {
335             assertThat(tda).isEqualTo(tda1);
336             return true;
337         }, false /* traverseTopToBottom */);
338     }
339 
340     @Test
testReduceOnAllTaskDisplayAreas_returnsTheAccumulativeResult()341     public void testReduceOnAllTaskDisplayAreas_returnsTheAccumulativeResult() {
342         final RootDisplayArea root =
343                 new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm);
344         final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
345                 mWm, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
346         final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
347                 mWm, "TDA2", FEATURE_VENDOR_FIRST);
348         root.addChild(tda1, POSITION_TOP);
349         root.addChild(tda2, POSITION_TOP);
350 
351         /*  The hierarchy looks like this
352             Root
353               - TDA1 ------ bottom
354               - TDA2 ------ top
355          */
356 
357         String accumulativeName = root.reduceOnAllTaskDisplayAreas((tda, result) ->
358                 result + tda.getName(), "" /* initValue */);
359         assertThat(accumulativeName).isEqualTo("TDA2TDA1");
360 
361         accumulativeName = root.reduceOnAllTaskDisplayAreas((tda, result) ->
362                 result + tda.getName(), "" /* initValue */, false /* traverseTopToBottom */);
363         assertThat(accumulativeName).isEqualTo("TDA1TDA2");
364     }
365 
366     @Test
testGetItemFromTaskDisplayAreas_returnsWhenCallbackReturnNotNull()367     public void testGetItemFromTaskDisplayAreas_returnsWhenCallbackReturnNotNull() {
368         final RootDisplayArea root =
369                 new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm);
370         final TaskDisplayArea tda1 = new TaskDisplayArea(null /* displayContent */,
371                 mWm, "TDA1", FEATURE_DEFAULT_TASK_CONTAINER);
372         final TaskDisplayArea tda2 = new TaskDisplayArea(null /* displayContent */,
373                 mWm, "TDA2", FEATURE_VENDOR_FIRST);
374         root.addChild(tda1, POSITION_TOP);
375         root.addChild(tda2, POSITION_TOP);
376 
377         /*  The hierarchy looks like this
378             Root
379               - TDA1 ------ bottom
380               - TDA2 ------ top
381          */
382 
383         TaskDisplayArea result = root.getItemFromTaskDisplayAreas(tda -> {
384             assertThat(tda).isEqualTo(tda2);
385             return tda;
386         });
387 
388         assertThat(result).isEqualTo(tda2);
389 
390         result = root.getItemFromTaskDisplayAreas(tda -> {
391             assertThat(tda).isEqualTo(tda1);
392             return tda;
393         }, false /* traverseTopToBottom */);
394 
395         assertThat(result).isEqualTo(tda1);
396     }
397 
398     @Test
testSetMaxBounds()399     public void testSetMaxBounds() {
400         final Rect parentBounds = new Rect(0, 0, 100, 100);
401         final Rect childBounds1 = new Rect(parentBounds.left, parentBounds.top,
402                 parentBounds.right / 2, parentBounds.bottom);
403         final Rect childBounds2 = new Rect(parentBounds.right / 2, parentBounds.top,
404                 parentBounds.right, parentBounds.bottom);
405         TestDisplayArea parentDa = new TestDisplayArea(mWm, parentBounds, "Parent");
406         TestDisplayArea childDa1 = new TestDisplayArea(mWm, childBounds1, "Child1");
407         TestDisplayArea childDa2 = new TestDisplayArea(mWm, childBounds2, "Child2");
408         parentDa.addChild(childDa1, 0);
409         parentDa.addChild(childDa2, 1);
410 
411         assertEquals(parentBounds, parentDa.getMaxBounds());
412         assertEquals(childBounds1, childDa1.getMaxBounds());
413         assertEquals(childBounds2, childDa2.getMaxBounds());
414 
415         final WindowToken windowToken = createWindowToken(TYPE_APPLICATION);
416         childDa1.addChild(windowToken, 0);
417 
418         assertEquals("DisplayArea's children must have the same max bounds as itself",
419                 childBounds1, windowToken.getMaxBounds());
420     }
421 
422     @Test
testRestrictAppBoundsToOverrideBounds()423     public void testRestrictAppBoundsToOverrideBounds() {
424         final RootDisplayArea root =
425                 new DisplayAreaPolicyBuilderTest.SurfacelessDisplayAreaRoot(mWm);
426         final DisplayArea<DisplayArea> da = new DisplayArea<>(mWm, ANY, "Test_DA");
427         root.addChild(da, POSITION_TOP);
428         final Rect displayBounds = new Rect(0, 0, 1800, 2800);
429         final Rect displayAppBounds = new Rect(0, 100, 1800, 2800);
430         final Rect daBounds = new Rect(0, 1400, 1800, 2800);
431         root.setBounds(displayBounds);
432 
433         // DA inherit parent app bounds.
434         final Configuration displayConfig = new Configuration();
435         displayConfig.windowConfiguration.setAppBounds(displayAppBounds);
436         root.onRequestedOverrideConfigurationChanged(displayConfig);
437 
438         assertEquals(displayAppBounds, da.getConfiguration().windowConfiguration.getAppBounds());
439 
440         // Restrict DA appBounds to override Bounds
441         da.setBounds(daBounds);
442 
443         final Rect expectedDaAppBounds = new Rect(daBounds);
444         expectedDaAppBounds.intersect(displayAppBounds);
445         assertEquals(expectedDaAppBounds, da.getConfiguration().windowConfiguration.getAppBounds());
446     }
447 
448     @Test
testGetOrientation()449     public void testGetOrientation() {
450         final DisplayArea.Tokens area = new DisplayArea.Tokens(mWm, ABOVE_TASKS, "test");
451         final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY);
452         spyOn(token);
453         doReturn(mock(DisplayContent.class)).when(token).getDisplayContent();
454         doNothing().when(token).setParent(any());
455         final WindowState win = createWindowState(token);
456         spyOn(win);
457         doNothing().when(win).setParent(any());
458         win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
459         token.addChild(win, 0);
460         area.addChild(token);
461 
462         doReturn(true).when(win).isVisible();
463 
464         assertEquals("Visible window can request orientation",
465                 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
466                 area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR));
467 
468         doReturn(false).when(win).isVisible();
469 
470         assertEquals("Invisible window cannot request orientation",
471                 ActivityInfo.SCREEN_ORIENTATION_NOSENSOR,
472                 area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR));
473     }
474 
475     @Test
testSetIgnoreOrientationRequest_notCallSuperOnDescendantOrientationChanged()476     public void testSetIgnoreOrientationRequest_notCallSuperOnDescendantOrientationChanged() {
477         final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
478         final Task stack =
479                 new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
480         final ActivityRecord activity = stack.getTopNonFinishingActivity();
481 
482         tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
483 
484         activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
485 
486         verify(tda).onDescendantOrientationChanged(any());
487         verify(mDisplayContent, never()).onDescendantOrientationChanged(any());
488 
489         tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
490         activity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
491 
492         verify(tda, times(2)).onDescendantOrientationChanged(any());
493         verify(mDisplayContent).onDescendantOrientationChanged(any());
494     }
495 
496     @Test
testSetIgnoreOrientationRequest_callSuperOnDescendantOrientationChangedNoSensor()497     public void testSetIgnoreOrientationRequest_callSuperOnDescendantOrientationChangedNoSensor() {
498         final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
499         final Task stack =
500                 new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
501         final ActivityRecord activity = stack.getTopNonFinishingActivity();
502 
503         tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
504 
505         activity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
506 
507         verify(tda).onDescendantOrientationChanged(any());
508         verify(mDisplayContent).onDescendantOrientationChanged(any());
509 
510         tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
511         activity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
512 
513         verify(tda).onDescendantOrientationChanged(any());
514         verify(mDisplayContent).onDescendantOrientationChanged(any());
515     }
516 
517     @Test
testSetIgnoreOrientationRequest_callSuperOnDescendantOrientationChangedLocked()518     public void testSetIgnoreOrientationRequest_callSuperOnDescendantOrientationChangedLocked() {
519         final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
520         final Task stack =
521                 new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
522         final ActivityRecord activity = stack.getTopNonFinishingActivity();
523 
524         tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
525 
526         activity.setRequestedOrientation(SCREEN_ORIENTATION_LOCKED);
527 
528         verify(tda).onDescendantOrientationChanged(any());
529         verify(mDisplayContent).onDescendantOrientationChanged(any());
530 
531         tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
532         activity.setRequestedOrientation(SCREEN_ORIENTATION_LOCKED);
533 
534         verify(tda).onDescendantOrientationChanged(any());
535         verify(mDisplayContent).onDescendantOrientationChanged(any());
536     }
537 
538     @Test
testGetOrientationRequestingTaskDisplayArea_updateOrientationTaskDisplayArea()539     public void testGetOrientationRequestingTaskDisplayArea_updateOrientationTaskDisplayArea() {
540         final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
541         final Task stack =
542                 new TaskBuilder(mSupervisor).setOnTop(!ON_TOP).setCreateActivity(true).build();
543         final ActivityRecord activity = stack.getTopNonFinishingActivity();
544 
545         mDisplayContent.setFocusedApp(activity);
546         assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda);
547 
548         // TDA is no longer handling orientation request, clear the last focused TDA.
549         tda.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
550 
551         assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda);
552 
553         // TDA now handles orientation request, update last focused TDA based on the focused app.
554         tda.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
555 
556         assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda);
557     }
558 
559     @Test
testDisplayContentUpdateDisplayAreaOrganizers_onDisplayAreaAppeared()560     public void testDisplayContentUpdateDisplayAreaOrganizers_onDisplayAreaAppeared() {
561         final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
562                 mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST);
563         final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
564         spyOn(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController);
565         when(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController
566                 .getOrganizerByFeature(FEATURE_VENDOR_FIRST))
567                 .thenReturn(mockDisplayAreaOrganizer);
568 
569         mDisplayContent.addChild(displayArea, 0);
570         mDisplayContent.updateDisplayAreaOrganizers();
571 
572         assertEquals(mockDisplayAreaOrganizer, displayArea.mOrganizer);
573         verify(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController)
574                 .onDisplayAreaAppeared(
575                         eq(mockDisplayAreaOrganizer),
576                         argThat(it -> it == displayArea && it.getSurfaceControl() != null));
577     }
578 
579     @Test
testRemoveImmediately_onDisplayAreaVanished()580     public void testRemoveImmediately_onDisplayAreaVanished() {
581         final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
582                 mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST);
583         final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
584         doReturn(mock(IBinder.class)).when(mockDisplayAreaOrganizer).asBinder();
585         displayArea.mOrganizer = mockDisplayAreaOrganizer;
586         spyOn(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController);
587         mDisplayContent.addChild(displayArea, 0);
588 
589         displayArea.removeImmediately();
590 
591         assertNull(displayArea.mOrganizer);
592         verify(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController)
593                 .onDisplayAreaVanished(mockDisplayAreaOrganizer, displayArea);
594     }
595 
596     @Test
testGetDisplayAreaInfo()597     public void testGetDisplayAreaInfo() {
598         final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
599                 mWm, BELOW_TASKS, "NewArea", FEATURE_VENDOR_FIRST);
600         mDisplayContent.addChild(displayArea, 0);
601         final DisplayAreaInfo info = displayArea.getDisplayAreaInfo();
602 
603         assertThat(info.token).isEqualTo(displayArea.mRemoteToken.toWindowContainerToken());
604         assertThat(info.configuration).isEqualTo(displayArea.getConfiguration());
605         assertThat(info.displayId).isEqualTo(mDisplayContent.getDisplayId());
606         assertThat(info.featureId).isEqualTo(displayArea.mFeatureId);
607         assertThat(info.rootDisplayAreaId).isEqualTo(mDisplayContent.mFeatureId);
608 
609         final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea();
610         final int tdaIndex = tda.getParent().mChildren.indexOf(tda);
611         final RootDisplayArea root =
612                 new DisplayAreaGroup(mWm, "TestRoot", FEATURE_VENDOR_FIRST + 1);
613         mDisplayContent.addChild(root, tdaIndex + 1);
614         displayArea.reparent(root, 0);
615 
616         final DisplayAreaInfo info2 = displayArea.getDisplayAreaInfo();
617 
618         assertThat(info2.rootDisplayAreaId).isEqualTo(root.mFeatureId);
619     }
620 
621     @Test
testRegisterUnregisterOrganizer()622     public void testRegisterUnregisterOrganizer() {
623         final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
624         doReturn(mock(IBinder.class)).when(mockDisplayAreaOrganizer).asBinder();
625         final DisplayAreaOrganizerController controller =
626                 mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController;
627         controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST);
628         controller.unregisterOrganizer(mockDisplayAreaOrganizer);
629         controller.registerOrganizer(mockDisplayAreaOrganizer, FEATURE_VENDOR_FIRST);
630     }
631 
632     @Test
testSetAlwaysOnTop_movesDisplayAreaToTop()633     public void testSetAlwaysOnTop_movesDisplayAreaToTop() {
634         final Rect bounds = new Rect(0, 0, 100, 100);
635         DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent");
636         parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
637         DisplayArea<WindowContainer> child1 = new TestDisplayArea(mWm, bounds, "Child1");
638         child1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
639         DisplayArea<WindowContainer> child2 = new TestDisplayArea(mWm, bounds, "Child2");
640         child2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
641         parent.addChild(child2, 0);
642         parent.addChild(child1, 1);
643 
644         child2.setAlwaysOnTop(true);
645 
646         assertEquals(parent.getChildAt(1), child2);
647         assertThat(child2.isAlwaysOnTop()).isTrue();
648     }
649 
650     @Test
testDisplayAreaRequestsTopPosition_alwaysOnTopSiblingExists_doesNotMoveToTop()651     public void testDisplayAreaRequestsTopPosition_alwaysOnTopSiblingExists_doesNotMoveToTop() {
652         final Rect bounds = new Rect(0, 0, 100, 100);
653         DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent");
654         parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
655         DisplayArea<WindowContainer> alwaysOnTopChild = new TestDisplayArea(mWm, bounds,
656                 "AlwaysOnTopChild");
657         alwaysOnTopChild.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
658         DisplayArea<WindowContainer> child = new TestDisplayArea(mWm, bounds, "Child");
659         child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
660         parent.addChild(alwaysOnTopChild, 0);
661         parent.addChild(child, 1);
662         alwaysOnTopChild.setAlwaysOnTop(true);
663 
664         parent.positionChildAt(POSITION_TOP, child, false /* includingParents */);
665 
666         assertEquals(parent.getChildAt(1), alwaysOnTopChild);
667         assertEquals(parent.getChildAt(0), child);
668     }
669 
670     @Test
testAlwaysOnTopDisplayArea_requestsNonTopLocation_doesNotMove()671     public void testAlwaysOnTopDisplayArea_requestsNonTopLocation_doesNotMove() {
672         final Rect bounds = new Rect(0, 0, 100, 100);
673         DisplayArea<WindowContainer> parent = new TestDisplayArea(mWm, bounds, "Parent");
674         parent.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
675         DisplayArea<WindowContainer> alwaysOnTopChild = new TestDisplayArea(mWm, bounds,
676                 "AlwaysOnTopChild");
677         alwaysOnTopChild.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
678         DisplayArea<WindowContainer> child = new TestDisplayArea(mWm, bounds, "Child");
679         child.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
680         parent.addChild(alwaysOnTopChild, 0);
681         parent.addChild(child, 1);
682         alwaysOnTopChild.setAlwaysOnTop(true);
683 
684         parent.positionChildAt(POSITION_BOTTOM, alwaysOnTopChild, false /* includingParents */);
685 
686         assertEquals(parent.getChildAt(1), alwaysOnTopChild);
687         assertEquals(parent.getChildAt(0), child);
688     }
689 
690     private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
TestDisplayArea(WindowManagerService wms, Rect bounds, String name)691         private TestDisplayArea(WindowManagerService wms, Rect bounds, String name) {
692             super(wms, ANY, name);
693             setBounds(bounds);
694         }
695 
696         @Override
makeChildSurface(WindowContainer child)697         SurfaceControl.Builder makeChildSurface(WindowContainer child) {
698             return new MockSurfaceControlBuilder();
699         }
700     }
701 
createWindowState(WindowToken token)702     private WindowState createWindowState(WindowToken token) {
703         return new WindowState(mWm, mock(Session.class), new TestIWindow(), token,
704                 null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(),
705                 View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */,
706                 false /* ownerCanAddInternalSystemWindow */);
707     }
708 
createWindowToken(int type)709     private WindowToken createWindowToken(int type) {
710         return new WindowToken.Builder(mWm, new Binder(), type).build();
711     }
712 }
713