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.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
23 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
24 import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
25 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
26 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
27 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
28 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
29 import static android.os.Build.VERSION_CODES.P;
30 import static android.os.Build.VERSION_CODES.Q;
31 import static android.view.Display.DEFAULT_DISPLAY;
32 import static android.view.Display.FLAG_PRIVATE;
33 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
34 import static android.view.DisplayCutout.fromBoundingRect;
35 import static android.view.InsetsState.ITYPE_IME;
36 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
37 import static android.view.InsetsState.ITYPE_STATUS_BAR;
38 import static android.view.Surface.ROTATION_0;
39 import static android.view.Surface.ROTATION_180;
40 import static android.view.Surface.ROTATION_270;
41 import static android.view.Surface.ROTATION_90;
42 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
43 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
44 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
45 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
46 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
47 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
48 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
49 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
50 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
51 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
52 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
53 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
54 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
55 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
56 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
57 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
58 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
59 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
60 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
61 import static android.view.WindowManager.TRANSIT_CLOSE;
62 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
63 import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE;
64 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION;
65 
66 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
67 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
68 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
69 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
70 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
71 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
72 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
73 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
74 import static com.android.dx.mockito.inline.extended.ExtendedMockito.same;
75 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
76 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
77 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
78 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
79 import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
80 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
81 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
82 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
83 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
84 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
85 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
86 import static com.android.server.wm.WindowContainer.POSITION_TOP;
87 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
88 
89 import static com.google.common.truth.Truth.assertThat;
90 
91 import static org.hamcrest.Matchers.is;
92 import static org.junit.Assert.assertEquals;
93 import static org.junit.Assert.assertFalse;
94 import static org.junit.Assert.assertNotEquals;
95 import static org.junit.Assert.assertNotNull;
96 import static org.junit.Assert.assertNull;
97 import static org.junit.Assert.assertThat;
98 import static org.junit.Assert.assertTrue;
99 import static org.mockito.ArgumentMatchers.anyInt;
100 import static org.mockito.ArgumentMatchers.eq;
101 import static org.mockito.Mockito.atLeast;
102 import static org.mockito.Mockito.atLeastOnce;
103 import static org.mockito.Mockito.clearInvocations;
104 import static org.mockito.Mockito.doAnswer;
105 import static org.mockito.Mockito.doCallRealMethod;
106 
107 import android.app.ActivityTaskManager;
108 import android.app.WindowConfiguration;
109 import android.app.servertransaction.FixedRotationAdjustmentsItem;
110 import android.content.res.Configuration;
111 import android.graphics.Insets;
112 import android.graphics.Point;
113 import android.graphics.Rect;
114 import android.graphics.Region;
115 import android.hardware.display.VirtualDisplay;
116 import android.metrics.LogMaker;
117 import android.os.Binder;
118 import android.os.IBinder;
119 import android.os.RemoteException;
120 import android.os.SystemClock;
121 import android.platform.test.annotations.Presubmit;
122 import android.util.DisplayMetrics;
123 import android.view.DisplayCutout;
124 import android.view.DisplayInfo;
125 import android.view.Gravity;
126 import android.view.IDisplayWindowRotationCallback;
127 import android.view.IDisplayWindowRotationController;
128 import android.view.ISystemGestureExclusionListener;
129 import android.view.IWindowManager;
130 import android.view.InsetsState;
131 import android.view.InsetsVisibilities;
132 import android.view.MotionEvent;
133 import android.view.Surface;
134 import android.view.SurfaceControl;
135 import android.view.SurfaceControl.Transaction;
136 import android.view.View;
137 import android.view.WindowManager;
138 import android.window.WindowContainerToken;
139 
140 import androidx.test.filters.SmallTest;
141 
142 import com.android.internal.logging.MetricsLogger;
143 import com.android.internal.logging.nano.MetricsProto;
144 import com.android.server.LocalServices;
145 import com.android.server.policy.WindowManagerPolicy;
146 import com.android.server.wm.utils.WmDisplayCutout;
147 
148 import org.junit.Test;
149 import org.junit.runner.RunWith;
150 import org.mockito.ArgumentCaptor;
151 import org.mockito.Mockito;
152 import org.mockito.MockitoSession;
153 import org.mockito.quality.Strictness;
154 
155 import java.util.ArrayList;
156 import java.util.Arrays;
157 import java.util.Collections;
158 import java.util.LinkedList;
159 import java.util.List;
160 
161 /**
162  * Tests for the {@link DisplayContent} class.
163  *
164  * Build/Install/Run:
165  *  atest WmTests:DisplayContentTests
166  */
167 @SmallTest
168 @Presubmit
169 @RunWith(WindowTestRunner.class)
170 public class DisplayContentTests extends WindowTestsBase {
171 
172     @UseTestDisplay(addAllCommonWindows = true)
173     @Test
testForAllWindows()174     public void testForAllWindows() {
175         final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION,
176                 mDisplayContent, "exiting app");
177         final ActivityRecord exitingApp = exitingAppWindow.mActivityRecord;
178         // Wait until everything in animation handler get executed to prevent the exiting window
179         // from being removed during WindowSurfacePlacer Traversal.
180         waitUntilHandlersIdle();
181 
182         exitingApp.mIsExiting = true;
183         exitingApp.getTask().getRootTask().mExitingActivities.add(exitingApp);
184 
185         assertForAllWindowsOrder(Arrays.asList(
186                 mWallpaperWindow,
187                 exitingAppWindow,
188                 mChildAppWindowBelow,
189                 mAppWindow,
190                 mChildAppWindowAbove,
191                 mDockedDividerWindow,
192                 mImeWindow,
193                 mImeDialogWindow,
194                 mStatusBarWindow,
195                 mNotificationShadeWindow,
196                 mNavBarWindow));
197     }
198 
199     @UseTestDisplay(addAllCommonWindows = true)
200     @Test
testForAllWindows_WithAppImeTarget()201     public void testForAllWindows_WithAppImeTarget() {
202         final WindowState imeAppTarget =
203                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
204 
205         mDisplayContent.setImeLayeringTarget(imeAppTarget);
206 
207         assertForAllWindowsOrder(Arrays.asList(
208                 mWallpaperWindow,
209                 mChildAppWindowBelow,
210                 mAppWindow,
211                 mChildAppWindowAbove,
212                 imeAppTarget,
213                 mImeWindow,
214                 mImeDialogWindow,
215                 mDockedDividerWindow,
216                 mStatusBarWindow,
217                 mNotificationShadeWindow,
218                 mNavBarWindow));
219     }
220 
221     @UseTestDisplay(addAllCommonWindows = true)
222     @Test
testForAllWindows_WithChildWindowImeTarget()223     public void testForAllWindows_WithChildWindowImeTarget() throws Exception {
224         mDisplayContent.setImeLayeringTarget(mChildAppWindowAbove);
225 
226         assertForAllWindowsOrder(Arrays.asList(
227                 mWallpaperWindow,
228                 mChildAppWindowBelow,
229                 mAppWindow,
230                 mChildAppWindowAbove,
231                 mImeWindow,
232                 mImeDialogWindow,
233                 mDockedDividerWindow,
234                 mStatusBarWindow,
235                 mNotificationShadeWindow,
236                 mNavBarWindow));
237     }
238 
239     @UseTestDisplay(addAllCommonWindows = true)
240     @Test
testForAllWindows_WithStatusBarImeTarget()241     public void testForAllWindows_WithStatusBarImeTarget() throws Exception {
242         mDisplayContent.setImeLayeringTarget(mStatusBarWindow);
243 
244         assertForAllWindowsOrder(Arrays.asList(
245                 mWallpaperWindow,
246                 mChildAppWindowBelow,
247                 mAppWindow,
248                 mChildAppWindowAbove,
249                 mDockedDividerWindow,
250                 mStatusBarWindow,
251                 mImeWindow,
252                 mImeDialogWindow,
253                 mNotificationShadeWindow,
254                 mNavBarWindow));
255     }
256 
257     @UseTestDisplay(addAllCommonWindows = true)
258     @Test
testForAllWindows_WithNotificationShadeImeTarget()259     public void testForAllWindows_WithNotificationShadeImeTarget() throws Exception {
260         mDisplayContent.setImeLayeringTarget(mNotificationShadeWindow);
261 
262         assertForAllWindowsOrder(Arrays.asList(
263                 mWallpaperWindow,
264                 mChildAppWindowBelow,
265                 mAppWindow,
266                 mChildAppWindowAbove,
267                 mDockedDividerWindow,
268                 mStatusBarWindow,
269                 mNotificationShadeWindow,
270                 mImeWindow,
271                 mImeDialogWindow,
272                 mNavBarWindow));
273     }
274 
275     @UseTestDisplay(addAllCommonWindows = true)
276     @Test
testForAllWindows_WithInBetweenWindowToken()277     public void testForAllWindows_WithInBetweenWindowToken() {
278         // This window is set-up to be z-ordered between some windows that go in the same token like
279         // the nav bar and status bar.
280         final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION,
281                 mDisplayContent, "voiceInteractionWindow");
282 
283         assertForAllWindowsOrder(Arrays.asList(
284                 mWallpaperWindow,
285                 mChildAppWindowBelow,
286                 mAppWindow,
287                 mChildAppWindowAbove,
288                 mDockedDividerWindow,
289                 voiceInteractionWindow,
290                 mImeWindow,
291                 mImeDialogWindow,
292                 mStatusBarWindow,
293                 mNotificationShadeWindow,
294                 mNavBarWindow));
295     }
296 
297     @UseTestDisplay(addAllCommonWindows = true)
298     @Test
testComputeImeTarget()299     public void testComputeImeTarget() {
300         // Verify that an app window can be an ime target.
301         final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
302         appWin.setHasSurface(true);
303         assertTrue(appWin.canBeImeTarget());
304         WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
305         assertEquals(appWin, imeTarget);
306         appWin.mHidden = false;
307 
308         // Verify that an child window can be an ime target.
309         final WindowState childWin = createWindow(appWin,
310                 TYPE_APPLICATION_ATTACHED_DIALOG, "childWin");
311         childWin.setHasSurface(true);
312         assertTrue(childWin.canBeImeTarget());
313         imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
314         assertEquals(childWin, imeTarget);
315     }
316 
317     @UseTestDisplay(addAllCommonWindows = true)
318     @Test
testComputeImeTarget_startingWindow()319     public void testComputeImeTarget_startingWindow() {
320         ActivityRecord activity = createActivityRecord(mDisplayContent);
321 
322         final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
323                 "startingWin");
324         startingWin.setHasSurface(true);
325         assertTrue(startingWin.canBeImeTarget());
326 
327         WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
328         assertEquals(startingWin, imeTarget);
329         startingWin.mHidden = false;
330 
331         // Verify that the starting window still be an ime target even an app window launching
332         // behind it.
333         final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, activity, "appWin");
334         appWin.setHasSurface(true);
335         assertTrue(appWin.canBeImeTarget());
336 
337         imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
338         assertEquals(startingWin, imeTarget);
339         appWin.mHidden = false;
340 
341         // Verify that the starting window still be an ime target even the child window behind a
342         // launching app window
343         final WindowState childWin = createWindow(appWin,
344                 TYPE_APPLICATION_ATTACHED_DIALOG, "childWin");
345         childWin.setHasSurface(true);
346         assertTrue(childWin.canBeImeTarget());
347         imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */);
348         assertEquals(startingWin, imeTarget);
349     }
350 
351     @Test
testUpdateImeParent_forceUpdateRelativeLayer()352     public void testUpdateImeParent_forceUpdateRelativeLayer() {
353         final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer();
354         final ActivityRecord activity = createActivityRecord(mDisplayContent);
355 
356         final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity,
357                 "startingWin");
358         startingWin.setHasSurface(true);
359         assertTrue(startingWin.canBeImeTarget());
360         final SurfaceControl imeSurfaceParent = mock(SurfaceControl.class);
361         doReturn(imeSurfaceParent).when(mDisplayContent).computeImeParent();
362         spyOn(imeContainer);
363 
364         mDisplayContent.setImeInputTarget(startingWin);
365         mDisplayContent.onConfigurationChanged(new Configuration());
366         verify(mDisplayContent).updateImeParent();
367 
368         // Force reassign the relative layer when the IME surface parent is changed.
369         verify(imeContainer).assignRelativeLayer(any(), eq(imeSurfaceParent), anyInt(), eq(true));
370     }
371 
372     @Test
testComputeImeTargetReturnsNull_windowDidntRequestIme()373     public void testComputeImeTargetReturnsNull_windowDidntRequestIme() {
374         final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION,
375                 new ActivityBuilder(mAtm).setCreateTask(true).build(), "app");
376         final WindowState win2 = createWindow(null, TYPE_BASE_APPLICATION,
377                 new ActivityBuilder(mAtm).setCreateTask(true).build(), "app2");
378 
379         mDisplayContent.setImeInputTarget(win1);
380         mDisplayContent.setImeLayeringTarget(win2);
381 
382         doReturn(true).when(mDisplayContent).shouldImeAttachedToApp();
383         // Compute IME parent returns nothing if current target and window receiving input
384         // are different i.e. if current window didn't request IME.
385         assertNull("computeImeParent() should be null", mDisplayContent.computeImeParent());
386     }
387 
388     /**
389      * This tests root task movement between displays and proper root task's, task's and app token's
390      * display container references updates.
391      */
392     @Test
testMoveRootTaskBetweenDisplays()393     public void testMoveRootTaskBetweenDisplays() {
394         // Create a second display.
395         final DisplayContent dc = createNewDisplay();
396 
397         // Add root task with activity.
398         final Task rootTask = createTask(dc);
399         assertEquals(dc.getDisplayId(), rootTask.getDisplayContent().getDisplayId());
400         assertEquals(dc, rootTask.getDisplayContent());
401 
402         final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
403         final ActivityRecord activity = createNonAttachedActivityRecord(dc);
404         task.addChild(activity, 0);
405         assertEquals(dc, task.getDisplayContent());
406         assertEquals(dc, activity.getDisplayContent());
407 
408         // Move root task to first display.
409         rootTask.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true /* onTop */);
410         assertEquals(mDisplayContent.getDisplayId(), rootTask.getDisplayContent().getDisplayId());
411         assertEquals(mDisplayContent, rootTask.getDisplayContent());
412         assertEquals(mDisplayContent, task.getDisplayContent());
413         assertEquals(mDisplayContent, activity.getDisplayContent());
414     }
415 
416     /**
417      * This tests override configuration updates for display content.
418      */
419     @Test
testDisplayOverrideConfigUpdate()420     public void testDisplayOverrideConfigUpdate() {
421         final Configuration currentOverrideConfig =
422                 mDisplayContent.getRequestedOverrideConfiguration();
423 
424         // Create new, slightly changed override configuration and apply it to the display.
425         final Configuration newOverrideConfig = new Configuration(currentOverrideConfig);
426         newOverrideConfig.densityDpi += 120;
427         newOverrideConfig.fontScale += 0.3;
428 
429         mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, mDisplayContent);
430 
431         // Check that override config is applied.
432         assertEquals(newOverrideConfig, mDisplayContent.getRequestedOverrideConfiguration());
433     }
434 
435     /**
436      * This tests global configuration updates when default display config is updated.
437      */
438     @Test
testDefaultDisplayOverrideConfigUpdate()439     public void testDefaultDisplayOverrideConfigUpdate() {
440         DisplayContent defaultDisplay = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
441         final Configuration currentConfig = defaultDisplay.getConfiguration();
442 
443         // Create new, slightly changed override configuration and apply it to the display.
444         final Configuration newOverrideConfig = new Configuration(currentConfig);
445         newOverrideConfig.densityDpi += 120;
446         newOverrideConfig.fontScale += 0.3;
447 
448         mWm.setNewDisplayOverrideConfiguration(newOverrideConfig, defaultDisplay);
449 
450         // Check that global configuration is updated, as we've updated default display's config.
451         Configuration globalConfig = mWm.mRoot.getConfiguration();
452         assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi);
453         assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
454 
455         // Return back to original values.
456         mWm.setNewDisplayOverrideConfiguration(currentConfig, defaultDisplay);
457         globalConfig = mWm.mRoot.getConfiguration();
458         assertEquals(currentConfig.densityDpi, globalConfig.densityDpi);
459         assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
460     }
461 
462     /**
463      * Tests tapping on a root task in different display results in window gaining focus.
464      */
465     @Test
testInputEventBringsCorrectDisplayInFocus()466     public void testInputEventBringsCorrectDisplayInFocus() {
467         DisplayContent dc0 = mWm.getDefaultDisplayContentLocked();
468         // Create a second display
469         final DisplayContent dc1 = createNewDisplay();
470 
471         // Add root task with activity.
472         final Task rootTask0 = createTask(dc0);
473         final Task task0 = createTaskInRootTask(rootTask0, 0 /* userId */);
474         final ActivityRecord activity = createNonAttachedActivityRecord(dc0);
475         task0.addChild(activity, 0);
476         dc0.configureDisplayPolicy();
477         assertNotNull(dc0.mTapDetector);
478 
479         final Task rootTask1 = createTask(dc1);
480         final Task task1 = createTaskInRootTask(rootTask1, 0 /* userId */);
481         final ActivityRecord activity1 = createNonAttachedActivityRecord(dc0);
482         task1.addChild(activity1, 0);
483         dc1.configureDisplayPolicy();
484         assertNotNull(dc1.mTapDetector);
485 
486         // tap on primary display.
487         tapOnDisplay(dc0);
488         // Check focus is on primary display.
489         assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
490                 dc0.findFocusedWindow());
491 
492         // Tap on secondary display.
493         tapOnDisplay(dc1);
494         // Check focus is on secondary.
495         assertEquals(mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
496                 dc1.findFocusedWindow());
497     }
498 
499     @Test
testFocusedWindowMultipleDisplays()500     public void testFocusedWindowMultipleDisplays() {
501         doTestFocusedWindowMultipleDisplays(false /* perDisplayFocusEnabled */, Q);
502     }
503 
504     @Test
testFocusedWindowMultipleDisplaysPerDisplayFocusEnabled()505     public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabled() {
506         doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, Q);
507     }
508 
509     @Test
testFocusedWindowMultipleDisplaysPerDisplayFocusEnabledLegacyApp()510     public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabledLegacyApp() {
511         doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, P);
512     }
513 
doTestFocusedWindowMultipleDisplays(boolean perDisplayFocusEnabled, int targetSdk)514     private void doTestFocusedWindowMultipleDisplays(boolean perDisplayFocusEnabled,
515             int targetSdk) {
516         mWm.mPerDisplayFocusEnabled = perDisplayFocusEnabled;
517 
518         // Create a focusable window and check that focus is calculated correctly
519         final WindowState window1 =
520                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1");
521         window1.mActivityRecord.mTargetSdk = targetSdk;
522         updateFocusedWindow();
523         assertTrue(window1.isFocused());
524         assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
525 
526         // Check that a new display doesn't affect focus
527         final DisplayContent dc = createNewDisplay();
528         updateFocusedWindow();
529         assertTrue(window1.isFocused());
530         assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
531 
532         // Add a window to the second display, and it should be focused
533         final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2");
534         window2.mActivityRecord.mTargetSdk = targetSdk;
535         updateFocusedWindow();
536         assertTrue(window2.isFocused());
537         assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window1.isFocused());
538         assertEquals(window2, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
539 
540         // Move the first window to top including parents, and make sure focus is updated
541         window1.getParent().positionChildAt(POSITION_TOP, window1, true);
542         updateFocusedWindow();
543         assertTrue(window1.isFocused());
544         assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window2.isFocused());
545         assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
546 
547         // Make sure top focused display not changed if there is a focused app.
548         window1.mActivityRecord.mVisibleRequested = false;
549         window1.getDisplayContent().setFocusedApp(window1.mActivityRecord);
550         updateFocusedWindow();
551         assertTrue(!window1.isFocused());
552         assertEquals(window1.getDisplayId(),
553                 mWm.mRoot.getTopFocusedDisplayContent().getDisplayId());
554     }
555 
556     @Test
testShouldWaitForSystemDecorWindowsOnBoot_OnDefaultDisplay()557     public void testShouldWaitForSystemDecorWindowsOnBoot_OnDefaultDisplay() {
558         mWm.mSystemBooted = true;
559         final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked();
560         final WindowState[] windows = createNotDrawnWindowsOn(defaultDisplay,
561                 TYPE_WALLPAPER, TYPE_APPLICATION);
562         final WindowState wallpaper = windows[0];
563         assertTrue(wallpaper.mIsWallpaper);
564         wallpaper.mToken.asWallpaperToken().setVisibility(false);
565         // By default WindowState#mWallpaperVisible is false.
566         assertFalse(wallpaper.isVisible());
567 
568         // Verify waiting for windows to be drawn.
569         assertTrue(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
570 
571         // Verify not waiting for drawn window and invisible wallpaper.
572         setDrawnState(WindowStateAnimator.READY_TO_SHOW, wallpaper);
573         setDrawnState(WindowStateAnimator.HAS_DRAWN, windows[1]);
574         assertFalse(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot());
575     }
576 
577     @Test
testShouldWaitForSystemDecorWindowsOnBoot_OnSecondaryDisplay()578     public void testShouldWaitForSystemDecorWindowsOnBoot_OnSecondaryDisplay() {
579         mWm.mSystemBooted = true;
580         final DisplayContent secondaryDisplay = createNewDisplay();
581         final WindowState[] windows = createNotDrawnWindowsOn(secondaryDisplay,
582                 TYPE_WALLPAPER, TYPE_APPLICATION);
583 
584         // Verify not waiting for display without system decorations.
585         doReturn(false).when(secondaryDisplay).supportsSystemDecorations();
586         assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
587 
588         // Verify waiting for non-drawn windows on display with system decorations.
589         reset(secondaryDisplay);
590         doReturn(true).when(secondaryDisplay).supportsSystemDecorations();
591         assertTrue(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
592 
593         // Verify not waiting for drawn windows on display with system decorations.
594         setDrawnState(WindowStateAnimator.HAS_DRAWN, windows);
595         assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot());
596     }
597 
598     @Test
testImeIsAttachedToDisplayForLetterboxedApp()599     public void testImeIsAttachedToDisplayForLetterboxedApp() {
600         final DisplayContent dc = mDisplayContent;
601         final WindowState ws = createWindow(null, TYPE_APPLICATION, dc, "app window");
602         dc.setImeLayeringTarget(ws);
603 
604         // Adjust bounds so that matchesRootDisplayAreaBounds() returns false.
605         final Rect bounds = new Rect(dc.getBounds());
606         bounds.scale(0.5f);
607         ws.mActivityRecord.setBounds(bounds);
608         assertFalse("matchesRootDisplayAreaBounds() should return false",
609                 ws.matchesDisplayAreaBounds());
610 
611         assertTrue("IME shouldn't be attached to app",
612                 dc.computeImeParent() != dc.getImeTarget(IME_TARGET_LAYERING).getWindow()
613                         .mActivityRecord.getSurfaceControl());
614         assertEquals("IME should be attached to display",
615                 dc.getImeContainer().getParent().getSurfaceControl(), dc.computeImeParent());
616     }
617 
createNotDrawnWindowsOn(DisplayContent displayContent, int... types)618     private WindowState[] createNotDrawnWindowsOn(DisplayContent displayContent, int... types) {
619         final WindowState[] windows = new WindowState[types.length];
620         for (int i = 0; i < types.length; i++) {
621             final int type = types[i];
622             windows[i] = createWindow(null /* parent */, type, displayContent, "window-" + type);
623             windows[i].setHasSurface(true);
624             windows[i].mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
625         }
626         return windows;
627     }
628 
setDrawnState(int state, WindowState... windows)629     private static void setDrawnState(int state, WindowState... windows) {
630         for (WindowState window : windows) {
631             window.mHasSurface = state != WindowStateAnimator.NO_SURFACE;
632             window.mWinAnimator.mDrawState = state;
633         }
634     }
635 
636     /**
637      * This tests setting the maximum ui width on a display.
638      */
639     @Test
testMaxUiWidth()640     public void testMaxUiWidth() {
641         // Prevent base display metrics for test from being updated to the value of real display.
642         final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
643         final int baseWidth = 1440;
644         final int baseHeight = 2560;
645         final int baseDensity = 300;
646 
647         displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
648 
649         final int maxWidth = 300;
650         final int resultingHeight = (maxWidth * baseHeight) / baseWidth;
651         final int resultingDensity = baseDensity;
652 
653         displayContent.setMaxUiWidth(maxWidth);
654         verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
655 
656         // Assert setting values again does not change;
657         displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
658         verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity);
659 
660         final int smallerWidth = 200;
661         final int smallerHeight = 400;
662         final int smallerDensity = 100;
663 
664         // Specify smaller dimension, verify that it is honored
665         displayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity);
666         verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity);
667 
668         // Verify that setting the max width to a greater value than the base width has no effect
669         displayContent.setMaxUiWidth(maxWidth);
670         verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity);
671     }
672 
673     @Test
testSetForcedSize()674     public void testSetForcedSize() {
675         // Prevent base display metrics for test from being updated to the value of real display.
676         final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
677         final int baseWidth = 1280;
678         final int baseHeight = 720;
679         final int baseDensity = 320;
680 
681         displayContent.mInitialDisplayWidth = baseWidth;
682         displayContent.mInitialDisplayHeight = baseHeight;
683         displayContent.mInitialDisplayDensity = baseDensity;
684         displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
685 
686         final int forcedWidth = 1920;
687         final int forcedHeight = 1080;
688 
689         // Verify that forcing the size is honored and the density doesn't change.
690         displayContent.setForcedSize(forcedWidth, forcedHeight);
691         verifySizes(displayContent, forcedWidth, forcedHeight, baseDensity);
692 
693         // Verify that forcing the size is idempotent.
694         displayContent.setForcedSize(forcedWidth, forcedHeight);
695         verifySizes(displayContent, forcedWidth, forcedHeight, baseDensity);
696     }
697 
698     @Test
testSetForcedSize_WithMaxUiWidth()699     public void testSetForcedSize_WithMaxUiWidth() {
700         // Prevent base display metrics for test from being updated to the value of real display.
701         final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo();
702         final int baseWidth = 1280;
703         final int baseHeight = 720;
704         final int baseDensity = 320;
705 
706         displayContent.mInitialDisplayWidth = baseWidth;
707         displayContent.mInitialDisplayHeight = baseHeight;
708         displayContent.mInitialDisplayDensity = baseDensity;
709         displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity);
710 
711         displayContent.setMaxUiWidth(baseWidth);
712 
713         final int forcedWidth = 1920;
714         final int forcedHeight = 1080;
715 
716         // Verify that forcing bigger size doesn't work and density doesn't change.
717         displayContent.setForcedSize(forcedWidth, forcedHeight);
718         verifySizes(displayContent, baseWidth, baseHeight, baseDensity);
719 
720         // Verify that forcing the size is idempotent.
721         displayContent.setForcedSize(forcedWidth, forcedHeight);
722         verifySizes(displayContent, baseWidth, baseHeight, baseDensity);
723     }
724 
725     @Test
testDisplayCutout_rot0()726     public void testDisplayCutout_rot0() {
727         final DisplayContent dc = createNewDisplay();
728         dc.mInitialDisplayWidth = 200;
729         dc.mInitialDisplayHeight = 400;
730         final Rect r = new Rect(80, 0, 120, 10);
731         final DisplayCutout cutout = new WmDisplayCutout(
732                 fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_TOP), null)
733                         .computeSafeInsets(200, 400).getDisplayCutout();
734 
735         dc.mInitialDisplayCutout = cutout;
736         dc.getDisplayRotation().setRotation(Surface.ROTATION_0);
737         dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
738 
739         assertEquals(cutout, dc.getDisplayInfo().displayCutout);
740     }
741 
742     @Test
testDisplayCutout_rot90()743     public void testDisplayCutout_rot90() {
744         // Prevent mInitialDisplayCutout from being updated from real display (e.g. null
745         // if the device has no cutout).
746         final DisplayContent dc = createDisplayNoUpdateDisplayInfo();
747         // This test assumes it's a top cutout on a portrait display, so if it happens to be a
748         // landscape display let's rotate it.
749         if (dc.mInitialDisplayHeight < dc.mInitialDisplayWidth) {
750             int tmp = dc.mInitialDisplayHeight;
751             dc.mInitialDisplayHeight = dc.mInitialDisplayWidth;
752             dc.mInitialDisplayWidth = tmp;
753         }
754         // Rotation may use real display info to compute bound, so here also uses the
755         // same width and height.
756         final int displayWidth = dc.mInitialDisplayWidth;
757         final int displayHeight = dc.mInitialDisplayHeight;
758         final float density = dc.mInitialDisplayDensity;
759         final int cutoutWidth = 40;
760         final int cutoutHeight = 10;
761         final int left = (displayWidth - cutoutWidth) / 2;
762         final int top = 0;
763         final int right = (displayWidth + cutoutWidth) / 2;
764         final int bottom = cutoutHeight;
765 
766         final Rect zeroRect = new Rect();
767         final Rect[] bounds = new Rect[]{zeroRect, new Rect(left, top, right, bottom), zeroRect,
768                 zeroRect};
769         final DisplayCutout.CutoutPathParserInfo info = new DisplayCutout.CutoutPathParserInfo(
770                 displayWidth, displayHeight, density, "", Surface.ROTATION_0, 1f);
771         final DisplayCutout cutout = new WmDisplayCutout(
772                 DisplayCutout.constructDisplayCutout(bounds, Insets.NONE, info), null)
773                         .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
774 
775         dc.mInitialDisplayCutout = cutout;
776         dc.getDisplayRotation().setRotation(Surface.ROTATION_90);
777         dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo.
778 
779         // ----o----------      -------------
780         // |   |     |   |      |
781         // |   ------o   |      o---
782         // |             |      |  |
783         // |             |  ->  |  |
784         // |             |      ---o
785         // |             |      |
786         // |             |      -------------
787         final Rect[] bounds90 = new Rect[]{new Rect(top, left, bottom, right), zeroRect, zeroRect,
788                 zeroRect};
789         final DisplayCutout.CutoutPathParserInfo info90 = new DisplayCutout.CutoutPathParserInfo(
790                 displayWidth, displayHeight, density, "", Surface.ROTATION_90, 1f);
791         assertEquals(new WmDisplayCutout(
792                         DisplayCutout.constructDisplayCutout(bounds90, Insets.NONE, info90), null)
793                         .computeSafeInsets(displayHeight, displayWidth).getDisplayCutout(),
794                 dc.getDisplayInfo().displayCutout);
795     }
796 
797     @Test
testLayoutSeq_assignedDuringLayout()798     public void testLayoutSeq_assignedDuringLayout() {
799         final DisplayContent dc = createNewDisplay();
800         final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
801 
802         performLayout(dc);
803 
804         assertThat(win.mLayoutSeq, is(dc.mLayoutSeq));
805     }
806 
807     @Test
testOrientationDefinedByKeyguard()808     public void testOrientationDefinedByKeyguard() {
809         final DisplayContent dc = mDisplayContent;
810         dc.getDisplayPolicy().setAwake(true);
811 
812         // Create a window that requests landscape orientation. It will define device orientation
813         // by default.
814         final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
815         window.mActivityRecord.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
816 
817         final WindowState keyguard = createWindow(null, TYPE_NOTIFICATION_SHADE , dc, "keyguard");
818         keyguard.mHasSurface = true;
819         keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
820 
821         assertEquals("Screen orientation must be defined by the app window by default",
822                 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
823 
824         keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
825         mAtm.mKeyguardController.setKeyguardShown(true /* keyguardShowing */,
826                 false /* aodShowing */);
827         assertEquals("Visible keyguard must influence device orientation",
828                 SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation());
829 
830         mAtm.mKeyguardController.keyguardGoingAway(0 /* flags */);
831         assertEquals("Keyguard that is going away must not influence device orientation",
832                 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
833     }
834 
835     @Test
testOrientationForAspectRatio()836     public void testOrientationForAspectRatio() {
837         final DisplayContent dc = createNewDisplay();
838 
839         // When display content is created its configuration is not yet initialized, which could
840         // cause unnecessary configuration propagation, so initialize it here.
841         final Configuration config = new Configuration();
842         dc.computeScreenConfiguration(config);
843         dc.onRequestedOverrideConfigurationChanged(config);
844 
845         // Create a window that requests a fixed orientation. It will define device orientation
846         // by default.
847         final WindowState window = createWindow(null /* parent */, TYPE_APPLICATION_OVERLAY, dc,
848                 "window");
849         window.mHasSurface = true;
850         window.mAttrs.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE;
851 
852         // --------------------------------
853         // Test non-close-to-square display
854         // --------------------------------
855         dc.mBaseDisplayWidth = 1000;
856         dc.mBaseDisplayHeight = (int) (dc.mBaseDisplayWidth * dc.mCloseToSquareMaxAspectRatio * 2f);
857         dc.configureDisplayPolicy();
858 
859         assertEquals("Screen orientation must be defined by the window by default.",
860                 window.mAttrs.screenOrientation, dc.getOrientation());
861 
862         // ----------------------------
863         // Test close-to-square display - should be handled in the same way
864         // ----------------------------
865         dc.mBaseDisplayHeight = dc.mBaseDisplayWidth;
866         dc.configureDisplayPolicy();
867 
868         assertEquals(
869                 "Screen orientation must be defined by the window even on close-to-square display.",
870                 window.mAttrs.screenOrientation, dc.getOrientation());
871     }
872 
873     @Test
testDisableDisplayInfoOverrideFromWindowManager()874     public void testDisableDisplayInfoOverrideFromWindowManager() {
875         final DisplayContent dc = createNewDisplay();
876 
877         assertTrue(dc.mShouldOverrideDisplayConfiguration);
878         mWm.dontOverrideDisplayInfo(dc.getDisplayId());
879 
880         assertFalse(dc.mShouldOverrideDisplayConfiguration);
881         verify(mWm.mDisplayManagerInternal, times(1))
882                 .setDisplayInfoOverrideFromWindowManager(dc.getDisplayId(), null);
883     }
884 
885     @Test
testGetPreferredOptionsPanelGravityFromDifferentDisplays()886     public void testGetPreferredOptionsPanelGravityFromDifferentDisplays() {
887         final DisplayContent portraitDisplay = createNewDisplay();
888         portraitDisplay.mInitialDisplayHeight = 2000;
889         portraitDisplay.mInitialDisplayWidth = 1000;
890 
891         portraitDisplay.getDisplayRotation().setRotation(Surface.ROTATION_0);
892         assertFalse(isOptionsPanelAtRight(portraitDisplay.getDisplayId()));
893         portraitDisplay.getDisplayRotation().setRotation(ROTATION_90);
894         assertTrue(isOptionsPanelAtRight(portraitDisplay.getDisplayId()));
895 
896         final DisplayContent landscapeDisplay = createNewDisplay();
897         landscapeDisplay.mInitialDisplayHeight = 1000;
898         landscapeDisplay.mInitialDisplayWidth = 2000;
899 
900         landscapeDisplay.getDisplayRotation().setRotation(Surface.ROTATION_0);
901         assertTrue(isOptionsPanelAtRight(landscapeDisplay.getDisplayId()));
902         landscapeDisplay.getDisplayRotation().setRotation(ROTATION_90);
903         assertFalse(isOptionsPanelAtRight(landscapeDisplay.getDisplayId()));
904     }
905 
906     @UseTestDisplay(addWindows = W_INPUT_METHOD)
907     @Test
testInputMethodTargetUpdateWhenSwitchingOnDisplays()908     public void testInputMethodTargetUpdateWhenSwitchingOnDisplays() {
909         final DisplayContent newDisplay = createNewDisplay();
910 
911         final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
912         final Task rootTask = mDisplayContent.getTopRootTask();
913         final ActivityRecord activity = rootTask.topRunningActivity();
914         doReturn(true).when(activity).shouldBeVisibleUnchecked();
915 
916         final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1");
917         final Task rootTask1 = newDisplay.getTopRootTask();
918         final ActivityRecord activity1 = rootTask1.topRunningActivity();
919         doReturn(true).when(activity1).shouldBeVisibleUnchecked();
920         appWin.setHasSurface(true);
921         appWin1.setHasSurface(true);
922 
923         // Set current input method window on default display, make sure the input method target
924         // is appWin & null on the other display.
925         mDisplayContent.setInputMethodWindowLocked(mImeWindow);
926         newDisplay.setInputMethodWindowLocked(null);
927         assertEquals("appWin should be IME target window",
928                 appWin, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
929         assertNull("newDisplay Ime target: ", newDisplay.getImeTarget(IME_TARGET_LAYERING));
930 
931         // Switch input method window on new display & make sure the input method target also
932         // switched as expected.
933         newDisplay.setInputMethodWindowLocked(mImeWindow);
934         mDisplayContent.setInputMethodWindowLocked(null);
935         assertEquals("appWin1 should be IME target window", appWin1,
936                 newDisplay.getImeTarget(IME_TARGET_LAYERING));
937         assertNull("default display Ime target: ",
938                 mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
939     }
940 
941     @UseTestDisplay(addWindows = W_INPUT_METHOD)
942     @Test
testInputMethodSet_listenOnDisplayAreaConfigurationChanged()943     public void testInputMethodSet_listenOnDisplayAreaConfigurationChanged() {
944         spyOn(mAtm);
945         mDisplayContent.setInputMethodWindowLocked(mImeWindow);
946 
947         verify(mAtm).onImeWindowSetOnDisplayArea(
948                 mImeWindow.mSession.mPid, mDisplayContent.getImeContainer());
949     }
950 
951     @Test
testAllowsTopmostFullscreenOrientation()952     public void testAllowsTopmostFullscreenOrientation() {
953         final DisplayContent dc = createNewDisplay();
954         dc.getDisplayRotation().setFixedToUserRotation(
955                 IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
956 
957         final Task rootTask = new TaskBuilder(mSupervisor)
958                 .setDisplay(dc)
959                 .setCreateActivity(true)
960                 .build();
961         doReturn(true).when(rootTask).isVisible();
962 
963         final Task freeformRootTask = new TaskBuilder(mSupervisor)
964                 .setDisplay(dc)
965                 .setCreateActivity(true)
966                 .setWindowingMode(WINDOWING_MODE_FREEFORM)
967                 .build();
968         doReturn(true).when(freeformRootTask).isVisible();
969         freeformRootTask.getTopChild().setBounds(100, 100, 300, 400);
970 
971         assertTrue(dc.getDefaultTaskDisplayArea().isRootTaskVisible(WINDOWING_MODE_FREEFORM));
972 
973         freeformRootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
974         rootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT);
975         assertEquals(SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation());
976 
977         rootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
978         freeformRootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT);
979         assertEquals(SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
980     }
981 
982     @Test
testOnDescendantOrientationRequestChanged()983     public void testOnDescendantOrientationRequestChanged() {
984         final DisplayContent dc = createNewDisplay();
985         dc.getDisplayRotation().setFixedToUserRotation(
986                 IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
987         final int newOrientation = getRotatedOrientation(dc);
988 
989         final Task task = new TaskBuilder(mSupervisor)
990                 .setDisplay(dc).setCreateActivity(true).build();
991         final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity();
992         dc.setFocusedApp(activity);
993 
994         activity.setRequestedOrientation(newOrientation);
995 
996         assertTrue("The display should be rotated.", dc.getRotation() % 2 == 1);
997     }
998 
999     @Test
testOnDescendantOrientationRequestChanged_FrozenToUserRotation()1000     public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() {
1001         final DisplayContent dc = createNewDisplay();
1002         dc.getDisplayRotation().setFixedToUserRotation(
1003                 IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
1004         dc.getDisplayRotation().setUserRotation(
1005                 WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_180);
1006         final int newOrientation = getRotatedOrientation(dc);
1007 
1008         final Task task = new TaskBuilder(mSupervisor)
1009                 .setDisplay(dc).setCreateActivity(true).build();
1010         final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity();
1011         dc.setFocusedApp(activity);
1012 
1013         activity.setRequestedOrientation(newOrientation);
1014 
1015         verify(dc, never()).updateDisplayOverrideConfigurationLocked(any(), eq(activity),
1016                 anyBoolean(), same(null));
1017         assertEquals(ROTATION_180, dc.getRotation());
1018     }
1019 
1020     @Test
testFixedToUserRotationChanged()1021     public void testFixedToUserRotationChanged() {
1022         final DisplayContent dc = createNewDisplay();
1023         dc.getDisplayRotation().setFixedToUserRotation(
1024                 IWindowManager.FIXED_TO_USER_ROTATION_ENABLED);
1025         dc.getDisplayRotation().setUserRotation(
1026                 WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0);
1027         final int newOrientation = getRotatedOrientation(dc);
1028 
1029         final Task task = new TaskBuilder(mSupervisor)
1030                 .setDisplay(dc).setCreateActivity(true).build();
1031         final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity();
1032         dc.setFocusedApp(activity);
1033 
1034         activity.setRequestedOrientation(newOrientation);
1035 
1036         dc.getDisplayRotation().setFixedToUserRotation(
1037                 IWindowManager.FIXED_TO_USER_ROTATION_DISABLED);
1038 
1039         assertTrue("The display should be rotated.", dc.getRotation() % 2 == 1);
1040     }
1041 
1042     @Test
testComputeImeParent_app()1043     public void testComputeImeParent_app() throws Exception {
1044         final DisplayContent dc = createNewDisplay();
1045         dc.setImeLayeringTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
1046         dc.setImeInputTarget(dc.getImeTarget(IME_TARGET_LAYERING).getWindow());
1047         assertEquals(dc.getImeTarget(IME_TARGET_LAYERING).getWindow()
1048                         .mActivityRecord.getSurfaceControl(), dc.computeImeParent());
1049     }
1050 
1051     @Test
testComputeImeParent_app_notFullscreen()1052     public void testComputeImeParent_app_notFullscreen() throws Exception {
1053         final DisplayContent dc = createNewDisplay();
1054         dc.setImeLayeringTarget(createWindow(null, TYPE_STATUS_BAR, "app"));
1055         dc.getImeTarget(IME_TARGET_LAYERING).getWindow().setWindowingMode(
1056                 WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
1057         assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
1058     }
1059 
1060     @UseTestDisplay(addWindows = W_ACTIVITY)
1061     @Test
testComputeImeParent_app_notMatchParentBounds()1062     public void testComputeImeParent_app_notMatchParentBounds() {
1063         spyOn(mAppWindow.mActivityRecord);
1064         doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds();
1065         mDisplayContent.setImeLayeringTarget(mAppWindow);
1066         // The surface parent of IME should be the display instead of app window.
1067         assertEquals(mDisplayContent.getImeContainer().getParentSurfaceControl(),
1068                 mDisplayContent.computeImeParent());
1069     }
1070 
1071     @Test
testComputeImeParent_noApp()1072     public void testComputeImeParent_noApp() throws Exception {
1073         final DisplayContent dc = createNewDisplay();
1074         dc.setImeLayeringTarget(createWindow(null, TYPE_STATUS_BAR, "statusBar"));
1075         assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent());
1076     }
1077 
1078     @Test
testInputMethodInputTarget_isClearedWhenWindowStateIsRemoved()1079     public void testInputMethodInputTarget_isClearedWhenWindowStateIsRemoved() throws Exception {
1080         final DisplayContent dc = createNewDisplay();
1081 
1082         WindowState app = createWindow(null, TYPE_BASE_APPLICATION, dc, "app");
1083 
1084         dc.setImeInputTarget(app);
1085         assertEquals(app, dc.computeImeControlTarget());
1086 
1087         app.removeImmediately();
1088 
1089         assertNull(dc.getImeTarget(IME_TARGET_INPUT));
1090         assertNull(dc.computeImeControlTarget());
1091     }
1092 
1093     @Test
testComputeImeControlTarget()1094     public void testComputeImeControlTarget() throws Exception {
1095         final DisplayContent dc = createNewDisplay();
1096         dc.setRemoteInsetsController(createDisplayWindowInsetsController());
1097         dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
1098         dc.setImeLayeringTarget(dc.getImeTarget(IME_TARGET_INPUT).getWindow());
1099         assertEquals(dc.getImeTarget(IME_TARGET_INPUT).getWindow(), dc.computeImeControlTarget());
1100     }
1101 
1102     @Test
testComputeImeControlTarget_splitscreen()1103     public void testComputeImeControlTarget_splitscreen() throws Exception {
1104         final DisplayContent dc = createNewDisplay();
1105         dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
1106         dc.getImeTarget(IME_TARGET_INPUT).getWindow().setWindowingMode(
1107                 WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
1108         dc.setImeLayeringTarget(dc.getImeTarget(IME_TARGET_INPUT).getWindow());
1109         dc.setRemoteInsetsController(createDisplayWindowInsetsController());
1110         assertNotEquals(dc.getImeTarget(IME_TARGET_INPUT).getWindow(),
1111                 dc.computeImeControlTarget());
1112     }
1113 
1114     @UseTestDisplay(addWindows = W_ACTIVITY)
1115     @Test
testComputeImeControlTarget_notMatchParentBounds()1116     public void testComputeImeControlTarget_notMatchParentBounds() throws Exception {
1117         spyOn(mAppWindow.mActivityRecord);
1118         doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds();
1119         mDisplayContent.setImeInputTarget(mAppWindow);
1120         mDisplayContent.setImeLayeringTarget(
1121                 mDisplayContent.getImeTarget(IME_TARGET_INPUT).getWindow());
1122         mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
1123         assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget());
1124     }
1125 
1126     @Test
testUpdateSystemGestureExclusion()1127     public void testUpdateSystemGestureExclusion() throws Exception {
1128         final DisplayContent dc = createNewDisplay();
1129         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
1130         win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
1131         win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
1132 
1133         performLayout(dc);
1134 
1135         win.setHasSurface(true);
1136         dc.updateSystemGestureExclusion();
1137 
1138         final boolean[] invoked = { false };
1139         final ISystemGestureExclusionListener.Stub verifier =
1140                 new ISystemGestureExclusionListener.Stub() {
1141             @Override
1142             public void onSystemGestureExclusionChanged(int displayId, Region actual,
1143                     Region unrestricted) {
1144                 Region expected = Region.obtain();
1145                 expected.set(10, 20, 30, 40);
1146                 assertEquals(expected, actual);
1147                 invoked[0] = true;
1148             }
1149         };
1150         try {
1151             dc.registerSystemGestureExclusionListener(verifier);
1152         } finally {
1153             dc.unregisterSystemGestureExclusionListener(verifier);
1154         }
1155         assertTrue("SystemGestureExclusionListener was not invoked", invoked[0]);
1156     }
1157 
1158     @Test
testCalculateSystemGestureExclusion()1159     public void testCalculateSystemGestureExclusion() throws Exception {
1160         final DisplayContent dc = createNewDisplay();
1161         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
1162         win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
1163         win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40)));
1164 
1165         final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "win2");
1166         win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
1167         win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50)));
1168 
1169         performLayout(dc);
1170 
1171         win.setHasSurface(true);
1172         win2.setHasSurface(true);
1173 
1174         final Region expected = Region.obtain();
1175         expected.set(20, 30, 40, 50);
1176         assertEquals(expected, calculateSystemGestureExclusion(dc));
1177     }
1178 
calculateSystemGestureExclusion(DisplayContent dc)1179     private Region calculateSystemGestureExclusion(DisplayContent dc) {
1180         Region out = Region.obtain();
1181         Region unrestricted = Region.obtain();
1182         dc.calculateSystemGestureExclusion(out, unrestricted);
1183         return out;
1184     }
1185 
1186     @Test
testCalculateSystemGestureExclusion_modal()1187     public void testCalculateSystemGestureExclusion_modal() throws Exception {
1188         final DisplayContent dc = createNewDisplay();
1189         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "base");
1190         win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
1191         win.setSystemGestureExclusion(Collections.singletonList(new Rect(0, 0, 1000, 1000)));
1192 
1193         final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "modal");
1194         win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
1195         win2.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
1196         win2.getAttrs().width = 10;
1197         win2.getAttrs().height = 10;
1198         win2.setSystemGestureExclusion(Collections.emptyList());
1199 
1200         performLayout(dc);
1201 
1202         win.setHasSurface(true);
1203         win2.setHasSurface(true);
1204 
1205         final Region expected = Region.obtain();
1206         assertEquals(expected, calculateSystemGestureExclusion(dc));
1207     }
1208 
1209     @Test
testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow()1210     public void testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow() throws Exception {
1211         mWm.mConstants.mSystemGestureExcludedByPreQStickyImmersive = true;
1212 
1213         final DisplayContent dc = createNewDisplay();
1214         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
1215         win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
1216         win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
1217         win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION;
1218         win.getAttrs().insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
1219         final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
1220         requestedVisibilities.setVisibility(ITYPE_NAVIGATION_BAR, false);
1221         requestedVisibilities.setVisibility(ITYPE_STATUS_BAR, false);
1222         win.setRequestedVisibilities(requestedVisibilities);
1223         win.mActivityRecord.mTargetSdk = P;
1224 
1225         performLayout(dc);
1226 
1227         win.setHasSurface(true);
1228 
1229         final Region expected = Region.obtain();
1230         expected.set(dc.getBounds());
1231         assertEquals(expected, calculateSystemGestureExclusion(dc));
1232 
1233         win.setHasSurface(false);
1234     }
1235 
1236     @UseTestDisplay(addWindows = { W_ABOVE_ACTIVITY, W_ACTIVITY})
1237     @Test
testRequestResizeForEmptyFrames()1238     public void testRequestResizeForEmptyFrames() {
1239         final WindowState win = mChildAppWindowAbove;
1240         makeWindowVisible(win, win.getParentWindow());
1241         win.setRequestedSize(mDisplayContent.mBaseDisplayWidth, 0 /* height */);
1242         win.mAttrs.width = win.mAttrs.height = WindowManager.LayoutParams.WRAP_CONTENT;
1243         win.mAttrs.gravity = Gravity.CENTER;
1244         performLayout(mDisplayContent);
1245 
1246         // The frame is empty because the requested height is zero.
1247         assertTrue(win.getFrame().isEmpty());
1248         // The window should be scheduled to resize then the client may report a new non-empty size.
1249         win.updateResizingWindowIfNeeded();
1250         assertThat(mWm.mResizingWindows).contains(win);
1251     }
1252 
1253     @Test
testOrientationChangeLogging()1254     public void testOrientationChangeLogging() {
1255         MetricsLogger mockLogger = mock(MetricsLogger.class);
1256         Configuration oldConfig = new Configuration();
1257         oldConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
1258 
1259         Configuration newConfig = new Configuration();
1260         newConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
1261         final DisplayContent displayContent = createNewDisplay();
1262         Mockito.doReturn(mockLogger).when(displayContent).getMetricsLogger();
1263         Mockito.doReturn(oldConfig).doReturn(newConfig).when(displayContent).getConfiguration();
1264 
1265         displayContent.onConfigurationChanged(newConfig);
1266 
1267         ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class);
1268         verify(mockLogger).write(logMakerCaptor.capture());
1269         assertThat(logMakerCaptor.getValue().getCategory(),
1270                 is(MetricsProto.MetricsEvent.ACTION_PHONE_ORIENTATION_CHANGED));
1271         assertThat(logMakerCaptor.getValue().getSubtype(),
1272                 is(Configuration.ORIENTATION_PORTRAIT));
1273     }
1274 
1275     @Test
testHybridRotationAnimation()1276     public void testHybridRotationAnimation() {
1277         final DisplayContent displayContent = mDefaultDisplay;
1278         final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
1279         final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
1280         final WindowState app = createWindow(null, TYPE_BASE_APPLICATION, "app");
1281         final WindowState[] windows = { statusBar, navBar, app };
1282         makeWindowVisible(windows);
1283         final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
1284         displayPolicy.addWindowLw(statusBar, statusBar.mAttrs);
1285         displayPolicy.addWindowLw(navBar, navBar.mAttrs);
1286         final ScreenRotationAnimation rotationAnim = new ScreenRotationAnimation(displayContent,
1287                 displayContent.getRotation());
1288         spyOn(rotationAnim);
1289         // Assume that the display rotation is changed so it is frozen in preparation for animation.
1290         doReturn(true).when(rotationAnim).hasScreenshot();
1291         mWm.mDisplayFrozen = true;
1292         displayContent.getDisplayRotation().setRotation((displayContent.getRotation() + 1) % 4);
1293         displayContent.setRotationAnimation(rotationAnim);
1294         // The fade rotation animation also starts to hide some non-app windows.
1295         assertNotNull(displayContent.getFadeRotationAnimationController());
1296         assertTrue(statusBar.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
1297 
1298         for (WindowState w : windows) {
1299             w.setOrientationChanging(true);
1300         }
1301         // The display only waits for the app window to unfreeze.
1302         assertFalse(displayContent.waitForUnfreeze(statusBar));
1303         assertFalse(displayContent.waitForUnfreeze(navBar));
1304         assertTrue(displayContent.waitForUnfreeze(app));
1305         // If all windows animated by fade rotation animation have done the orientation change,
1306         // the animation controller should be cleared.
1307         statusBar.setOrientationChanging(false);
1308         navBar.setOrientationChanging(false);
1309         assertNull(displayContent.getFadeRotationAnimationController());
1310     }
1311 
1312     @UseTestDisplay(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR,
1313             W_INPUT_METHOD, W_NOTIFICATION_SHADE })
1314     @Test
testApplyTopFixedRotationTransform()1315     public void testApplyTopFixedRotationTransform() {
1316         final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
1317         // Only non-movable (gesture) navigation bar will be animated by fixed rotation animation.
1318         doReturn(false).when(displayPolicy).navigationBarCanMove();
1319         displayPolicy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs);
1320         displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
1321         displayPolicy.addWindowLw(mNotificationShadeWindow, mNotificationShadeWindow.mAttrs);
1322         makeWindowVisible(mStatusBarWindow, mNavBarWindow);
1323         final Configuration config90 = new Configuration();
1324         mDisplayContent.computeScreenConfiguration(config90, ROTATION_90);
1325 
1326         final Configuration config = new Configuration();
1327         mDisplayContent.getDisplayRotation().setRotation(ROTATION_0);
1328         mDisplayContent.computeScreenConfiguration(config);
1329         mDisplayContent.onRequestedOverrideConfigurationChanged(config);
1330         assertNotEquals(config90.windowConfiguration.getMaxBounds(),
1331                 config.windowConfiguration.getMaxBounds());
1332 
1333         final ActivityRecord app = mAppWindow.mActivityRecord;
1334         app.setVisible(false);
1335         mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
1336         mDisplayContent.mOpeningApps.add(app);
1337         final int newOrientation = getRotatedOrientation(mDisplayContent);
1338         app.setRequestedOrientation(newOrientation);
1339 
1340         assertTrue(app.isFixedRotationTransforming());
1341         assertTrue(mAppWindow.matchesDisplayAreaBounds());
1342         assertFalse(mAppWindow.areAppWindowBoundsLetterboxed());
1343         assertTrue(mDisplayContent.getDisplayRotation().shouldRotateSeamlessly(
1344                 ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */,
1345                 false /* forceUpdate */));
1346 
1347         assertNotNull(mDisplayContent.getFadeRotationAnimationController());
1348         assertTrue(mStatusBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
1349         assertTrue(mNavBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
1350         // Notification shade may have its own view animation in real case so do not fade out it.
1351         assertFalse(mNotificationShadeWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
1352 
1353         // If the visibility of insets state is changed, the rotated state should be updated too.
1354         final InsetsState rotatedState = app.getFixedRotationTransformInsetsState();
1355         final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
1356         assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(),
1357                 rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
1358         state.getSource(ITYPE_STATUS_BAR).setVisible(
1359                 !rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
1360         mDisplayContent.getInsetsStateController().notifyInsetsChanged();
1361         assertEquals(state.getSource(ITYPE_STATUS_BAR).isVisible(),
1362                 rotatedState.getSource(ITYPE_STATUS_BAR).isVisible());
1363 
1364         final Rect outFrame = new Rect();
1365         final Rect outInsets = new Rect();
1366         final Rect outStableInsets = new Rect();
1367         final Rect outSurfaceInsets = new Rect();
1368         mAppWindow.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
1369         // The animation frames should not be rotated because display hasn't rotated.
1370         assertEquals(mDisplayContent.getBounds(), outFrame);
1371 
1372         // The display should keep current orientation and the rotated configuration should apply
1373         // to the activity.
1374         assertEquals(config.orientation, mDisplayContent.getConfiguration().orientation);
1375         assertEquals(config90.orientation, app.getConfiguration().orientation);
1376         assertEquals(config90.windowConfiguration.getBounds(), app.getBounds());
1377 
1378         // Make wallaper laid out with the fixed rotation transform.
1379         final WindowToken wallpaperToken = mWallpaperWindow.mToken;
1380         wallpaperToken.linkFixedRotationTransform(app);
1381         mWallpaperWindow.mLayoutNeeded = true;
1382         performLayout(mDisplayContent);
1383 
1384         // Force the negative offset to verify it can be updated.
1385         mWallpaperWindow.mXOffset = mWallpaperWindow.mYOffset = -1;
1386         assertTrue(mDisplayContent.mWallpaperController.updateWallpaperOffset(mWallpaperWindow,
1387                 false /* sync */));
1388         assertThat(mWallpaperWindow.mXOffset).isGreaterThan(-1);
1389         assertThat(mWallpaperWindow.mYOffset).isGreaterThan(-1);
1390 
1391         // The wallpaper need to animate with transformed position, so its surface position should
1392         // not be reset.
1393         final Transaction t = wallpaperToken.getPendingTransaction();
1394         spyOn(t);
1395         mWallpaperWindow.mToken.onAnimationLeashCreated(t, null /* leash */);
1396         verify(t, never()).setPosition(any(), eq(0), eq(0));
1397 
1398         // Launch another activity before the transition is finished.
1399         final Task task2 = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build();
1400         final ActivityRecord app2 = new ActivityBuilder(mAtm).setTask(task2)
1401                 .setUseProcess(app.app).build();
1402         app2.setVisible(false);
1403         mDisplayContent.mOpeningApps.add(app2);
1404         app2.setRequestedOrientation(newOrientation);
1405 
1406         // The activity should share the same transform state as the existing one. The activity
1407         // should also be the fixed rotation launching app because it is the latest top.
1408         assertTrue(app.hasFixedRotationTransform(app2));
1409         assertTrue(mDisplayContent.isFixedRotationLaunchingApp(app2));
1410 
1411         final Configuration expectedProcConfig = new Configuration(app2.app.getConfiguration());
1412         expectedProcConfig.windowConfiguration.setActivityType(
1413                 WindowConfiguration.ACTIVITY_TYPE_UNDEFINED);
1414         assertEquals("The process should receive rotated configuration for compatibility",
1415                 expectedProcConfig, app2.app.getConfiguration());
1416 
1417         // If the rotated activity requests to show IME, the IME window should use the
1418         // transformation from activity to lay out in the same orientation.
1419         mDisplayContent.setImeLayeringTarget(mAppWindow);
1420         LocalServices.getService(WindowManagerInternal.class).onToggleImeRequested(true /* show */,
1421                 app.token, app.token, mDisplayContent.mDisplayId);
1422         assertTrue(mImeWindow.mToken.hasFixedRotationTransform());
1423         assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_FIXED_TRANSFORM));
1424 
1425         // The fixed rotation transform can only be finished when all animation finished.
1426         doReturn(false).when(app2).isAnimating(anyInt(), anyInt());
1427         mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token);
1428         assertTrue(app.hasFixedRotationTransform());
1429         assertTrue(app2.hasFixedRotationTransform());
1430 
1431         // The display should be rotated after the launch is finished.
1432         doReturn(false).when(app).isAnimating(anyInt(), anyInt());
1433         mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token);
1434 
1435         // The fixed rotation should be cleared and the new rotation is applied to display.
1436         assertFalse(app.hasFixedRotationTransform());
1437         assertFalse(app2.hasFixedRotationTransform());
1438         assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation);
1439         assertNull(mDisplayContent.getFadeRotationAnimationController());
1440     }
1441 
1442     @Test
testFinishFixedRotationNoAppTransitioningTask()1443     public void testFinishFixedRotationNoAppTransitioningTask() {
1444         unblockDisplayRotation(mDisplayContent);
1445         final ActivityRecord app = createActivityRecord(mDisplayContent);
1446         final Task task = app.getTask();
1447         final ActivityRecord app2 = new ActivityBuilder(mWm.mAtmService).setTask(task).build();
1448         mDisplayContent.setFixedRotationLaunchingApp(app2, (mDisplayContent.getRotation() + 1) % 4);
1449         doReturn(true).when(task).isAppTransitioning();
1450         // If the task is animating transition, this should be no-op.
1451         mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
1452 
1453         assertTrue(app2.hasFixedRotationTransform());
1454         assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
1455 
1456         doReturn(false).when(task).isAppTransitioning();
1457         // Although this notifies app instead of app2 that uses the fixed rotation, app2 should
1458         // still finish the transform because there is no more transition event.
1459         mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token);
1460 
1461         assertFalse(app2.hasFixedRotationTransform());
1462         assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp());
1463     }
1464 
1465     @UseTestDisplay(addWindows = W_ACTIVITY)
1466     @Test
testRotateSeamlesslyWithFixedRotation()1467     public void testRotateSeamlesslyWithFixedRotation() {
1468         final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
1469         final ActivityRecord app = mAppWindow.mActivityRecord;
1470         mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
1471         mAppWindow.mAttrs.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
1472 
1473         // Use seamless rotation if the top app is rotated.
1474         assertTrue(displayRotation.shouldRotateSeamlessly(ROTATION_0 /* oldRotation */,
1475                 ROTATION_90 /* newRotation */, false /* forceUpdate */));
1476 
1477         mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(app);
1478 
1479         // Use normal rotation because animating recents is an intermediate state.
1480         assertFalse(displayRotation.shouldRotateSeamlessly(ROTATION_0 /* oldRotation */,
1481                 ROTATION_90 /* newRotation */, false /* forceUpdate */));
1482     }
1483 
1484     @Test
testFixedRotationWithPip()1485     public void testFixedRotationWithPip() {
1486         final DisplayContent displayContent = mDefaultDisplay;
1487         unblockDisplayRotation(displayContent);
1488         // Unblock the condition in PinnedTaskController#continueOrientationChangeIfNeeded.
1489         doNothing().when(displayContent).prepareAppTransition(anyInt());
1490         // Make resume-top really update the activity state.
1491         setBooted(mAtm);
1492         clearInvocations(mWm);
1493         // Speed up the test by a few seconds.
1494         mAtm.deferWindowLayout();
1495 
1496         final ActivityRecord homeActivity = createActivityRecord(
1497                 displayContent.getDefaultTaskDisplayArea().getRootHomeTask());
1498         final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm).setCreateTask(true).build();
1499         final Task pinnedTask = pinnedActivity.getRootTask();
1500         doReturn((displayContent.getRotation() + 1) % 4).when(displayContent)
1501                 .rotationForActivityInDifferentOrientation(eq(homeActivity));
1502         // Enter PiP from fullscreen.
1503         pinnedTask.setWindowingMode(WINDOWING_MODE_PINNED);
1504 
1505         assertTrue(displayContent.hasTopFixedRotationLaunchingApp());
1506         assertTrue(displayContent.mPinnedTaskController.shouldDeferOrientationChange());
1507         verify(mWm, never()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
1508         clearInvocations(pinnedTask);
1509 
1510         // Assume that the PiP enter animation is done then the new bounds are set. Expect the
1511         // orientation update is no longer deferred.
1512         displayContent.mPinnedTaskController.setEnterPipBounds(pinnedTask.getBounds());
1513         // The Task Configuration was frozen to skip the change of orientation.
1514         verify(pinnedTask, never()).onConfigurationChanged(any());
1515         assertFalse(displayContent.mPinnedTaskController.shouldDeferOrientationChange());
1516         assertFalse(displayContent.hasTopFixedRotationLaunchingApp());
1517         assertEquals(homeActivity.getConfiguration().orientation,
1518                 displayContent.getConfiguration().orientation);
1519 
1520         doReturn((displayContent.getRotation() + 1) % 4).when(displayContent)
1521                 .rotationForActivityInDifferentOrientation(eq(pinnedActivity));
1522         // Leave PiP to fullscreen. Simulate the step of PipTaskOrganizer that sets the activity
1523         // to fullscreen, so fixed rotation will apply on it.
1524         pinnedActivity.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
1525         assertTrue(displayContent.hasTopFixedRotationLaunchingApp());
1526 
1527         // Assume the animation of PipTaskOrganizer is done and then commit fullscreen to task.
1528         pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
1529         displayContent.continueUpdateOrientationForDiffOrienLaunchingApp();
1530         assertFalse(displayContent.mPinnedTaskController.isFreezingTaskConfig(pinnedTask));
1531         assertEquals(pinnedActivity.getConfiguration().orientation,
1532                 displayContent.getConfiguration().orientation);
1533     }
1534 
1535     @Test
testNoFixedRotationOnResumedScheduledApp()1536     public void testNoFixedRotationOnResumedScheduledApp() {
1537         unblockDisplayRotation(mDisplayContent);
1538         final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
1539         app.setVisible(false);
1540         app.setState(ActivityRecord.State.RESUMED, "test");
1541         mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
1542         mDisplayContent.mOpeningApps.add(app);
1543         final int newOrientation = getRotatedOrientation(mDisplayContent);
1544         app.setRequestedOrientation(newOrientation);
1545 
1546         // The condition should reject using fixed rotation because the resumed client in real case
1547         // might get display info immediately. And the fixed rotation adjustments haven't arrived
1548         // client side so the info may be inconsistent with the requested orientation.
1549         verify(mDisplayContent).handleTopActivityLaunchingInDifferentOrientation(eq(app),
1550                 eq(true) /* checkOpening */);
1551         assertFalse(app.isFixedRotationTransforming());
1552         assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp());
1553     }
1554 
1555     @Test
testRecentsNotRotatingWithFixedRotation()1556     public void testRecentsNotRotatingWithFixedRotation() {
1557         unblockDisplayRotation(mDisplayContent);
1558         final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
1559         // Skip freezing so the unrelated conditions in updateRotationUnchecked won't disturb.
1560         doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt());
1561 
1562         final ActivityRecord activity = createActivityRecord(mDisplayContent);
1563         final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent);
1564         recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
1565         doReturn(mock(RecentsAnimationController.class)).when(mWm).getRecentsAnimationController();
1566 
1567         // Do not rotate if the recents animation is animating on top.
1568         mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
1569         displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
1570         assertFalse(displayRotation.updateRotationUnchecked(false));
1571 
1572         // Rotation can be updated if the recents animation is finished.
1573         mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();
1574         assertTrue(displayRotation.updateRotationUnchecked(false));
1575 
1576         // Rotation can be updated if the policy is not ok to animate (e.g. going to sleep).
1577         mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
1578         displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
1579         ((TestWindowManagerPolicy) mWm.mPolicy).mOkToAnimate = false;
1580         assertTrue(displayRotation.updateRotationUnchecked(false));
1581 
1582         // Rotation can be updated if the recents animation is animating but it is not on top, e.g.
1583         // switching activities in different orientations by quickstep gesture.
1584         mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
1585         mDisplayContent.setFixedRotationLaunchingAppUnchecked(activity);
1586         displayRotation.setRotation((displayRotation.getRotation() + 1) % 4);
1587         assertTrue(displayRotation.updateRotationUnchecked(false));
1588 
1589         // The recents activity should not apply fixed rotation if the top activity is not opaque.
1590         mDisplayContent.mFocusedApp = activity;
1591         doReturn(false).when(mDisplayContent.mFocusedApp).occludesParent();
1592         doReturn(ROTATION_90).when(mDisplayContent).rotationForActivityInDifferentOrientation(
1593                 eq(recentsActivity));
1594         mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity);
1595         assertFalse(recentsActivity.hasFixedRotationTransform());
1596     }
1597 
1598     @Test
testClearIntermediateFixedRotationAdjustments()1599     public void testClearIntermediateFixedRotationAdjustments() throws RemoteException {
1600         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
1601         mDisplayContent.setFixedRotationLaunchingApp(activity,
1602                 (mDisplayContent.getRotation() + 1) % 4);
1603         // Create a window so FixedRotationAdjustmentsItem can be sent.
1604         createWindow(null, TYPE_APPLICATION_STARTING, activity, "AppWin");
1605         final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
1606         activity2.setVisible(false);
1607         clearInvocations(mAtm.getLifecycleManager());
1608         // The first activity has applied fixed rotation but the second activity becomes the top
1609         // before the transition is done and it has the same rotation as display, so the dispatched
1610         // rotation adjustment of first activity must be cleared.
1611         mDisplayContent.handleTopActivityLaunchingInDifferentOrientation(activity2,
1612                 false /* checkOpening */);
1613 
1614         final ArgumentCaptor<FixedRotationAdjustmentsItem> adjustmentsCaptor =
1615                 ArgumentCaptor.forClass(FixedRotationAdjustmentsItem.class);
1616         verify(mAtm.getLifecycleManager(), atLeastOnce()).scheduleTransaction(
1617                 eq(activity.app.getThread()), adjustmentsCaptor.capture());
1618         // The transformation is kept for animation in real case.
1619         assertTrue(activity.hasFixedRotationTransform());
1620         final FixedRotationAdjustmentsItem clearAdjustments = FixedRotationAdjustmentsItem.obtain(
1621                 activity.token, null /* fixedRotationAdjustments */);
1622         // The captor may match other items. The first one must be the item to clear adjustments.
1623         assertEquals(clearAdjustments, adjustmentsCaptor.getAllValues().get(0));
1624     }
1625 
1626     @Test
testRemoteRotation()1627     public void testRemoteRotation() {
1628         DisplayContent dc = createNewDisplay();
1629 
1630         final DisplayRotation dr = dc.getDisplayRotation();
1631         doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
1632         // Rotate 180 degree so the display doesn't have configuration change. This condition is
1633         // used for the later verification of stop-freezing (without setting mWaitingForConfig).
1634         doReturn((dr.getRotation() + 2) % 4).when(dr).rotationForOrientation(anyInt(), anyInt());
1635         final boolean[] continued = new boolean[1];
1636         doAnswer(
1637                 invocation -> {
1638                     continued[0] = true;
1639                     return true;
1640                 }).when(dc).updateDisplayOverrideConfigurationLocked();
1641         final boolean[] called = new boolean[1];
1642         mWm.mDisplayRotationController =
1643                 new IDisplayWindowRotationController.Stub() {
1644                     @Override
1645                     public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
1646                             IDisplayWindowRotationCallback callback) {
1647                         called[0] = true;
1648 
1649                         try {
1650                             callback.continueRotateDisplay(toRotation, null);
1651                         } catch (RemoteException e) {
1652                             assertTrue(false);
1653                         }
1654                     }
1655                 };
1656 
1657         // kill any existing rotation animation (vestigial from test setup).
1658         dc.setRotationAnimation(null);
1659 
1660         mWm.updateRotation(true /* alwaysSendConfiguration */, false /* forceRelayout */);
1661         // If remote rotation is not finished, the display should not be able to unfreeze.
1662         mWm.stopFreezingDisplayLocked();
1663         assertTrue(mWm.mDisplayFrozen);
1664 
1665         assertTrue(called[0]);
1666         waitUntilHandlersIdle();
1667         assertTrue(continued[0]);
1668 
1669         mWm.stopFreezingDisplayLocked();
1670         assertFalse(mWm.mDisplayFrozen);
1671     }
1672 
1673     @Test
testShellTransitRotation()1674     public void testShellTransitRotation() {
1675         DisplayContent dc = createNewDisplay();
1676 
1677         final TestTransitionPlayer testPlayer = registerTestTransitionPlayer();
1678         final DisplayRotation dr = dc.getDisplayRotation();
1679         doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
1680         // Rotate 180 degree so the display doesn't have configuration change. This condition is
1681         // used for the later verification of stop-freezing (without setting mWaitingForConfig).
1682         doReturn((dr.getRotation() + 2) % 4).when(dr).rotationForOrientation(anyInt(), anyInt());
1683         mWm.mDisplayRotationController =
1684                 new IDisplayWindowRotationController.Stub() {
1685                     @Override
1686                     public void onRotateDisplay(int displayId, int fromRotation, int toRotation,
1687                             IDisplayWindowRotationCallback callback) {
1688                         try {
1689                             callback.continueRotateDisplay(toRotation, null);
1690                         } catch (RemoteException e) {
1691                             assertTrue(false);
1692                         }
1693                     }
1694                 };
1695 
1696         // kill any existing rotation animation (vestigial from test setup).
1697         dc.setRotationAnimation(null);
1698 
1699         final int origRot = dc.getConfiguration().windowConfiguration.getRotation();
1700 
1701         mWm.updateRotation(true /* alwaysSendConfiguration */, false /* forceRelayout */);
1702         // Should create a transition request without performing rotation
1703         assertNotNull(testPlayer.mLastRequest);
1704         assertEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
1705 
1706         // Once transition starts, rotation is applied and transition shows DC rotating.
1707         testPlayer.start();
1708         assertNotEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
1709         assertNotNull(testPlayer.mLastReady);
1710         assertEquals(dc, DisplayRotation.getDisplayFromTransition(testPlayer.mLastTransit));
1711         WindowContainerToken dcToken = dc.mRemoteToken.toWindowContainerToken();
1712         assertNotEquals(testPlayer.mLastReady.getChange(dcToken).getEndRotation(),
1713                 testPlayer.mLastReady.getChange(dcToken).getStartRotation());
1714         testPlayer.finish();
1715     }
1716 
1717     @Test
testValidWindowingLayer()1718     public void testValidWindowingLayer() {
1719         final SurfaceControl windowingLayer = mDisplayContent.getWindowingLayer();
1720         assertNotNull(windowingLayer);
1721 
1722         final List<DisplayArea<?>> windowedMagnificationAreas =
1723                 mDisplayContent.mDisplayAreaPolicy.getDisplayAreas(FEATURE_WINDOWED_MAGNIFICATION);
1724         if (windowedMagnificationAreas != null) {
1725             assertEquals("There should be only one DisplayArea for FEATURE_WINDOWED_MAGNIFICATION",
1726                     1, windowedMagnificationAreas.size());
1727             assertEquals(windowedMagnificationAreas.get(0).mSurfaceControl, windowingLayer);
1728         } else {
1729             assertNotEquals(mDisplayContent.mSurfaceControl, windowingLayer);
1730         }
1731     }
1732 
1733     @Test
testFindScrollCaptureTargetWindow_behindWindow()1734     public void testFindScrollCaptureTargetWindow_behindWindow() {
1735         DisplayContent display = createNewDisplay();
1736         Task rootTask = createTask(display);
1737         Task task = createTaskInRootTask(rootTask, 0 /* userId */);
1738         WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window");
1739         WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
1740 
1741         WindowState result = display.findScrollCaptureTargetWindow(behindWindow,
1742                 ActivityTaskManager.INVALID_TASK_ID);
1743         assertEquals(activityWindow, result);
1744     }
1745 
1746     @Test
testFindScrollCaptureTargetWindow_cantReceiveKeys()1747     public void testFindScrollCaptureTargetWindow_cantReceiveKeys() {
1748         DisplayContent display = createNewDisplay();
1749         Task rootTask = createTask(display);
1750         Task task = createTaskInRootTask(rootTask, 0 /* userId */);
1751         WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window");
1752         WindowState invisible = createWindow(null, TYPE_APPLICATION, "invisible");
1753         invisible.mViewVisibility = View.INVISIBLE;  // make canReceiveKeys return false
1754 
1755         WindowState result = display.findScrollCaptureTargetWindow(null,
1756                 ActivityTaskManager.INVALID_TASK_ID);
1757         assertEquals(activityWindow, result);
1758     }
1759 
1760     @Test
testFindScrollCaptureTargetWindow_secure()1761     public void testFindScrollCaptureTargetWindow_secure() {
1762         DisplayContent display = createNewDisplay();
1763         Task rootTask = createTask(display);
1764         Task task = createTaskInRootTask(rootTask, 0 /* userId */);
1765         WindowState secureWindow = createWindow(null, TYPE_APPLICATION, "Secure Window");
1766         secureWindow.mAttrs.flags |= FLAG_SECURE;
1767 
1768         WindowState result = display.findScrollCaptureTargetWindow(null,
1769                 ActivityTaskManager.INVALID_TASK_ID);
1770         assertNull(result);
1771     }
1772 
1773     @Test
testFindScrollCaptureTargetWindow_secureTaskId()1774     public void testFindScrollCaptureTargetWindow_secureTaskId() {
1775         DisplayContent display = createNewDisplay();
1776         Task rootTask = createTask(display);
1777         Task task = createTaskInRootTask(rootTask, 0 /* userId */);
1778         WindowState secureWindow = createWindow(null, TYPE_APPLICATION, "Secure Window");
1779         secureWindow.mAttrs.flags |= FLAG_SECURE;
1780 
1781         WindowState result = display.findScrollCaptureTargetWindow(null,  task.mTaskId);
1782         assertNull(result);
1783     }
1784 
1785     @Test
testFindScrollCaptureTargetWindow_taskId()1786     public void testFindScrollCaptureTargetWindow_taskId() {
1787         DisplayContent display = createNewDisplay();
1788         Task rootTask = createTask(display);
1789         Task task = createTaskInRootTask(rootTask, 0 /* userId */);
1790         WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window");
1791         WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
1792 
1793         WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId);
1794         assertEquals(window, result);
1795     }
1796 
1797     @Test
testFindScrollCaptureTargetWindow_taskIdCantReceiveKeys()1798     public void testFindScrollCaptureTargetWindow_taskIdCantReceiveKeys() {
1799         DisplayContent display = createNewDisplay();
1800         Task rootTask = createTask(display);
1801         Task task = createTaskInRootTask(rootTask, 0 /* userId */);
1802         WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window");
1803         window.mViewVisibility = View.INVISIBLE;  // make canReceiveKeys return false
1804         WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
1805 
1806         WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId);
1807         assertEquals(window, result);
1808     }
1809 
1810     @Test
testEnsureActivitiesVisibleNotRecursive()1811     public void testEnsureActivitiesVisibleNotRecursive() {
1812         final TaskDisplayArea mockTda = mock(TaskDisplayArea.class);
1813         final boolean[] called = { false };
1814         doAnswer(invocation -> {
1815             // The assertion will fail if DisplayArea#ensureActivitiesVisible is called twice.
1816             assertFalse(called[0]);
1817             called[0] = true;
1818             mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
1819             return null;
1820         }).when(mockTda).ensureActivitiesVisible(any(), anyInt(), anyBoolean(), anyBoolean());
1821 
1822         mDisplayContent.ensureActivitiesVisible(null, 0, false, false);
1823     }
1824 
1825     @Test
testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode()1826     public void testSetWindowingModeAtomicallyUpdatesWindoingModeAndDisplayWindowingMode() {
1827         final DisplayContent dc = createNewDisplay();
1828         final Task rootTask = new TaskBuilder(mSupervisor)
1829                 .setDisplay(dc)
1830                 .build();
1831         doAnswer(invocation -> {
1832             Object[] args = invocation.getArguments();
1833             final Configuration config = ((Configuration) args[0]);
1834             assertEquals(config.windowConfiguration.getWindowingMode(),
1835                     config.windowConfiguration.getDisplayWindowingMode());
1836             return null;
1837         }).when(rootTask).onConfigurationChanged(any());
1838         dc.setWindowingMode(WINDOWING_MODE_FREEFORM);
1839         dc.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
1840     }
1841 
1842     @Test
testForceDesktopMode()1843     public void testForceDesktopMode() {
1844         mWm.mForceDesktopModeOnExternalDisplays = true;
1845         // Not applicable for default display
1846         assertFalse(mDefaultDisplay.forceDesktopMode());
1847 
1848         // Not applicable for private secondary display.
1849         final DisplayInfo displayInfo = new DisplayInfo();
1850         displayInfo.copyFrom(mDisplayInfo);
1851         displayInfo.flags = FLAG_PRIVATE;
1852         final DisplayContent privateDc = createNewDisplay(displayInfo);
1853         assertFalse(privateDc.forceDesktopMode());
1854 
1855         // Applicable for public secondary display.
1856         final DisplayContent publicDc = createNewDisplay();
1857         assertTrue(publicDc.forceDesktopMode());
1858 
1859         // Make sure forceDesktopMode() is false when the force config is disabled.
1860         mWm.mForceDesktopModeOnExternalDisplays = false;
1861         assertFalse(publicDc.forceDesktopMode());
1862     }
1863 
1864     @Test
testDisplaySettingsReappliedWhenDisplayChanged()1865     public void testDisplaySettingsReappliedWhenDisplayChanged() {
1866         final DisplayInfo displayInfo = new DisplayInfo();
1867         displayInfo.copyFrom(mDisplayInfo);
1868         final DisplayContent dc = createNewDisplay(displayInfo);
1869 
1870         // Generate width/height/density values different from the default of the display.
1871         final int forcedWidth = dc.mBaseDisplayWidth + 1;
1872         final int forcedHeight = dc.mBaseDisplayHeight + 1;;
1873         final int forcedDensity = dc.mBaseDisplayDensity + 1;;
1874         // Update the forced size and density in settings and the unique id to simualate a display
1875         // remap.
1876         dc.mWmService.mDisplayWindowSettings.setForcedSize(dc, forcedWidth, forcedHeight);
1877         dc.mWmService.mDisplayWindowSettings.setForcedDensity(dc, forcedDensity, 0 /* userId */);
1878         dc.mCurrentUniqueDisplayId = mDisplayInfo.uniqueId + "-test";
1879         // Trigger display changed.
1880         dc.onDisplayChanged();
1881         // Ensure overridden size and denisty match the most up-to-date values in settings for the
1882         // display.
1883         verifySizes(dc, forcedWidth, forcedHeight, forcedDensity);
1884     }
1885 
1886     @UseTestDisplay(addWindows = { W_ACTIVITY, W_INPUT_METHOD })
1887     @Test
testComputeImeTarget_shouldNotCheckOutdatedImeTargetLayerWhenRemoved()1888     public void testComputeImeTarget_shouldNotCheckOutdatedImeTargetLayerWhenRemoved() {
1889         final WindowState child1 = createWindow(mAppWindow, FIRST_SUB_WINDOW, "child1");
1890         final WindowState nextImeTargetApp = createWindow(null /* parent */,
1891                 TYPE_BASE_APPLICATION, "nextImeTargetApp");
1892         spyOn(child1);
1893         doReturn(true).when(child1).inSplitScreenWindowingMode();
1894         mDisplayContent.setImeLayeringTarget(child1);
1895 
1896         spyOn(nextImeTargetApp);
1897         spyOn(mAppWindow);
1898         doReturn(true).when(nextImeTargetApp).canBeImeTarget();
1899         doReturn(true).when(nextImeTargetApp).isActivityTypeHome();
1900         doReturn(false).when(mAppWindow).canBeImeTarget();
1901 
1902         child1.removeImmediately();
1903 
1904         verify(mDisplayContent).computeImeTarget(true);
1905         assertNull(mDisplayContent.getImeTarget(IME_TARGET_INPUT));
1906         verify(child1, never()).needsRelativeLayeringToIme();
1907     }
1908 
1909     @UseTestDisplay(addWindows = {W_INPUT_METHOD}, addAllCommonWindows = true)
1910     @Test
testAttachAndShowImeScreenshotOnTarget()1911     public void testAttachAndShowImeScreenshotOnTarget() {
1912         // Preparation: Simulate screen state is on.
1913         spyOn(mWm.mPolicy);
1914         doReturn(true).when(mWm.mPolicy).isScreenOn();
1915 
1916         // Preparation: Simulate snapshot IME surface.
1917         spyOn(mWm.mTaskSnapshotController);
1918         doReturn(mock(SurfaceControl.ScreenshotHardwareBuffer.class)).when(
1919                 mWm.mTaskSnapshotController).snapshotImeFromAttachedTask(any());
1920         final SurfaceControl imeSurface = mock(SurfaceControl.class);
1921         spyOn(imeSurface);
1922         doReturn(true).when(imeSurface).isValid();
1923         doReturn(imeSurface).when(mDisplayContent).createImeSurface(any(), any());
1924 
1925         // Preparation: Simulate snapshot Task.
1926         ActivityRecord act1 = createActivityRecord(mDisplayContent);
1927         final WindowState appWin1 = createWindow(null, TYPE_BASE_APPLICATION, act1, "appWin1");
1928         spyOn(appWin1);
1929         spyOn(appWin1.mWinAnimator);
1930         appWin1.setHasSurface(true);
1931         assertTrue(appWin1.canBeImeTarget());
1932         doReturn(true).when(appWin1.mWinAnimator).getShown();
1933         doReturn(true).when(appWin1.mActivityRecord).isSurfaceShowing();
1934         appWin1.mWinAnimator.mLastAlpha = 1f;
1935 
1936         // Test step 1: appWin1 is the current IME target and soft-keyboard is visible.
1937         mDisplayContent.computeImeTarget(true);
1938         assertEquals(appWin1, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
1939         spyOn(mDisplayContent.mInputMethodWindow);
1940         doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible();
1941         mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true);
1942 
1943         // Test step 2: Simulate launching appWin2 and appWin1 is in app transition.
1944         ActivityRecord act2 = createActivityRecord(mDisplayContent);
1945         final WindowState appWin2 = createWindow(null, TYPE_BASE_APPLICATION, act2, "appWin2");
1946         appWin2.setHasSurface(true);
1947         assertTrue(appWin2.canBeImeTarget());
1948         doReturn(true).when(appWin1).isAnimating(PARENTS | TRANSITION,
1949                 ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS);
1950 
1951         // Test step 3: Verify appWin2 will be the next IME target and the IME snapshot surface will
1952         // be shown at this time.
1953         final Transaction t = mDisplayContent.getPendingTransaction();
1954         spyOn(t);
1955         mDisplayContent.setImeInputTarget(appWin2);
1956         mDisplayContent.computeImeTarget(true);
1957         assertEquals(appWin2, mDisplayContent.getImeTarget(IME_TARGET_LAYERING));
1958         assertTrue(mDisplayContent.shouldImeAttachedToApp());
1959 
1960         verify(mDisplayContent, atLeast(1)).attachAndShowImeScreenshotOnTarget();
1961         verify(mWm.mTaskSnapshotController).snapshotImeFromAttachedTask(appWin1.getTask());
1962         assertNotNull(mDisplayContent.mImeScreenshot);
1963         verify(t).show(mDisplayContent.mImeScreenshot);
1964     }
1965 
1966     @UseTestDisplay(addWindows = {W_INPUT_METHOD}, addAllCommonWindows = true)
1967     @Test
testShowImeScreenshot()1968     public void testShowImeScreenshot() {
1969         final Task rootTask = createTask(mDisplayContent);
1970         final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
1971         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
1972         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
1973         task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
1974         doReturn(true).when(task).okToAnimate();
1975         ArrayList<WindowContainer> sources = new ArrayList<>();
1976         sources.add(activity);
1977 
1978         mDisplayContent.setImeLayeringTarget(win);
1979         spyOn(mDisplayContent);
1980 
1981         // Expecting the IME screenshot only be attached when performing task closing transition.
1982         task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
1983                 false /* isVoiceInteraction */, sources);
1984         verify(mDisplayContent).showImeScreenshot();
1985 
1986         clearInvocations(mDisplayContent);
1987         activity.applyAnimation(null, TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, false /* enter */,
1988                 false /* isVoiceInteraction */, sources);
1989         verify(mDisplayContent, never()).showImeScreenshot();
1990     }
1991 
1992     @Test
testRotateBounds_keepSamePhysicalPosition()1993     public void testRotateBounds_keepSamePhysicalPosition() {
1994         final DisplayContent dc =
1995                 new TestDisplayContent.Builder(mAtm, 1000, 2000).build();
1996         final Rect initBounds = new Rect(0, 0, 700, 1500);
1997         final Rect rotateBounds = new Rect(initBounds);
1998 
1999         // Rotate from 0 to 0
2000         dc.rotateBounds(ROTATION_0, ROTATION_0, rotateBounds);
2001 
2002         assertEquals(new Rect(0, 0, 700, 1500), rotateBounds);
2003 
2004         // Rotate from 0 to 90
2005         rotateBounds.set(initBounds);
2006         dc.rotateBounds(ROTATION_0, ROTATION_90, rotateBounds);
2007 
2008         assertEquals(new Rect(0, 300, 1500, 1000), rotateBounds);
2009 
2010         // Rotate from 0 to 180
2011         rotateBounds.set(initBounds);
2012         dc.rotateBounds(ROTATION_0, ROTATION_180, rotateBounds);
2013 
2014         assertEquals(new Rect(300, 500, 1000, 2000), rotateBounds);
2015 
2016         // Rotate from 0 to 270
2017         rotateBounds.set(initBounds);
2018         dc.rotateBounds(ROTATION_0, ROTATION_270, rotateBounds);
2019 
2020         assertEquals(new Rect(500, 0, 2000, 700), rotateBounds);
2021     }
2022 
2023     /**
2024      * Creates a TestDisplayContent using the constructor that takes in display width and height as
2025      * parameters and validates that the newly-created TestDisplayContent's DisplayInfo and
2026      * WindowConfiguration match the parameters passed into the constructor. Additionally, this test
2027      * checks that device-specific overrides are not applied.
2028      */
2029     @Test
testCreateTestDisplayContentFromDimensions()2030     public void testCreateTestDisplayContentFromDimensions() {
2031         final int displayWidth = 1000;
2032         final int displayHeight = 2000;
2033         final int windowingMode = WINDOWING_MODE_FULLSCREEN;
2034         final boolean ignoreOrientationRequests = false;
2035         final float fixedOrientationLetterboxRatio = 0;
2036         final DisplayContent testDisplayContent = new TestDisplayContent.Builder(mAtm, displayWidth,
2037                 displayHeight).build();
2038 
2039         // test display info
2040         final DisplayInfo di = testDisplayContent.getDisplayInfo();
2041         assertEquals(displayWidth, di.logicalWidth);
2042         assertEquals(displayHeight, di.logicalHeight);
2043         assertEquals(TestDisplayContent.DEFAULT_LOGICAL_DISPLAY_DENSITY, di.logicalDensityDpi);
2044 
2045         // test configuration
2046         final WindowConfiguration windowConfig = testDisplayContent.getConfiguration()
2047                 .windowConfiguration;
2048         assertEquals(displayWidth, windowConfig.getBounds().width());
2049         assertEquals(displayHeight, windowConfig.getBounds().height());
2050         assertEquals(windowingMode, windowConfig.getWindowingMode());
2051 
2052         // test misc display overrides
2053         assertEquals(ignoreOrientationRequests, testDisplayContent.mIgnoreOrientationRequest);
2054         assertEquals(fixedOrientationLetterboxRatio,
2055                 mWm.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(),
2056                 0 /* delta */);
2057     }
2058 
2059     /**
2060      * Creates a TestDisplayContent using the constructor that takes in a DisplayInfo as a parameter
2061      * and validates that the newly-created TestDisplayContent's DisplayInfo and WindowConfiguration
2062      * match the width, height, and density values set in the DisplayInfo passed as a parameter.
2063      * Additionally, this test checks that device-specific overrides are not applied.
2064      */
2065     @Test
testCreateTestDisplayContentFromDisplayInfo()2066     public void testCreateTestDisplayContentFromDisplayInfo() {
2067         final int displayWidth = 1000;
2068         final int displayHeight = 2000;
2069         final int windowingMode = WINDOWING_MODE_FULLSCREEN;
2070         final boolean ignoreOrientationRequests = false;
2071         final float fixedOrientationLetterboxRatio = 0;
2072         final DisplayInfo testDisplayInfo = new DisplayInfo();
2073         mContext.getDisplay().getDisplayInfo(testDisplayInfo);
2074         testDisplayInfo.logicalWidth = displayWidth;
2075         testDisplayInfo.logicalHeight = displayHeight;
2076         testDisplayInfo.logicalDensityDpi = TestDisplayContent.DEFAULT_LOGICAL_DISPLAY_DENSITY;
2077         final DisplayContent testDisplayContent = new TestDisplayContent.Builder(mAtm,
2078                 testDisplayInfo).build();
2079 
2080         // test display info
2081         final DisplayInfo di = testDisplayContent.getDisplayInfo();
2082         assertEquals(displayWidth, di.logicalWidth);
2083         assertEquals(displayHeight, di.logicalHeight);
2084         assertEquals(TestDisplayContent.DEFAULT_LOGICAL_DISPLAY_DENSITY, di.logicalDensityDpi);
2085 
2086         // test configuration
2087         final WindowConfiguration windowConfig = testDisplayContent.getConfiguration()
2088                 .windowConfiguration;
2089         assertEquals(displayWidth, windowConfig.getBounds().width());
2090         assertEquals(displayHeight, windowConfig.getBounds().height());
2091         assertEquals(windowingMode, windowConfig.getWindowingMode());
2092 
2093         // test misc display overrides
2094         assertEquals(ignoreOrientationRequests, testDisplayContent.mIgnoreOrientationRequest);
2095         assertEquals(fixedOrientationLetterboxRatio,
2096                 mWm.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(),
2097                 0 /* delta */);
2098     }
2099 
2100     /**
2101      * Verifies {@link DisplayContent#remove} should not resume home root task on the removing
2102      * display.
2103      */
2104     @Test
testNotResumeHomeRootTaskOnRemovingDisplay()2105     public void testNotResumeHomeRootTaskOnRemovingDisplay() {
2106         // Create a display which supports system decoration and allows reparenting root tasks to
2107         // another display when the display is removed.
2108         final DisplayContent display = new TestDisplayContent.Builder(
2109                 mAtm, 1000, 1500).setSystemDecorations(true).build();
2110         doReturn(false).when(display).shouldDestroyContentOnRemove();
2111 
2112         // Put home root task on the display.
2113         final Task homeRootTask = new TaskBuilder(mSupervisor)
2114                 .setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build();
2115 
2116         // Put a finishing standard activity which will be reparented.
2117         final Task rootTask = createTaskWithActivity(display.getDefaultTaskDisplayArea(),
2118                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true /* twoLevelTask */);
2119         rootTask.topRunningActivity().makeFinishingLocked();
2120 
2121         clearInvocations(homeRootTask);
2122         display.remove();
2123 
2124         // The removed display should have no focused root task and its home root task should never
2125         // resume.
2126         assertNull(display.getFocusedRootTask());
2127         verify(homeRootTask, never()).resumeTopActivityUncheckedLocked(any(), any());
2128     }
2129 
2130     /**
2131      * Verifies the correct activity is returned when querying the top running activity.
2132      */
2133     @Test
testTopRunningActivity()2134     public void testTopRunningActivity() {
2135         final DisplayContent display = mRootWindowContainer.getDefaultDisplay();
2136         final KeyguardController keyguard = mSupervisor.getKeyguardController();
2137         final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
2138         final ActivityRecord activity = rootTask.getTopNonFinishingActivity();
2139 
2140         // Create empty root task on top.
2141         final Task emptyRootTask = new TaskBuilder(mSupervisor).build();
2142 
2143         // Make sure the top running activity is not affected when keyguard is not locked.
2144         assertTopRunningActivity(activity, display);
2145 
2146         // Check to make sure activity not reported when it cannot show on lock and lock is on.
2147         doReturn(true).when(keyguard).isKeyguardLocked();
2148         assertEquals(activity, display.topRunningActivity());
2149         assertNull(display.topRunningActivity(true /* considerKeyguardState */));
2150 
2151         // Move root task with activity to top.
2152         rootTask.moveToFront("testRootTaskToFront");
2153         assertEquals(rootTask, display.getFocusedRootTask());
2154         assertEquals(activity, display.topRunningActivity());
2155         assertNull(display.topRunningActivity(true /* considerKeyguardState */));
2156 
2157         // Add activity that should be shown on the keyguard.
2158         final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mAtm)
2159                 .setTask(rootTask)
2160                 .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
2161                 .build();
2162 
2163         // Ensure the show when locked activity is returned.
2164         assertTopRunningActivity(showWhenLockedActivity, display);
2165 
2166         // Move empty root task to front. The running activity in focusable root task which below
2167         // the empty root task should be returned.
2168         emptyRootTask.moveToFront("emptyRootTaskToFront");
2169         assertEquals(rootTask, display.getFocusedRootTask());
2170         assertTopRunningActivity(showWhenLockedActivity, display);
2171     }
2172 
assertTopRunningActivity(ActivityRecord top, DisplayContent display)2173     private static void assertTopRunningActivity(ActivityRecord top, DisplayContent display) {
2174         assertEquals(top, display.topRunningActivity());
2175         assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */));
2176     }
2177 
2178     @Test
testKeyguardGoingAwayWhileAodShown()2179     public void testKeyguardGoingAwayWhileAodShown() {
2180         mDisplayContent.getDisplayPolicy().setAwake(true);
2181 
2182         final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
2183         final ActivityRecord activity = appWin.mActivityRecord;
2184 
2185         mAtm.mKeyguardController.setKeyguardShown(true /* keyguardShowing */,
2186                 true /* aodShowing */);
2187         assertFalse(activity.isVisibleRequested());
2188 
2189         mAtm.mKeyguardController.keyguardGoingAway(0 /* flags */);
2190         assertTrue(activity.isVisibleRequested());
2191     }
2192 
2193     @Test
testRemoveRootTaskInWindowingModes()2194     public void testRemoveRootTaskInWindowingModes() {
2195         removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes(
2196                 WINDOWING_MODE_FULLSCREEN));
2197     }
2198 
2199     @Test
testRemoveRootTaskWithActivityTypes()2200     public void testRemoveRootTaskWithActivityTypes() {
2201         removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksWithActivityTypes(
2202                 ACTIVITY_TYPE_STANDARD));
2203     }
2204 
2205     @UseTestDisplay(addWindows = W_INPUT_METHOD)
2206     @Test
testImeChildWindowFocusWhenImeLayeringTargetChanges()2207     public void testImeChildWindowFocusWhenImeLayeringTargetChanges() {
2208         final WindowState imeChildWindow =
2209                 createWindow(mImeWindow, TYPE_APPLICATION_ATTACHED_DIALOG, "imeChildWindow");
2210         makeWindowVisibleAndDrawn(imeChildWindow, mImeWindow);
2211         assertTrue(imeChildWindow.canReceiveKeys());
2212         mDisplayContent.setInputMethodWindowLocked(mImeWindow);
2213 
2214         // Verify imeChildWindow can be focused window if the next IME target requests IME visible.
2215         final WindowState imeAppTarget =
2216                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
2217         mDisplayContent.setImeLayeringTarget(imeAppTarget);
2218         spyOn(imeAppTarget);
2219         doReturn(true).when(imeAppTarget).getRequestedVisibility(ITYPE_IME);
2220         assertEquals(imeChildWindow, mDisplayContent.findFocusedWindow());
2221 
2222         // Verify imeChildWindow doesn't be focused window if the next IME target does not
2223         // request IME visible.
2224         final WindowState nextImeAppTarget =
2225                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget");
2226         mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
2227         assertNotEquals(imeChildWindow, mDisplayContent.findFocusedWindow());
2228     }
2229 
2230     @UseTestDisplay(addWindows = W_INPUT_METHOD)
2231     @Test
testImeMenuDialogFocusWhenImeLayeringTargetChanges()2232     public void testImeMenuDialogFocusWhenImeLayeringTargetChanges() {
2233         final WindowState imeMenuDialog =
2234                 createWindow(mImeWindow, TYPE_INPUT_METHOD_DIALOG, "imeMenuDialog");
2235         makeWindowVisibleAndDrawn(imeMenuDialog, mImeWindow);
2236         assertTrue(imeMenuDialog.canReceiveKeys());
2237         mDisplayContent.setInputMethodWindowLocked(mImeWindow);
2238 
2239         // Verify imeMenuDialog can be focused window if the next IME target requests IME visible.
2240         final WindowState imeAppTarget =
2241                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget");
2242         mDisplayContent.setImeLayeringTarget(imeAppTarget);
2243         spyOn(imeAppTarget);
2244         doReturn(true).when(imeAppTarget).getRequestedVisibility(ITYPE_IME);
2245         assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
2246 
2247         // Verify imeMenuDialog doesn't be focused window if the next IME target does not
2248         // request IME visible.
2249         final WindowState nextImeAppTarget =
2250                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget");
2251         spyOn(nextImeAppTarget);
2252         doReturn(true).when(nextImeAppTarget).isAnimating(PARENTS | TRANSITION,
2253                 ANIMATION_TYPE_APP_TRANSITION);
2254         mDisplayContent.setImeLayeringTarget(nextImeAppTarget);
2255         assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow());
2256     }
2257 
2258     @Test
testVirtualDisplayContent()2259     public void testVirtualDisplayContent() {
2260         MockitoSession mockSession = mockitoSession()
2261                 .initMocks(this)
2262                 .spyStatic(SurfaceControl.class)
2263                 .strictness(Strictness.LENIENT)
2264                 .startMocking();
2265 
2266         // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
2267         // mirror.
2268         final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
2269 
2270         // GIVEN SurfaceControl can successfully mirror the provided surface.
2271         Point surfaceSize = new Point(
2272                 mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
2273                 mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
2274         surfaceControlMirrors(surfaceSize);
2275 
2276         // WHEN creating the DisplayContent for a new virtual display.
2277         final DisplayContent virtualDisplay = new TestDisplayContent.Builder(mAtm,
2278                 mDisplayInfo).build();
2279 
2280         // THEN mirroring is initiated for the default display's DisplayArea.
2281         assertThat(virtualDisplay.mTokenToMirror).isEqualTo(tokenToMirror);
2282 
2283         mockSession.finishMocking();
2284     }
2285 
2286     @Test
testVirtualDisplayContent_capturedAreaResized()2287     public void testVirtualDisplayContent_capturedAreaResized() {
2288         MockitoSession mockSession = mockitoSession()
2289                 .initMocks(this)
2290                 .spyStatic(SurfaceControl.class)
2291                 .strictness(Strictness.LENIENT)
2292                 .startMocking();
2293 
2294         // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
2295         // mirror.
2296         final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
2297 
2298         // GIVEN SurfaceControl can successfully mirror the provided surface.
2299         Point surfaceSize = new Point(
2300                 mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
2301                 mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
2302         SurfaceControl mirroredSurface = surfaceControlMirrors(surfaceSize);
2303 
2304         // WHEN creating the DisplayContent for a new virtual display.
2305         final DisplayContent virtualDisplay = new TestDisplayContent.Builder(mAtm,
2306                 mDisplayInfo).build();
2307 
2308         // THEN mirroring is initiated for the default display's DisplayArea.
2309         assertThat(virtualDisplay.mTokenToMirror).isEqualTo(tokenToMirror);
2310 
2311         float xScale = 0.7f;
2312         float yScale = 2f;
2313         Rect displayAreaBounds = new Rect(0, 0, Math.round(surfaceSize.x * xScale),
2314                 Math.round(surfaceSize.y * yScale));
2315         virtualDisplay.updateMirroredSurface(mTransaction, displayAreaBounds, surfaceSize);
2316 
2317         // THEN content in the captured DisplayArea is scaled to fit the surface size.
2318         verify(mTransaction, atLeastOnce()).setMatrix(mirroredSurface, 1.0f / yScale, 0, 0,
2319                 1.0f / yScale);
2320         // THEN captured content is positioned in the centre of the output surface.
2321         float scaledWidth = displayAreaBounds.width() / xScale;
2322         float xInset = (surfaceSize.x - scaledWidth) / 2;
2323         verify(mTransaction, atLeastOnce()).setPosition(mirroredSurface, xInset, 0);
2324 
2325         mockSession.finishMocking();
2326     }
2327 
2328     @Test
testVirtualDisplayContent_withoutSurface()2329     public void testVirtualDisplayContent_withoutSurface() {
2330         MockitoSession mockSession = mockitoSession()
2331                 .initMocks(this)
2332                 .spyStatic(SurfaceControl.class)
2333                 .strictness(Strictness.LENIENT)
2334                 .startMocking();
2335 
2336         // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
2337         // mirror.
2338         setUpDefaultTaskDisplayAreaWindowToken();
2339 
2340         // GIVEN SurfaceControl does not mirror a null surface.
2341         Point surfaceSize = new Point(
2342                 mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
2343                 mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
2344 
2345         // GIVEN a new VirtualDisplay with an associated surface.
2346         final VirtualDisplay display = createVirtualDisplay(surfaceSize, null /* surface */);
2347         final int displayId = display.getDisplay().getDisplayId();
2348         mWm.mRoot.onDisplayAdded(displayId);
2349 
2350         // WHEN getting the DisplayContent for the new virtual display.
2351         DisplayContent actualDC = mWm.mRoot.getDisplayContent(displayId);
2352 
2353         // THEN mirroring is not started, since a null surface indicates the VirtualDisplay is off.
2354         assertThat(actualDC.mTokenToMirror).isNull();
2355 
2356         display.release();
2357         mockSession.finishMocking();
2358     }
2359 
2360     @Test
testVirtualDisplayContent_withSurface()2361     public void testVirtualDisplayContent_withSurface() {
2362         MockitoSession mockSession = mockitoSession()
2363                 .initMocks(this)
2364                 .spyStatic(SurfaceControl.class)
2365                 .strictness(Strictness.LENIENT)
2366                 .startMocking();
2367 
2368         // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
2369         // mirror.
2370         final IBinder tokenToMirror = setUpDefaultTaskDisplayAreaWindowToken();
2371 
2372         // GIVEN SurfaceControl can successfully mirror the provided surface.
2373         Point surfaceSize = new Point(
2374                 mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
2375                 mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
2376         surfaceControlMirrors(surfaceSize);
2377 
2378         // GIVEN a new VirtualDisplay with an associated surface.
2379         final VirtualDisplay display = createVirtualDisplay(surfaceSize, new Surface());
2380         final int displayId = display.getDisplay().getDisplayId();
2381         mWm.mRoot.onDisplayAdded(displayId);
2382 
2383         // WHEN getting the DisplayContent for the new virtual display.
2384         DisplayContent actualDC = mWm.mRoot.getDisplayContent(displayId);
2385 
2386         // THEN mirroring is initiated for the default display's DisplayArea.
2387         assertThat(actualDC.mTokenToMirror).isEqualTo(tokenToMirror);
2388 
2389         display.release();
2390         mockSession.finishMocking();
2391     }
2392 
2393     private class TestToken extends Binder {
2394     }
2395 
2396     /**
2397      * Creates a WindowToken associated with the default task DisplayArea, in order for that
2398      * DisplayArea to be mirrored.
2399      */
setUpDefaultTaskDisplayAreaWindowToken()2400     private IBinder setUpDefaultTaskDisplayAreaWindowToken() {
2401         // GIVEN MediaProjection has already initialized the WindowToken of the DisplayArea to
2402         // mirror.
2403         final IBinder tokenToMirror = new TestToken();
2404         doReturn(tokenToMirror).when(mWm.mDisplayManagerInternal).getWindowTokenClientToMirror(
2405                 anyInt());
2406 
2407         // GIVEN the default task display area is represented by the WindowToken.
2408         spyOn(mWm.mWindowContextListenerController);
2409         doReturn(mDefaultDisplay.getDefaultTaskDisplayArea()).when(
2410                 mWm.mWindowContextListenerController).getContainer(any());
2411         return tokenToMirror;
2412     }
2413 
2414     /**
2415      * SurfaceControl successfully creates a mirrored surface of the given size.
2416      */
surfaceControlMirrors(Point surfaceSize)2417     private SurfaceControl surfaceControlMirrors(Point surfaceSize) {
2418         // Do not set the parent, since the mirrored surface is the root of a new surface hierarchy.
2419         SurfaceControl mirroredSurface = new SurfaceControl.Builder()
2420                 .setName("mirroredSurface")
2421                 .setBufferSize(surfaceSize.x, surfaceSize.y)
2422                 .setCallsite("mirrorSurface")
2423                 .build();
2424         doReturn(mirroredSurface).when(() -> SurfaceControl.mirrorSurface(any()));
2425         doReturn(surfaceSize).when(mWm.mDisplayManagerInternal).getDisplaySurfaceDefaultSize(
2426                 anyInt());
2427         return mirroredSurface;
2428     }
2429 
createVirtualDisplay(Point size, Surface surface)2430     private VirtualDisplay createVirtualDisplay(Point size, Surface surface) {
2431         return mWm.mDisplayManager.createVirtualDisplay("VirtualDisplay", size.x, size.y,
2432                 DisplayMetrics.DENSITY_140, surface, VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR);
2433     }
2434 
removeRootTaskTests(Runnable runnable)2435     private void removeRootTaskTests(Runnable runnable) {
2436         final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
2437         final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
2438                 ACTIVITY_TYPE_STANDARD, ON_TOP);
2439         final Task rootTask2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
2440                 ACTIVITY_TYPE_STANDARD, ON_TOP);
2441         final Task rootTask3 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
2442                 ACTIVITY_TYPE_STANDARD, ON_TOP);
2443         final Task rootTask4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN,
2444                 ACTIVITY_TYPE_STANDARD, ON_TOP);
2445         final Task task1 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask1).build();
2446         final Task task2 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask2).build();
2447         final Task task3 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask3).build();
2448         final Task task4 = new TaskBuilder(mSupervisor).setParentTaskFragment(rootTask4).build();
2449 
2450         // Reordering root tasks while removing root tasks.
2451         doAnswer(invocation -> {
2452             taskDisplayArea.positionChildAt(POSITION_TOP, rootTask3, false /*includingParents*/);
2453             return true;
2454         }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
2455 
2456         // Removing root tasks from the display while removing root tasks.
2457         doAnswer(invocation -> {
2458             taskDisplayArea.removeRootTask(rootTask2);
2459             return true;
2460         }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
2461 
2462         runnable.run();
2463         verify(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
2464         verify(mSupervisor).removeTask(eq(task3), anyBoolean(), anyBoolean(), any());
2465         verify(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any());
2466         verify(mSupervisor).removeTask(eq(task1), anyBoolean(), anyBoolean(), any());
2467     }
2468 
isOptionsPanelAtRight(int displayId)2469     private boolean isOptionsPanelAtRight(int displayId) {
2470         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
2471     }
2472 
verifySizes(DisplayContent displayContent, int expectedBaseWidth, int expectedBaseHeight, int expectedBaseDensity)2473     private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth,
2474                              int expectedBaseHeight, int expectedBaseDensity) {
2475         assertEquals(expectedBaseWidth, displayContent.mBaseDisplayWidth);
2476         assertEquals(expectedBaseHeight, displayContent.mBaseDisplayHeight);
2477         assertEquals(expectedBaseDensity, displayContent.mBaseDisplayDensity);
2478     }
2479 
updateFocusedWindow()2480     private void updateFocusedWindow() {
2481         mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
2482     }
2483 
performLayout(DisplayContent dc)2484     static void performLayout(DisplayContent dc) {
2485         dc.setLayoutNeeded();
2486         dc.performLayout(true /* initial */, false /* updateImeWindows */);
2487     }
2488 
2489     /**
2490      * Create DisplayContent that does not update display base/initial values from device to keep
2491      * the values set by test.
2492      */
createDisplayNoUpdateDisplayInfo()2493     private DisplayContent createDisplayNoUpdateDisplayInfo() {
2494         final DisplayContent displayContent = createNewDisplay();
2495         doNothing().when(displayContent).updateDisplayInfo();
2496         return displayContent;
2497     }
2498 
assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop)2499     private void assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop) {
2500         final LinkedList<WindowState> actualWindows = new LinkedList<>();
2501 
2502         // Test forward traversal.
2503         mDisplayContent.forAllWindows(actualWindows::addLast, false /* traverseTopToBottom */);
2504         assertThat("bottomToTop", actualWindows, is(expectedWindowsBottomToTop));
2505 
2506         actualWindows.clear();
2507 
2508         // Test backward traversal.
2509         mDisplayContent.forAllWindows(actualWindows::addLast, true /* traverseTopToBottom */);
2510         assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop)));
2511     }
2512 
getRotatedOrientation(DisplayContent dc)2513     static int getRotatedOrientation(DisplayContent dc) {
2514         return dc.mBaseDisplayWidth > dc.mBaseDisplayHeight
2515                 ? SCREEN_ORIENTATION_PORTRAIT
2516                 : SCREEN_ORIENTATION_LANDSCAPE;
2517     }
2518 
reverseList(List<WindowState> list)2519     private static List<WindowState> reverseList(List<WindowState> list) {
2520         final ArrayList<WindowState> result = new ArrayList<>(list);
2521         Collections.reverse(result);
2522         return result;
2523     }
2524 
tapOnDisplay(final DisplayContent dc)2525     private void tapOnDisplay(final DisplayContent dc) {
2526         final DisplayMetrics dm = dc.getDisplayMetrics();
2527         final float x = dm.widthPixels / 2;
2528         final float y = dm.heightPixels / 2;
2529         final long downTime = SystemClock.uptimeMillis();
2530         final long eventTime = SystemClock.uptimeMillis() + 100;
2531         // sending ACTION_DOWN
2532         final MotionEvent downEvent = MotionEvent.obtain(
2533                 downTime,
2534                 downTime,
2535                 MotionEvent.ACTION_DOWN,
2536                 x,
2537                 y,
2538                 0 /*metaState*/);
2539         downEvent.setDisplayId(dc.getDisplayId());
2540         dc.mTapDetector.onPointerEvent(downEvent);
2541 
2542         // sending ACTION_UP
2543         final MotionEvent upEvent = MotionEvent.obtain(
2544                 downTime,
2545                 eventTime,
2546                 MotionEvent.ACTION_UP,
2547                 x,
2548                 y,
2549                 0 /*metaState*/);
2550         upEvent.setDisplayId(dc.getDisplayId());
2551         dc.mTapDetector.onPointerEvent(upEvent);
2552     }
2553 }
2554