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