1 /*
2  * Copyright (C) 2016 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
22 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_USER;
23 import static android.os.Build.VERSION_CODES.P;
24 import static android.os.Build.VERSION_CODES.Q;
25 import static android.view.Display.DEFAULT_DISPLAY;
26 import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
27 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
28 import static android.view.DisplayCutout.fromBoundingRect;
29 import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
30 import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
31 import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
32 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
33 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
34 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
35 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
36 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
37 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
38 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
39 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
40 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
41 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
42 
43 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
44 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
45 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
46 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
47 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
48 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
49 import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
50 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
51 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
52 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
53 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
54 import static com.android.server.wm.WindowContainer.POSITION_TOP;
55 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
56 
57 import static org.hamcrest.Matchers.is;
58 import static org.junit.Assert.assertEquals;
59 import static org.junit.Assert.assertFalse;
60 import static org.junit.Assert.assertNotNull;
61 import static org.junit.Assert.assertNull;
62 import static org.junit.Assert.assertThat;
63 import static org.junit.Assert.assertTrue;
64 import static org.mockito.ArgumentMatchers.eq;
65 
66 import android.annotation.SuppressLint;
67 import android.app.WindowConfiguration;
68 import android.content.res.Configuration;
69 import android.graphics.Rect;
70 import android.graphics.Region;
71 import android.metrics.LogMaker;
72 import android.os.SystemClock;
73 import android.platform.test.annotations.Presubmit;
74 import android.util.DisplayMetrics;
75 import android.util.MutableBoolean;
76 import android.view.DisplayCutout;
77 import android.view.Gravity;
78 import android.view.ISystemGestureExclusionListener;
79 import android.view.MotionEvent;
80 import android.view.Surface;
81 import android.view.ViewRootImpl;
82 import android.view.test.InsetsModeSession;
83 
84 import androidx.test.filters.SmallTest;
85 
86 import com.android.dx.mockito.inline.extended.ExtendedMockito;
87 import com.android.internal.logging.MetricsLogger;
88 import com.android.internal.logging.nano.MetricsProto;
89 import com.android.server.wm.utils.WmDisplayCutout;
90 
91 import org.junit.Test;
92 import org.mockito.ArgumentCaptor;
93 import org.mockito.Mockito;
94 
95 import java.util.ArrayList;
96 import java.util.Arrays;
97 import java.util.Collections;
98 import java.util.LinkedList;
99 import java.util.List;
100 
101 /**
102  * Tests for the {@link DisplayContent} class.
103  *
104  * Build/Install/Run:
105  *  atest WmTests:DisplayContentTests
106  */
107 @SmallTest
108 @Presubmit
109 public class DisplayContentTests extends WindowTestsBase {
110 
111     @Test
testForAllWindows()112     public void testForAllWindows() {
113         final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION,
114                 mDisplayContent, "exiting app");
115         final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
116         // Wait until everything in animation handler get executed to prevent the exiting window
117         // from being removed during WindowSurfacePlacer Traversal.
118         waitUntilHandlersIdle();
119 
120         exitingAppToken.mIsExiting = true;
121         exitingAppToken.getTask().mStack.mExitingAppTokens.add(exitingAppToken);
122 
123         assertForAllWindowsOrder(Arrays.asList(
124                 mWallpaperWindow,
125                 exitingAppWindow,
126                 mChildAppWindowBelow,
127                 mAppWindow,
128                 mChildAppWindowAbove,
129                 mDockedDividerWindow,
130                 mStatusBarWindow,
131                 mNavBarWindow,
132                 mImeWindow,
133                 mImeDialogWindow));
134     }
135 
136     @Test
testForAllWindows_WithAppImeTarget()137     public void testForAllWindows_WithAppImeTarget() {
138         final WindowState imeAppTarget =
139                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
140 
141         mDisplayContent.mInputMethodTarget = imeAppTarget;
142 
143         assertForAllWindowsOrder(Arrays.asList(
144                 mWallpaperWindow,
145                 mChildAppWindowBelow,
146                 mAppWindow,
147                 mChildAppWindowAbove,
148                 imeAppTarget,
149                 mImeWindow,
150                 mImeDialogWindow,
151                 mDockedDividerWindow,
152                 mStatusBarWindow,
153                 mNavBarWindow));
154     }
155 
156     @Test
testForAllWindows_WithChildWindowImeTarget()157     public void testForAllWindows_WithChildWindowImeTarget() throws Exception {
158         mDisplayContent.mInputMethodTarget = mChildAppWindowAbove;
159 
160         assertForAllWindowsOrder(Arrays.asList(
161                 mWallpaperWindow,
162                 mChildAppWindowBelow,
163                 mAppWindow,
164                 mChildAppWindowAbove,
165                 mImeWindow,
166                 mImeDialogWindow,
167                 mDockedDividerWindow,
168                 mStatusBarWindow,
169                 mNavBarWindow));
170     }
171 
172     @Test
testForAllWindows_WithStatusBarImeTarget()173     public void testForAllWindows_WithStatusBarImeTarget() throws Exception {
174         mDisplayContent.mInputMethodTarget = mStatusBarWindow;
175 
176         assertForAllWindowsOrder(Arrays.asList(
177                 mWallpaperWindow,
178                 mChildAppWindowBelow,
179                 mAppWindow,
180                 mChildAppWindowAbove,
181                 mDockedDividerWindow,
182                 mStatusBarWindow,
183                 mImeWindow,
184                 mImeDialogWindow,
185                 mNavBarWindow));
186     }
187 
188     @Test
testForAllWindows_WithInBetweenWindowToken()189     public void testForAllWindows_WithInBetweenWindowToken() {
190         // This window is set-up to be z-ordered between some windows that go in the same token like
191         // the nav bar and status bar.
192         final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION,
193                 mDisplayContent, "voiceInteractionWindow");
194 
195         assertForAllWindowsOrder(Arrays.asList(
196                 mWallpaperWindow,
197                 mChildAppWindowBelow,
198                 mAppWindow,
199                 mChildAppWindowAbove,
200                 mDockedDividerWindow,
201                 voiceInteractionWindow,
202                 mStatusBarWindow,
203                 mNavBarWindow,
204                 mImeWindow,
205                 mImeDialogWindow));
206     }
207 
208     @Test
testComputeImeTarget()209     public void testComputeImeTarget() {
210         // Verify that an app window can be an ime target.
211         final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
212         appWin.setHasSurface(true);
213         assertTrue(appWin.canBeImeTarget());
214         WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
215         assertEquals(appWin, imeTarget);
216         appWin.mHidden = false;
217 
218         // Verify that an child window can be an ime target.
219         final WindowState childWin = createWindow(appWin,
220                 TYPE_APPLICATION_ATTACHED_DIALOG, "childWin");
221         childWin.setHasSurface(true);
222         assertTrue(childWin.canBeImeTarget());
223         imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
224         assertEquals(childWin, imeTarget);
225     }
226 
227     /**
228      * This tests stack movement between displays and proper stack's, task's and app token's display
229      * container references updates.
230      */
231     @Test
testMoveStackBetweenDisplays()232     public void testMoveStackBetweenDisplays() {
233         // Create a second display.
234         final DisplayContent dc = createNewDisplay();
235 
236         // Add stack with activity.
237         final TaskStack stack = createTaskStackOnDisplay(dc);
238         assertEquals(dc.getDisplayId(), stack.getDisplayContent().getDisplayId());
239         assertEquals(dc, stack.getParent().getParent());
240         assertEquals(dc, stack.getDisplayContent());
241 
242         final Task task = createTaskInStack(stack, 0 /* userId */);
243         final WindowTestUtils.TestAppWindowToken token = WindowTestUtils.createTestAppWindowToken(
244                 dc);
245         task.addChild(token, 0);
246         assertEquals(dc, task.getDisplayContent());
247         assertEquals(dc, token.getDisplayContent());
248 
249         // Move stack to first display.
250         mDisplayContent.moveStackToDisplay(stack, true /* onTop */);
251         assertEquals(mDisplayContent.getDisplayId(), stack.getDisplayContent().getDisplayId());
252         assertEquals(mDisplayContent, stack.getParent().getParent());
253         assertEquals(mDisplayContent, stack.getDisplayContent());
254         assertEquals(mDisplayContent, task.getDisplayContent());
255         assertEquals(mDisplayContent, token.getDisplayContent());
256     }
257 
258     /**
259      * This tests override configuration updates for display content.
260      */
261     @Test
testDisplayOverrideConfigUpdate()262     public void testDisplayOverrideConfigUpdate() {
263         final Configuration currentOverrideConfig =
264                 mDisplayContent.getRequestedOverrideConfiguration();
265 
266         // Create new, slightly changed override configuration and apply it to the display.
267         final Configuration newOverrideConfig = new Configuration(currentOverrideConfig);
268         newOverrideConfig.densityDpi += 120;
269         newOverrideConfig.fontScale += 0.3;
270 
271         mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, mDisplayContent);
272 
273         // Check that override config is applied.
274         assertEquals(newOverrideConfig, mDisplayContent.getRequestedOverrideConfiguration());
275     }
276 
277     /**
278      * This tests global configuration updates when default display config is updated.
279      */
280     @Test
testDefaultDisplayOverrideConfigUpdate()281     public void testDefaultDisplayOverrideConfigUpdate() {
282         DisplayContent defaultDisplay = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
283         final Configuration currentConfig = defaultDisplay.getConfiguration();
284 
285         // Create new, slightly changed override configuration and apply it to the display.
286         final Configuration newOverrideConfig = new Configuration(currentConfig);
287         newOverrideConfig.densityDpi += 120;
288         newOverrideConfig.fontScale += 0.3;
289 
290         mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, defaultDisplay);
291 
292         // Check that global configuration is updated, as we've updated default display's config.
293         Configuration globalConfig = mWm.mRoot.getConfiguration();
294         assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi);
295         assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
296 
297         // Return back to original values.
298         mWm.setNewDisplayOverrideConfiguration(currentConfig, defaultDisplay);
299         globalConfig = mWm.mRoot.getConfiguration();
300         assertEquals(currentConfig.densityDpi, globalConfig.densityDpi);
301         assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
302     }
303 
304     /**
305      * Tests tapping on a stack in different display results in window gaining focus.
306      */
307     @Test
testInputEventBringsCorrectDisplayInFocus()308     public void testInputEventBringsCorrectDisplayInFocus() {
309         DisplayContent dc0 = mWm.getDefaultDisplayContentLocked();
310         // Create a second display
311         final DisplayContent dc1 = createNewDisplay();
312 
313         // Add stack with activity.
314         final TaskStack stack0 = createTaskStackOnDisplay(dc0);
315         final Task task0 = createTaskInStack(stack0, 0 /* userId */);
316         final WindowTestUtils.TestAppWindowToken token =
317                 WindowTestUtils.createTestAppWindowToken(dc0);
318         task0.addChild(token, 0);
319         dc0.configureDisplayPolicy();
320         assertNotNull(dc0.mTapDetector);
321 
322         final TaskStack stack1 = createTaskStackOnDisplay(dc1);
323         final Task task1 = createTaskInStack(stack1, 0 /* userId */);
324         final WindowTestUtils.TestAppWindowToken token1 =
325                 WindowTestUtils.createTestAppWindowToken(dc0);
326         task1.addChild(token1, 0);
327         dc1.configureDisplayPolicy();
328         assertNotNull(dc1.mTapDetector);
329 
330         // tap on primary display.
331         tapOnDisplay(dc0);
332         // Check focus is on primary display.
333         assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
334                 dc0.findFocusedWindow());
335 
336         // Tap on secondary display.
337         tapOnDisplay(dc1);
338         // Check focus is on secondary.
339         assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
340                 dc1.findFocusedWindow());
341     }
342 
343     @Test
testFocusedWindowMultipleDisplays()344     public void testFocusedWindowMultipleDisplays() {
345         doTestFocusedWindowMultipleDisplays(false /* perDisplayFocusEnabled */, Q);
346     }
347 
348     @Test
testFocusedWindowMultipleDisplaysPerDisplayFocusEnabled()349     public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabled() {
350         doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, Q);
351     }
352 
353     @Test
testFocusedWindowMultipleDisplaysPerDisplayFocusEnabledLegacyApp()354     public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabledLegacyApp() {
355         doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, P);
356     }
357 
doTestFocusedWindowMultipleDisplays(boolean perDisplayFocusEnabled, int targetSdk)358     private void doTestFocusedWindowMultipleDisplays(boolean perDisplayFocusEnabled,
359             int targetSdk) {
360         mWm.mPerDisplayFocusEnabled = perDisplayFocusEnabled;
361 
362         // Create a focusable window and check that focus is calculated correctly
363         final WindowState window1 =
364                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1");
365         window1.mAppToken.mTargetSdk = targetSdk;
366         updateFocusedWindow();
367         assertTrue(window1.isFocused());
368         assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
369 
370         // Check that a new display doesn't affect focus
371         final DisplayContent dc = createNewDisplay();
372         updateFocusedWindow();
373         assertTrue(window1.isFocused());
374         assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
375 
376         // Add a window to the second display, and it should be focused
377         final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2");
378         window2.mAppToken.mTargetSdk = targetSdk;
379         updateFocusedWindow();
380         assertTrue(window2.isFocused());
381         assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window1.isFocused());
382         assertEquals(window2, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
383 
384         // Move the first window to top including parents, and make sure focus is updated
385         window1.getParent().positionChildAt(POSITION_TOP, window1, true);
386         updateFocusedWindow();
387         assertTrue(window1.isFocused());
388         assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window2.isFocused());
389         assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
390 
391         // Make sure top focused display not changed if there is a focused app.
392         window1.mAppToken.hiddenRequested = true;
393         window1.getDisplayContent().setFocusedApp(window1.mAppToken);
394         updateFocusedWindow();
395         assertTrue(!window1.isFocused());
396         assertEquals(window1.getDisplayId(),
397                 mWm.mRoot.getTopFocusedDisplayContent().getDisplayId());
398     }
399 
400     /**
401      * This tests setting the maximum ui width on a display.
402      */
403     @Test
testMaxUiWidth()404     public void testMaxUiWidth() {
405         // Prevent base display metrics for test from being updated to the value of real display.
406         final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
407         final int baseWidth = 1440;
408         final int baseHeight = 2560;
409         final int baseDensity = 300;
410 
411         displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
412 
413         final int maxWidth = 300;
414         final int resultingHeight = (maxWidth * baseHeight) / baseWidth;
415         final int resultingDensity = (maxWidth * baseDensity) / baseWidth;
416 
417         displayContent.setMaxUiWidth(maxWidth);
418         verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
419 
420         // Assert setting values again does not change;
421         displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
422         verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
423 
424         final int smallerWidth = 200;
425         final int smallerHeight = 400;
426         final int smallerDensity = 100;
427 
428         // Specify smaller dimension, verify that it is honored
429         displayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity);
430         verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity);
431 
432         // Verify that setting the max width to a greater value than the base width has no effect
433         displayContent.setMaxUiWidth(maxWidth);
434         verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity);
435     }
436 
437     @Test
testDisplayCutout_rot0()438     public void testDisplayCutout_rot0() {
439         synchronized (mWm.getWindowManagerLock()) {
440             final DisplayContent dc = createNewDisplay();
441             dc.mInitialDisplayWidth = 200;
442             dc.mInitialDisplayHeight = 400;
443             Rect r = new Rect(80, 0, 120, 10);
444             final DisplayCutout cutout = new WmDisplayCutout(
445                     fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_TOP), null)
446                     .computeSafeInsets(200, 400).getDisplayCutout();
447 
448             dc.mInitialDisplayCutout = cutout;
449             dc.setRotation(Surface.ROTATION_0);
450             dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
451 
452             assertEquals(cutout, dc.getDisplayInfo().displayCutout);
453         }
454     }
455 
456     @Test
testDisplayCutout_rot90()457     public void testDisplayCutout_rot90() {
458         synchronized (mWm.getWindowManagerLock()) {
459             // Prevent mInitialDisplayCutout from being updated from real display (e.g. null
460             // if the device has no cutout).
461             final DisplayContent dc = createDisplayNoUpdateDisplayInfo();
462             // Rotation may use real display info to compute bound, so here also uses the
463             // same width and height.
464             final int displayWidth = dc.mInitialDisplayWidth;
465             final int displayHeight = dc.mInitialDisplayHeight;
466             final int cutoutWidth = 40;
467             final int cutoutHeight = 10;
468             final int left = (displayWidth - cutoutWidth) / 2;
469             final int top = 0;
470             final int right = (displayWidth + cutoutWidth) / 2;
471             final int bottom = cutoutHeight;
472 
473             final Rect r1 = new Rect(left, top, right, bottom);
474             final DisplayCutout cutout = new WmDisplayCutout(
475                     fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP),
476                     null)
477                     .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
478 
479             dc.mInitialDisplayCutout = cutout;
480             dc.setRotation(Surface.ROTATION_90);
481             dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
482 
483             // ----o----------      -------------
484             // |   |     |   |      |
485             // |   ------o   |      o---
486             // |             |      |  |
487             // |             |  ->  |  |
488             // |             |      ---o
489             // |             |      |
490             // |             |      -------------
491             final Rect r = new Rect(top, left, bottom, right);
492             assertEquals(new WmDisplayCutout(
493                     fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null)
494                     .computeSafeInsets(displayHeight, displayWidth)
495                     .getDisplayCutout(), dc.getDisplayInfo().displayCutout);
496         }
497     }
498 
499     @Test
testLayoutSeq_assignedDuringLayout()500     public void testLayoutSeq_assignedDuringLayout() {
501         synchronized (mWm.getWindowManagerLock()) {
502 
503             final DisplayContent dc = createNewDisplay();
504             final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
505 
506             dc.setLayoutNeeded();
507             dc.performLayout(true /* initial */, false /* updateImeWindows */);
508 
509             assertThat(win.mLayoutSeq, is(dc.mLayoutSeq));
510         }
511     }
512 
513     @Test
514     @SuppressLint("InlinedApi")
testOrientationDefinedByKeyguard()515     public void testOrientationDefinedByKeyguard() {
516         final DisplayContent dc = createNewDisplay();
517 
518         // When display content is created its configuration is not yet initialized, which could
519         // cause unnecessary configuration propagation, so initialize it here.
520         final Configuration config = new Configuration();
521         dc.computeScreenConfiguration(config);
522         dc.onRequestedOverrideConfigurationChanged(config);
523 
524         // Create a window that requests landscape orientation. It will define device orientation
525         // by default.
526         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
527         window.mAppToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
528 
529         final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR, dc, "keyguard");
530         keyguard.mHasSurface = true;
531         keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
532 
533         assertEquals("Screen orientation must be defined by the app window by default",
534                 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
535 
536         keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
537         assertEquals("Visible keyguard must influence device orientation",
538                 SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation());
539 
540         mWm.setKeyguardGoingAway(true);
541         assertEquals("Keyguard that is going away must not influence device orientation",
542                 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
543     }
544 
545     @Test
testOrientationForAspectRatio()546     public void testOrientationForAspectRatio() {
547         final DisplayContent dc = createNewDisplay();
548 
549         // When display content is created its configuration is not yet initialized, which could
550         // cause unnecessary configuration propagation, so initialize it here.
551         final Configuration config = new Configuration();
552         dc.computeScreenConfiguration(config);
553         dc.onRequestedOverrideConfigurationChanged(config);
554 
555         // Create a window that requests a fixed orientation. It will define device orientation
556         // by default.
557         final WindowState window = createWindow(null /* parent */, TYPE_APPLICATION_OVERLAY, dc,
558                 "window");
559         window.mHasSurface = true;
560         window.mAttrs.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE;
561 
562         // --------------------------------
563         // Test non-close-to-square display
564         // --------------------------------
565         dc.mBaseDisplayWidth = 1000;
566         dc.mBaseDisplayHeight = (int) (dc.mBaseDisplayWidth * dc.mCloseToSquareMaxAspectRatio * 2f);
567         dc.configureDisplayPolicy();
568 
569         assertEquals("Screen orientation must be defined by the window by default.",
570                 window.mAttrs.screenOrientation, dc.getOrientation());
571 
572         // ----------------------------
573         // Test close-to-square display
574         // ----------------------------
575         dc.mBaseDisplayHeight = dc.mBaseDisplayWidth;
576         dc.configureDisplayPolicy();
577 
578         assertEquals("Screen orientation must be SCREEN_ORIENTATION_USER.",
579                 SCREEN_ORIENTATION_USER, dc.getOrientation());
580     }
581 
582     @Test
testDisableDisplayInfoOverrideFromWindowManager()583     public void testDisableDisplayInfoOverrideFromWindowManager() {
584         final DisplayContent dc = createNewDisplay();
585 
586         assertTrue(dc.mShouldOverrideDisplayConfiguration);
587         mWm.dontOverrideDisplayInfo(dc.getDisplayId());
588 
589         assertFalse(dc.mShouldOverrideDisplayConfiguration);
590         verify(mWm.mDisplayManagerInternal, times(1))
591                 .setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null);
592     }
593 
594     @Test
testClearLastFocusWhenReparentingFocusedWindow()595     public void testClearLastFocusWhenReparentingFocusedWindow() {
596         final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
597         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
598                 defaultDisplay, "window");
599         defaultDisplay.mLastFocus = window;
600         mDisplayContent.mCurrentFocus = window;
601         mDisplayContent.reParentWindowToken(window.mToken);
602 
603         assertNull(defaultDisplay.mLastFocus);
604     }
605 
606     @Test
testGetPreferredOptionsPanelGravityFromDifferentDisplays()607     public void testGetPreferredOptionsPanelGravityFromDifferentDisplays() {
608         final DisplayContent portraitDisplay = createNewDisplay();
609         portraitDisplay.mInitialDisplayHeight = 2000;
610         portraitDisplay.mInitialDisplayWidth = 1000;
611 
612         portraitDisplay.setRotation(Surface.ROTATION_0);
613         assertFalse(isOptionsPanelAtRight(portraitDisplay.getDisplayId()));
614         portraitDisplay.setRotation(Surface.ROTATION_90);
615         assertTrue(isOptionsPanelAtRight(portraitDisplay.getDisplayId()));
616 
617         final DisplayContent landscapeDisplay = createNewDisplay();
618         landscapeDisplay.mInitialDisplayHeight = 1000;
619         landscapeDisplay.mInitialDisplayWidth = 2000;
620 
621         landscapeDisplay.setRotation(Surface.ROTATION_0);
622         assertTrue(isOptionsPanelAtRight(landscapeDisplay.getDisplayId()));
623         landscapeDisplay.setRotation(Surface.ROTATION_90);
624         assertFalse(isOptionsPanelAtRight(landscapeDisplay.getDisplayId()));
625     }
626 
627     @Test
testInputMethodTargetUpdateWhenSwitchingOnDisplays()628     public void testInputMethodTargetUpdateWhenSwitchingOnDisplays() {
629         final DisplayContent newDisplay = createNewDisplay();
630 
631         final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
632         final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1");
633         appWin.setHasSurface(true);
634         appWin1.setHasSurface(true);
635 
636         // Set current input method window on default display, make sure the input method target
637         // is appWin & null on the other display.
638         mDisplayContent.setInputMethodWindowLocked(mImeWindow);
639         newDisplay.setInputMethodWindowLocked(null);
640         assertTrue("appWin should be IME target window",
641                 appWin.equals(mDisplayContent.mInputMethodTarget));
642         assertNull("newDisplay Ime target: ", newDisplay.mInputMethodTarget);
643 
644         // Switch input method window on new display & make sure the input method target also
645         // switched as expected.
646         newDisplay.setInputMethodWindowLocked(mImeWindow);
647         mDisplayContent.setInputMethodWindowLocked(null);
648         assertTrue("appWin1 should be IME target window",
649                 appWin1.equals(newDisplay.mInputMethodTarget));
650         assertNull("default display Ime target: ", mDisplayContent.mInputMethodTarget);
651     }
652 
653     @Test
testOnDescendantOrientationRequestChanged()654     public void testOnDescendantOrientationRequestChanged() {
655         final DisplayContent dc = createNewDisplay();
656         mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
657         final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
658                 ? SCREEN_ORIENTATION_PORTRAIT
659                 : SCREEN_ORIENTATION_LANDSCAPE;
660 
661         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
662         window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
663         window.mAppToken.setOrientation(newOrientation);
664 
665         ActivityRecord activityRecord = mock(ActivityRecord.class);
666 
667         assertTrue("Display should rotate to handle orientation request by default.",
668                 dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
669 
670         final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
671         verify(mWm.mAtmService).updateDisplayOverrideConfigurationLocked(captor.capture(),
672                 same(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
673         final Configuration newDisplayConfig = captor.getValue();
674         assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
675     }
676 
677     @Test
testOnDescendantOrientationRequestChanged_FrozenToUserRotation()678     public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
679         final DisplayContent dc = createNewDisplay();
680         dc.getDisplayRotation().setFixedToUserRotation(
681                 DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
682         mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
683         final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
684                 ? SCREEN_ORIENTATION_PORTRAIT
685                 : SCREEN_ORIENTATION_LANDSCAPE;
686 
687         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
688         window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
689         window.mAppToken.setOrientation(newOrientation);
690 
691         ActivityRecord activityRecord = mock(ActivityRecord.class);
692 
693         assertFalse("Display shouldn't rotate to handle orientation request if fixed to"
694                         + " user rotation.",
695                 dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
696         verify(mWm.mAtmService, never()).updateDisplayOverrideConfigurationLocked(any(),
697                 eq(activityRecord), anyBoolean(), eq(dc.getDisplayId()));
698     }
699 
700     @Test
testComputeImeParent_app()701     public void testComputeImeParent_app() throws Exception {
702         try (final InsetsModeSession session =
703                      new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
704             final DisplayContent dc = createNewDisplay();
705             dc.mInputMethodTarget = createWindow(null, TYPE_BASE_APPLICATION, "app");
706             assertEquals(dc.mInputMethodTarget.mAppToken.getSurfaceControl(),
707                     dc.computeImeParent());
708         }
709     }
710 
711     @Test
testComputeImeParent_app_notFullscreen()712     public void testComputeImeParent_app_notFullscreen() throws Exception {
713         try (final InsetsModeSession session =
714                      new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
715             final DisplayContent dc = createNewDisplay();
716             dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "app");
717             dc.mInputMethodTarget.setWindowingMode(
718                     WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
719             assertEquals(dc.getWindowingLayer(), dc.computeImeParent());
720         }
721     }
722 
723     @Test
testComputeImeParent_app_notMatchParentBounds()724     public void testComputeImeParent_app_notMatchParentBounds() {
725         spyOn(mAppWindow.mAppToken);
726         doReturn(false).when(mAppWindow.mAppToken).matchParentBounds();
727         mDisplayContent.mInputMethodTarget = mAppWindow;
728         // The surface parent of IME should be the display instead of app window.
729         assertEquals(mDisplayContent.getWindowingLayer(), mDisplayContent.computeImeParent());
730     }
731 
732     @Test
testComputeImeParent_noApp()733     public void testComputeImeParent_noApp() throws Exception {
734         try (final InsetsModeSession session =
735                      new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_IME)) {
736             final DisplayContent dc = createNewDisplay();
737             dc.mInputMethodTarget = createWindow(null, TYPE_STATUS_BAR, "statusBar");
738             assertEquals(dc.getWindowingLayer(), dc.computeImeParent());
739         }
740     }
741 
742     @Test
testUpdateSystemGestureExclusion()743     public void testUpdateSystemGestureExclusion() throws Exception {
744         final DisplayContent dc = createNewDisplay();
745         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
746         win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
747         win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
748 
749         dc.setLayoutNeeded();
750         dc.performLayout(true /* initial */, false /* updateImeWindows */);
751 
752         win.setHasSurface(true);
753         dc.updateSystemGestureExclusion();
754 
755         final MutableBoolean invoked = new MutableBoolean(false);
756         final ISystemGestureExclusionListener.Stub verifier =
757                 new ISystemGestureExclusionListener.Stub() {
758             @Override
759             public void onSystemGestureExclusionChanged(int displayId, Region actual,
760                     Region unrestricted) {
761                 Region expected = Region.obtain();
762                 expected.set(10, 20, 30, 40);
763                 assertEquals(expected, actual);
764                 invoked.value = true;
765             }
766         };
767         try {
768             dc.registerSystemGestureExclusionListener(verifier);
769         } finally {
770             dc.unregisterSystemGestureExclusionListener(verifier);
771         }
772         assertTrue("SystemGestureExclusionListener was not invoked", invoked.value);
773     }
774 
775     @Test
testCalculateSystemGestureExclusion()776     public void testCalculateSystemGestureExclusion() throws Exception {
777         final DisplayContent dc = createNewDisplay();
778         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
779         win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
780         win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
781 
782         final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "win2");
783         win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
784         win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50)));
785 
786         dc.setLayoutNeeded();
787         dc.performLayout(true /* initial */, false /* updateImeWindows */);
788 
789         win.setHasSurface(true);
790         win2.setHasSurface(true);
791 
792         final Region expected = Region.obtain();
793         expected.set(20, 30, 40, 50);
794         assertEquals(expected, calculateSystemGestureExclusion(dc));
795     }
796 
calculateSystemGestureExclusion(DisplayContent dc)797     private Region calculateSystemGestureExclusion(DisplayContent dc) {
798         Region out = Region.obtain();
799         Region unrestricted = Region.obtain();
800         dc.calculateSystemGestureExclusion(out, unrestricted);
801         return out;
802     }
803 
804     @Test
testCalculateSystemGestureExclusion_modal()805     public void testCalculateSystemGestureExclusion_modal() throws Exception {
806         final DisplayContent dc = createNewDisplay();
807         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "base");
808         win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
809         win.setSystemGestureExclusion(Collections.singletonList(new Rect(0, 0, 1000, 1000)));
810 
811         final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "modal");
812         win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
813         win2.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
814         win2.getAttrs().width = 10;
815         win2.getAttrs().height = 10;
816         win2.setSystemGestureExclusion(Collections.emptyList());
817 
818         dc.setLayoutNeeded();
819         dc.performLayout(true /* initial */, false /* updateImeWindows */);
820 
821         win.setHasSurface(true);
822         win2.setHasSurface(true);
823 
824         final Region expected = Region.obtain();
825         assertEquals(expected, calculateSystemGestureExclusion(dc));
826     }
827 
828     @Test
testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow()829     public void testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow() throws Exception {
830         synchronized (mWm.mGlobalLock) {
831             mWm.mSystemGestureExcludedByPreQStickyImmersive = true;
832 
833             final DisplayContent dc = createNewDisplay();
834             final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
835             win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
836             win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
837             win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
838             win.getAttrs().subtreeSystemUiVisibility = win.mSystemUiVisibility =
839                     SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION
840                             | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
841             win.mAppToken.mTargetSdk = P;
842 
843             dc.setLayoutNeeded();
844             dc.performLayout(true /* initial */, false /* updateImeWindows */);
845 
846             win.setHasSurface(true);
847 
848             final Region expected = Region.obtain();
849             expected.set(dc.getBounds());
850             assertEquals(expected, calculateSystemGestureExclusion(dc));
851 
852             win.setHasSurface(false);
853         }
854     }
855 
856     @Test
testOrientationChangeLogging()857     public void testOrientationChangeLogging() {
858         MetricsLogger mockLogger = mock(MetricsLogger.class);
859         Configuration oldConfig = new Configuration();
860         oldConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
861 
862         Configuration newConfig = new Configuration();
863         newConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
864         final DisplayContent displayContent = spy(createNewDisplay());
865         Mockito.doReturn(mockLogger).when(displayContent).getMetricsLogger();
866         Mockito.doReturn(oldConfig).doReturn(newConfig).when(displayContent).getConfiguration();
867 
868         displayContent.onConfigurationChanged(newConfig);
869 
870         ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
871         verify(mockLogger).write(logMakerCaptor.capture());
872         assertThat(logMakerCaptor.getValue().getCategory(),
873                 is(MetricsProto.MetricsEvent.ACTION_PHONE_ORIENTATION_CHANGED));
874         assertThat(logMakerCaptor.getValue().getSubtype(),
875                 is(Configuration.ORIENTATION_PORTRAIT));
876     }
877 
isOptionsPanelAtRight(int displayId)878     private boolean isOptionsPanelAtRight(int displayId) {
879         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
880     }
881 
verifySizes(DisplayContent displayContent, int expectedBaseWidth, int expectedBaseHeight, int expectedBaseDensity)882     private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth,
883                              int expectedBaseHeight, int expectedBaseDensity) {
884         assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth);
885         assertEquals(displayContent.mBaseDisplayHeight, expectedBaseHeight);
886         assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity);
887     }
888 
updateFocusedWindow()889     private void updateFocusedWindow() {
890         synchronized (mWm.mGlobalLock) {
891             mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false);
892         }
893     }
894 
895     /**
896      * Create DisplayContent that does not update display base/initial values from device to keep
897      * the values set by test.
898      */
createDisplayNoUpdateDisplayInfo()899     private DisplayContent createDisplayNoUpdateDisplayInfo() {
900         final DisplayContent displayContent = spy(createNewDisplay());
901         doNothing().when(displayContent).updateDisplayInfo();
902         return displayContent;
903     }
904 
assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop)905     private void assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop) {
906         final LinkedList<WindowState> actualWindows = new LinkedList<>();
907 
908         // Test forward traversal.
909         mDisplayContent.forAllWindows(actualWindows::addLast, false /* traverseTopToBottom */);
910         assertThat("bottomToTop", actualWindows, is(expectedWindowsBottomToTop));
911 
912         actualWindows.clear();
913 
914         // Test backward traversal.
915         mDisplayContent.forAllWindows(actualWindows::addLast, true /* traverseTopToBottom */);
916         assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop)));
917     }
918 
reverseList(List<WindowState> list)919     private static List<WindowState> reverseList(List<WindowState> list) {
920         final ArrayList<WindowState> result = new ArrayList<>(list);
921         Collections.reverse(result);
922         return result;
923     }
924 
tapOnDisplay(final DisplayContent dc)925     private void tapOnDisplay(final DisplayContent dc) {
926         final DisplayMetrics dm = dc.getDisplayMetrics();
927         final float x = dm.widthPixels / 2;
928         final float y = dm.heightPixels / 2;
929         final long downTime = SystemClock.uptimeMillis();
930         final long eventTime = SystemClock.uptimeMillis() + 100;
931         // sending ACTION_DOWN
932         final MotionEvent downEvent = MotionEvent.obtain(
933                 downTime,
934                 downTime,
935                 MotionEvent.ACTION_DOWN,
936                 x,
937                 y,
938                 0 /*metaState*/);
939         downEvent.setDisplayId(dc.getDisplayId());
940         dc.mTapDetector.onPointerEvent(downEvent);
941 
942         // sending ACTION_UP
943         final MotionEvent upEvent = MotionEvent.obtain(
944                 downTime,
945                 eventTime,
946                 MotionEvent.ACTION_UP,
947                 x,
948                 y,
949                 0 /*metaState*/);
950         upEvent.setDisplayId(dc.getDisplayId());
951         dc.mTapDetector.onPointerEvent(upEvent);
952     }
953 }
954