• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.DisplayCutout.NO_CUTOUT;
20 import static android.view.InsetsSource.ID_IME;
21 import static android.view.Surface.ROTATION_0;
22 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
23 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
24 import static android.view.ViewRootImpl.CLIENT_TRANSIENT;
25 import static android.view.WindowInsets.Type.navigationBars;
26 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
27 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
28 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
29 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
30 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
31 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
32 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
33 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
34 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
35 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
36 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
37 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
38 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
39 
40 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
41 
42 import static org.junit.Assert.assertEquals;
43 import static org.junit.Assert.assertFalse;
44 import static org.junit.Assert.assertNotEquals;
45 import static org.junit.Assert.assertNotNull;
46 import static org.junit.Assert.assertNull;
47 import static org.junit.Assert.assertTrue;
48 import static org.mockito.ArgumentMatchers.anyInt;
49 import static org.mockito.Mockito.doReturn;
50 import static org.mockito.Mockito.spy;
51 import static org.mockito.Mockito.when;
52 
53 import android.graphics.Insets;
54 import android.graphics.PixelFormat;
55 import android.graphics.Rect;
56 import android.platform.test.annotations.Presubmit;
57 import android.view.DisplayInfo;
58 import android.view.InsetsFrameProvider;
59 import android.view.InsetsSource;
60 import android.view.InsetsState;
61 import android.view.Surface;
62 import android.view.WindowInsets;
63 import android.view.WindowManager;
64 
65 import androidx.test.filters.SmallTest;
66 
67 import com.android.window.flags.Flags;
68 
69 import org.junit.Assume;
70 import org.junit.Test;
71 import org.junit.runner.RunWith;
72 
73 @SmallTest
74 @Presubmit
75 @RunWith(WindowTestRunner.class)
76 public class DisplayPolicyTests extends WindowTestsBase {
77 
createOpaqueFullscreen(boolean hasLightNavBar)78     private WindowState createOpaqueFullscreen(boolean hasLightNavBar) {
79         final WindowState win = newWindowBuilder("opaqueFullscreen", TYPE_BASE_APPLICATION).build();
80         final WindowManager.LayoutParams attrs = win.mAttrs;
81         attrs.width = MATCH_PARENT;
82         attrs.height = MATCH_PARENT;
83         attrs.flags =
84                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
85         attrs.format = PixelFormat.OPAQUE;
86         attrs.insetsFlags.appearance = hasLightNavBar ? APPEARANCE_LIGHT_NAVIGATION_BARS : 0;
87         return win;
88     }
89 
createDreamWindow()90     private WindowState createDreamWindow() {
91         final WindowState win = createDreamWindow("dream", TYPE_BASE_APPLICATION);
92         final WindowManager.LayoutParams attrs = win.mAttrs;
93         attrs.width = MATCH_PARENT;
94         attrs.height = MATCH_PARENT;
95         attrs.flags =
96                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
97         attrs.format = PixelFormat.OPAQUE;
98         return win;
99     }
100 
createDimmingDialogWindow(boolean canBeImTarget)101     private WindowState createDimmingDialogWindow(boolean canBeImTarget) {
102         final WindowState win = spy(newWindowBuilder("dimmingDialog", TYPE_APPLICATION).build());
103         final WindowManager.LayoutParams attrs = win.mAttrs;
104         attrs.width = WRAP_CONTENT;
105         attrs.height = WRAP_CONTENT;
106         attrs.flags = FLAG_DIM_BEHIND | (canBeImTarget ? 0 : FLAG_ALT_FOCUSABLE_IM);
107         attrs.format = PixelFormat.TRANSLUCENT;
108         when(win.isDimming()).thenReturn(true);
109         return win;
110     }
111 
createInputMethodWindow(boolean visible, boolean drawNavBar, boolean hasLightNavBar)112     private WindowState createInputMethodWindow(boolean visible, boolean drawNavBar,
113             boolean hasLightNavBar) {
114         final WindowState win = newWindowBuilder("inputMethod", TYPE_INPUT_METHOD).build();
115         final WindowManager.LayoutParams attrs = win.mAttrs;
116         attrs.width = MATCH_PARENT;
117         attrs.height = MATCH_PARENT;
118         attrs.flags = FLAG_NOT_FOCUSABLE | FLAG_LAYOUT_IN_SCREEN
119                 | (drawNavBar ? FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS : 0);
120         attrs.format = PixelFormat.TRANSPARENT;
121         attrs.insetsFlags.appearance = hasLightNavBar ? APPEARANCE_LIGHT_NAVIGATION_BARS : 0;
122         win.mHasSurface = visible;
123         return win;
124     }
125 
126     @Test
testChooseNavigationColorWindowLw()127     public void testChooseNavigationColorWindowLw() {
128         final WindowState candidate = createOpaqueFullscreen(false);
129         final WindowState dimmingImTarget = createDimmingDialogWindow(true);
130         final WindowState dimmingNonImTarget = createDimmingDialogWindow(false);
131 
132         final WindowState visibleIme = createInputMethodWindow(true, true, false);
133         final WindowState invisibleIme = createInputMethodWindow(false, true, false);
134         final WindowState imeNonDrawNavBar = createInputMethodWindow(true, false, false);
135 
136         // If everything is null, return null.
137         assertNull(null, DisplayPolicy.chooseNavigationColorWindowLw(
138                 null, null, true));
139 
140         // If no IME windows, return candidate window.
141         assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
142                 candidate, null, true));
143         assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
144                 dimmingImTarget, null, true));
145         assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
146                 dimmingNonImTarget, null, true));
147 
148         // If IME is not visible, return candidate window.
149         assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw(
150                 null, invisibleIme, true));
151         assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
152                 candidate, invisibleIme, true));
153         assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
154                 dimmingImTarget, invisibleIme, true));
155         assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
156                 dimmingNonImTarget, invisibleIme, true));
157 
158         // If IME is visible, return candidate when the candidate window is not dimming.
159         assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
160                 null, visibleIme, true));
161         assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
162                 candidate, visibleIme, true));
163 
164         // If IME is visible and the candidate window is dimming, checks whether the dimming window
165         // can be IME tartget or not.
166         assertEquals(visibleIme, DisplayPolicy.chooseNavigationColorWindowLw(
167                 dimmingImTarget, visibleIme, true));
168         assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
169                 dimmingNonImTarget, visibleIme, true));
170 
171         // Only IME windows that have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS should be navigation color
172         // window.
173         assertEquals(null, DisplayPolicy.chooseNavigationColorWindowLw(
174                 null, imeNonDrawNavBar, true));
175         assertEquals(candidate, DisplayPolicy.chooseNavigationColorWindowLw(
176                 candidate, imeNonDrawNavBar, true));
177         assertEquals(dimmingImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
178                 dimmingImTarget, imeNonDrawNavBar, true));
179         assertEquals(dimmingNonImTarget, DisplayPolicy.chooseNavigationColorWindowLw(
180                 dimmingNonImTarget, imeNonDrawNavBar, true));
181     }
182 
183     @Test
testChooseNavigationBackgroundWindow()184     public void testChooseNavigationBackgroundWindow() {
185         final WindowState drawBarWin = createOpaqueFullscreen(false);
186         final WindowState nonDrawBarWin = createDimmingDialogWindow(true);
187 
188         final WindowState visibleIme = createInputMethodWindow(true, true, false);
189         final WindowState invisibleIme = createInputMethodWindow(false, true, false);
190         final WindowState nonDrawBarIme = createInputMethodWindow(true, false, false);
191 
192         assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow(
193                 drawBarWin, null, true));
194         assertNull(DisplayPolicy.chooseNavigationBackgroundWindow(
195                 null, null, true));
196         assertNull(DisplayPolicy.chooseNavigationBackgroundWindow(
197                 nonDrawBarWin, null, true));
198 
199         assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow(
200                 drawBarWin, visibleIme, true));
201         assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow(
202                 null, visibleIme, true));
203         assertEquals(visibleIme, DisplayPolicy.chooseNavigationBackgroundWindow(
204                 nonDrawBarWin, visibleIme, true));
205 
206         assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow(
207                 drawBarWin, invisibleIme, true));
208         assertNull(DisplayPolicy.chooseNavigationBackgroundWindow(
209                 null, invisibleIme, true));
210         assertNull(DisplayPolicy.chooseNavigationBackgroundWindow(
211                 nonDrawBarWin, invisibleIme, true));
212 
213         assertEquals(drawBarWin, DisplayPolicy.chooseNavigationBackgroundWindow(
214                 drawBarWin, nonDrawBarIme, true));
215         assertNull(DisplayPolicy.chooseNavigationBackgroundWindow(
216                 null, nonDrawBarIme, true));
217         assertNull(DisplayPolicy.chooseNavigationBackgroundWindow(
218                 nonDrawBarWin, nonDrawBarIme, true));
219     }
220 
221     @SetupWindows(addWindows = W_NAVIGATION_BAR)
222     @Test
testUpdateLightNavigationBarLw()223     public void testUpdateLightNavigationBarLw() {
224         DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
225         final WindowState opaqueDarkNavBar = createOpaqueFullscreen(false);
226         final WindowState opaqueLightNavBar = createOpaqueFullscreen(true);
227 
228         final WindowState dimming = createDimmingDialogWindow(false);
229 
230         final WindowState imeDrawDarkNavBar = createInputMethodWindow(true, true, false);
231         final WindowState imeDrawLightNavBar = createInputMethodWindow(true, true, true);
232 
233         mDisplayContent.setLayoutNeeded();
234         mDisplayContent.performLayout(true /* initial */, false /* updateImeWindows */);
235 
236         final InsetsSource navSource = new InsetsSource(
237                 InsetsSource.createId(null, 0, navigationBars()), navigationBars());
238         navSource.setFrame(mNavBarWindow.getFrame());
239         opaqueDarkNavBar.mAboveInsetsState.addSource(navSource);
240         opaqueLightNavBar.mAboveInsetsState.addSource(navSource);
241         dimming.mAboveInsetsState.addSource(navSource);
242         imeDrawDarkNavBar.mAboveInsetsState.addSource(navSource);
243         imeDrawLightNavBar.mAboveInsetsState.addSource(navSource);
244 
245         // If there is no window, APPEARANCE_LIGHT_NAVIGATION_BARS is not allowed.
246         assertEquals(0,
247                 displayPolicy.updateLightNavigationBarLw(APPEARANCE_LIGHT_NAVIGATION_BARS, null));
248 
249         // Control window overrides APPEARANCE_LIGHT_NAVIGATION_BARS flag.
250         assertEquals(0, displayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar));
251         assertEquals(0, displayPolicy.updateLightNavigationBarLw(
252                 APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueDarkNavBar));
253         assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw(
254                 0, opaqueLightNavBar));
255         assertEquals(APPEARANCE_LIGHT_NAVIGATION_BARS, displayPolicy.updateLightNavigationBarLw(
256                 APPEARANCE_LIGHT_NAVIGATION_BARS, opaqueLightNavBar));
257     }
258 
259     @SetupWindows(addWindows = { W_ACTIVITY, W_STATUS_BAR })
260     @Test
testComputeTopFullscreenOpaqueWindow()261     public void testComputeTopFullscreenOpaqueWindow() {
262         final WindowManager.LayoutParams attrs = mAppWindow.mAttrs;
263         attrs.x = attrs.y = 0;
264         attrs.height = attrs.width = WindowManager.LayoutParams.MATCH_PARENT;
265         final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
266         policy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs);
267 
268         policy.applyPostLayoutPolicyLw(
269                 mAppWindow, attrs, null /* attached */, null /* imeTarget */);
270 
271         assertEquals(mAppWindow, policy.getTopFullscreenOpaqueWindow());
272     }
273 
274     @SetupWindows(addWindows = W_NOTIFICATION_SHADE)
275     @Test
testVisibleProcessWhileDozing()276     public void testVisibleProcessWhileDozing() {
277         final WindowProcessController wpc = mNotificationShadeWindow.getProcess();
278         final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
279         policy.addWindowLw(mNotificationShadeWindow, mNotificationShadeWindow.mAttrs);
280 
281         policy.screenTurnedOff(false /* acquireSleepToken */);
282         policy.setAwake(false);
283         policy.screenTurningOn(null /* screenOnListener */);
284         assertTrue(wpc.isShowingUiWhileDozing());
285         policy.screenTurnedOff(false /* acquireSleepToken */);
286         assertFalse(wpc.isShowingUiWhileDozing());
287 
288         policy.screenTurningOn(null /* screenOnListener */);
289         assertTrue(wpc.isShowingUiWhileDozing());
290         policy.setAwake(true);
291         assertFalse(wpc.isShowingUiWhileDozing());
292     }
293 
294     @Test(expected = IllegalArgumentException.class)
testMainAppWindowDisallowFitSystemWindowTypes()295     public void testMainAppWindowDisallowFitSystemWindowTypes() {
296         final DisplayPolicy policy = mDisplayContent.getDisplayPolicy();
297         final WindowState activity = createBaseApplicationWindow();
298         activity.mAttrs.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
299 
300         policy.adjustWindowParamsLw(activity, activity.mAttrs);
301     }
302 
createApplicationWindow()303     private WindowState createApplicationWindow() {
304         final WindowState win = newWindowBuilder("Application", TYPE_APPLICATION).build();
305         final WindowManager.LayoutParams attrs = win.mAttrs;
306         attrs.width = MATCH_PARENT;
307         attrs.height = MATCH_PARENT;
308         attrs.flags = FLAG_SHOW_WHEN_LOCKED | FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
309         attrs.format = PixelFormat.OPAQUE;
310         win.mHasSurface = true;
311         return win;
312     }
313 
createBaseApplicationWindow()314     private WindowState createBaseApplicationWindow() {
315         final WindowState win = newWindowBuilder("Application", TYPE_BASE_APPLICATION).build();
316         final WindowManager.LayoutParams attrs = win.mAttrs;
317         attrs.width = MATCH_PARENT;
318         attrs.height = MATCH_PARENT;
319         attrs.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
320         attrs.format = PixelFormat.OPAQUE;
321         win.mHasSurface = true;
322         return win;
323     }
324 
325     @Test
testOverlappingWithNavBar()326     public void testOverlappingWithNavBar() {
327         final InsetsSource navSource = new InsetsSource(
328                 InsetsSource.createId(null, 0, navigationBars()), navigationBars());
329         navSource.setFrame(new Rect(100, 200, 200, 300));
330         testOverlappingWithNavBarType(navSource);
331     }
332 
333     @Test
testOverlappingWithExtraNavBar()334     public void testOverlappingWithExtraNavBar() {
335         final InsetsSource navSource = new InsetsSource(
336                 InsetsSource.createId(null, 1, navigationBars()), navigationBars());
337         navSource.setFrame(new Rect(100, 200, 200, 300));
338         testOverlappingWithNavBarType(navSource);
339     }
340 
testOverlappingWithNavBarType(InsetsSource navSource)341     private void testOverlappingWithNavBarType(InsetsSource navSource) {
342         final WindowState targetWin = createApplicationWindow();
343         final WindowFrames winFrame = targetWin.getWindowFrames();
344         winFrame.mFrame.set(new Rect(100, 100, 200, 200));
345         targetWin.mAboveInsetsState.addSource(navSource);
346 
347         assertFalse("Freeform is overlapping with navigation bar",
348                 DisplayPolicy.isOverlappingWithNavBar(targetWin));
349 
350         winFrame.mFrame.set(new Rect(100, 101, 200, 201));
351         assertTrue("Freeform should be overlapping with navigation bar (bottom)",
352                 DisplayPolicy.isOverlappingWithNavBar(targetWin));
353 
354         winFrame.mFrame.set(new Rect(99, 200, 199, 300));
355         assertTrue("Freeform should be overlapping with navigation bar (right)",
356                 DisplayPolicy.isOverlappingWithNavBar(targetWin));
357 
358         winFrame.mFrame.set(new Rect(199, 200, 299, 300));
359         assertTrue("Freeform should be overlapping with navigation bar (left)",
360                 DisplayPolicy.isOverlappingWithNavBar(targetWin));
361     }
362 
363     @Test
testSwitchDecorInsets()364     public void testSwitchDecorInsets() {
365         final WindowState win = createApplicationWindow();
366         final WindowState bar = createNavBarWithProvidedInsets(mDisplayContent);
367         bar.getFrame().set(0, mDisplayContent.mDisplayFrames.mHeight - NAV_BAR_HEIGHT,
368                 mDisplayContent.mDisplayFrames.mWidth, mDisplayContent.mDisplayFrames.mHeight);
369         final int insetsId = bar.mAttrs.providedInsets[0].getId();
370         final InsetsSourceProvider provider = mDisplayContent.getInsetsStateController()
371                 .getOrCreateSourceProvider(insetsId, bar.mAttrs.providedInsets[0].getType());
372         provider.setServerVisible(true);
373         provider.updateSourceFrame(bar.getFrame());
374 
375         final InsetsState prevInsetsState = new InsetsState();
376         prevInsetsState.addSource(new InsetsSource(provider.getSource()));
377 
378         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
379         final DisplayInfo info = mDisplayContent.getDisplayInfo();
380         final int w = info.logicalWidth;
381         final int h = info.logicalHeight;
382         displayPolicy.updateDecorInsetsInfo();
383         final Rect prevConfigFrame = new Rect(displayPolicy.getDecorInsetsInfo(info.rotation,
384                 info.logicalWidth, info.logicalHeight).mConfigFrame);
385 
386         displayPolicy.updateCachedDecorInsets();
387         mDisplayContent.updateBaseDisplayMetrics(w / 2, h / 2,
388                 info.logicalDensityDpi, info.physicalXDpi, info.physicalYDpi);
389         // There is no previous cache. But the current state will be cached.
390         assertFalse(displayPolicy.shouldKeepCurrentDecorInsets());
391 
392         // Switch to original state.
393         displayPolicy.updateCachedDecorInsets();
394         mDisplayContent.updateBaseDisplayMetrics(w, h,
395                 info.logicalDensityDpi, info.physicalXDpi, info.physicalYDpi);
396         assertTrue(displayPolicy.shouldKeepCurrentDecorInsets());
397         // The current insets are restored from cache directly.
398         assertEquals(prevConfigFrame, displayPolicy.getDecorInsetsInfo(info.rotation,
399                 info.logicalWidth, info.logicalHeight).mConfigFrame);
400         // Assume that the InsetsSource in current InsetsState is not updated yet. And it will be
401         // replaced by the one in cache.
402         InsetsState currentInsetsState = new InsetsState();
403         final InsetsSource prevSource = new InsetsSource(provider.getSource());
404         prevSource.getFrame().scale(0.5f);
405         currentInsetsState.addSource(prevSource);
406         currentInsetsState = mDisplayContent.getInsetsPolicy().adjustInsetsForWindow(
407                 win, currentInsetsState);
408         if (com.android.window.flags.Flags.useCachedInsetsForDisplaySwitch()) {
409             assertEquals(prevInsetsState.peekSource(insetsId),
410                     currentInsetsState.peekSource(insetsId));
411         }
412 
413         // If screen is not fully turned on, then the cache should be preserved.
414         displayPolicy.screenTurnedOff(false /* acquireSleepToken */);
415         final TransitionController transitionController = mDisplayContent.mTransitionController;
416         spyOn(transitionController);
417         doReturn(true).when(transitionController).isCollecting();
418         doReturn(Integer.MAX_VALUE).when(transitionController).getCollectingTransitionId();
419         // Make CachedDecorInsets.canPreserve return false.
420         displayPolicy.physicalDisplayUpdated();
421         assertFalse(displayPolicy.shouldKeepCurrentDecorInsets());
422         displayPolicy.getDecorInsetsInfo(info.rotation, info.logicalWidth, info.logicalHeight)
423                 .mConfigFrame.offset(1, 1);
424         // Even if CachedDecorInsets.canPreserve returns false, the cache won't be cleared.
425         displayPolicy.updateDecorInsetsInfo();
426         // Successful to restore from cache.
427         displayPolicy.updateCachedDecorInsets();
428         assertEquals(prevConfigFrame, displayPolicy.getDecorInsetsInfo(info.rotation,
429                 info.logicalWidth, info.logicalHeight).mConfigFrame);
430     }
431 
432     @Test
testUpdateDisplayConfigurationByDecor()433     public void testUpdateDisplayConfigurationByDecor() {
434         doReturn(NO_CUTOUT).when(mDisplayContent).calculateDisplayCutoutForRotation(anyInt());
435         final WindowState navbar = createNavBarWithProvidedInsets(mDisplayContent);
436         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
437         final DisplayInfo di = mDisplayContent.getDisplayInfo();
438         final int prevScreenHeightDp = mDisplayContent.getConfiguration().screenHeightDp;
439         if (Flags.insetsDecoupledConfiguration()) {
440             // No configuration update when flag enables.
441             assertFalse(displayPolicy.updateDecorInsetsInfo());
442             assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
443                     di.logicalHeight, di.logicalWidth).mOverrideConfigInsets.bottom);
444 
445             final int barHeight = 2 * NAV_BAR_HEIGHT;
446             navbar.mAttrs.providedInsets[0].setInsetsSize(Insets.of(0, 0, 0, barHeight));
447             assertFalse(displayPolicy.updateDecorInsetsInfo());
448             assertEquals(barHeight, displayPolicy.getDecorInsetsInfo(di.rotation,
449                     di.logicalHeight, di.logicalWidth).mOverrideConfigInsets.bottom);
450             return;
451         }
452 
453         assertTrue(navbar.providesDisplayDecorInsets() && displayPolicy.updateDecorInsetsInfo());
454         assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
455                 di.logicalWidth, di.logicalHeight).mConfigInsets.bottom);
456         mDisplayContent.sendNewConfiguration();
457         assertNotEquals(prevScreenHeightDp, mDisplayContent.getConfiguration().screenHeightDp);
458         assertFalse(navbar.providesDisplayDecorInsets() && displayPolicy.updateDecorInsetsInfo());
459 
460         final WindowState statusBar = createStatusBarWithProvidedInsets(mDisplayContent);
461         if (mWm.mConfigTypes == WindowInsets.Type.navigationBars()) {
462             assertFalse(statusBar.providesDisplayDecorInsets()
463                     && displayPolicy.updateDecorInsetsInfo());
464             assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation,
465                     di.logicalWidth, di.logicalHeight).mConfigInsets.top);
466         } else {
467             assertTrue(statusBar.providesDisplayDecorInsets()
468                     && displayPolicy.updateDecorInsetsInfo());
469             assertEquals(STATUS_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
470                     di.logicalWidth, di.logicalHeight).mConfigInsets.top);
471         }
472 
473         // Flush the pending change (DecorInsets.Info#mNeedUpdate) for the rotation to be tested.
474         displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90, di.logicalHeight, di.logicalWidth);
475         // Add a window that provides the same insets in current rotation. But it specifies
476         // different insets in other rotations.
477         final WindowState bar2 = newWindowBuilder("bar2", navbar.mAttrs.type).build();
478         bar2.mAttrs.providedInsets = new InsetsFrameProvider[] {
479                 new InsetsFrameProvider(bar2, 0, WindowInsets.Type.navigationBars())
480                         .setInsetsSize(Insets.of(0, 0, 0, NAV_BAR_HEIGHT))
481         };
482         bar2.mAttrs.setFitInsetsTypes(0);
483         bar2.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4];
484         final int doubleHeightFor90 = NAV_BAR_HEIGHT * 2;
485         for (int i = ROTATION_0; i <= Surface.ROTATION_270; i++) {
486             final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
487             params.setFitInsetsTypes(0);
488             if (i == Surface.ROTATION_90) {
489                 params.providedInsets = new InsetsFrameProvider[] {
490                         new InsetsFrameProvider(bar2, 0, WindowInsets.Type.navigationBars())
491                                 .setInsetsSize(Insets.of(0, 0, 0, doubleHeightFor90))
492                 };
493             } else {
494                 params.providedInsets = bar2.mAttrs.providedInsets;
495             }
496             bar2.mAttrs.paramsForRotation[i] = params;
497         }
498         displayPolicy.addWindowLw(bar2, bar2.mAttrs);
499         // Current rotation is 0 and the top insets is still STATUS_BAR_HEIGHT, so no change.
500         assertFalse(displayPolicy.updateDecorInsetsInfo());
501         // The insets in other rotations should be still updated.
502         assertEquals(doubleHeightFor90, displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90,
503                 di.logicalHeight, di.logicalWidth).mConfigInsets.bottom);
504         // Restore to previous height and the insets can still be updated.
505         bar2.mAttrs.paramsForRotation[Surface.ROTATION_90].providedInsets[0].setInsetsSize(
506                 Insets.of(0, 0, 0, NAV_BAR_HEIGHT));
507         assertFalse(displayPolicy.updateDecorInsetsInfo());
508         assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(Surface.ROTATION_90,
509                 di.logicalHeight, di.logicalWidth).mConfigInsets.bottom);
510 
511         navbar.removeIfPossible();
512         bar2.removeIfPossible();
513         assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation, di.logicalWidth,
514                 di.logicalHeight).mNonDecorInsets.bottom);
515     }
516 
517     @SetupWindows(addWindows = W_INPUT_METHOD)
518     @Test
testImeInsetsGivenContentFrame()519     public void testImeInsetsGivenContentFrame() {
520         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
521 
522         mDisplayContent.setInputMethodWindowLocked(mImeWindow);
523         makeWindowVisible(mImeWindow);
524         mImeWindow.getControllableInsetProvider().setServerVisible(true);
525 
526         mImeWindow.mGivenContentInsets.set(0, 10, 0, 0);
527 
528         displayPolicy.layoutWindowLw(mImeWindow, null, mDisplayContent.mDisplayFrames);
529         final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
530         final InsetsSource imeSource = state.peekSource(ID_IME);
531 
532         assertNotNull(imeSource);
533         assertFalse(imeSource.getFrame().isEmpty());
534         assertEquals(mImeWindow.getWindowFrames().mFrame.height() - 10,
535                 imeSource.getFrame().height());
536     }
537 
538     @SetupWindows(addWindows = { W_ACTIVITY, W_NAVIGATION_BAR })
539     @Test
testCanSystemBarsBeShownByUser()540     public void testCanSystemBarsBeShownByUser() {
541         Assume.assumeFalse(CLIENT_TRANSIENT);
542         ((TestWindowManagerPolicy) mWm.mPolicy).mIsUserSetupComplete = true;
543         mAppWindow.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
544         mAppWindow.setRequestedVisibleTypes(0, navigationBars());
545         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
546         displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
547         final InsetsSourceProvider navBarProvider = mNavBarWindow.getControllableInsetProvider();
548         navBarProvider.updateControlForTarget(mAppWindow, false, null /* statsToken */);
549         navBarProvider.getSource().setVisible(false);
550 
551         displayPolicy.setCanSystemBarsBeShownByUser(false);
552         displayPolicy.requestTransientBars(mNavBarWindow, true);
553         assertFalse(mDisplayContent.getInsetsPolicy().isTransient(navigationBars()));
554 
555         displayPolicy.setCanSystemBarsBeShownByUser(true);
556         displayPolicy.requestTransientBars(mNavBarWindow, true);
557         assertTrue(mDisplayContent.getInsetsPolicy().isTransient(navigationBars()));
558     }
559 
560     @UseTestDisplay(addWindows = { W_NAVIGATION_BAR })
561     @Test
testTransientBarsSuppressedOnDreams()562     public void testTransientBarsSuppressedOnDreams() {
563         Assume.assumeFalse(CLIENT_TRANSIENT);
564         final WindowState win = createDreamWindow();
565 
566         ((TestWindowManagerPolicy) mWm.mPolicy).mIsUserSetupComplete = true;
567         win.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
568         win.setRequestedVisibleTypes(0, navigationBars());
569 
570         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
571         displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
572         final InsetsSourceProvider navBarProvider = mNavBarWindow.getControllableInsetProvider();
573         navBarProvider.updateControlForTarget(win, false, null /* statsToken */);
574         navBarProvider.getSource().setVisible(false);
575 
576         displayPolicy.setCanSystemBarsBeShownByUser(true);
577         displayPolicy.requestTransientBars(mNavBarWindow, true);
578 
579         assertFalse(mDisplayContent.getInsetsPolicy().isTransient(navigationBars()));
580     }
581 }
582