OSDN Git Service

Merge "docs: Add documentation for equals() method" into qt-dev
[android-x86/frameworks-base.git] / services / tests / wmtests / src / com / android / server / wm / DisplayContentTests.java
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
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
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
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
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
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
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
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
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
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
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
344     public void testFocusedWindowMultipleDisplays() {
345         doTestFocusedWindowMultipleDisplays(false /* perDisplayFocusEnabled */, Q);
346     }
347
348     @Test
349     public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabled() {
350         doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, Q);
351     }
352
353     @Test
354     public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabledLegacyApp() {
355         doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, P);
356     }
357
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
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
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
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
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")
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
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
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
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
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
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
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
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
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
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
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
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
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 expected = Region.obtain();
761                 expected.set(10, 20, 30, 40);
762                 assertEquals(expected, actual);
763                 invoked.value = true;
764             }
765         };
766         try {
767             dc.registerSystemGestureExclusionListener(verifier);
768         } finally {
769             dc.unregisterSystemGestureExclusionListener(verifier);
770         }
771         assertTrue("SystemGestureExclusionListener was not invoked", invoked.value);
772     }
773
774     @Test
775     public void testCalculateSystemGestureExclusion() throws Exception {
776         final DisplayContent dc = createNewDisplay();
777         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
778         win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
779         win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
780
781         final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "win2");
782         win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
783         win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50)));
784
785         dc.setLayoutNeeded();
786         dc.performLayout(true /* initial */, false /* updateImeWindows */);
787
788         win.setHasSurface(true);
789         win2.setHasSurface(true);
790
791         final Region expected = Region.obtain();
792         expected.set(20, 30, 40, 50);
793         assertEquals(expected, dc.calculateSystemGestureExclusion());
794     }
795
796     @Test
797     public void testCalculateSystemGestureExclusion_modal() throws Exception {
798         final DisplayContent dc = createNewDisplay();
799         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "base");
800         win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
801         win.setSystemGestureExclusion(Collections.singletonList(new Rect(0, 0, 1000, 1000)));
802
803         final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "modal");
804         win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
805         win2.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
806         win2.getAttrs().width = 10;
807         win2.getAttrs().height = 10;
808         win2.setSystemGestureExclusion(Collections.emptyList());
809
810         dc.setLayoutNeeded();
811         dc.performLayout(true /* initial */, false /* updateImeWindows */);
812
813         win.setHasSurface(true);
814         win2.setHasSurface(true);
815
816         final Region expected = Region.obtain();
817         assertEquals(expected, dc.calculateSystemGestureExclusion());
818     }
819
820     @Test
821     public void testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow() throws Exception {
822         synchronized (mWm.mGlobalLock) {
823             mWm.mSystemGestureExcludedByPreQStickyImmersive = true;
824
825             final DisplayContent dc = createNewDisplay();
826             final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
827             win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
828             win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
829             win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
830             win.getAttrs().subtreeSystemUiVisibility = win.mSystemUiVisibility =
831                     SYSTEM_UI_FLAG_FULLSCREEN | SYSTEM_UI_FLAG_HIDE_NAVIGATION
832                             | SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
833             win.mAppToken.mTargetSdk = P;
834
835             dc.setLayoutNeeded();
836             dc.performLayout(true /* initial */, false /* updateImeWindows */);
837
838             win.setHasSurface(true);
839
840             final Region expected = Region.obtain();
841             expected.set(dc.getBounds());
842             assertEquals(expected, dc.calculateSystemGestureExclusion());
843
844             win.setHasSurface(false);
845         }
846     }
847
848     @Test
849     public void testOrientationChangeLogging() {
850         MetricsLogger mockLogger = mock(MetricsLogger.class);
851         Configuration oldConfig = new Configuration();
852         oldConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
853
854         Configuration newConfig = new Configuration();
855         newConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
856         final DisplayContent displayContent = spy(createNewDisplay());
857         Mockito.doReturn(mockLogger).when(displayContent).getMetricsLogger();
858         Mockito.doReturn(oldConfig).doReturn(newConfig).when(displayContent).getConfiguration();
859
860         displayContent.onConfigurationChanged(newConfig);
861
862         ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
863         verify(mockLogger).write(logMakerCaptor.capture());
864         assertThat(logMakerCaptor.getValue().getCategory(),
865                 is(MetricsProto.MetricsEvent.ACTION_PHONE_ORIENTATION_CHANGED));
866         assertThat(logMakerCaptor.getValue().getSubtype(),
867                 is(Configuration.ORIENTATION_PORTRAIT));
868     }
869
870     private boolean isOptionsPanelAtRight(int displayId) {
871         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
872     }
873
874     private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth,
875                              int expectedBaseHeight, int expectedBaseDensity) {
876         assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth);
877         assertEquals(displayContent.mBaseDisplayHeight, expectedBaseHeight);
878         assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity);
879     }
880
881     private void updateFocusedWindow() {
882         synchronized (mWm.mGlobalLock) {
883             mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false);
884         }
885     }
886
887     /**
888      * Create DisplayContent that does not update display base/initial values from device to keep
889      * the values set by test.
890      */
891     private DisplayContent createDisplayNoUpdateDisplayInfo() {
892         final DisplayContent displayContent = spy(createNewDisplay());
893         doNothing().when(displayContent).updateDisplayInfo();
894         return displayContent;
895     }
896
897     private void assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop) {
898         final LinkedList<WindowState> actualWindows = new LinkedList<>();
899
900         // Test forward traversal.
901         mDisplayContent.forAllWindows(actualWindows::addLast, false /* traverseTopToBottom */);
902         assertThat("bottomToTop", actualWindows, is(expectedWindowsBottomToTop));
903
904         actualWindows.clear();
905
906         // Test backward traversal.
907         mDisplayContent.forAllWindows(actualWindows::addLast, true /* traverseTopToBottom */);
908         assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop)));
909     }
910
911     private static List<WindowState> reverseList(List<WindowState> list) {
912         final ArrayList<WindowState> result = new ArrayList<>(list);
913         Collections.reverse(result);
914         return result;
915     }
916
917     private void tapOnDisplay(final DisplayContent dc) {
918         final DisplayMetrics dm = dc.getDisplayMetrics();
919         final float x = dm.widthPixels / 2;
920         final float y = dm.heightPixels / 2;
921         final long downTime = SystemClock.uptimeMillis();
922         final long eventTime = SystemClock.uptimeMillis() + 100;
923         // sending ACTION_DOWN
924         final MotionEvent downEvent = MotionEvent.obtain(
925                 downTime,
926                 downTime,
927                 MotionEvent.ACTION_DOWN,
928                 x,
929                 y,
930                 0 /*metaState*/);
931         downEvent.setDisplayId(dc.getDisplayId());
932         dc.mTapDetector.onPointerEvent(downEvent);
933
934         // sending ACTION_UP
935         final MotionEvent upEvent = MotionEvent.obtain(
936                 downTime,
937                 eventTime,
938                 MotionEvent.ACTION_UP,
939                 x,
940                 y,
941                 0 /*metaState*/);
942         upEvent.setDisplayId(dc.getDisplayId());
943         dc.mTapDetector.onPointerEvent(upEvent);
944     }
945 }