1 /*
2  * Copyright (C) 2018 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.InsetsState.ITYPE_CLIMATE_BAR;
20 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
21 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
22 import static android.view.InsetsState.ITYPE_STATUS_BAR;
23 import static android.view.InsetsState.ITYPE_TOP_GESTURES;
24 import static android.view.Surface.ROTATION_0;
25 import static android.view.Surface.ROTATION_270;
26 import static android.view.Surface.ROTATION_90;
27 import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION;
28 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
29 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
30 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
31 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
32 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
33 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
34 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
35 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
36 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
37 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
38 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
39 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
40 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
41 import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM;
42 import static android.view.WindowManagerPolicyConstants.ALT_BAR_LEFT;
43 import static android.view.WindowManagerPolicyConstants.ALT_BAR_RIGHT;
44 import static android.view.WindowManagerPolicyConstants.ALT_BAR_TOP;
45 
46 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
47 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
48 
49 import static org.hamcrest.Matchers.is;
50 import static org.junit.Assert.assertEquals;
51 import static org.junit.Assert.assertNotEquals;
52 import static org.junit.Assert.assertThat;
53 import static org.mockito.ArgumentMatchers.any;
54 import static org.mockito.ArgumentMatchers.eq;
55 import static org.mockito.Mockito.doNothing;
56 import static org.mockito.Mockito.spy;
57 import static org.testng.Assert.expectThrows;
58 
59 import android.graphics.Insets;
60 import android.graphics.Rect;
61 import android.platform.test.annotations.Presubmit;
62 import android.util.Pair;
63 import android.util.SparseArray;
64 import android.view.DisplayCutout;
65 import android.view.DisplayInfo;
66 import android.view.Gravity;
67 import android.view.InsetsState;
68 import android.view.InsetsVisibilities;
69 import android.view.PrivacyIndicatorBounds;
70 import android.view.RoundedCorners;
71 import android.view.WindowInsets.Side;
72 import android.view.WindowInsets.Type;
73 
74 import androidx.test.filters.SmallTest;
75 
76 import com.android.server.wm.utils.WmDisplayCutout;
77 
78 import org.junit.Before;
79 import org.junit.Test;
80 import org.junit.runner.RunWith;
81 
82 import java.io.PrintWriter;
83 import java.io.StringWriter;
84 
85 /**
86  * Tests for the {@link DisplayPolicy} class.
87  *
88  * Build/Install/Run:
89  *  atest WmTests:DisplayPolicyLayoutTests
90  */
91 @SmallTest
92 @Presubmit
93 @WindowTestsBase.UseTestDisplay(
94         addWindows = { WindowTestsBase.W_STATUS_BAR, WindowTestsBase.W_NAVIGATION_BAR })
95 @RunWith(WindowTestRunner.class)
96 public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase {
97 
98     private DisplayFrames mFrames;
99     private WindowState mWindow;
100     private int mRotation = ROTATION_0;
101     private boolean mHasDisplayCutout;
102     private boolean mIsLongEdgeDisplayCutout;
103     private boolean mHasRoundedCorners;
104 
105     private final Rect mDisplayBounds = new Rect();
106 
107     @Before
setUp()108     public void setUp() throws Exception {
109         mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
110         // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
111         // changing those frames.
112         doNothing().when(mWindow).computeFrame(any());
113 
114         spyOn(mStatusBarWindow);
115         spyOn(mNavBarWindow);
116 
117         // Disabling this call for most tests since it can override the systemUiFlags when called.
118         doNothing().when(mDisplayPolicy).updateSystemBarAttributes();
119 
120         updateDisplayFrames();
121     }
122 
addWindowWithRawInsetsState(WindowState win)123     void addWindowWithRawInsetsState(WindowState win) {
124         addWindow(win);
125         // Without mPerformLayout in display content, the window cannot see any insets. Override the
126         // insets state with the global one.
127         final InsetsState insetsState =
128                 win.getDisplayContent().getInsetsStateController().getRawInsetsState();
129         win.mAboveInsetsState.set(insetsState);
130     }
131 
setRotation(int rotation, boolean includingWindows)132     public void setRotation(int rotation, boolean includingWindows) {
133         mRotation = rotation;
134         updateDisplayFrames();
135         if (includingWindows) {
136             mNavBarWindow.getWindowConfiguration().setRotation(rotation);
137             mStatusBarWindow.getWindowConfiguration().setRotation(rotation);
138             mWindow.getWindowConfiguration().setRotation(rotation);
139         }
140     }
141 
addDisplayCutout()142     public void addDisplayCutout() {
143         mHasDisplayCutout = true;
144         updateDisplayFrames();
145     }
146 
addLongEdgeDisplayCutout()147     public void addLongEdgeDisplayCutout() {
148         mHasDisplayCutout = true;
149         mIsLongEdgeDisplayCutout = true;
150         updateDisplayFrames();
151     }
152 
addRoundedCorners()153     public void addRoundedCorners() {
154         mHasRoundedCorners = true;
155         updateDisplayFrames();
156     }
157 
updateDisplayFrames()158     private void updateDisplayFrames() {
159         mFrames = createDisplayFrames(
160                 mDisplayContent.getInsetsStateController().getRawInsetsState());
161         mDisplayBounds.set(0, 0, mFrames.mDisplayWidth, mFrames.mDisplayHeight);
162         mDisplayContent.mDisplayFrames = mFrames;
163         mDisplayContent.setBounds(mDisplayBounds);
164         mDisplayPolicy.layoutWindowLw(mNavBarWindow, null, mFrames);
165         mDisplayPolicy.layoutWindowLw(mStatusBarWindow, null, mFrames);
166     }
167 
createDisplayFrames(InsetsState insetsState)168     private DisplayFrames createDisplayFrames(InsetsState insetsState) {
169         final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
170                 mHasDisplayCutout, mIsLongEdgeDisplayCutout);
171         final RoundedCorners roundedCorners = mHasRoundedCorners
172                 ? mDisplayContent.calculateRoundedCornersForRotation(mRotation)
173                 : RoundedCorners.NO_ROUNDED_CORNERS;
174         return new DisplayFrames(mDisplayContent.getDisplayId(),
175                 insetsState, info.first, info.second, roundedCorners, new PrivacyIndicatorBounds());
176     }
177 
178     @Test
addingWindow_doesNotTamperWithSysuiFlags()179     public void addingWindow_doesNotTamperWithSysuiFlags() {
180         mWindow.mAttrs.flags |= FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
181         addWindow(mWindow);
182 
183         assertEquals(0, mWindow.mAttrs.systemUiVisibility);
184         assertEquals(0, mWindow.mAttrs.subtreeSystemUiVisibility);
185     }
186 
187     @Test
addingWindow_withInsetsTypes()188     public void addingWindow_withInsetsTypes() {
189         mDisplayPolicy.removeWindowLw(mStatusBarWindow);  // Removes the existing one.
190 
191         WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
192         win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
193         win.getFrame().set(0, 0, 500, 100);
194 
195         addWindow(win);
196         InsetsStateController controller = mDisplayContent.getInsetsStateController();
197         controller.onPostLayout();
198 
199         InsetsSourceProvider statusBarProvider = controller.getSourceProvider(ITYPE_STATUS_BAR);
200         assertEquals(new Rect(0, 0, 500, 100), statusBarProvider.getSource().getFrame());
201         assertEquals(Insets.of(0, 100, 0, 0),
202                 statusBarProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
203                         false /* ignoreVisibility */));
204 
205         InsetsSourceProvider topGesturesProvider = controller.getSourceProvider(ITYPE_TOP_GESTURES);
206         assertEquals(new Rect(0, 0, 500, 100), topGesturesProvider.getSource().getFrame());
207         assertEquals(Insets.of(0, 100, 0, 0),
208                 topGesturesProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
209                         false /* ignoreVisibility */));
210 
211         InsetsSourceProvider navigationBarProvider = controller.getSourceProvider(
212                 ITYPE_NAVIGATION_BAR);
213         assertNotEquals(new Rect(0, 0, 500, 100), navigationBarProvider.getSource().getFrame());
214     }
215 
216     @Test
addingWindow_InWindowTypeWithPredefinedInsets()217     public void addingWindow_InWindowTypeWithPredefinedInsets() {
218         mDisplayPolicy.removeWindowLw(mStatusBarWindow);  // Removes the existing one.
219         WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar");
220         win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
221         win.getFrame().set(0, 0, 500, 100);
222 
223         addWindow(win);
224         mDisplayContent.getInsetsStateController().onPostLayout();
225 
226         InsetsSourceProvider provider =
227                 mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR);
228         if (INSETS_LAYOUT_GENERALIZATION) {
229             // In the new flexible insets setup, the insets frame should always respect the window
230             // layout result.
231             assertEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
232         } else {
233             assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame());
234         }
235     }
236 
237     @Test
addingWindow_throwsException_WithMultipleInsetTypes()238     public void addingWindow_throwsException_WithMultipleInsetTypes() {
239         WindowState win1 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
240         win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR};
241 
242         expectThrows(IllegalArgumentException.class, () -> addWindow(win1));
243 
244         WindowState win2 = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
245         win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_CLIMATE_BAR, ITYPE_EXTRA_NAVIGATION_BAR};
246 
247         expectThrows(IllegalArgumentException.class, () -> addWindow(win2));
248     }
249 
250     @Test
addingWindow_variousGravities_alternateBarPosUpdated()251     public void addingWindow_variousGravities_alternateBarPosUpdated() {
252         mDisplayPolicy.removeWindowLw(mNavBarWindow);  // Removes the existing one.
253 
254         WindowState win1 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel1");
255         win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
256         win1.mAttrs.gravity = Gravity.TOP;
257         win1.getFrame().set(0, 0, 200, 500);
258         addWindow(win1);
259 
260         assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_TOP);
261         mDisplayPolicy.removeWindowLw(win1);
262 
263         WindowState win2 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel2");
264         win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
265         win2.mAttrs.gravity = Gravity.BOTTOM;
266         win2.getFrame().set(0, 0, 200, 500);
267         addWindow(win2);
268 
269         assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_BOTTOM);
270         mDisplayPolicy.removeWindowLw(win2);
271 
272         WindowState win3 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel3");
273         win3.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
274         win3.mAttrs.gravity = Gravity.LEFT;
275         win3.getFrame().set(0, 0, 200, 500);
276         addWindow(win3);
277 
278         assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_LEFT);
279         mDisplayPolicy.removeWindowLw(win3);
280 
281         WindowState win4 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel4");
282         win4.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
283         win4.mAttrs.gravity = Gravity.RIGHT;
284         win4.getFrame().set(0, 0, 200, 500);
285         addWindow(win4);
286 
287         assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_RIGHT);
288         mDisplayPolicy.removeWindowLw(win4);
289     }
290 
291     @Test
layoutWindowLw_fitStatusBars()292     public void layoutWindowLw_fitStatusBars() {
293         mWindow.mAttrs.setFitInsetsTypes(Type.statusBars());
294         addWindowWithRawInsetsState(mWindow);
295 
296         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
297 
298         assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0);
299         assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
300     }
301 
302     @Test
layoutWindowLw_fitNavigationBars()303     public void layoutWindowLw_fitNavigationBars() {
304         mWindow.mAttrs.setFitInsetsTypes(Type.navigationBars());
305         addWindowWithRawInsetsState(mWindow);
306 
307         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
308 
309         assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, NAV_BAR_HEIGHT);
310         assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
311     }
312 
313     @Test
layoutWindowLw_fitAllSides()314     public void layoutWindowLw_fitAllSides() {
315         mWindow.mAttrs.setFitInsetsSides(Side.all());
316         addWindowWithRawInsetsState(mWindow);
317 
318         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
319 
320         assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
321         assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
322     }
323 
324     @Test
layoutWindowLw_fitTopOnly()325     public void layoutWindowLw_fitTopOnly() {
326         mWindow.mAttrs.setFitInsetsSides(Side.TOP);
327         addWindowWithRawInsetsState(mWindow);
328 
329         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
330 
331         assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0);
332         assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
333     }
334 
335     @Test
layoutWindowLw_fitInsetsIgnoringVisibility()336     public void layoutWindowLw_fitInsetsIgnoringVisibility() {
337         final InsetsState state =
338                 mDisplayContent.getInsetsStateController().getRawInsetsState();
339         state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
340         state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
341         mWindow.mAttrs.setFitInsetsIgnoringVisibility(true);
342         addWindowWithRawInsetsState(mWindow);
343 
344         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
345 
346         assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
347         assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
348     }
349 
350     @Test
layoutWindowLw_fitInsetsNotIgnoringVisibility()351     public void layoutWindowLw_fitInsetsNotIgnoringVisibility() {
352         final InsetsState state =
353                 mDisplayContent.getInsetsStateController().getRawInsetsState();
354         state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
355         state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
356         mWindow.mAttrs.setFitInsetsIgnoringVisibility(false);
357         addWindowWithRawInsetsState(mWindow);
358 
359         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
360 
361         assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0);
362         assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
363     }
364 
365     @Test
layoutWindowLw_insetParentFrameByIme()366     public void layoutWindowLw_insetParentFrameByIme() {
367         final InsetsState state =
368                 mDisplayContent.getInsetsStateController().getRawInsetsState();
369         state.getSource(InsetsState.ITYPE_IME).setVisible(true);
370         state.getSource(InsetsState.ITYPE_IME).setFrame(
371                 0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
372         mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
373         addWindowWithRawInsetsState(mWindow);
374 
375         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
376 
377         assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
378         assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, IME_HEIGHT);
379     }
380 
381     @Test
layoutWindowLw_fitDisplayCutout()382     public void layoutWindowLw_fitDisplayCutout() {
383         addDisplayCutout();
384 
385         mWindow.mAttrs.setFitInsetsTypes(Type.displayCutout());
386         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
387         addWindowWithRawInsetsState(mWindow);
388 
389         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
390 
391         assertInsetByTopBottom(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0);
392         assertInsetByTopBottom(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0);
393     }
394 
395     @Test
layoutWindowLw_withDisplayCutout()396     public void layoutWindowLw_withDisplayCutout() {
397         addDisplayCutout();
398 
399         mWindow.mAttrs.flags =
400                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
401         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
402         addWindowWithRawInsetsState(mWindow);
403 
404         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
405 
406         assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
407         assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0);
408     }
409 
410     @Test
layoutWindowLw_withDisplayCutout_never()411     public void layoutWindowLw_withDisplayCutout_never() {
412         addDisplayCutout();
413 
414         mWindow.mAttrs.flags =
415                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
416         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
417         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
418         addWindowWithRawInsetsState(mWindow);
419 
420         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
421 
422         assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
423         assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0);
424     }
425 
426     @Test
layoutWindowLw_withDisplayCutout_shortEdges()427     public void layoutWindowLw_withDisplayCutout_shortEdges() {
428         addDisplayCutout();
429 
430         mWindow.mAttrs.flags =
431                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
432         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
433         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
434         addWindowWithRawInsetsState(mWindow);
435 
436         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
437 
438         assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
439         assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0);
440     }
441 
442     @Test
layoutWindowLw_withDisplayCutout_always()443     public void layoutWindowLw_withDisplayCutout_always() {
444         addDisplayCutout();
445 
446         mWindow.mAttrs.flags =
447                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
448         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
449         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
450         addWindowWithRawInsetsState(mWindow);
451 
452         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
453 
454         assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
455         assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0);
456     }
457 
458     @Test
layoutWindowLw_withDisplayCutout_layoutFullscreen()459     public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
460         addDisplayCutout();
461 
462         mWindow.mAttrs.flags =
463                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
464         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
465         addWindowWithRawInsetsState(mWindow);
466 
467         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
468 
469         assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
470         assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0);
471     }
472 
473     @Test
layoutWindowLw_withDisplayCutout_fullscreen()474     public void layoutWindowLw_withDisplayCutout_fullscreen() {
475         addDisplayCutout();
476 
477         mWindow.mAttrs.flags =
478                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
479         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
480         mDisplayContent.getInsetsStateController().getRawInsetsState()
481                 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
482         final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
483         requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
484         mWindow.setRequestedVisibilities(requestedVisibilities);
485         addWindowWithRawInsetsState(mWindow);
486 
487         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
488 
489         assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
490         assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0);
491     }
492 
493     @Test
layoutWindowLw_withDisplayCutout_fullscreenInCutout()494     public void layoutWindowLw_withDisplayCutout_fullscreenInCutout() {
495         addDisplayCutout();
496 
497         mWindow.mAttrs.flags =
498                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
499         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
500         mDisplayContent.getInsetsStateController().getRawInsetsState()
501                 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
502         final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
503         requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
504         mWindow.setRequestedVisibilities(requestedVisibilities);
505         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
506         addWindowWithRawInsetsState(mWindow);
507 
508         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
509 
510         assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
511         assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0);
512     }
513 
514 
515     @Test
layoutWindowLw_withDisplayCutout_landscape()516     public void layoutWindowLw_withDisplayCutout_landscape() {
517         addDisplayCutout();
518         setRotation(ROTATION_90, true /* includingWindows */);
519         mWindow.mAttrs.flags =
520                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
521         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
522         addWindowWithRawInsetsState(mWindow);
523 
524         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
525 
526         assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
527         assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
528     }
529 
530     @Test
layoutWindowLw_withDisplayCutout_seascape()531     public void layoutWindowLw_withDisplayCutout_seascape() {
532         addDisplayCutout();
533         setRotation(ROTATION_270, true /* includingWindows */);
534 
535         mWindow.mAttrs.flags =
536                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
537         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
538         addWindowWithRawInsetsState(mWindow);
539 
540         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
541 
542         assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
543         assertInsetBy(mWindow.getDisplayFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
544     }
545 
546     @Test
layoutWindowLw_withDisplayCutout_fullscreen_landscape()547     public void layoutWindowLw_withDisplayCutout_fullscreen_landscape() {
548         addDisplayCutout();
549         setRotation(ROTATION_90, true /* includingWindows */);
550 
551         mWindow.mAttrs.flags =
552                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
553         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
554         addWindowWithRawInsetsState(mWindow);
555 
556         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
557 
558         assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
559     }
560 
561     @Test
layoutWindowLw_withDisplayCutout_floatingInScreen()562     public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
563         addDisplayCutout();
564 
565         mWindow.mAttrs.flags = FLAG_LAYOUT_IN_SCREEN;
566         mWindow.mAttrs.setFitInsetsTypes(Type.systemBars() & ~Type.statusBars());
567         mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
568         mWindow.mAttrs.width = DISPLAY_WIDTH;
569         mWindow.mAttrs.height = DISPLAY_HEIGHT;
570         addWindowWithRawInsetsState(mWindow);
571 
572         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
573 
574         assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
575         assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
576     }
577 
578     @Test
layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape()579     public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
580         addDisplayCutout();
581         setRotation(ROTATION_90, true /* includingWindows */);
582 
583         mWindow.mAttrs.flags =
584                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
585         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
586         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
587         addWindowWithRawInsetsState(mWindow);
588 
589         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
590 
591         assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
592     }
593 
594     @Test
layoutWindowLw_withLongEdgeDisplayCutout()595     public void layoutWindowLw_withLongEdgeDisplayCutout() {
596         addLongEdgeDisplayCutout();
597 
598         mWindow.mAttrs.flags =
599                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
600         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
601         addWindowWithRawInsetsState(mWindow);
602 
603         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
604 
605         assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
606         assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
607     }
608 
609     @Test
layoutWindowLw_withLongEdgeDisplayCutout_never()610     public void layoutWindowLw_withLongEdgeDisplayCutout_never() {
611         addLongEdgeDisplayCutout();
612 
613         mWindow.mAttrs.flags =
614                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
615         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
616         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
617         addWindowWithRawInsetsState(mWindow);
618 
619         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
620 
621         assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
622         assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
623     }
624 
625     @Test
layoutWindowLw_withLongEdgeDisplayCutout_shortEdges()626     public void layoutWindowLw_withLongEdgeDisplayCutout_shortEdges() {
627         addLongEdgeDisplayCutout();
628 
629         mWindow.mAttrs.flags =
630                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
631         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
632         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
633         addWindowWithRawInsetsState(mWindow);
634 
635         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
636 
637         assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
638         assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
639     }
640 
641     @Test
layoutWindowLw_withLongEdgeDisplayCutout_always()642     public void layoutWindowLw_withLongEdgeDisplayCutout_always() {
643         addLongEdgeDisplayCutout();
644 
645         mWindow.mAttrs.flags =
646                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
647         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
648         mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
649         addWindowWithRawInsetsState(mWindow);
650 
651         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
652 
653         assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
654         assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0);
655     }
656 
657     @Test
layoutWindowLw_withForwardInset_SoftInputAdjustNothing()658     public void layoutWindowLw_withForwardInset_SoftInputAdjustNothing() {
659         mWindow.mAttrs.flags =
660                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
661         mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
662         mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING;
663         addWindowWithRawInsetsState(mWindow);
664 
665         final int forwardedInsetBottom = 50;
666         mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom));
667         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
668 
669         assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
670         assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0);
671     }
672 
673     @Test
layoutHint_appWindow()674     public void layoutHint_appWindow() {
675         mWindow.mAttrs.setFitInsetsTypes(0);
676 
677         final DisplayCutout.ParcelableWrapper outDisplayCutout =
678                 new DisplayCutout.ParcelableWrapper();
679         final InsetsState outState = new InsetsState();
680 
681         mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outState,
682                 true /* localClient */);
683 
684         assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
685         assertThat(outState.getSource(ITYPE_STATUS_BAR).getFrame(),
686                 is(new Rect(0, 0, DISPLAY_WIDTH, STATUS_BAR_HEIGHT)));
687         assertThat(outState.getSource(ITYPE_NAVIGATION_BAR).getFrame(),
688                 is(new Rect(0, DISPLAY_HEIGHT - NAV_BAR_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT)));
689     }
690 
691     /**
692      * Verify that {@link DisplayPolicy#simulateLayoutDisplay} outputs the same display frames as
693      * the real one.
694      */
695     @Test
testSimulateLayoutDisplay()696     public void testSimulateLayoutDisplay() {
697         assertSimulateLayoutSameDisplayFrames();
698         setRotation(ROTATION_90, false /* includingWindows */);
699         assertSimulateLayoutSameDisplayFrames();
700         addDisplayCutout();
701         assertSimulateLayoutSameDisplayFrames();
702         addRoundedCorners();
703         assertSimulateLayoutSameDisplayFrames();
704     }
705 
assertSimulateLayoutSameDisplayFrames()706     private void assertSimulateLayoutSameDisplayFrames() {
707         final String prefix = "";
708         final InsetsState simulatedInsetsState = new InsetsState();
709         final DisplayFrames simulatedDisplayFrames = createDisplayFrames(simulatedInsetsState);
710         // Force the display bounds because it is not synced with display frames in policy test.
711         mDisplayContent.getWindowConfiguration().setBounds(mFrames.mUnrestricted);
712         mDisplayContent.getInsetsStateController().onPostLayout();
713         mDisplayPolicy.simulateLayoutDisplay(simulatedDisplayFrames,
714                 new SparseArray<>() /* barContentFrames */);
715 
716         final StringWriter realFramesDump = new StringWriter();
717         mFrames.dump(prefix, new PrintWriter(realFramesDump));
718         final StringWriter simulatedFramesDump = new StringWriter();
719         simulatedDisplayFrames.dump(prefix, new PrintWriter(simulatedFramesDump));
720 
721         assertEquals(new ToStringComparatorWrapper<>(realFramesDump),
722                 new ToStringComparatorWrapper<>(simulatedFramesDump));
723 
724         final InsetsState realInsetsState = new InsetsState(
725                 mDisplayContent.getInsetsStateController().getRawInsetsState());
726         // Exclude comparing IME insets because currently the simulated layout only focuses on the
727         // insets from status bar and navigation bar.
728         realInsetsState.removeSource(InsetsState.ITYPE_IME);
729         realInsetsState.removeSource(InsetsState.ITYPE_CAPTION_BAR);
730 
731         assertEquals(new ToStringComparatorWrapper<>(realInsetsState),
732                 new ToStringComparatorWrapper<>(simulatedInsetsState));
733     }
734 
735     @Test
testFixedRotationInsetsSourceFrame()736     public void testFixedRotationInsetsSourceFrame() {
737         mDisplayContent.mBaseDisplayHeight = DISPLAY_HEIGHT;
738         mDisplayContent.mBaseDisplayWidth = DISPLAY_WIDTH;
739         doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent)
740                 .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
741         mWindow.mAboveInsetsState.set(
742                 mDisplayContent.getInsetsStateController().getRawInsetsState());
743         final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
744                 .getSource(ITYPE_STATUS_BAR).getFrame();
745         mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
746         final Rect rotatedFrame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
747                 .getSource(ITYPE_STATUS_BAR).getFrame();
748 
749         assertEquals(DISPLAY_WIDTH, frame.width());
750         assertEquals(DISPLAY_HEIGHT, rotatedFrame.width());
751     }
752 
753     /**
754      * Asserts that {@code actual} is inset by the given amounts from the full display rect.
755      *
756      * Convenience wrapper for when only the top and bottom inset are non-zero.
757      */
assertInsetByTopBottom(Rect actual, int expectedInsetTop, int expectedInsetBottom)758     private void assertInsetByTopBottom(Rect actual, int expectedInsetTop,
759             int expectedInsetBottom) {
760         assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom);
761     }
762 
763     /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */
assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop, int expectedInsetRight, int expectedInsetBottom)764     private void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop,
765             int expectedInsetRight, int expectedInsetBottom) {
766         assertEquals(new Rect(expectedInsetLeft, expectedInsetTop,
767                 mFrames.mDisplayWidth - expectedInsetRight,
768                 mFrames.mDisplayHeight - expectedInsetBottom), actual);
769     }
770 }
771