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_BEHIND; 26 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 27 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 28 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 29 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 30 import static android.os.Build.VERSION_CODES.P; 31 import static android.os.Build.VERSION_CODES.Q; 32 import static android.view.Display.DEFAULT_DISPLAY; 33 import static android.view.Display.FLAG_PRIVATE; 34 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; 35 import static android.view.DisplayCutout.fromBoundingRect; 36 import static android.view.Surface.ROTATION_0; 37 import static android.view.Surface.ROTATION_180; 38 import static android.view.Surface.ROTATION_270; 39 import static android.view.Surface.ROTATION_90; 40 import static android.view.WindowInsets.Type.ime; 41 import static android.view.WindowInsets.Type.navigationBars; 42 import static android.view.WindowInsets.Type.statusBars; 43 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 44 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; 45 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 46 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; 47 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 48 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 49 import static android.view.WindowManager.LayoutParams.FLAG_SECURE; 50 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 51 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; 52 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; 53 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION; 54 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 55 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; 56 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 57 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; 58 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 59 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; 60 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 61 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; 62 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT; 63 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 64 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; 65 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 66 import static android.view.WindowManager.TRANSIT_CLOSE; 67 import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE; 68 import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; 69 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; 70 71 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; 72 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; 73 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 74 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 75 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 76 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; 77 import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; 78 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 79 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 80 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; 81 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; 82 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM; 83 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; 84 import static com.android.server.wm.WindowContainer.POSITION_TOP; 85 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; 86 import static com.android.window.flags.Flags.FLAG_CAMERA_COMPAT_FOR_FREEFORM; 87 import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE; 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.clearInvocations; 103 import static org.mockito.Mockito.doAnswer; 104 import static org.mockito.Mockito.doCallRealMethod; 105 import static org.mockito.Mockito.when; 106 107 import android.annotation.NonNull; 108 import android.app.ActivityTaskManager; 109 import android.app.WindowConfiguration; 110 import android.content.res.Configuration; 111 import android.graphics.Insets; 112 import android.graphics.Rect; 113 import android.graphics.Region; 114 import android.hardware.HardwareBuffer; 115 import android.metrics.LogMaker; 116 import android.os.Binder; 117 import android.os.RemoteException; 118 import android.os.UserHandle; 119 import android.os.UserManager; 120 import android.platform.test.annotations.EnableFlags; 121 import android.platform.test.annotations.DisableFlags; 122 import android.platform.test.annotations.Presubmit; 123 import android.util.ArraySet; 124 import android.view.Display; 125 import android.view.DisplayCutout; 126 import android.view.DisplayInfo; 127 import android.view.Gravity; 128 import android.view.IDisplayChangeWindowCallback; 129 import android.view.IDisplayChangeWindowController; 130 import android.view.ISystemGestureExclusionListener; 131 import android.view.IWindowManager; 132 import android.view.InsetsState; 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.DisplayAreaInfo; 139 import android.window.IDisplayAreaOrganizer; 140 import android.window.ScreenCapture; 141 import android.window.WindowContainerToken; 142 import android.window.WindowContainerTransaction; 143 144 import androidx.test.filters.SmallTest; 145 146 import com.android.internal.logging.MetricsLogger; 147 import com.android.internal.logging.nano.MetricsProto; 148 import com.android.server.LocalServices; 149 import com.android.server.policy.WindowManagerPolicy; 150 import com.android.server.wm.utils.WmDisplayCutout; 151 152 import org.junit.Test; 153 import org.junit.runner.RunWith; 154 import org.mockito.ArgumentCaptor; 155 import org.mockito.Mockito; 156 157 import java.util.ArrayList; 158 import java.util.Arrays; 159 import java.util.Collections; 160 import java.util.LinkedList; 161 import java.util.List; 162 import java.util.concurrent.CompletableFuture; 163 import java.util.concurrent.ExecutionException; 164 import java.util.concurrent.TimeUnit; 165 import java.util.concurrent.TimeoutException; 166 167 /** 168 * Tests for the {@link DisplayContent} class. 169 * 170 * Build/Install/Run: 171 * atest WmTests:DisplayContentTests 172 */ 173 @SmallTest 174 @Presubmit 175 @RunWith(WindowTestRunner.class) 176 public class DisplayContentTests extends WindowTestsBase { 177 178 @SetupWindows(addAllCommonWindows = true) 179 @Test testForAllWindows()180 public void testForAllWindows() { 181 final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION, 182 mDisplayContent, "exiting app"); 183 final ActivityRecord exitingApp = exitingAppWindow.mActivityRecord; 184 exitingApp.startAnimation(exitingApp.getPendingTransaction(), mock(AnimationAdapter.class), 185 false /* hidden */, SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION); 186 exitingApp.mIsExiting = true; 187 // If the activity is animating, its window should not be removed. 188 mDisplayContent.handleCompleteDeferredRemoval(); 189 190 final ArrayList<WindowState> windows = new ArrayList<>(Arrays.asList( 191 mWallpaperWindow, 192 mChildAppWindowBelow, 193 mAppWindow, 194 mChildAppWindowAbove, 195 exitingAppWindow, 196 mDockedDividerWindow, 197 mImeWindow, 198 mImeDialogWindow, 199 mStatusBarWindow, 200 mNotificationShadeWindow, 201 mNavBarWindow)); 202 assertForAllWindowsOrder(windows); 203 204 exitingApp.cancelAnimation(); 205 // The exiting window will be removed because its parent is no longer animating. 206 mDisplayContent.handleCompleteDeferredRemoval(); 207 windows.remove(exitingAppWindow); 208 assertForAllWindowsOrder(windows); 209 } 210 211 @SetupWindows(addAllCommonWindows = true) 212 @Test testForAllWindows_WithAppImeTarget()213 public void testForAllWindows_WithAppImeTarget() { 214 final WindowState imeAppTarget = 215 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); 216 217 mDisplayContent.setImeLayeringTarget(imeAppTarget); 218 219 assertForAllWindowsOrder(Arrays.asList( 220 mWallpaperWindow, 221 mChildAppWindowBelow, 222 mAppWindow, 223 mChildAppWindowAbove, 224 imeAppTarget, 225 mImeWindow, 226 mImeDialogWindow, 227 mDockedDividerWindow, 228 mStatusBarWindow, 229 mNotificationShadeWindow, 230 mNavBarWindow)); 231 } 232 233 @SetupWindows(addAllCommonWindows = true) 234 @Test testForAllWindows_WithChildWindowImeTarget()235 public void testForAllWindows_WithChildWindowImeTarget() throws Exception { 236 mDisplayContent.setImeLayeringTarget(mChildAppWindowAbove); 237 238 assertForAllWindowsOrder(Arrays.asList( 239 mWallpaperWindow, 240 mChildAppWindowBelow, 241 mAppWindow, 242 mChildAppWindowAbove, 243 mImeWindow, 244 mImeDialogWindow, 245 mDockedDividerWindow, 246 mStatusBarWindow, 247 mNotificationShadeWindow, 248 mNavBarWindow)); 249 } 250 251 @SetupWindows(addAllCommonWindows = true) 252 @Test testForAllWindows_WithStatusBarImeTarget()253 public void testForAllWindows_WithStatusBarImeTarget() throws Exception { 254 mDisplayContent.setImeLayeringTarget(mStatusBarWindow); 255 256 assertForAllWindowsOrder(Arrays.asList( 257 mWallpaperWindow, 258 mChildAppWindowBelow, 259 mAppWindow, 260 mChildAppWindowAbove, 261 mDockedDividerWindow, 262 mStatusBarWindow, 263 mImeWindow, 264 mImeDialogWindow, 265 mNotificationShadeWindow, 266 mNavBarWindow)); 267 } 268 269 @SetupWindows(addAllCommonWindows = true) 270 @Test testForAllWindows_WithNotificationShadeImeTarget()271 public void testForAllWindows_WithNotificationShadeImeTarget() throws Exception { 272 mDisplayContent.setImeLayeringTarget(mNotificationShadeWindow); 273 274 assertForAllWindowsOrder(Arrays.asList( 275 mWallpaperWindow, 276 mChildAppWindowBelow, 277 mAppWindow, 278 mChildAppWindowAbove, 279 mDockedDividerWindow, 280 mStatusBarWindow, 281 mNotificationShadeWindow, 282 mImeWindow, 283 mImeDialogWindow, 284 mNavBarWindow)); 285 } 286 287 @SetupWindows(addAllCommonWindows = true) 288 @Test testForAllWindows_WithInBetweenWindowToken()289 public void testForAllWindows_WithInBetweenWindowToken() { 290 // This window is set-up to be z-ordered between some windows that go in the same token like 291 // the nav bar and status bar. 292 final WindowState voiceInteractionWindow = createWindow(null, TYPE_VOICE_INTERACTION, 293 mDisplayContent, "voiceInteractionWindow"); 294 295 assertForAllWindowsOrder(Arrays.asList( 296 mWallpaperWindow, 297 mChildAppWindowBelow, 298 mAppWindow, 299 mChildAppWindowAbove, 300 mDockedDividerWindow, 301 mImeWindow, 302 mImeDialogWindow, 303 mStatusBarWindow, 304 mNotificationShadeWindow, 305 voiceInteractionWindow, // It can show above lock screen. 306 mNavBarWindow)); 307 } 308 309 @SetupWindows(addAllCommonWindows = true) 310 @Test testComputeImeTarget()311 public void testComputeImeTarget() { 312 // Verify that an app window can be an ime target. 313 final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); 314 appWin.setHasSurface(true); 315 assertTrue(appWin.canBeImeTarget()); 316 WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */); 317 assertEquals(appWin, imeTarget); 318 appWin.mHidden = false; 319 320 // Verify that an child window can be an ime target. 321 final WindowState childWin = createWindow(appWin, 322 TYPE_APPLICATION_ATTACHED_DIALOG, "childWin"); 323 childWin.setHasSurface(true); 324 assertTrue(childWin.canBeImeTarget()); 325 imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */); 326 assertEquals(childWin, imeTarget); 327 } 328 329 @SetupWindows(addAllCommonWindows = true) 330 @Test testComputeImeTarget_startingWindow()331 public void testComputeImeTarget_startingWindow() { 332 ActivityRecord activity = createActivityRecord(mDisplayContent); 333 334 final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity, 335 "startingWin"); 336 startingWin.setHasSurface(true); 337 assertTrue(startingWin.canBeImeTarget()); 338 339 WindowState imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */); 340 assertEquals(startingWin, imeTarget); 341 startingWin.mHidden = false; 342 343 // Verify that the starting window still be an ime target even an app window launching 344 // behind it. 345 final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, activity, "appWin"); 346 appWin.setHasSurface(true); 347 assertTrue(appWin.canBeImeTarget()); 348 349 imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */); 350 assertEquals(startingWin, imeTarget); 351 appWin.mHidden = false; 352 353 // Verify that the starting window still be an ime target even the child window behind a 354 // launching app window 355 final WindowState childWin = createWindow(appWin, 356 TYPE_APPLICATION_ATTACHED_DIALOG, "childWin"); 357 childWin.setHasSurface(true); 358 assertTrue(childWin.canBeImeTarget()); 359 imeTarget = mDisplayContent.computeImeTarget(false /* updateImeTarget */); 360 assertEquals(startingWin, imeTarget); 361 } 362 363 @Test testUpdateImeParent_forceUpdateRelativeLayer()364 public void testUpdateImeParent_forceUpdateRelativeLayer() { 365 final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer(); 366 final ActivityRecord activity = createActivityRecord(mDisplayContent); 367 368 final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity, 369 "startingWin"); 370 startingWin.setHasSurface(true); 371 assertTrue(startingWin.canBeImeTarget()); 372 final SurfaceControl imeSurfaceParent = mock(SurfaceControl.class); 373 doReturn(imeSurfaceParent).when(mDisplayContent).computeImeParent(); 374 spyOn(imeContainer); 375 376 mDisplayContent.setImeInputTarget(startingWin); 377 mDisplayContent.onConfigurationChanged(new Configuration()); 378 verify(mDisplayContent).updateImeParent(); 379 380 // Force reassign the relative layer when the IME surface parent is changed. 381 verify(imeContainer).assignRelativeLayer(any(), eq(imeSurfaceParent), anyInt(), eq(true)); 382 } 383 384 @Test testComputeImeTargetReturnsNull_windowDidntRequestIme()385 public void testComputeImeTargetReturnsNull_windowDidntRequestIme() { 386 final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, 387 new ActivityBuilder(mAtm).setCreateTask(true).build(), "app"); 388 final WindowState win2 = createWindow(null, TYPE_BASE_APPLICATION, 389 new ActivityBuilder(mAtm).setCreateTask(true).build(), "app2"); 390 391 mDisplayContent.setImeInputTarget(win1); 392 mDisplayContent.setImeLayeringTarget(win2); 393 394 doReturn(true).when(mDisplayContent).shouldImeAttachedToApp(); 395 // Compute IME parent returns nothing if current target and window receiving input 396 // are different i.e. if current window didn't request IME. 397 assertNull("computeImeParent() should be null", mDisplayContent.computeImeParent()); 398 } 399 400 @Test testUpdateImeParent_skipForOrganizedImeContainer()401 public void testUpdateImeParent_skipForOrganizedImeContainer() { 402 final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer(); 403 final ActivityRecord activity = createActivityRecord(mDisplayContent); 404 405 final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity, 406 "startingWin"); 407 startingWin.setHasSurface(true); 408 assertTrue(startingWin.canBeImeTarget()); 409 final SurfaceControl imeSurfaceParent = mock(SurfaceControl.class); 410 doReturn(imeSurfaceParent).when(mDisplayContent).computeImeParent(); 411 412 // Main precondition for this test: organize the ImeContainer. 413 final IDisplayAreaOrganizer mockImeOrganizer = mock(IDisplayAreaOrganizer.class); 414 when(mockImeOrganizer.asBinder()).thenReturn(new Binder()); 415 imeContainer.setOrganizer(mockImeOrganizer); 416 417 mDisplayContent.updateImeParent(); 418 419 assertNull("Don't reparent the surface of an organized ImeContainer.", 420 mDisplayContent.mInputMethodSurfaceParent); 421 422 // Clean up organizer. 423 imeContainer.setOrganizer(null); 424 } 425 426 @Test testImeContainerIsReparentedUnderParentWhenOrganized()427 public void testImeContainerIsReparentedUnderParentWhenOrganized() { 428 final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer(); 429 final ActivityRecord activity = createActivityRecord(mDisplayContent); 430 431 final WindowState startingWin = createWindow(null, TYPE_APPLICATION_STARTING, activity, 432 "startingWin"); 433 startingWin.setHasSurface(true); 434 assertTrue(startingWin.canBeImeTarget()); 435 436 final Transaction transaction = mDisplayContent.getPendingTransaction(); 437 spyOn(transaction); 438 439 // Organized the ime container. 440 final IDisplayAreaOrganizer mockImeOrganizer = mock(IDisplayAreaOrganizer.class); 441 when(mockImeOrganizer.asBinder()).thenReturn(new Binder()); 442 imeContainer.setOrganizer(mockImeOrganizer); 443 444 // Verify that the ime container surface is reparented under 445 // its parent surface as a consequence of the setOrganizer call. 446 SurfaceControl imeParentSurfaceControl = imeContainer.getParentSurfaceControl(); 447 verify(transaction).reparent(imeContainer.getSurfaceControl(), imeParentSurfaceControl); 448 449 // Clean up organizer. 450 imeContainer.setOrganizer(null); 451 } 452 453 /** 454 * This tests root task movement between displays and proper root task's, task's and app token's 455 * display container references updates. 456 */ 457 @Test testMoveRootTaskBetweenDisplays()458 public void testMoveRootTaskBetweenDisplays() { 459 // Create a second display. 460 final DisplayContent dc = createNewDisplay(); 461 462 // Add root task with activity. 463 final Task rootTask = createTask(dc); 464 assertEquals(dc.getDisplayId(), rootTask.getDisplayContent().getDisplayId()); 465 assertEquals(dc, rootTask.getDisplayContent()); 466 467 final Task task = createTaskInRootTask(rootTask, 0 /* userId */); 468 final ActivityRecord activity = createNonAttachedActivityRecord(dc); 469 task.addChild(activity, 0); 470 assertEquals(dc, task.getDisplayContent()); 471 assertEquals(dc, activity.getDisplayContent()); 472 473 // Move root task to first display. 474 rootTask.reparent(mDisplayContent.getDefaultTaskDisplayArea(), true /* onTop */); 475 assertEquals(mDisplayContent.getDisplayId(), rootTask.getDisplayContent().getDisplayId()); 476 assertEquals(mDisplayContent, rootTask.getDisplayContent()); 477 assertEquals(mDisplayContent, task.getDisplayContent()); 478 assertEquals(mDisplayContent, activity.getDisplayContent()); 479 } 480 481 /** 482 * This tests global configuration updates when default display config is updated. 483 */ 484 @Test testDefaultDisplayOverrideConfigUpdate()485 public void testDefaultDisplayOverrideConfigUpdate() { 486 DisplayContent defaultDisplay = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY); 487 final Configuration currentConfig = defaultDisplay.getConfiguration(); 488 489 // Create new, slightly changed override configuration and apply it to the display. 490 final Configuration newOverrideConfig = new Configuration(currentConfig); 491 newOverrideConfig.densityDpi += 120; 492 newOverrideConfig.fontScale += 0.3; 493 494 defaultDisplay.updateDisplayOverrideConfigurationLocked(newOverrideConfig, 495 null /* starting */, false /* deferResume */); 496 497 // Check that global configuration is updated, as we've updated default display's config. 498 Configuration globalConfig = mWm.mRoot.getConfiguration(); 499 assertEquals(newOverrideConfig.densityDpi, globalConfig.densityDpi); 500 assertEquals(newOverrideConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); 501 502 // Return back to original values. 503 defaultDisplay.updateDisplayOverrideConfigurationLocked(currentConfig, 504 null /* starting */, false /* deferResume */); 505 globalConfig = mWm.mRoot.getConfiguration(); 506 assertEquals(currentConfig.densityDpi, globalConfig.densityDpi); 507 assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */); 508 } 509 510 @Test testFocusedWindowMultipleDisplays()511 public void testFocusedWindowMultipleDisplays() { 512 doTestFocusedWindowMultipleDisplays(false /* perDisplayFocusEnabled */, Q); 513 } 514 515 @Test testFocusedWindowMultipleDisplaysPerDisplayFocusEnabled()516 public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabled() { 517 doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, Q); 518 } 519 520 @Test testFocusedWindowMultipleDisplaysPerDisplayFocusEnabledLegacyApp()521 public void testFocusedWindowMultipleDisplaysPerDisplayFocusEnabledLegacyApp() { 522 doTestFocusedWindowMultipleDisplays(true /* perDisplayFocusEnabled */, P); 523 } 524 doTestFocusedWindowMultipleDisplays(boolean perDisplayFocusEnabled, int targetSdk)525 private void doTestFocusedWindowMultipleDisplays(boolean perDisplayFocusEnabled, 526 int targetSdk) { 527 mWm.mPerDisplayFocusEnabled = perDisplayFocusEnabled; 528 529 // Create a focusable window and check that focus is calculated correctly 530 final WindowState window1 = 531 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1"); 532 window1.mActivityRecord.mTargetSdk = targetSdk; 533 updateFocusedWindow(); 534 assertTrue(window1.isFocused()); 535 assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); 536 537 // Check that a new display doesn't affect focus 538 final DisplayContent dc = createNewDisplay(); 539 updateFocusedWindow(); 540 assertTrue(window1.isFocused()); 541 assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); 542 543 // Add a window to the second display, and it should be focused 544 final ActivityRecord app2 = new ActivityBuilder(mAtm) 545 .setTask(new TaskBuilder(mSupervisor).setDisplay(dc).build()) 546 .setUseProcess(window1.getProcess()).setOnTop(true).build(); 547 final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, app2, "window2"); 548 window2.mActivityRecord.mTargetSdk = targetSdk; 549 updateFocusedWindow(); 550 assertTrue(window2.isFocused()); 551 assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window1.isFocused()); 552 assertEquals(window2, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); 553 554 // Move the first window to top including parents, and make sure focus is updated 555 window1.getParent().positionChildAt(POSITION_TOP, window1, true); 556 updateFocusedWindow(); 557 assertTrue(window1.isFocused()); 558 assertEquals(perDisplayFocusEnabled && targetSdk >= Q, window2.isFocused()); 559 assertEquals(window1, mWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus); 560 561 // Make sure top focused display not changed if there is a focused app. 562 window1.mActivityRecord.setVisibleRequested(false); 563 window1.getDisplayContent().setFocusedApp(window1.mActivityRecord); 564 updateFocusedWindow(); 565 assertTrue(!window1.isFocused()); 566 assertEquals(window1.getDisplayId(), 567 mWm.mRoot.getTopFocusedDisplayContent().getDisplayId()); 568 } 569 570 @Test testShouldWaitForSystemDecorWindowsOnBoot_OnDefaultDisplay()571 public void testShouldWaitForSystemDecorWindowsOnBoot_OnDefaultDisplay() { 572 mWm.mSystemBooted = true; 573 final DisplayContent defaultDisplay = mWm.getDefaultDisplayContentLocked(); 574 final WindowState[] windows = createNotDrawnWindowsOn(defaultDisplay, 575 TYPE_WALLPAPER, TYPE_APPLICATION); 576 final WindowState wallpaper = windows[0]; 577 assertTrue(wallpaper.mIsWallpaper); 578 wallpaper.mToken.asWallpaperToken().setVisibility(false); 579 // By default WindowState#mWallpaperVisible is false. 580 assertFalse(wallpaper.isVisible()); 581 582 // Verify waiting for windows to be drawn. 583 assertTrue(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot()); 584 585 // Verify not waiting for drawn window and invisible wallpaper. 586 setDrawnState(WindowStateAnimator.READY_TO_SHOW, wallpaper); 587 setDrawnState(WindowStateAnimator.HAS_DRAWN, windows[1]); 588 assertFalse(defaultDisplay.shouldWaitForSystemDecorWindowsOnBoot()); 589 } 590 591 @Test testShouldWaitForSystemDecorWindowsOnBoot_OnSecondaryDisplay()592 public void testShouldWaitForSystemDecorWindowsOnBoot_OnSecondaryDisplay() { 593 mWm.mSystemBooted = true; 594 final DisplayContent secondaryDisplay = createNewDisplay(); 595 final WindowState[] windows = createNotDrawnWindowsOn(secondaryDisplay, 596 TYPE_WALLPAPER, TYPE_APPLICATION); 597 598 // Verify not waiting for display without system decorations. 599 doReturn(false).when(secondaryDisplay).supportsSystemDecorations(); 600 assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot()); 601 602 // Verify waiting for non-drawn windows on display with system decorations. 603 reset(secondaryDisplay); 604 doReturn(true).when(secondaryDisplay).supportsSystemDecorations(); 605 assertTrue(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot()); 606 607 // Verify not waiting for drawn windows on display with system decorations. 608 setDrawnState(WindowStateAnimator.HAS_DRAWN, windows); 609 assertFalse(secondaryDisplay.shouldWaitForSystemDecorWindowsOnBoot()); 610 } 611 612 @Test testDisplayHasContent()613 public void testDisplayHasContent() { 614 final WindowState window = createWindow(null, TYPE_APPLICATION_OVERLAY, "window"); 615 setDrawnState(WindowStateAnimator.COMMIT_DRAW_PENDING, window); 616 assertFalse(mDisplayContent.getLastHasContent()); 617 // The pending draw state should be committed and the has-content state is also updated. 618 mDisplayContent.applySurfaceChangesTransaction(); 619 assertTrue(window.isDrawn()); 620 assertTrue(mDisplayContent.getLastHasContent()); 621 // If the only window is no longer visible, has-content will be false. 622 setDrawnState(WindowStateAnimator.NO_SURFACE, window); 623 mDisplayContent.applySurfaceChangesTransaction(); 624 assertFalse(mDisplayContent.getLastHasContent()); 625 } 626 627 @Test testImeIsAttachedToDisplayForLetterboxedApp()628 public void testImeIsAttachedToDisplayForLetterboxedApp() { 629 final DisplayContent dc = mDisplayContent; 630 final WindowState ws = createWindow(null, TYPE_APPLICATION, dc, "app window"); 631 dc.setImeLayeringTarget(ws); 632 dc.setImeInputTarget(ws); 633 634 // Adjust bounds so that matchesRootDisplayAreaBounds() returns false. 635 final Rect bounds = new Rect(dc.getBounds()); 636 bounds.scale(0.5f); 637 ws.mActivityRecord.setBounds(bounds); 638 assertFalse("matchesRootDisplayAreaBounds() should return false", 639 ws.matchesDisplayAreaBounds()); 640 641 assertTrue("IME shouldn't be attached to app", 642 dc.computeImeParent() != dc.getImeTarget(IME_TARGET_LAYERING).getWindow() 643 .mActivityRecord.getSurfaceControl()); 644 assertEquals("IME should be attached to display", 645 dc.getImeContainer().getParent().getSurfaceControl(), dc.computeImeParent()); 646 } 647 createNotDrawnWindowsOn(DisplayContent displayContent, int... types)648 private WindowState[] createNotDrawnWindowsOn(DisplayContent displayContent, int... types) { 649 final WindowState[] windows = new WindowState[types.length]; 650 for (int i = 0; i < types.length; i++) { 651 final int type = types[i]; 652 windows[i] = createWindow(null /* parent */, type, displayContent, "window-" + type); 653 windows[i].setHasSurface(true); 654 windows[i].mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING; 655 } 656 return windows; 657 } 658 setDrawnState(int state, WindowState... windows)659 private static void setDrawnState(int state, WindowState... windows) { 660 for (WindowState window : windows) { 661 window.mHasSurface = state != WindowStateAnimator.NO_SURFACE; 662 window.mWinAnimator.mDrawState = state; 663 } 664 } 665 666 /** 667 * This tests setting the maximum ui width on a display. 668 */ 669 @Test testMaxUiWidth()670 public void testMaxUiWidth() { 671 // Prevent base display metrics for test from being updated to the value of real display. 672 final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo(); 673 final int baseWidth = 1440; 674 final int baseHeight = 2560; 675 final int baseDensity = 300; 676 final float baseXDpi = 60; 677 final float baseYDpi = 60; 678 679 displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity, baseYDpi, 680 baseYDpi); 681 682 final int maxWidth = 300; 683 final float ratioChange = maxWidth / (float) baseWidth; 684 final int resultingHeight = (int) (baseHeight * ratioChange); 685 final int resultingDensity = (int) (baseDensity * ratioChange); 686 final float resultingXDpi = baseXDpi * ratioChange; 687 final float resultingYDpi = baseYDpi * ratioChange; 688 689 displayContent.setMaxUiWidth(maxWidth); 690 verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity, resultingXDpi, 691 resultingYDpi); 692 693 // Assert setting values again does not change; 694 displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity, baseXDpi, 695 baseYDpi); 696 verifySizes(displayContent, maxWidth, resultingHeight, resultingDensity, resultingXDpi, 697 resultingYDpi); 698 699 final int smallerWidth = 200; 700 final int smallerHeight = 400; 701 final int smallerDensity = 100; 702 703 // Specify smaller dimension, verify that it is honored 704 displayContent.updateBaseDisplayMetrics(smallerWidth, smallerHeight, smallerDensity, 705 baseXDpi, baseYDpi); 706 verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity, baseXDpi, 707 baseYDpi); 708 709 // Verify that setting the max width to a greater value than the base width has no effect 710 displayContent.setMaxUiWidth(maxWidth); 711 verifySizes(displayContent, smallerWidth, smallerHeight, smallerDensity, baseXDpi, 712 baseYDpi); 713 } 714 715 @Test testSetForcedSize()716 public void testSetForcedSize() { 717 // Prevent base display metrics for test from being updated to the value of real display. 718 final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo(); 719 final int baseWidth = 1280; 720 final int baseHeight = 720; 721 final int baseDensity = 320; 722 final float baseXDpi = 60; 723 final float baseYDpi = 60; 724 725 displayContent.mInitialDisplayWidth = baseWidth; 726 displayContent.mInitialDisplayHeight = baseHeight; 727 displayContent.mInitialDisplayDensity = baseDensity; 728 displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity, baseXDpi, 729 baseYDpi); 730 731 final int forcedWidth = 1920; 732 final int forcedHeight = 1080; 733 734 // Verify that forcing the size is honored and the density doesn't change. 735 displayContent.setForcedSize(forcedWidth, forcedHeight); 736 verifySizes(displayContent, forcedWidth, forcedHeight, baseDensity); 737 738 // Verify that forcing the size is idempotent. 739 displayContent.setForcedSize(forcedWidth, forcedHeight); 740 verifySizes(displayContent, forcedWidth, forcedHeight, baseDensity); 741 } 742 743 @Test testSetForcedSize_WithMaxUiWidth()744 public void testSetForcedSize_WithMaxUiWidth() { 745 // Prevent base display metrics for test from being updated to the value of real display. 746 final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo(); 747 final int baseWidth = 1280; 748 final int baseHeight = 720; 749 final int baseDensity = 320; 750 final float baseXDpi = 60; 751 final float baseYDpi = 60; 752 753 displayContent.mInitialDisplayWidth = baseWidth; 754 displayContent.mInitialDisplayHeight = baseHeight; 755 displayContent.mInitialDisplayDensity = baseDensity; 756 displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity, baseXDpi, 757 baseYDpi); 758 759 displayContent.setMaxUiWidth(baseWidth); 760 761 final int forcedWidth = 1920; 762 final int forcedHeight = 1080; 763 764 // Verify that forcing bigger size doesn't work and density doesn't change. 765 displayContent.setForcedSize(forcedWidth, forcedHeight); 766 verifySizes(displayContent, baseWidth, baseHeight, baseDensity); 767 768 // Verify that forcing the size is idempotent. 769 displayContent.setForcedSize(forcedWidth, forcedHeight); 770 verifySizes(displayContent, baseWidth, baseHeight, baseDensity); 771 } 772 773 @Test testSetForcedDensity()774 public void testSetForcedDensity() { 775 final DisplayContent displayContent = createDisplayNoUpdateDisplayInfo(); 776 final int baseWidth = 1280; 777 final int baseHeight = 720; 778 final int baseDensity = 320; 779 final float baseXDpi = 60; 780 final float baseYDpi = 60; 781 final int originalMinTaskSizeDp = displayContent.mMinSizeOfResizeableTaskDp; 782 783 displayContent.mInitialDisplayWidth = baseWidth; 784 displayContent.mInitialDisplayHeight = baseHeight; 785 displayContent.mInitialDisplayDensity = baseDensity; 786 displayContent.updateBaseDisplayMetrics(baseWidth, baseHeight, baseDensity, baseXDpi, 787 baseYDpi); 788 789 final int forcedDensity = 600; 790 791 // Verify that forcing the density is honored and the size doesn't change. 792 displayContent.setForcedDensity(forcedDensity, 0 /* userId */); 793 verifySizes(displayContent, baseWidth, baseHeight, forcedDensity); 794 795 // Verify that forcing the density is idempotent. 796 displayContent.setForcedDensity(forcedDensity, 0 /* userId */); 797 verifySizes(displayContent, baseWidth, baseHeight, forcedDensity); 798 799 // Verify that minimal task size (dp) doesn't change with density of display. 800 assertEquals(originalMinTaskSizeDp, displayContent.mMinSizeOfResizeableTaskDp); 801 802 // Verify that forcing resolution won't affect the already forced density. 803 displayContent.setForcedSize(1800, 1200); 804 verifySizes(displayContent, 1800, 1200, forcedDensity); 805 } 806 807 @Test testDisplayCutout_rot0()808 public void testDisplayCutout_rot0() { 809 final DisplayContent dc = createNewDisplay(); 810 dc.mInitialDisplayWidth = 200; 811 dc.mInitialDisplayHeight = 400; 812 final Rect r = new Rect(80, 0, 120, 10); 813 final DisplayCutout cutout = new WmDisplayCutout( 814 fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_TOP), null) 815 .computeSafeInsets(200, 400).getDisplayCutout(); 816 817 dc.mInitialDisplayCutout = cutout; 818 dc.getDisplayRotation().setRotation(Surface.ROTATION_0); 819 dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo. 820 821 assertEquals(cutout, dc.getDisplayInfo().displayCutout); 822 } 823 824 @Test testDisplayCutout_rot90()825 public void testDisplayCutout_rot90() { 826 // Prevent mInitialDisplayCutout from being updated from real display (e.g. null 827 // if the device has no cutout). 828 final DisplayContent dc = createDisplayNoUpdateDisplayInfo(); 829 // This test assumes it's a top cutout on a portrait display, so if it happens to be a 830 // landscape display let's rotate it. 831 if (dc.mInitialDisplayHeight < dc.mInitialDisplayWidth) { 832 int tmp = dc.mInitialDisplayHeight; 833 dc.mInitialDisplayHeight = dc.mInitialDisplayWidth; 834 dc.mInitialDisplayWidth = tmp; 835 } 836 // Rotation may use real display info to compute bound, so here also uses the 837 // same width and height. 838 final int displayWidth = dc.mInitialDisplayWidth; 839 final int displayHeight = dc.mInitialDisplayHeight; 840 final float density = dc.mInitialDisplayDensity; 841 final int cutoutWidth = 40; 842 final int cutoutHeight = 10; 843 final int left = (displayWidth - cutoutWidth) / 2; 844 final int top = 0; 845 final int right = (displayWidth + cutoutWidth) / 2; 846 final int bottom = cutoutHeight; 847 848 final Rect zeroRect = new Rect(); 849 final Rect[] bounds = new Rect[]{zeroRect, new Rect(left, top, right, bottom), zeroRect, 850 zeroRect}; 851 final DisplayCutout.CutoutPathParserInfo info = new DisplayCutout.CutoutPathParserInfo( 852 displayWidth, displayHeight, displayWidth, displayHeight, density, "", 853 Surface.ROTATION_0, 1f, 1f); 854 final DisplayCutout cutout = new WmDisplayCutout( 855 DisplayCutout.constructDisplayCutout(bounds, Insets.NONE, info), null) 856 .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout(); 857 858 dc.mInitialDisplayCutout = cutout; 859 dc.getDisplayRotation().setRotation(Surface.ROTATION_90); 860 dc.computeScreenConfiguration(new Configuration()); // recomputes dc.mDisplayInfo. 861 862 // ----o---------- ------------- 863 // | | | | | 864 // | ------o | o--- 865 // | | | | 866 // | | -> | | 867 // | | ---o 868 // | | | 869 // | | ------------- 870 final Rect[] bounds90 = new Rect[]{new Rect(top, left, bottom, right), zeroRect, zeroRect, 871 zeroRect}; 872 final DisplayCutout.CutoutPathParserInfo info90 = new DisplayCutout.CutoutPathParserInfo( 873 displayWidth, displayHeight, displayWidth, displayHeight, density, "", 874 Surface.ROTATION_90, 1f, 1f); 875 assertEquals(new WmDisplayCutout( 876 DisplayCutout.constructDisplayCutout(bounds90, Insets.NONE, info90), null) 877 .computeSafeInsets(displayHeight, displayWidth).getDisplayCutout(), 878 dc.getDisplayInfo().displayCutout); 879 } 880 881 @Test testLayoutSeq_assignedDuringLayout()882 public void testLayoutSeq_assignedDuringLayout() { 883 final DisplayContent dc = createNewDisplay(); 884 final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); 885 886 performLayout(dc); 887 888 assertThat(win.mLayoutSeq, is(dc.mLayoutSeq)); 889 } 890 891 @Test testOrientationDefinedByKeyguard()892 public void testOrientationDefinedByKeyguard() { 893 final DisplayContent dc = mDisplayContent; 894 dc.getDisplayPolicy().setAwake(true); 895 896 // Create a window that requests landscape orientation. It will define device orientation 897 // by default. 898 final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w"); 899 window.mActivityRecord.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); 900 901 final WindowState keyguard = createWindow(null, TYPE_NOTIFICATION_SHADE , dc, "keyguard"); 902 keyguard.mHasSurface = true; 903 keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED; 904 905 assertEquals("Screen orientation must be defined by the app window by default", 906 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); 907 908 keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_PORTRAIT; 909 mAtm.mKeyguardController.setKeyguardShown(window.getDisplayId(), true /* keyguardShowing */, 910 false /* aodShowing */); 911 assertEquals("Visible keyguard must influence device orientation", 912 SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation()); 913 914 mAtm.mKeyguardController.keyguardGoingAway(window.getDisplayId(), 0 /* flags */); 915 assertEquals("Keyguard that is going away must not influence device orientation", 916 SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); 917 } 918 919 @Test testOrientationForAspectRatio()920 public void testOrientationForAspectRatio() { 921 final DisplayContent dc = createNewDisplay(); 922 923 // When display content is created its configuration is not yet initialized, which could 924 // cause unnecessary configuration propagation, so initialize it here. 925 final Configuration config = new Configuration(); 926 dc.computeScreenConfiguration(config); 927 dc.onRequestedOverrideConfigurationChanged(config); 928 929 // Create a window that requests a fixed orientation. It will define device orientation 930 // by default. 931 final WindowState window = createWindow(null /* parent */, TYPE_APPLICATION_OVERLAY, dc, 932 "window"); 933 window.mHasSurface = true; 934 window.mAttrs.screenOrientation = SCREEN_ORIENTATION_LANDSCAPE; 935 936 // -------------------------------- 937 // Test non-close-to-square display 938 // -------------------------------- 939 dc.mBaseDisplayWidth = 1000; 940 dc.mBaseDisplayHeight = (int) (dc.mBaseDisplayWidth * dc.mCloseToSquareMaxAspectRatio * 2f); 941 dc.configureDisplayPolicy(); 942 943 assertEquals("Screen orientation must be defined by the window by default.", 944 window.mAttrs.screenOrientation, dc.getOrientation()); 945 946 // ---------------------------- 947 // Test close-to-square display - should be handled in the same way 948 // ---------------------------- 949 dc.mBaseDisplayHeight = dc.mBaseDisplayWidth; 950 dc.configureDisplayPolicy(); 951 952 assertEquals( 953 "Screen orientation must be defined by the window even on close-to-square display.", 954 window.mAttrs.screenOrientation, dc.getOrientation()); 955 956 // Assume that a decor window occupies the display height, so the configuration orientation 957 // should be landscape. 958 dc.getDisplayPolicy().getDecorInsetsInfo(ROTATION_0, dc.mBaseDisplayHeight, 959 dc.mBaseDisplayWidth).mConfigFrame.set(0, 0, 1000, 990); 960 dc.computeScreenConfiguration(config, ROTATION_0); 961 dc.onRequestedOverrideConfigurationChanged(config); 962 assertEquals(Configuration.ORIENTATION_LANDSCAPE, config.orientation); 963 assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getNaturalConfigurationOrientation()); 964 window.setOverrideOrientation(SCREEN_ORIENTATION_NOSENSOR); 965 assertEquals(Configuration.ORIENTATION_LANDSCAPE, 966 window.getRequestedConfigurationOrientation()); 967 // Note that getNaturalOrientation is based on logical display size. So it is portrait if 968 // the display width equals to height. 969 assertEquals(Configuration.ORIENTATION_PORTRAIT, dc.getNaturalOrientation()); 970 } 971 972 @Test testGetPreferredOptionsPanelGravityFromDifferentDisplays()973 public void testGetPreferredOptionsPanelGravityFromDifferentDisplays() { 974 final DisplayContent portraitDisplay = createNewDisplay(); 975 portraitDisplay.mInitialDisplayHeight = 2000; 976 portraitDisplay.mInitialDisplayWidth = 1000; 977 978 portraitDisplay.getDisplayRotation().setRotation(Surface.ROTATION_0); 979 assertFalse(isOptionsPanelAtRight(portraitDisplay.getDisplayId())); 980 portraitDisplay.getDisplayRotation().setRotation(ROTATION_90); 981 assertTrue(isOptionsPanelAtRight(portraitDisplay.getDisplayId())); 982 983 final DisplayContent landscapeDisplay = createNewDisplay(); 984 landscapeDisplay.mInitialDisplayHeight = 1000; 985 landscapeDisplay.mInitialDisplayWidth = 2000; 986 987 landscapeDisplay.getDisplayRotation().setRotation(Surface.ROTATION_0); 988 assertTrue(isOptionsPanelAtRight(landscapeDisplay.getDisplayId())); 989 landscapeDisplay.getDisplayRotation().setRotation(ROTATION_90); 990 assertFalse(isOptionsPanelAtRight(landscapeDisplay.getDisplayId())); 991 } 992 993 @SetupWindows(addWindows = W_INPUT_METHOD) 994 @Test testInputMethodTargetUpdateWhenSwitchingOnDisplays()995 public void testInputMethodTargetUpdateWhenSwitchingOnDisplays() { 996 final DisplayContent newDisplay = createNewDisplay(); 997 998 final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); 999 final Task rootTask = mDisplayContent.getTopRootTask(); 1000 final ActivityRecord activity = rootTask.topRunningActivity(); 1001 doReturn(true).when(activity).shouldBeVisibleUnchecked(); 1002 1003 final WindowState appWin1 = createWindow(null, TYPE_APPLICATION, newDisplay, "appWin1"); 1004 final Task rootTask1 = newDisplay.getTopRootTask(); 1005 final ActivityRecord activity1 = rootTask1.topRunningActivity(); 1006 doReturn(true).when(activity1).shouldBeVisibleUnchecked(); 1007 appWin.setHasSurface(true); 1008 appWin1.setHasSurface(true); 1009 1010 // Set current input method window on default display, make sure the input method target 1011 // is appWin & null on the other display. 1012 mDisplayContent.setInputMethodWindowLocked(mImeWindow); 1013 newDisplay.setInputMethodWindowLocked(null); 1014 assertEquals("appWin should be IME target window", 1015 appWin, mDisplayContent.getImeTarget(IME_TARGET_LAYERING)); 1016 assertNull("newDisplay Ime target: ", newDisplay.getImeTarget(IME_TARGET_LAYERING)); 1017 1018 // Switch input method window on new display & make sure the input method target also 1019 // switched as expected. 1020 newDisplay.setInputMethodWindowLocked(mImeWindow); 1021 mDisplayContent.setInputMethodWindowLocked(null); 1022 assertEquals("appWin1 should be IME target window", appWin1, 1023 newDisplay.getImeTarget(IME_TARGET_LAYERING)); 1024 assertNull("default display Ime target: ", 1025 mDisplayContent.getImeTarget(IME_TARGET_LAYERING)); 1026 } 1027 1028 @Test testAllowsTopmostFullscreenOrientation()1029 public void testAllowsTopmostFullscreenOrientation() { 1030 final DisplayContent dc = createNewDisplay(); 1031 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, dc.getOrientation()); 1032 dc.getDisplayRotation().setFixedToUserRotation( 1033 IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); 1034 1035 final Task rootTask = new TaskBuilder(mSupervisor) 1036 .setDisplay(dc) 1037 .setCreateActivity(true) 1038 .build(); 1039 doReturn(true).when(rootTask).isVisible(); 1040 1041 final Task freeformRootTask = new TaskBuilder(mSupervisor) 1042 .setDisplay(dc) 1043 .setCreateActivity(true) 1044 .setWindowingMode(WINDOWING_MODE_FREEFORM) 1045 .build(); 1046 doReturn(true).when(freeformRootTask).isVisible(); 1047 freeformRootTask.getTopChild().setBounds(100, 100, 300, 400); 1048 1049 assertTrue(dc.getDefaultTaskDisplayArea().isRootTaskVisible(WINDOWING_MODE_FREEFORM)); 1050 1051 freeformRootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE); 1052 rootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT); 1053 assertEquals(SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation()); 1054 1055 rootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_LANDSCAPE); 1056 freeformRootTask.getTopNonFinishingActivity().setOrientation(SCREEN_ORIENTATION_PORTRAIT); 1057 assertEquals(SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation()); 1058 } 1059 updateAllDisplayContentAndRotation(DisplayContent dc)1060 private void updateAllDisplayContentAndRotation(DisplayContent dc) { 1061 // NB updateOrientation will not revert the user orientation until a settings change 1062 // takes effect. 1063 dc.updateOrientation(); 1064 dc.onDisplayChanged(dc); 1065 dc.mWmService.updateRotation(true /* alwaysSendConfiguration */, 1066 false /* forceRelayout */); 1067 waitUntilHandlersIdle(); 1068 } 1069 1070 @Test testNoSensorRevert()1071 public void testNoSensorRevert() { 1072 final DisplayContent dc = mDisplayContent; 1073 spyOn(dc); 1074 doReturn(true).when(dc).getIgnoreOrientationRequest(); 1075 final DisplayRotation dr = dc.getDisplayRotation(); 1076 spyOn(dr); 1077 doReturn(false).when(dr).useDefaultSettingsProvider(); 1078 final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build(); 1079 app.setOrientation(SCREEN_ORIENTATION_LANDSCAPE, app); 1080 1081 assertFalse(dc.getRotationReversionController().isAnyOverrideActive()); 1082 dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_LOCKED, 1083 ROTATION_90, /* caller= */ "DisplayContentTests"); 1084 updateAllDisplayContentAndRotation(dc); 1085 assertEquals(ROTATION_90, dc.getDisplayRotation() 1086 .rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_90)); 1087 1088 app.setOrientation(SCREEN_ORIENTATION_NOSENSOR); 1089 updateAllDisplayContentAndRotation(dc); 1090 assertTrue(dc.getRotationReversionController().isAnyOverrideActive()); 1091 assertEquals(ROTATION_0, dc.getRotation()); 1092 1093 app.setOrientation(SCREEN_ORIENTATION_UNSPECIFIED); 1094 updateAllDisplayContentAndRotation(dc); 1095 assertFalse(dc.getRotationReversionController().isAnyOverrideActive()); 1096 assertEquals(WindowManagerPolicy.USER_ROTATION_LOCKED, 1097 dc.getDisplayRotation().getUserRotationMode()); 1098 assertEquals(ROTATION_90, dc.getDisplayRotation().getUserRotation()); 1099 assertEquals(ROTATION_90, dc.getDisplayRotation() 1100 .rotationForOrientation(SCREEN_ORIENTATION_UNSPECIFIED, ROTATION_0)); 1101 dc.getDisplayRotation().setUserRotation(WindowManagerPolicy.USER_ROTATION_FREE, 1102 ROTATION_0, /* caller= */ "DisplayContentTests"); 1103 } 1104 1105 @Test testOnDescendantOrientationRequestChanged()1106 public void testOnDescendantOrientationRequestChanged() { 1107 final DisplayContent dc = createNewDisplay(); 1108 dc.getDisplayRotation().setFixedToUserRotation( 1109 IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); 1110 dc.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FULLSCREEN); 1111 final int newOrientation = getRotatedOrientation(dc); 1112 1113 final Task task = new TaskBuilder(mSupervisor) 1114 .setDisplay(dc).setCreateActivity(true).build(); 1115 final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity(); 1116 dc.setFocusedApp(activity); 1117 1118 activity.setRequestedOrientation(newOrientation); 1119 1120 assertTrue("The display should be rotated.", dc.getRotation() % 2 == 1); 1121 } 1122 1123 @Test testOnDescendantOrientationRequestChanged_FrozenToUserRotation()1124 public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() { 1125 final DisplayContent dc = createNewDisplay(); 1126 dc.getDisplayRotation().setFixedToUserRotation( 1127 IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); 1128 dc.getDisplayRotation().setUserRotation( 1129 WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_180, 1130 /* caller= */ "DisplayContentTests"); 1131 final int newOrientation = getRotatedOrientation(dc); 1132 1133 final Task task = new TaskBuilder(mSupervisor) 1134 .setDisplay(dc).setCreateActivity(true).build(); 1135 final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity(); 1136 dc.setFocusedApp(activity); 1137 1138 activity.setRequestedOrientation(newOrientation); 1139 1140 verify(dc, never()).updateDisplayOverrideConfigurationLocked(any(), eq(activity), 1141 anyBoolean()); 1142 assertEquals(ROTATION_180, dc.getRotation()); 1143 } 1144 1145 @Test testOrientationBehind()1146 public void testOrientationBehind() { 1147 final ActivityRecord prev = new ActivityBuilder(mAtm).setCreateTask(true) 1148 .setScreenOrientation(getRotatedOrientation(mDisplayContent)).build(); 1149 prev.setVisibleRequested(false); 1150 final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true) 1151 .setScreenOrientation(SCREEN_ORIENTATION_BEHIND).build(); 1152 assertNotEquals(WindowConfiguration.ROTATION_UNDEFINED, 1153 mDisplayContent.rotationForActivityInDifferentOrientation(top)); 1154 1155 mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0); 1156 top.setVisibility(true); 1157 mDisplayContent.updateOrientation(); 1158 // The top uses "behind", so the orientation is decided by the previous. 1159 assertEquals(prev, mDisplayContent.getLastOrientationSource()); 1160 // The top will use the rotation from "prev" with fixed rotation. 1161 assertTrue(top.hasFixedRotationTransform()); 1162 } 1163 1164 @Test testFixedToUserRotationChanged()1165 public void testFixedToUserRotationChanged() { 1166 final DisplayContent dc = createNewDisplay(); 1167 dc.getDisplayRotation().setFixedToUserRotation( 1168 IWindowManager.FIXED_TO_USER_ROTATION_ENABLED); 1169 dc.getDisplayRotation().setUserRotation( 1170 WindowManagerPolicy.USER_ROTATION_LOCKED, ROTATION_0, 1171 /* caller= */ "DisplayContentTests"); 1172 dc.getDefaultTaskDisplayArea().setWindowingMode(WINDOWING_MODE_FULLSCREEN); 1173 final int newOrientation = getRotatedOrientation(dc); 1174 1175 final Task task = new TaskBuilder(mSupervisor) 1176 .setDisplay(dc).setCreateActivity(true).build(); 1177 final ActivityRecord activity = task.getTopMostTask().getTopNonFinishingActivity(); 1178 dc.setFocusedApp(activity); 1179 1180 activity.setRequestedOrientation(newOrientation); 1181 1182 dc.getDisplayRotation().setFixedToUserRotation( 1183 IWindowManager.FIXED_TO_USER_ROTATION_DISABLED); 1184 1185 assertTrue("The display should be rotated.", dc.getRotation() % 2 == 1); 1186 } 1187 1188 @Test testComputeImeParent_app()1189 public void testComputeImeParent_app() throws Exception { 1190 final DisplayContent dc = createNewDisplay(); 1191 dc.setImeLayeringTarget(createWindow(null, TYPE_BASE_APPLICATION, "app")); 1192 dc.setImeInputTarget(dc.getImeTarget(IME_TARGET_LAYERING).getWindow()); 1193 assertEquals(dc.getImeTarget(IME_TARGET_LAYERING).getWindow() 1194 .mActivityRecord.getSurfaceControl(), dc.computeImeParent()); 1195 } 1196 1197 @Test testComputeImeParent_app_notFullscreen()1198 public void testComputeImeParent_app_notFullscreen() throws Exception { 1199 final DisplayContent dc = createNewDisplay(); 1200 dc.setImeLayeringTarget(createWindow(null, TYPE_STATUS_BAR, "app")); 1201 dc.getImeTarget(IME_TARGET_LAYERING).getWindow().setWindowingMode( 1202 WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW); 1203 dc.setImeInputTarget(dc.getImeTarget(IME_TARGET_LAYERING).getWindow()); 1204 assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent()); 1205 } 1206 1207 @SetupWindows(addWindows = W_ACTIVITY) 1208 @Test testComputeImeParent_app_notMatchParentBounds()1209 public void testComputeImeParent_app_notMatchParentBounds() { 1210 spyOn(mAppWindow.mActivityRecord); 1211 doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds(); 1212 mDisplayContent.setImeLayeringTarget(mAppWindow); 1213 // The surface parent of IME should be the display instead of app window. 1214 assertEquals(mDisplayContent.getImeContainer().getParentSurfaceControl(), 1215 mDisplayContent.computeImeParent()); 1216 } 1217 1218 @Test testComputeImeParent_noApp()1219 public void testComputeImeParent_noApp() throws Exception { 1220 final DisplayContent dc = createNewDisplay(); 1221 dc.setImeLayeringTarget(createWindow(null, TYPE_STATUS_BAR, "statusBar")); 1222 dc.setImeInputTarget(dc.getImeTarget(IME_TARGET_LAYERING).getWindow()); 1223 assertEquals(dc.getImeContainer().getParentSurfaceControl(), dc.computeImeParent()); 1224 } 1225 1226 @SetupWindows(addWindows = W_ACTIVITY) 1227 @Test testComputeImeParent_inputTargetNotUpdate()1228 public void testComputeImeParent_inputTargetNotUpdate() throws Exception { 1229 WindowState app1 = createWindow(null, TYPE_BASE_APPLICATION, "app1"); 1230 WindowState app2 = createWindow(null, TYPE_BASE_APPLICATION, "app2"); 1231 doReturn(true).when(mDisplayContent).shouldImeAttachedToApp(); 1232 mDisplayContent.setImeLayeringTarget(app1); 1233 mDisplayContent.setImeInputTarget(app1); 1234 assertEquals(app1.mActivityRecord.getSurfaceControl(), mDisplayContent.computeImeParent()); 1235 mDisplayContent.setImeLayeringTarget(app2); 1236 // Expect null means no change IME parent when the IME layering target not yet 1237 // request IME to be the input target. 1238 assertNull(mDisplayContent.computeImeParent()); 1239 } 1240 1241 @SetupWindows(addWindows = W_ACTIVITY) 1242 @Test testComputeImeParent_updateParentWhenTargetNotUseIme()1243 public void testComputeImeParent_updateParentWhenTargetNotUseIme() throws Exception { 1244 WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay"); 1245 overlay.setBounds(100, 100, 200, 200); 1246 overlay.mAttrs.flags = FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM; 1247 WindowState app = createWindow(null, TYPE_BASE_APPLICATION, "app"); 1248 mDisplayContent.setImeLayeringTarget(overlay); 1249 mDisplayContent.setImeInputTarget(app); 1250 assertFalse(mDisplayContent.shouldImeAttachedToApp()); 1251 assertEquals(mDisplayContent.getImeContainer().getParentSurfaceControl(), 1252 mDisplayContent.computeImeParent()); 1253 } 1254 1255 @Test testComputeImeParent_remoteControlTarget()1256 public void testComputeImeParent_remoteControlTarget() throws Exception { 1257 final DisplayContent dc = mDisplayContent; 1258 WindowState app1 = createWindow(null, TYPE_BASE_APPLICATION, "app1"); 1259 WindowState app2 = createWindow(null, TYPE_BASE_APPLICATION, "app2"); 1260 1261 dc.setImeLayeringTarget(app1); 1262 dc.setImeInputTarget(app2); 1263 dc.setRemoteInsetsController(createDisplayWindowInsetsController()); 1264 dc.getImeTarget(IME_TARGET_LAYERING).getWindow().setWindowingMode( 1265 WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW); 1266 dc.getImeInputTarget().getWindowState().setWindowingMode( 1267 WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW); 1268 1269 // Expect ImeParent is null since ImeLayeringTarget and ImeInputTarget are different. 1270 assertNull(dc.computeImeParent()); 1271 1272 // ImeLayeringTarget and ImeInputTarget are updated to the same. 1273 dc.setImeInputTarget(app1); 1274 assertEquals(dc.getImeTarget(IME_TARGET_LAYERING), dc.getImeInputTarget()); 1275 1276 // The ImeParent should be the display. 1277 assertEquals(dc.getImeContainer().getParent().getSurfaceControl(), dc.computeImeParent()); 1278 } 1279 1280 @Test testInputMethodInputTarget_isClearedWhenWindowStateIsRemoved()1281 public void testInputMethodInputTarget_isClearedWhenWindowStateIsRemoved() throws Exception { 1282 final DisplayContent dc = createNewDisplay(); 1283 1284 WindowState app = createWindow(null, TYPE_BASE_APPLICATION, dc, "app"); 1285 1286 dc.setImeInputTarget(app); 1287 assertEquals(app, dc.computeImeControlTarget()); 1288 1289 app.removeImmediately(); 1290 1291 assertNull(dc.getImeInputTarget()); 1292 assertNull(dc.computeImeControlTarget()); 1293 } 1294 1295 @Test testComputeImeControlTarget()1296 public void testComputeImeControlTarget() throws Exception { 1297 final DisplayContent dc = createNewDisplay(); 1298 dc.setRemoteInsetsController(createDisplayWindowInsetsController()); 1299 dc.mCurrentFocus = createWindow(null, TYPE_BASE_APPLICATION, "app"); 1300 1301 // Expect returning null IME control target when the focus window has not yet been the 1302 // IME input target (e.g. IME is restarting) in fullscreen windowing mode. 1303 dc.setImeInputTarget(null); 1304 assertFalse(dc.mCurrentFocus.inMultiWindowMode()); 1305 assertNull(dc.computeImeControlTarget()); 1306 1307 dc.setImeInputTarget(dc.mCurrentFocus); 1308 dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState()); 1309 assertEquals(dc.getImeInputTarget().getWindowState(), dc.computeImeControlTarget()); 1310 } 1311 1312 @Test testComputeImeControlTarget_splitscreen()1313 public void testComputeImeControlTarget_splitscreen() throws Exception { 1314 final DisplayContent dc = createNewDisplay(); 1315 dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app")); 1316 dc.getImeInputTarget().getWindowState().setWindowingMode( 1317 WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW); 1318 dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState()); 1319 dc.setRemoteInsetsController(createDisplayWindowInsetsController()); 1320 assertNotEquals(dc.getImeInputTarget().getWindowState(), 1321 dc.computeImeControlTarget()); 1322 } 1323 1324 @SetupWindows(addWindows = W_INPUT_METHOD) 1325 @Test testImeSecureFlagGetUpdatedAfterImeInputTarget()1326 public void testImeSecureFlagGetUpdatedAfterImeInputTarget() { 1327 // Verify IME window can get up-to-date secure flag update when the IME input target 1328 // set before setCanScreenshot called. 1329 final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); 1330 SurfaceControl.Transaction t = mDisplayContent.mInputMethodWindow.getPendingTransaction(); 1331 spyOn(t); 1332 mDisplayContent.setImeInputTarget(app); 1333 mDisplayContent.mInputMethodWindow.setCanScreenshot(t, false /* canScreenshot */); 1334 1335 verify(t).setSecure(eq(mDisplayContent.mInputMethodWindow.mSurfaceControl), eq(true)); 1336 } 1337 1338 @SetupWindows(addWindows = W_ACTIVITY) 1339 @Test testComputeImeControlTarget_notMatchParentBounds()1340 public void testComputeImeControlTarget_notMatchParentBounds() throws Exception { 1341 spyOn(mAppWindow.mActivityRecord); 1342 doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds(); 1343 mDisplayContent.setImeInputTarget(mAppWindow); 1344 mDisplayContent.setImeLayeringTarget( 1345 mDisplayContent.getImeInputTarget().getWindowState()); 1346 mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController()); 1347 assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget()); 1348 } 1349 1350 @SetupWindows(addWindows = W_ACTIVITY) 1351 @Test testShouldImeAttachedToApp_targetBoundsDifferentFromImeContainer_returnsFalse()1352 public void testShouldImeAttachedToApp_targetBoundsDifferentFromImeContainer_returnsFalse() 1353 throws Exception { 1354 Rect imeContainerBounds = new Rect(0, 0, 100, 100); 1355 Rect imeTargetBounds = new Rect(0, 0, 100, 200); 1356 spyOn(mAppWindow); 1357 spyOn(mAppWindow.mActivityRecord); 1358 doReturn(imeTargetBounds).when(mAppWindow).getBounds(); 1359 doReturn(true).when(mAppWindow.mActivityRecord).matchParentBounds(); 1360 mDisplayContent.setImeInputTarget(mAppWindow); 1361 mDisplayContent.setImeLayeringTarget( 1362 mDisplayContent.getImeInputTarget().getWindowState()); 1363 mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController()); 1364 final DisplayArea.Tokens imeContainer = mDisplayContent.getImeContainer(); 1365 spyOn(imeContainer); 1366 doReturn(imeContainerBounds).when(imeContainer).getBounds(); 1367 1368 assertFalse(mDisplayContent.shouldImeAttachedToApp()); 1369 } 1370 1371 @Test testUpdateSystemGestureExclusion()1372 public void testUpdateSystemGestureExclusion() throws Exception { 1373 final DisplayContent dc = createNewDisplay(); 1374 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); 1375 win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 1376 win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40))); 1377 1378 performLayout(dc); 1379 1380 win.setHasSurface(true); 1381 dc.updateSystemGestureExclusion(); 1382 1383 final boolean[] invoked = { false }; 1384 final ISystemGestureExclusionListener.Stub verifier = 1385 new ISystemGestureExclusionListener.Stub() { 1386 @Override 1387 public void onSystemGestureExclusionChanged(int displayId, Region actual, 1388 Region unrestricted) { 1389 Region expected = Region.obtain(); 1390 expected.set(10, 20, 30, 40); 1391 assertEquals(expected, actual); 1392 invoked[0] = true; 1393 } 1394 }; 1395 try { 1396 dc.registerSystemGestureExclusionListener(verifier); 1397 } finally { 1398 dc.unregisterSystemGestureExclusionListener(verifier); 1399 } 1400 assertTrue("SystemGestureExclusionListener was not invoked", invoked[0]); 1401 } 1402 1403 @Test testCalculateSystemGestureExclusion()1404 public void testCalculateSystemGestureExclusion() throws Exception { 1405 final DisplayContent dc = createNewDisplay(); 1406 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); 1407 win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 1408 win.setSystemGestureExclusion(Collections.singletonList(new Rect(10, 20, 30, 40))); 1409 1410 final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "win2"); 1411 win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 1412 win2.setSystemGestureExclusion(Collections.singletonList(new Rect(20, 30, 40, 50))); 1413 1414 performLayout(dc); 1415 1416 win.setHasSurface(true); 1417 win2.setHasSurface(true); 1418 1419 final Region expected = Region.obtain(); 1420 expected.set(20, 30, 40, 50); 1421 assertEquals(expected, calculateSystemGestureExclusion(dc)); 1422 } 1423 calculateSystemGestureExclusion(DisplayContent dc)1424 private Region calculateSystemGestureExclusion(DisplayContent dc) { 1425 Region out = Region.obtain(); 1426 Region unrestricted = Region.obtain(); 1427 dc.calculateSystemGestureExclusion(out, unrestricted); 1428 return out; 1429 } 1430 1431 @Test testCalculateSystemGestureExclusion_modal()1432 public void testCalculateSystemGestureExclusion_modal() throws Exception { 1433 final DisplayContent dc = createNewDisplay(); 1434 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "base"); 1435 win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 1436 win.setSystemGestureExclusion(Collections.singletonList(new Rect(0, 0, 1000, 1000))); 1437 1438 final WindowState win2 = createWindow(null, TYPE_APPLICATION, dc, "modal"); 1439 win2.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 1440 win2.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; 1441 win2.getAttrs().width = 10; 1442 win2.getAttrs().height = 10; 1443 win2.setSystemGestureExclusion(Collections.emptyList()); 1444 1445 performLayout(dc); 1446 1447 win.setHasSurface(true); 1448 win2.setHasSurface(true); 1449 1450 final Region expected = Region.obtain(); 1451 assertEquals(expected, calculateSystemGestureExclusion(dc)); 1452 } 1453 1454 @Test testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow()1455 public void testCalculateSystemGestureExclusion_immersiveStickyLegacyWindow() throws Exception { 1456 mWm.mConstants.mSystemGestureExcludedByPreQStickyImmersive = true; 1457 1458 final DisplayContent dc = createNewDisplay(); 1459 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); 1460 win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 1461 win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; 1462 win.getAttrs().privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION; 1463 win.getAttrs().insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; 1464 win.setRequestedVisibleTypes(0, navigationBars() | statusBars()); 1465 win.mActivityRecord.mTargetSdk = P; 1466 1467 performLayout(dc); 1468 1469 win.setHasSurface(true); 1470 1471 final Region expected = Region.obtain(); 1472 expected.set(dc.getBounds()); 1473 assertEquals(expected, calculateSystemGestureExclusion(dc)); 1474 1475 win.setHasSurface(false); 1476 } 1477 1478 @Test testCalculateSystemGestureExclusion_unrestricted()1479 public void testCalculateSystemGestureExclusion_unrestricted() throws Exception { 1480 mWm.mConstants.mSystemGestureExcludedByPreQStickyImmersive = true; 1481 1482 final DisplayContent dc = createNewDisplay(); 1483 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win"); 1484 win.getAttrs().flags |= FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR; 1485 win.getAttrs().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 1486 win.getAttrs().privateFlags |= PRIVATE_FLAG_UNRESTRICTED_GESTURE_EXCLUSION; 1487 win.setSystemGestureExclusion(Collections.singletonList(dc.getBounds())); 1488 1489 performLayout(dc); 1490 1491 win.setHasSurface(true); 1492 1493 final Region expected = Region.obtain(); 1494 expected.set(dc.getBounds()); 1495 assertEquals(expected, calculateSystemGestureExclusion(dc)); 1496 1497 win.setHasSurface(false); 1498 } 1499 1500 @SetupWindows(addWindows = { W_ABOVE_ACTIVITY, W_ACTIVITY }) 1501 @Test testRequestResizeForEmptyFrames()1502 public void testRequestResizeForEmptyFrames() { 1503 final WindowState win = mChildAppWindowAbove; 1504 makeWindowVisible(win, win.getParentWindow()); 1505 win.setRequestedSize(mDisplayContent.mBaseDisplayWidth, 0 /* height */); 1506 win.mAttrs.width = win.mAttrs.height = WindowManager.LayoutParams.WRAP_CONTENT; 1507 win.mAttrs.gravity = Gravity.CENTER; 1508 performLayout(mDisplayContent); 1509 1510 // The frame is empty because the requested height is zero. 1511 assertTrue(win.getFrame().isEmpty()); 1512 // The window should be scheduled to resize then the client may report a new non-empty size. 1513 win.updateResizingWindowIfNeeded(); 1514 assertThat(mWm.mResizingWindows).contains(win); 1515 } 1516 1517 @Test testOrientationChangeLogging()1518 public void testOrientationChangeLogging() { 1519 MetricsLogger mockLogger = mock(MetricsLogger.class); 1520 Configuration oldConfig = new Configuration(); 1521 oldConfig.orientation = Configuration.ORIENTATION_LANDSCAPE; 1522 1523 Configuration newConfig = new Configuration(); 1524 newConfig.orientation = Configuration.ORIENTATION_PORTRAIT; 1525 final DisplayContent displayContent = createNewDisplay(); 1526 Mockito.doReturn(mockLogger).when(displayContent).getMetricsLogger(); 1527 Mockito.doReturn(oldConfig).doReturn(newConfig).when(displayContent).getConfiguration(); 1528 1529 displayContent.onConfigurationChanged(newConfig); 1530 1531 ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); 1532 verify(mockLogger).write(logMakerCaptor.capture()); 1533 assertThat(logMakerCaptor.getValue().getCategory(), 1534 is(MetricsProto.MetricsEvent.ACTION_PHONE_ORIENTATION_CHANGED)); 1535 assertThat(logMakerCaptor.getValue().getSubtype(), 1536 is(Configuration.ORIENTATION_PORTRAIT)); 1537 } 1538 1539 @Test testHybridRotationAnimation()1540 public void testHybridRotationAnimation() { 1541 final DisplayContent displayContent = mDefaultDisplay; 1542 final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar"); 1543 final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar"); 1544 final WindowState app = createWindow(null, TYPE_BASE_APPLICATION, "app"); 1545 final WindowState[] windows = { statusBar, navBar, app }; 1546 makeWindowVisible(windows); 1547 final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); 1548 displayPolicy.addWindowLw(statusBar, statusBar.mAttrs); 1549 displayPolicy.addWindowLw(navBar, navBar.mAttrs); 1550 final ScreenRotationAnimation rotationAnim = new ScreenRotationAnimation(displayContent, 1551 displayContent.getRotation()); 1552 spyOn(rotationAnim); 1553 // Assume that the display rotation is changed so it is frozen in preparation for animation. 1554 doReturn(true).when(rotationAnim).hasScreenshot(); 1555 displayContent.getDisplayRotation().setRotation((displayContent.getRotation() + 1) % 4); 1556 displayContent.setRotationAnimation(rotationAnim); 1557 // The fade rotation animation also starts to hide some non-app windows. 1558 assertNotNull(displayContent.getAsyncRotationController()); 1559 assertTrue(statusBar.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM)); 1560 1561 for (WindowState w : windows) { 1562 w.setOrientationChanging(true); 1563 } 1564 // The display only waits for the app window to unfreeze. 1565 assertFalse(displayContent.shouldSyncRotationChange(statusBar)); 1566 assertFalse(displayContent.shouldSyncRotationChange(navBar)); 1567 assertTrue(displayContent.shouldSyncRotationChange(app)); 1568 // If all windows animated by fade rotation animation have done the orientation change, 1569 // the animation controller should be cleared. 1570 statusBar.setOrientationChanging(false); 1571 navBar.setOrientationChanging(false); 1572 assertNull(displayContent.getAsyncRotationController()); 1573 } 1574 1575 @SetupWindows(addWindows = { W_ACTIVITY, W_WALLPAPER, W_STATUS_BAR, W_NAVIGATION_BAR, 1576 W_INPUT_METHOD, W_NOTIFICATION_SHADE }) 1577 @Test testApplyTopFixedRotationTransform()1578 public void testApplyTopFixedRotationTransform() { 1579 final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); 1580 spyOn(displayPolicy); 1581 // Only non-movable (gesture) navigation bar will be animated by fixed rotation animation. 1582 doReturn(false).when(displayPolicy).navigationBarCanMove(); 1583 displayPolicy.addWindowLw(mStatusBarWindow, mStatusBarWindow.mAttrs); 1584 displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); 1585 displayPolicy.addWindowLw(mNotificationShadeWindow, mNotificationShadeWindow.mAttrs); 1586 makeWindowVisible(mStatusBarWindow, mNavBarWindow); 1587 final Configuration config90 = new Configuration(); 1588 mDisplayContent.computeScreenConfiguration(config90, ROTATION_90); 1589 1590 final Configuration config = new Configuration(); 1591 mDisplayContent.getDisplayRotation().setRotation(ROTATION_0); 1592 mDisplayContent.computeScreenConfiguration(config); 1593 mDisplayContent.onRequestedOverrideConfigurationChanged(config); 1594 assertNotEquals(config90.windowConfiguration.getMaxBounds(), 1595 config.windowConfiguration.getMaxBounds()); 1596 1597 final ActivityRecord app = mAppWindow.mActivityRecord; 1598 app.setVisible(false); 1599 mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN); 1600 mDisplayContent.mOpeningApps.add(app); 1601 final int newOrientation = getRotatedOrientation(mDisplayContent); 1602 app.setRequestedOrientation(newOrientation); 1603 1604 assertTrue(app.isFixedRotationTransforming()); 1605 assertTrue(mAppWindow.matchesDisplayAreaBounds()); 1606 assertFalse(mAppWindow.areAppWindowBoundsLetterboxed()); 1607 assertTrue(mDisplayContent.getDisplayRotation().shouldRotateSeamlessly( 1608 ROTATION_0 /* oldRotation */, ROTATION_90 /* newRotation */, 1609 false /* forceUpdate */)); 1610 1611 final AsyncRotationController asyncRotationController = 1612 mDisplayContent.getAsyncRotationController(); 1613 assertNotNull(asyncRotationController); 1614 assertTrue(mStatusBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM)); 1615 assertTrue(mNavBarWindow.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM)); 1616 // Notification shade may have its own view animation in real case so do not fade out it. 1617 assertFalse(mNotificationShadeWindow.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM)); 1618 1619 // If the visibility of insets state is changed, the rotated state should be updated too. 1620 final int statusBarId = mStatusBarWindow.getControllableInsetProvider().getSource().getId(); 1621 final InsetsState rotatedState = app.getFixedRotationTransformInsetsState(); 1622 final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState(); 1623 assertEquals(state.isSourceOrDefaultVisible(statusBarId, statusBars()), 1624 rotatedState.isSourceOrDefaultVisible(statusBarId, statusBars())); 1625 state.setSourceVisible(statusBarId, 1626 !rotatedState.isSourceOrDefaultVisible(statusBarId, statusBars())); 1627 mDisplayContent.getInsetsStateController().notifyInsetsChanged(); 1628 assertEquals(state.isSourceOrDefaultVisible(statusBarId, statusBars()), 1629 rotatedState.isSourceOrDefaultVisible(statusBarId, statusBars())); 1630 1631 final Rect outFrame = new Rect(); 1632 final Rect outInsets = new Rect(); 1633 final Rect outStableInsets = new Rect(); 1634 final Rect outSurfaceInsets = new Rect(); 1635 mAppWindow.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets); 1636 // The animation frames should not be rotated because display hasn't rotated. 1637 assertEquals(mDisplayContent.getBounds(), outFrame); 1638 1639 // The display should keep current orientation and the rotated configuration should apply 1640 // to the activity. 1641 assertEquals(config.orientation, mDisplayContent.getConfiguration().orientation); 1642 assertEquals(config90.orientation, app.getConfiguration().orientation); 1643 assertEquals(config90.windowConfiguration.getBounds(), app.getBounds()); 1644 1645 // Associate wallpaper with the fixed rotation transform. 1646 final WindowToken wallpaperToken = mWallpaperWindow.mToken; 1647 wallpaperToken.linkFixedRotationTransform(app); 1648 1649 // Force the negative offset to verify it can be updated. 1650 mWallpaperWindow.mXOffset = mWallpaperWindow.mYOffset = -1; 1651 assertTrue(mDisplayContent.mWallpaperController.updateWallpaperOffset(mWallpaperWindow, 1652 false /* sync */)); 1653 assertThat(mWallpaperWindow.mXOffset).isNotEqualTo(-1); 1654 assertThat(mWallpaperWindow.mYOffset).isNotEqualTo(-1); 1655 1656 // The wallpaper need to animate with transformed position, so its surface position should 1657 // not be reset. 1658 final Transaction t = wallpaperToken.getPendingTransaction(); 1659 spyOn(t); 1660 mWallpaperWindow.mToken.onAnimationLeashCreated(t, null /* leash */); 1661 verify(t, never()).setPosition(any(), eq(0), eq(0)); 1662 1663 // Launch another activity before the transition is finished. 1664 final Task task2 = new TaskBuilder(mSupervisor).setDisplay(mDisplayContent).build(); 1665 final ActivityRecord app2 = new ActivityBuilder(mAtm).setTask(task2) 1666 .setUseProcess(app.app).build(); 1667 app2.setVisible(false); 1668 mDisplayContent.mOpeningApps.add(app2); 1669 app2.setRequestedOrientation(newOrientation); 1670 1671 // The activity should share the same transform state as the existing one. The activity 1672 // should also be the fixed rotation launching app because it is the latest top. 1673 assertTrue(app.hasFixedRotationTransform(app2)); 1674 assertTrue(mDisplayContent.isFixedRotationLaunchingApp(app2)); 1675 1676 final Configuration expectedProcConfig = new Configuration(app2.app.getConfiguration()); 1677 expectedProcConfig.windowConfiguration.setActivityType( 1678 WindowConfiguration.ACTIVITY_TYPE_UNDEFINED); 1679 assertEquals("The process should receive rotated configuration for compatibility", 1680 expectedProcConfig, app2.app.getConfiguration()); 1681 1682 // If the rotated activity requests to show IME, the IME window should use the 1683 // transformation from activity to lay out in the same orientation. 1684 LocalServices.getService(WindowManagerInternal.class).onToggleImeRequested(true /* show */, 1685 app.token, app.token, mDisplayContent.mDisplayId); 1686 assertTrue(asyncRotationController.isTargetToken(mImeWindow.mToken)); 1687 assertTrue(mImeWindow.mToken.hasFixedRotationTransform()); 1688 assertTrue(mImeWindow.isAnimating(PARENTS, ANIMATION_TYPE_TOKEN_TRANSFORM)); 1689 1690 // The fixed rotation transform can only be finished when all animation finished. 1691 doReturn(false).when(app2).isAnimating(anyInt(), anyInt()); 1692 mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app2.token); 1693 assertTrue(app.hasFixedRotationTransform()); 1694 assertTrue(app2.hasFixedRotationTransform()); 1695 1696 // The display should be rotated after the launch is finished. 1697 doReturn(false).when(app).isAnimating(anyInt(), anyInt()); 1698 mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token); 1699 mStatusBarWindow.finishSeamlessRotation(t); 1700 mNavBarWindow.finishSeamlessRotation(t); 1701 1702 // The fixed rotation should be cleared and the new rotation is applied to display. 1703 assertFalse(app.hasFixedRotationTransform()); 1704 assertFalse(app2.hasFixedRotationTransform()); 1705 assertEquals(config90.orientation, mDisplayContent.getConfiguration().orientation); 1706 assertNull(mDisplayContent.getAsyncRotationController()); 1707 } 1708 1709 @Test testFinishFixedRotationNoAppTransitioningTask()1710 public void testFinishFixedRotationNoAppTransitioningTask() { 1711 unblockDisplayRotation(mDisplayContent); 1712 final ActivityRecord app = createActivityRecord(mDisplayContent); 1713 final Task task = app.getTask(); 1714 final ActivityRecord app2 = new ActivityBuilder(mWm.mAtmService).setTask(task).build(); 1715 mDisplayContent.setFixedRotationLaunchingApp(app2, (mDisplayContent.getRotation() + 1) % 4); 1716 doReturn(true).when(app).inTransitionSelfOrParent(); 1717 // If the task contains a transition, this should be no-op. 1718 mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token); 1719 1720 assertTrue(app2.hasFixedRotationTransform()); 1721 assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp()); 1722 1723 // The display should be unlikely to be in transition, but if it happens, the fixed 1724 // rotation should proceed to finish because the activity/task level transition is finished. 1725 doReturn(true).when(mDisplayContent).inTransition(); 1726 doReturn(false).when(app).inTransitionSelfOrParent(); 1727 // Although this notifies app instead of app2 that uses the fixed rotation, app2 should 1728 // still finish the transform because there is no more transition event. 1729 mDisplayContent.mFixedRotationTransitionListener.onAppTransitionFinishedLocked(app.token); 1730 1731 assertFalse(app2.hasFixedRotationTransform()); 1732 assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp()); 1733 } 1734 1735 @SetupWindows(addWindows = W_ACTIVITY) 1736 @Test testRotateSeamlesslyWithFixedRotation()1737 public void testRotateSeamlesslyWithFixedRotation() { 1738 final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); 1739 final ActivityRecord app = mAppWindow.mActivityRecord; 1740 mDisplayContent.setFixedRotationLaunchingAppUnchecked(app); 1741 mAppWindow.mAttrs.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 1742 1743 // Use seamless rotation if the top app is rotated. 1744 assertTrue(displayRotation.shouldRotateSeamlessly(ROTATION_0 /* oldRotation */, 1745 ROTATION_90 /* newRotation */, false /* forceUpdate */)); 1746 1747 mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(app); 1748 1749 // Use normal rotation because animating recents is an intermediate state. 1750 assertFalse(displayRotation.shouldRotateSeamlessly(ROTATION_0 /* oldRotation */, 1751 ROTATION_90 /* newRotation */, false /* forceUpdate */)); 1752 } 1753 1754 @Test testFixedRotationWithPip()1755 public void testFixedRotationWithPip() { 1756 final DisplayContent displayContent = mDefaultDisplay; 1757 unblockDisplayRotation(displayContent); 1758 // Unblock the condition in PinnedTaskController#continueOrientationChangeIfNeeded. 1759 doNothing().when(displayContent).prepareAppTransition(anyInt()); 1760 // Make resume-top really update the activity state. 1761 setBooted(mAtm); 1762 clearInvocations(mWm); 1763 // Speed up the test by a few seconds. 1764 mAtm.deferWindowLayout(); 1765 1766 final ActivityRecord homeActivity = createActivityRecord( 1767 displayContent.getDefaultTaskDisplayArea().getRootHomeTask()); 1768 final ActivityRecord pinnedActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); 1769 final Task pinnedTask = pinnedActivity.getRootTask(); 1770 doReturn((displayContent.getRotation() + 1) % 4).when(displayContent) 1771 .rotationForActivityInDifferentOrientation(eq(homeActivity)); 1772 // Enter PiP from fullscreen. 1773 pinnedTask.setWindowingMode(WINDOWING_MODE_PINNED); 1774 1775 assertTrue(displayContent.hasTopFixedRotationLaunchingApp()); 1776 assertTrue(displayContent.mPinnedTaskController.shouldDeferOrientationChange()); 1777 verify(mWm, never()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); 1778 clearInvocations(pinnedTask); 1779 1780 // Assume that the PiP enter animation is done then the new bounds are set. Expect the 1781 // orientation update is no longer deferred. 1782 displayContent.mPinnedTaskController.setEnterPipBounds(pinnedTask.getBounds()); 1783 // The Task Configuration was frozen to skip the change of orientation. 1784 verify(pinnedTask, never()).onConfigurationChanged(any()); 1785 assertFalse(displayContent.mPinnedTaskController.shouldDeferOrientationChange()); 1786 assertFalse(displayContent.hasTopFixedRotationLaunchingApp()); 1787 assertEquals(homeActivity.getConfiguration().orientation, 1788 displayContent.getConfiguration().orientation); 1789 1790 doReturn((displayContent.getRotation() + 1) % 4).when(displayContent) 1791 .rotationForActivityInDifferentOrientation(eq(pinnedActivity)); 1792 // Leave PiP to fullscreen. Simulate the step of PipTaskOrganizer that sets the activity 1793 // to fullscreen, so fixed rotation will apply on it. 1794 pinnedActivity.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 1795 assertTrue(displayContent.hasTopFixedRotationLaunchingApp()); 1796 1797 // Assume the animation of PipTaskOrganizer is done and then commit fullscreen to task. 1798 pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 1799 displayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); 1800 assertFalse(displayContent.mPinnedTaskController.isFreezingTaskConfig(pinnedTask)); 1801 assertEquals(pinnedActivity.getConfiguration().orientation, 1802 displayContent.getConfiguration().orientation); 1803 1804 // No need to apply rotation if the display ignores orientation request. 1805 doCallRealMethod().when(displayContent).rotationForActivityInDifferentOrientation(any()); 1806 pinnedActivity.setOverrideOrientation(SCREEN_ORIENTATION_LANDSCAPE); 1807 displayContent.setIgnoreOrientationRequest(true); 1808 assertEquals(WindowConfiguration.ROTATION_UNDEFINED, 1809 displayContent.rotationForActivityInDifferentOrientation(pinnedActivity)); 1810 } 1811 1812 @Test testNoFixedRotationOnResumedScheduledApp()1813 public void testNoFixedRotationOnResumedScheduledApp() { 1814 unblockDisplayRotation(mDisplayContent); 1815 final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build(); 1816 app.setVisible(false); 1817 app.setState(ActivityRecord.State.RESUMED, "test"); 1818 mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN); 1819 mDisplayContent.mOpeningApps.add(app); 1820 final int newOrientation = getRotatedOrientation(mDisplayContent); 1821 app.setRequestedOrientation(newOrientation); 1822 1823 // The condition should reject using fixed rotation because the resumed client in real case 1824 // might get display info immediately. And the fixed rotation adjustments haven't arrived 1825 // client side so the info may be inconsistent with the requested orientation. 1826 verify(mDisplayContent).updateOrientation(eq(app), anyBoolean()); 1827 assertFalse(app.isFixedRotationTransforming()); 1828 assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp()); 1829 } 1830 1831 @Test testRecentsNotRotatingWithFixedRotation()1832 public void testRecentsNotRotatingWithFixedRotation() { 1833 unblockDisplayRotation(mDisplayContent); 1834 final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); 1835 // Skip freezing so the unrelated conditions in updateRotationUnchecked won't disturb. 1836 doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); 1837 1838 final ActivityRecord activity = createActivityRecord(mDisplayContent); 1839 final ActivityRecord recentsActivity = createActivityRecord(mDisplayContent); 1840 recentsActivity.setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR); 1841 doReturn(mock(RecentsAnimationController.class)).when(mWm).getRecentsAnimationController(); 1842 1843 // Do not rotate if the recents animation is animating on top. 1844 mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity); 1845 displayRotation.setRotation((displayRotation.getRotation() + 1) % 4); 1846 assertFalse(displayRotation.updateRotationUnchecked(false)); 1847 1848 // Rotation can be updated if the recents animation is finished. 1849 mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(); 1850 assertTrue(displayRotation.updateRotationUnchecked(false)); 1851 1852 // Rotation can be updated if the policy is not ok to animate (e.g. going to sleep). 1853 mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity); 1854 displayRotation.setRotation((displayRotation.getRotation() + 1) % 4); 1855 ((TestWindowManagerPolicy) mWm.mPolicy).mOkToAnimate = false; 1856 assertTrue(displayRotation.updateRotationUnchecked(false)); 1857 1858 // Rotation can be updated if the recents animation is animating but it is not on top, e.g. 1859 // switching activities in different orientations by quickstep gesture. 1860 mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity); 1861 mDisplayContent.setFixedRotationLaunchingAppUnchecked(activity); 1862 displayRotation.setRotation((displayRotation.getRotation() + 1) % 4); 1863 assertTrue(displayRotation.updateRotationUnchecked(false)); 1864 1865 // The recents activity should not apply fixed rotation if the top activity is not opaque. 1866 mDisplayContent.mFocusedApp = activity; 1867 doReturn(false).when(mDisplayContent.mFocusedApp).occludesParent(); 1868 doReturn(ROTATION_90).when(mDisplayContent).rotationForActivityInDifferentOrientation( 1869 eq(recentsActivity)); 1870 mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recentsActivity); 1871 assertFalse(recentsActivity.hasFixedRotationTransform()); 1872 } 1873 1874 @Test testSecondaryInternalDisplayRotationFollowsDefaultDisplay()1875 public void testSecondaryInternalDisplayRotationFollowsDefaultDisplay() { 1876 // Skip freezing so the unrelated conditions in updateRotationUnchecked won't disturb. 1877 doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); 1878 1879 final DisplayRotationCoordinator coordinator = 1880 mRootWindowContainer.getDisplayRotationCoordinator(); 1881 final DisplayContent defaultDisplayContent = mDisplayContent; 1882 final DisplayRotation defaultDisplayRotation = defaultDisplayContent.getDisplayRotation(); 1883 coordinator.removeDefaultDisplayRotationChangedCallback(); 1884 1885 DeviceStateController deviceStateController = mock(DeviceStateController.class); 1886 when(deviceStateController.shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay()) 1887 .thenReturn(true); 1888 1889 // Create secondary display 1890 final DisplayContent secondaryDisplayContent = 1891 createSecondaryDisplayContent(Display.TYPE_INTERNAL, deviceStateController); 1892 final DisplayRotation secondaryDisplayRotation = 1893 secondaryDisplayContent.getDisplayRotation(); 1894 try { 1895 // TestDisplayContent bypasses this method but we need it for this test 1896 doCallRealMethod().when(secondaryDisplayRotation).updateRotationUnchecked(anyBoolean()); 1897 1898 // TestDisplayContent creates this as a mock. Lets set it up to test our use case. 1899 when(secondaryDisplayContent.mDeviceStateController 1900 .shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay()).thenReturn( 1901 true); 1902 1903 // Check that secondary display registered callback 1904 assertEquals(secondaryDisplayRotation.mDefaultDisplayRotationChangedCallback, 1905 coordinator.mDefaultDisplayRotationChangedCallback); 1906 1907 // Set the default display to a known orientation. This may be a zero or non-zero 1908 // rotation since mDisplayInfo.logicalWidth/Height depends on the DUT's default display 1909 defaultDisplayRotation.updateOrientation(SCREEN_ORIENTATION_PORTRAIT, false); 1910 assertEquals(defaultDisplayRotation.mPortraitRotation, 1911 defaultDisplayRotation.getRotation()); 1912 assertEquals(defaultDisplayRotation.mPortraitRotation, 1913 coordinator.getDefaultDisplayCurrentRotation()); 1914 1915 // Check that in the initial state, the secondary display is in the right rotation 1916 assertRotationsAreCorrectlyReversed(defaultDisplayRotation.getRotation(), 1917 secondaryDisplayRotation.getRotation()); 1918 1919 // Update primary display rotation, check display coordinator rotation is the default 1920 // display's landscape rotation, and that the secondary display rotation is correct. 1921 defaultDisplayRotation.updateOrientation(SCREEN_ORIENTATION_LANDSCAPE, false); 1922 assertEquals(defaultDisplayRotation.mLandscapeRotation, 1923 defaultDisplayRotation.getRotation()); 1924 assertEquals(defaultDisplayRotation.mLandscapeRotation, 1925 coordinator.getDefaultDisplayCurrentRotation()); 1926 assertRotationsAreCorrectlyReversed(defaultDisplayRotation.getRotation(), 1927 secondaryDisplayRotation.getRotation()); 1928 } finally { 1929 secondaryDisplayRotation.removeDefaultDisplayRotationChangedCallback(); 1930 } 1931 } 1932 1933 @Test testSecondaryNonInternalDisplayDoesNotFollowDefaultDisplay()1934 public void testSecondaryNonInternalDisplayDoesNotFollowDefaultDisplay() { 1935 // Skip freezing so the unrelated conditions in updateRotationUnchecked won't disturb. 1936 doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); 1937 1938 final DisplayRotationCoordinator coordinator = 1939 mRootWindowContainer.getDisplayRotationCoordinator(); 1940 coordinator.removeDefaultDisplayRotationChangedCallback(); 1941 1942 DeviceStateController deviceStateController = mock(DeviceStateController.class); 1943 when(deviceStateController.shouldMatchBuiltInDisplayOrientationToReverseDefaultDisplay()) 1944 .thenReturn(true); 1945 1946 // Create secondary non-internal displays 1947 createSecondaryDisplayContent(Display.TYPE_EXTERNAL, deviceStateController); 1948 assertNull(coordinator.mDefaultDisplayRotationChangedCallback); 1949 createSecondaryDisplayContent(Display.TYPE_VIRTUAL, deviceStateController); 1950 assertNull(coordinator.mDefaultDisplayRotationChangedCallback); 1951 } 1952 createSecondaryDisplayContent(int displayType, @NonNull DeviceStateController deviceStateController)1953 private DisplayContent createSecondaryDisplayContent(int displayType, 1954 @NonNull DeviceStateController deviceStateController) { 1955 final DisplayInfo secondaryDisplayInfo = new DisplayInfo(); 1956 secondaryDisplayInfo.copyFrom(mDisplayInfo); 1957 secondaryDisplayInfo.type = displayType; 1958 1959 return new TestDisplayContent.Builder(mAtm, secondaryDisplayInfo) 1960 .setDeviceStateController(deviceStateController) 1961 .build(); 1962 } 1963 assertRotationsAreCorrectlyReversed(@urface.Rotation int rotation1, @Surface.Rotation int rotation2)1964 private static void assertRotationsAreCorrectlyReversed(@Surface.Rotation int rotation1, 1965 @Surface.Rotation int rotation2) { 1966 if (rotation1 == ROTATION_0) { 1967 assertEquals(rotation1, rotation2); 1968 } else if (rotation1 == ROTATION_180) { 1969 assertEquals(rotation1, rotation2); 1970 } else if (rotation1 == ROTATION_90) { 1971 assertEquals(ROTATION_270, rotation2); 1972 } else if (rotation1 == ROTATION_270) { 1973 assertEquals(ROTATION_90, rotation2); 1974 } else { 1975 throw new IllegalArgumentException("Unknown rotation: " + rotation1 + ", " + rotation2); 1976 } 1977 } 1978 1979 @Test testRemoteRotation()1980 public void testRemoteRotation() { 1981 final DisplayContent dc = mDisplayContent; 1982 final DisplayRotation dr = dc.getDisplayRotation(); 1983 spyOn(dr); 1984 doReturn((dr.getRotation() + 2) % 4).when(dr).rotationForOrientation(anyInt(), anyInt()); 1985 final boolean[] continued = new boolean[1]; 1986 doAnswer(invocation -> { 1987 continued[0] = true; 1988 mDisplayContent.mWaitingForConfig = false; 1989 mAtm.addWindowLayoutReasons(ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED); 1990 return true; 1991 }).when(dc).updateDisplayOverrideConfigurationLocked(); 1992 final boolean[] called = new boolean[1]; 1993 mWm.mDisplayChangeController = 1994 new IDisplayChangeWindowController.Stub() { 1995 @Override 1996 public void onDisplayChange(int displayId, int fromRotation, int toRotation, 1997 DisplayAreaInfo newDisplayAreaInfo, 1998 IDisplayChangeWindowCallback callback) throws RemoteException { 1999 called[0] = true; 2000 2001 try { 2002 callback.continueDisplayChange(null); 2003 } catch (RemoteException e) { 2004 assertTrue(false); 2005 } 2006 } 2007 }; 2008 2009 // kill any existing rotation animation (vestigial from test setup). 2010 dc.setRotationAnimation(null); 2011 2012 mWm.updateRotation(true /* alwaysSendConfiguration */, false /* forceRelayout */); 2013 // If remote rotation is not finished, the display should not be able to unfreeze. 2014 mWm.stopFreezingDisplayLocked(); 2015 assertTrue(mWm.mDisplayFrozen); 2016 2017 assertTrue(called[0]); 2018 waitUntilHandlersIdle(); 2019 assertTrue(continued[0]); 2020 2021 mWm.stopFreezingDisplayLocked(); 2022 assertFalse(mWm.mDisplayFrozen); 2023 } 2024 2025 @Test testRemoteDisplayChange()2026 public void testRemoteDisplayChange() { 2027 mWm.mDisplayChangeController = mock(IDisplayChangeWindowController.class); 2028 final Boolean[] isWaitingForRemote = new Boolean[2]; 2029 final var callbacks = new RemoteDisplayChangeController.ContinueRemoteDisplayChangeCallback[ 2030 isWaitingForRemote.length]; 2031 for (int i = 0; i < isWaitingForRemote.length; i++) { 2032 final int index = i; 2033 var callback = new RemoteDisplayChangeController.ContinueRemoteDisplayChangeCallback() { 2034 @Override 2035 public void onContinueRemoteDisplayChange(WindowContainerTransaction transaction) { 2036 isWaitingForRemote[index] = 2037 mDisplayContent.mRemoteDisplayChangeController 2038 .isWaitingForRemoteDisplayChange(); 2039 } 2040 }; 2041 mDisplayContent.mRemoteDisplayChangeController.performRemoteDisplayChange( 2042 ROTATION_0, ROTATION_0, null /* newDisplayAreaInfo */, callback); 2043 callbacks[i] = callback; 2044 } 2045 2046 // The last callback is completed, all callbacks should be notified. 2047 mDisplayContent.mRemoteDisplayChangeController.continueDisplayChange(callbacks[1], 2048 null /* transaction */); 2049 // When notifying 0, the callback 1 still exists. 2050 assertTrue(isWaitingForRemote[0]); 2051 assertFalse(isWaitingForRemote[1]); 2052 2053 // The first callback is completed, other callbacks after it should remain. 2054 for (int i = 0; i < isWaitingForRemote.length; i++) { 2055 isWaitingForRemote[i] = null; 2056 mDisplayContent.mRemoteDisplayChangeController.performRemoteDisplayChange( 2057 ROTATION_0, ROTATION_0, null /* newDisplayAreaInfo */, callbacks[i]); 2058 } 2059 mDisplayContent.mRemoteDisplayChangeController.continueDisplayChange(callbacks[0], 2060 null /* transaction */); 2061 assertTrue(isWaitingForRemote[0]); 2062 assertNull(isWaitingForRemote[1]); 2063 2064 // Complete the last callback. It should be able to consume pending config change. 2065 mDisplayContent.mWaitingForConfig = true; 2066 mDisplayContent.mRemoteDisplayChangeController.continueDisplayChange(callbacks[1], 2067 null /* transaction */); 2068 assertFalse(isWaitingForRemote[1]); 2069 assertFalse(mDisplayContent.mWaitingForConfig); 2070 } 2071 2072 @Test testShellTransitRotation()2073 public void testShellTransitRotation() { 2074 final DisplayContent dc = mDisplayContent; 2075 // Create 2 visible activities to verify that they can both receive the new configuration. 2076 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setCreateTask(true).build(); 2077 final ActivityRecord activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build(); 2078 doReturn(true).when(activity1).isSyncFinished(any()); 2079 doReturn(true).when(activity2).isSyncFinished(any()); 2080 2081 final TestTransitionPlayer testPlayer = registerTestTransitionPlayer(); 2082 final DisplayRotation dr = dc.getDisplayRotation(); 2083 spyOn(dr); 2084 doReturn((dr.getRotation() + 1) % 4).when(dr).rotationForOrientation(anyInt(), anyInt()); 2085 mWm.mDisplayChangeController = 2086 new IDisplayChangeWindowController.Stub() { 2087 @Override 2088 public void onDisplayChange(int displayId, int fromRotation, int toRotation, 2089 DisplayAreaInfo newDisplayAreaInfo, 2090 IDisplayChangeWindowCallback callback) throws RemoteException { 2091 try { 2092 callback.continueDisplayChange(null); 2093 } catch (RemoteException e) { 2094 assertTrue(false); 2095 } 2096 } 2097 }; 2098 2099 final int origRot = dc.getConfiguration().windowConfiguration.getRotation(); 2100 dc.setLastHasContent(); 2101 mWm.updateRotation(true /* alwaysSendConfiguration */, false /* forceRelayout */); 2102 // Should create a transition request without performing rotation 2103 assertNotNull(testPlayer.mLastRequest); 2104 assertEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation()); 2105 2106 // Once transition starts, rotation is applied and transition shows DC rotating. 2107 testPlayer.startTransition(); 2108 waitUntilHandlersIdle(); 2109 verify(activity1).ensureActivityConfiguration(anyBoolean()); 2110 verify(activity2).ensureActivityConfiguration(anyBoolean()); 2111 assertNotEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation()); 2112 assertNotNull(testPlayer.mLastReady); 2113 assertTrue(testPlayer.mController.isPlaying()); 2114 WindowContainerToken dcToken = dc.mRemoteToken.toWindowContainerToken(); 2115 assertNotEquals(testPlayer.mLastReady.getChange(dcToken).getEndRotation(), 2116 testPlayer.mLastReady.getChange(dcToken).getStartRotation()); 2117 testPlayer.finish(); 2118 2119 // The AsyncRotationController should only exist if there is an ongoing rotation change. 2120 dc.finishAsyncRotationIfPossible(); 2121 dc.setLastHasContent(); 2122 doReturn(dr.getRotation() + 1).when(dr).rotationForOrientation(anyInt(), anyInt()); 2123 dr.updateRotationUnchecked(true /* forceUpdate */); 2124 assertNotNull(dc.getAsyncRotationController()); 2125 doReturn(dr.getRotation() - 1).when(dr).rotationForOrientation(anyInt(), anyInt()); 2126 dr.updateRotationUnchecked(true /* forceUpdate */); 2127 assertNull("Cancel AsyncRotationController for the intermediate rotation changes 0->1->0", 2128 dc.getAsyncRotationController()); 2129 } 2130 2131 @Test testValidWindowingLayer()2132 public void testValidWindowingLayer() { 2133 final SurfaceControl windowingLayer = mDisplayContent.getWindowingLayer(); 2134 assertNotNull(windowingLayer); 2135 2136 final List<DisplayArea<?>> windowedMagnificationAreas = 2137 mDisplayContent.mDisplayAreaPolicy.getDisplayAreas(FEATURE_WINDOWED_MAGNIFICATION); 2138 if (windowedMagnificationAreas != null) { 2139 assertEquals("There should be only one DisplayArea for FEATURE_WINDOWED_MAGNIFICATION", 2140 1, windowedMagnificationAreas.size()); 2141 assertEquals(windowedMagnificationAreas.get(0).mSurfaceControl, windowingLayer); 2142 assertEquals(windowingLayer, 2143 mDisplayContent.mDisplayAreaPolicy.getWindowingArea().mSurfaceControl); 2144 } else { 2145 assertNotEquals(mDisplayContent.mSurfaceControl, windowingLayer); 2146 } 2147 2148 // When migrating the surface of default trusted display, the children should belong to the 2149 // surface of DisplayContent directly. 2150 clearInvocations(mTransaction); 2151 mDisplayContent.migrateToNewSurfaceControl(mTransaction); 2152 for (int i = mDisplayContent.getChildCount() - 1; i >= 0; i--) { 2153 final SurfaceControl childSc = mDisplayContent.getChildAt(i).mSurfaceControl; 2154 verify(mTransaction).reparent(eq(childSc), eq(mDisplayContent.mSurfaceControl)); 2155 verify(mTransaction, never()).reparent(eq(childSc), eq(windowingLayer)); 2156 } 2157 2158 // If a display doesn't have WINDOWED_MAGNIFICATION (e.g. untrusted), it will have an 2159 // additional windowing layer to put the window content. 2160 clearInvocations(mTransaction); 2161 final DisplayInfo info = new DisplayInfo(mDisplayInfo); 2162 info.flags &= ~Display.FLAG_TRUSTED; 2163 final DisplayContent dc2 = createNewDisplay(info); 2164 final SurfaceControl dc2WinLayer = dc2.getWindowingLayer(); 2165 final DisplayArea<?> dc2WinArea = dc2.mDisplayAreaPolicy.getWindowingArea(); 2166 assertEquals(dc2WinLayer, dc2WinArea.mSurfaceControl); 2167 2168 // When migrating the surface of a display with additional windowing layer, the children 2169 // layer of display should still belong to the display. 2170 clearInvocations(mTransaction); 2171 dc2.migrateToNewSurfaceControl(mTransaction); 2172 verify(mTransaction).reparent(eq(dc2WinLayer), eq(dc2.mSurfaceControl)); 2173 for (int i = dc2.getChildCount() - 1; i >= 0; i--) { 2174 verify(mTransaction).reparent(eq(dc2.getChildAt(i).mSurfaceControl), 2175 eq(dc2.mSurfaceControl)); 2176 } 2177 2178 // When migrating the surface of child area under windowing area, the new child surfaces 2179 // should reparent to the windowing layer. 2180 clearInvocations(mTransaction); 2181 for (int i = dc2WinArea.getChildCount() - 1; i >= 0; i--) { 2182 final WindowContainer<?> child = dc2WinArea.getChildAt(i); 2183 child.migrateToNewSurfaceControl(mTransaction); 2184 verify(mTransaction).reparent(eq(child.mSurfaceControl), eq(dc2WinLayer)); 2185 } 2186 } 2187 2188 @Test testFindScrollCaptureTargetWindow_behindWindow()2189 public void testFindScrollCaptureTargetWindow_behindWindow() { 2190 DisplayContent display = createNewDisplay(); 2191 Task rootTask = createTask(display); 2192 Task task = createTaskInRootTask(rootTask, 0 /* userId */); 2193 WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window"); 2194 WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot"); 2195 2196 WindowState result = display.findScrollCaptureTargetWindow(behindWindow, 2197 ActivityTaskManager.INVALID_TASK_ID); 2198 assertEquals(activityWindow, result); 2199 } 2200 2201 @Test testFindScrollCaptureTargetWindow_cantReceiveKeys()2202 public void testFindScrollCaptureTargetWindow_cantReceiveKeys() { 2203 DisplayContent display = createNewDisplay(); 2204 Task rootTask = createTask(display); 2205 Task task = createTaskInRootTask(rootTask, 0 /* userId */); 2206 WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window"); 2207 WindowState invisible = createWindow(null, TYPE_APPLICATION, "invisible"); 2208 invisible.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false 2209 2210 WindowState result = display.findScrollCaptureTargetWindow(null, 2211 ActivityTaskManager.INVALID_TASK_ID); 2212 assertEquals(activityWindow, result); 2213 } 2214 2215 @Test testFindScrollCaptureTargetWindow_secure()2216 public void testFindScrollCaptureTargetWindow_secure() { 2217 DisplayContent display = createNewDisplay(); 2218 Task rootTask = createTask(display); 2219 Task task = createTaskInRootTask(rootTask, 0 /* userId */); 2220 WindowState secureWindow = createWindow(null, TYPE_APPLICATION, "Secure Window"); 2221 secureWindow.mAttrs.flags |= FLAG_SECURE; 2222 2223 WindowState result = display.findScrollCaptureTargetWindow(null, 2224 ActivityTaskManager.INVALID_TASK_ID); 2225 assertNull(result); 2226 } 2227 2228 @Test testFindScrollCaptureTargetWindow_secureTaskId()2229 public void testFindScrollCaptureTargetWindow_secureTaskId() { 2230 DisplayContent display = createNewDisplay(); 2231 Task rootTask = createTask(display); 2232 Task task = createTaskInRootTask(rootTask, 0 /* userId */); 2233 WindowState secureWindow = createWindow(null, TYPE_APPLICATION, "Secure Window"); 2234 secureWindow.mAttrs.flags |= FLAG_SECURE; 2235 2236 WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId); 2237 assertNull(result); 2238 } 2239 2240 @Test testFindScrollCaptureTargetWindow_taskId()2241 public void testFindScrollCaptureTargetWindow_taskId() { 2242 DisplayContent display = createNewDisplay(); 2243 Task rootTask = createTask(display); 2244 Task task = createTaskInRootTask(rootTask, 0 /* userId */); 2245 WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window"); 2246 WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot"); 2247 2248 WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId); 2249 assertEquals(window, result); 2250 } 2251 2252 @Test testFindScrollCaptureTargetWindow_taskIdCantReceiveKeys()2253 public void testFindScrollCaptureTargetWindow_taskIdCantReceiveKeys() { 2254 DisplayContent display = createNewDisplay(); 2255 Task rootTask = createTask(display); 2256 Task task = createTaskInRootTask(rootTask, 0 /* userId */); 2257 WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window"); 2258 window.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false 2259 WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot"); 2260 2261 WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId); 2262 assertEquals(window, result); 2263 } 2264 2265 @Test testEnsureActivitiesVisibleNotRecursive()2266 public void testEnsureActivitiesVisibleNotRecursive() { 2267 final TaskDisplayArea mockTda = mock(TaskDisplayArea.class); 2268 final boolean[] called = { false }; 2269 doAnswer(invocation -> { 2270 // The assertion will fail if DisplayArea#ensureActivitiesVisible is called twice. 2271 assertFalse(called[0]); 2272 called[0] = true; 2273 mDisplayContent.ensureActivitiesVisible(null, false); 2274 return null; 2275 }).when(mockTda).ensureActivitiesVisible(any(), anyBoolean()); 2276 2277 mDisplayContent.ensureActivitiesVisible(null, false); 2278 } 2279 2280 @Test testForceDesktopMode()2281 public void testForceDesktopMode() { 2282 mWm.mForceDesktopModeOnExternalDisplays = true; 2283 // Not applicable for default display 2284 assertFalse(mDefaultDisplay.forceDesktopMode()); 2285 2286 // Not applicable for private secondary display. 2287 final DisplayInfo displayInfo = new DisplayInfo(); 2288 displayInfo.copyFrom(mDisplayInfo); 2289 displayInfo.flags = FLAG_PRIVATE; 2290 final DisplayContent privateDc = createNewDisplay(displayInfo); 2291 assertFalse(privateDc.forceDesktopMode()); 2292 2293 // Applicable for public secondary display. 2294 final DisplayContent publicDc = createNewDisplay(); 2295 assertTrue(publicDc.forceDesktopMode()); 2296 2297 // Make sure forceDesktopMode() is false when the force config is disabled. 2298 mWm.mForceDesktopModeOnExternalDisplays = false; 2299 assertFalse(publicDc.forceDesktopMode()); 2300 } 2301 2302 @Test testDisplaySettingsReappliedWhenDisplayChanged()2303 public void testDisplaySettingsReappliedWhenDisplayChanged() { 2304 final DisplayInfo displayInfo = new DisplayInfo(); 2305 displayInfo.copyFrom(mDisplayInfo); 2306 final DisplayContent dc = createNewDisplay(displayInfo); 2307 2308 // Generate width/height/density values different from the default of the display. 2309 final int forcedWidth = dc.mBaseDisplayWidth + 1; 2310 final int forcedHeight = dc.mBaseDisplayHeight + 1;; 2311 final int forcedDensity = dc.mBaseDisplayDensity + 1;; 2312 // Update the forced size and density in settings and the unique id to simualate a display 2313 // remap. 2314 dc.mWmService.mDisplayWindowSettings.setForcedSize(dc, forcedWidth, forcedHeight); 2315 dc.mWmService.mDisplayWindowSettings.setForcedDensity(displayInfo, forcedDensity, 2316 0 /* userId */); 2317 dc.mCurrentUniqueDisplayId = mDisplayInfo.uniqueId + "-test"; 2318 // Trigger display changed. 2319 updateDisplay(dc); 2320 // Ensure overridden size and denisty match the most up-to-date values in settings for the 2321 // display. 2322 verifySizes(dc, forcedWidth, forcedHeight, forcedDensity); 2323 } 2324 2325 @SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD }) 2326 @Test testComputeImeTarget_shouldNotCheckOutdatedImeTargetLayerWhenRemoved()2327 public void testComputeImeTarget_shouldNotCheckOutdatedImeTargetLayerWhenRemoved() { 2328 final WindowState child1 = createWindow(mAppWindow, FIRST_SUB_WINDOW, "child1"); 2329 final WindowState nextImeTargetApp = createWindow(null /* parent */, 2330 TYPE_BASE_APPLICATION, "nextImeTargetApp"); 2331 spyOn(child1); 2332 doReturn(false).when(mDisplayContent).shouldImeAttachedToApp(); 2333 mDisplayContent.setImeLayeringTarget(child1); 2334 2335 spyOn(nextImeTargetApp); 2336 spyOn(mAppWindow); 2337 doReturn(true).when(nextImeTargetApp).canBeImeTarget(); 2338 doReturn(true).when(nextImeTargetApp).isActivityTypeHome(); 2339 doReturn(false).when(mAppWindow).canBeImeTarget(); 2340 2341 child1.removeImmediately(); 2342 2343 verify(mDisplayContent).computeImeTarget(true); 2344 assertNull(mDisplayContent.getImeInputTarget()); 2345 verify(child1, never()).needsRelativeLayeringToIme(); 2346 } 2347 2348 @SetupWindows(addWindows = W_INPUT_METHOD) 2349 @Test testAttachAndShowImeScreenshotOnTarget()2350 public void testAttachAndShowImeScreenshotOnTarget() { 2351 // Preparation: Simulate screen state is on. 2352 spyOn(mWm.mPolicy); 2353 doReturn(true).when(mWm.mPolicy).isScreenOn(); 2354 2355 // Preparation: Simulate snapshot IME surface. 2356 spyOn(mWm.mTaskSnapshotController); 2357 ScreenCapture.ScreenshotHardwareBuffer mockHwBuffer = mock( 2358 ScreenCapture.ScreenshotHardwareBuffer.class); 2359 doReturn(mock(HardwareBuffer.class)).when(mockHwBuffer).getHardwareBuffer(); 2360 doReturn(mockHwBuffer).when(mWm.mTaskSnapshotController).snapshotImeFromAttachedTask(any()); 2361 2362 // Preparation: Simulate snapshot Task. 2363 ActivityRecord act1 = createActivityRecord(mDisplayContent); 2364 final WindowState appWin1 = createWindow(null, TYPE_BASE_APPLICATION, act1, "appWin1"); 2365 spyOn(appWin1); 2366 spyOn(appWin1.mWinAnimator); 2367 appWin1.setHasSurface(true); 2368 assertTrue(appWin1.canBeImeTarget()); 2369 doReturn(true).when(appWin1.mWinAnimator).getShown(); 2370 doReturn(true).when(appWin1.mActivityRecord).isSurfaceShowing(); 2371 appWin1.mWinAnimator.mLastAlpha = 1f; 2372 2373 // Test step 1: appWin1 is the current IME target and soft-keyboard is visible. 2374 mDisplayContent.computeImeTarget(true); 2375 assertEquals(appWin1, mDisplayContent.getImeTarget(IME_TARGET_LAYERING)); 2376 mDisplayContent.setImeInputTarget(appWin1); 2377 spyOn(mDisplayContent.mInputMethodWindow); 2378 doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible(); 2379 mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true); 2380 2381 // Test step 2: Simulate launching appWin2 and appWin1 is in app transition. 2382 ActivityRecord act2 = createActivityRecord(mDisplayContent); 2383 final WindowState appWin2 = createWindow(null, TYPE_BASE_APPLICATION, act2, "appWin2"); 2384 appWin2.setHasSurface(true); 2385 assertTrue(appWin2.canBeImeTarget()); 2386 doReturn(true).when(appWin1).inTransitionSelfOrParent(); 2387 2388 // Test step 3: Verify appWin2 will be the next IME target and the IME snapshot surface will 2389 // be attached and shown on the display at this time. 2390 mDisplayContent.computeImeTarget(true); 2391 assertEquals(appWin2, mDisplayContent.getImeTarget(IME_TARGET_LAYERING)); 2392 assertTrue(mDisplayContent.shouldImeAttachedToApp()); 2393 2394 verify(mDisplayContent, atLeast(1)).showImeScreenshot(); 2395 verify(mWm.mTaskSnapshotController).snapshotImeFromAttachedTask(appWin1.getTask()); 2396 assertNotNull(mDisplayContent.mImeScreenshot); 2397 } 2398 2399 @SetupWindows(addWindows = W_INPUT_METHOD) 2400 @Test testShowImeScreenshot()2401 public void testShowImeScreenshot() { 2402 final Task rootTask = createTask(mDisplayContent); 2403 final Task task = createTaskInRootTask(rootTask, 0 /* userId */); 2404 final ActivityRecord activity = createActivityRecord(mDisplayContent, task); 2405 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); 2406 task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE); 2407 doReturn(true).when(task).okToAnimate(); 2408 ArrayList<WindowContainer> sources = new ArrayList<>(); 2409 sources.add(activity); 2410 2411 mDisplayContent.setImeLayeringTarget(win); 2412 spyOn(mDisplayContent); 2413 2414 // Expecting the IME screenshot only be attached when performing task closing transition. 2415 task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */, 2416 false /* isVoiceInteraction */, sources); 2417 verify(mDisplayContent).showImeScreenshot(); 2418 2419 clearInvocations(mDisplayContent); 2420 activity.applyAnimation(null, TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE, false /* enter */, 2421 false /* isVoiceInteraction */, sources); 2422 verify(mDisplayContent, never()).showImeScreenshot(); 2423 } 2424 2425 @SetupWindows(addWindows = W_INPUT_METHOD) 2426 @Test testShowImeScreenshot_removeCurSnapshotBeforeCreateNext()2427 public void testShowImeScreenshot_removeCurSnapshotBeforeCreateNext() { 2428 final Task rootTask = createTask(mDisplayContent); 2429 final Task task = createTaskInRootTask(rootTask, 0 /* userId */); 2430 final ActivityRecord activity = createActivityRecord(mDisplayContent, task); 2431 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); 2432 2433 mDisplayContent.setImeLayeringTarget(win); 2434 mDisplayContent.setImeInputTarget(win); 2435 spyOn(mDisplayContent); 2436 spyOn(mDisplayContent.mInputMethodWindow); 2437 doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible(); 2438 mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true); 2439 2440 // Verify when the timing of 2 showImeScreenshot invocations are very close, will first 2441 // detach the current snapshot then create the next one. 2442 mDisplayContent.showImeScreenshot(); 2443 DisplayContent.ImeScreenshot curSnapshot = mDisplayContent.mImeScreenshot; 2444 spyOn(curSnapshot); 2445 mDisplayContent.showImeScreenshot(); 2446 verify(curSnapshot).detach(any()); 2447 assertNotNull(mDisplayContent.mImeScreenshot); 2448 assertNotEquals(curSnapshot, mDisplayContent.mImeScreenshot); 2449 } 2450 2451 @UseTestDisplay(addWindows = {W_INPUT_METHOD}) 2452 @Test testRemoveImeScreenshot_whenTargetSurfaceWasInvisible()2453 public void testRemoveImeScreenshot_whenTargetSurfaceWasInvisible() { 2454 final Task rootTask = createTask(mDisplayContent); 2455 final Task task = createTaskInRootTask(rootTask, 0 /* userId */); 2456 final ActivityRecord activity = createActivityRecord(mDisplayContent, task); 2457 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); 2458 win.onSurfaceShownChanged(true); 2459 makeWindowVisible(win, mDisplayContent.mInputMethodWindow); 2460 task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE); 2461 doReturn(true).when(task).okToAnimate(); 2462 ArrayList<WindowContainer> sources = new ArrayList<>(); 2463 sources.add(activity); 2464 2465 mDisplayContent.setImeLayeringTarget(win); 2466 mDisplayContent.setImeInputTarget(win); 2467 mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true); 2468 task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */, 2469 false /* isVoiceInteraction */, sources); 2470 assertNotNull(mDisplayContent.mImeScreenshot); 2471 2472 win.onSurfaceShownChanged(false); 2473 assertNull(mDisplayContent.mImeScreenshot); 2474 } 2475 2476 @UseTestDisplay(addWindows = {W_INPUT_METHOD}) 2477 @Test testRemoveImeScreenshot_whenWindowRemoveImmediately()2478 public void testRemoveImeScreenshot_whenWindowRemoveImmediately() { 2479 final Task rootTask = createTask(mDisplayContent); 2480 final Task task = createTaskInRootTask(rootTask, 0 /* userId */); 2481 final ActivityRecord activity = createActivityRecord(mDisplayContent, task); 2482 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win"); 2483 makeWindowVisible(mDisplayContent.mInputMethodWindow); 2484 2485 mDisplayContent.setImeLayeringTarget(win); 2486 mDisplayContent.setImeInputTarget(win); 2487 mDisplayContent.getInsetsStateController().getImeSourceProvider().setImeShowing(true); 2488 mDisplayContent.showImeScreenshot(); 2489 assertNotNull(mDisplayContent.mImeScreenshot); 2490 2491 // Expect IME snapshot will be removed when the win is IME layering target and invoked 2492 // removeImeSurfaceByTarget. 2493 win.removeImmediately(); 2494 assertNull(mDisplayContent.mImeScreenshot); 2495 } 2496 2497 @Test testRotateBounds_keepSamePhysicalPosition()2498 public void testRotateBounds_keepSamePhysicalPosition() { 2499 final DisplayContent dc = 2500 new TestDisplayContent.Builder(mAtm, 1000, 2000).build(); 2501 final Rect initBounds = new Rect(0, 0, 700, 1500); 2502 final Rect rotateBounds = new Rect(initBounds); 2503 2504 // Rotate from 0 to 0 2505 dc.rotateBounds(ROTATION_0, ROTATION_0, rotateBounds); 2506 2507 assertEquals(new Rect(0, 0, 700, 1500), rotateBounds); 2508 2509 // Rotate from 0 to 90 2510 rotateBounds.set(initBounds); 2511 dc.rotateBounds(ROTATION_0, ROTATION_90, rotateBounds); 2512 2513 assertEquals(new Rect(0, 300, 1500, 1000), rotateBounds); 2514 2515 // Rotate from 0 to 180 2516 rotateBounds.set(initBounds); 2517 dc.rotateBounds(ROTATION_0, ROTATION_180, rotateBounds); 2518 2519 assertEquals(new Rect(300, 500, 1000, 2000), rotateBounds); 2520 2521 // Rotate from 0 to 270 2522 rotateBounds.set(initBounds); 2523 dc.rotateBounds(ROTATION_0, ROTATION_270, rotateBounds); 2524 2525 assertEquals(new Rect(500, 0, 2000, 700), rotateBounds); 2526 } 2527 2528 /** 2529 * Creates a TestDisplayContent using the constructor that takes in display width and height as 2530 * parameters and validates that the newly-created TestDisplayContent's DisplayInfo and 2531 * WindowConfiguration match the parameters passed into the constructor. Additionally, this test 2532 * checks that device-specific overrides are not applied. 2533 */ 2534 @Test testCreateTestDisplayContentFromDimensions()2535 public void testCreateTestDisplayContentFromDimensions() { 2536 final int displayWidth = 540; 2537 final int displayHeight = 960; 2538 final int density = 192; 2539 final int expectedWidthDp = 450; // = 540/(192/160) 2540 final int expectedHeightDp = 800; // = 960/(192/160) 2541 final int windowingMode = WINDOWING_MODE_FULLSCREEN; 2542 final boolean ignoreOrientationRequests = false; 2543 final float fixedOrientationLetterboxRatio = 0; 2544 final DisplayContent testDisplayContent = new TestDisplayContent.Builder(mAtm, displayWidth, 2545 displayHeight).setDensityDpi(density).build(); 2546 2547 // test display info 2548 final DisplayInfo di = testDisplayContent.getDisplayInfo(); 2549 assertEquals(displayWidth, di.logicalWidth); 2550 assertEquals(displayHeight, di.logicalHeight); 2551 assertEquals(density, di.logicalDensityDpi); 2552 2553 // test configuration 2554 final Configuration config = testDisplayContent.getConfiguration(); 2555 assertEquals(expectedWidthDp, config.screenWidthDp); 2556 assertEquals(expectedHeightDp, config.screenHeightDp); 2557 final WindowConfiguration windowConfig = config.windowConfiguration; 2558 assertEquals(displayWidth, windowConfig.getBounds().width()); 2559 assertEquals(displayHeight, windowConfig.getBounds().height()); 2560 assertEquals(windowingMode, windowConfig.getWindowingMode()); 2561 assertEquals(Configuration.SCREENLAYOUT_SIZE_NORMAL, 2562 config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK); 2563 2564 // test misc display overrides 2565 assertEquals(ignoreOrientationRequests, testDisplayContent.mSetIgnoreOrientationRequest); 2566 assertEquals(fixedOrientationLetterboxRatio, 2567 mWm.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(), 2568 0 /* delta */); 2569 } 2570 2571 /** 2572 * Creates a TestDisplayContent using the constructor that takes in a DisplayInfo as a parameter 2573 * and validates that the newly-created TestDisplayContent's DisplayInfo and WindowConfiguration 2574 * match the width, height, and density values set in the DisplayInfo passed as a parameter. 2575 * Additionally, this test checks that device-specific overrides are not applied. 2576 */ 2577 @Test testCreateTestDisplayContentFromDisplayInfo()2578 public void testCreateTestDisplayContentFromDisplayInfo() { 2579 final int displayWidth = 1000; 2580 final int displayHeight = 2000; 2581 final int windowingMode = WINDOWING_MODE_FULLSCREEN; 2582 final boolean ignoreOrientationRequests = false; 2583 final float fixedOrientationLetterboxRatio = 0; 2584 final DisplayInfo testDisplayInfo = new DisplayInfo(); 2585 mContext.getDisplay().getDisplayInfo(testDisplayInfo); 2586 testDisplayInfo.logicalWidth = displayWidth; 2587 testDisplayInfo.logicalHeight = displayHeight; 2588 testDisplayInfo.logicalDensityDpi = TestDisplayContent.DEFAULT_LOGICAL_DISPLAY_DENSITY; 2589 final DisplayContent testDisplayContent = new TestDisplayContent.Builder(mAtm, 2590 testDisplayInfo).build(); 2591 2592 // test display info 2593 final DisplayInfo di = testDisplayContent.getDisplayInfo(); 2594 assertEquals(displayWidth, di.logicalWidth); 2595 assertEquals(displayHeight, di.logicalHeight); 2596 assertEquals(TestDisplayContent.DEFAULT_LOGICAL_DISPLAY_DENSITY, di.logicalDensityDpi); 2597 2598 // test configuration 2599 final WindowConfiguration windowConfig = testDisplayContent.getConfiguration() 2600 .windowConfiguration; 2601 assertEquals(displayWidth, windowConfig.getBounds().width()); 2602 assertEquals(displayHeight, windowConfig.getBounds().height()); 2603 assertEquals(windowingMode, windowConfig.getWindowingMode()); 2604 assertEquals(Configuration.SCREENLAYOUT_SIZE_LARGE, testDisplayContent 2605 .getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK); 2606 2607 // test misc display overrides 2608 assertEquals(ignoreOrientationRequests, testDisplayContent.mSetIgnoreOrientationRequest); 2609 assertEquals(fixedOrientationLetterboxRatio, 2610 mWm.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(), 2611 0 /* delta */); 2612 } 2613 2614 /** 2615 * Verifies {@link DisplayContent#remove} should not resume home root task on the removing 2616 * display. 2617 */ 2618 @Test testNotResumeHomeRootTaskOnRemovingDisplay()2619 public void testNotResumeHomeRootTaskOnRemovingDisplay() { 2620 // Create a display which supports system decoration and allows reparenting root tasks to 2621 // another display when the display is removed. 2622 final DisplayContent display = new TestDisplayContent.Builder( 2623 mAtm, 1000, 1500).setSystemDecorations(true).build(); 2624 doReturn(false).when(display).shouldDestroyContentOnRemove(); 2625 2626 // Put home root task on the display. 2627 final Task homeRootTask = new TaskBuilder(mSupervisor) 2628 .setDisplay(display).setActivityType(ACTIVITY_TYPE_HOME).build(); 2629 2630 // Put a finishing standard activity which will be reparented. 2631 final Task rootTask = createTaskWithActivity(display.getDefaultTaskDisplayArea(), 2632 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP, true /* twoLevelTask */); 2633 rootTask.topRunningActivity().makeFinishingLocked(); 2634 2635 clearInvocations(homeRootTask); 2636 display.remove(); 2637 2638 // The removed display should have no focused root task and its home root task should never 2639 // resume. 2640 assertNull(display.getFocusedRootTask()); 2641 verify(homeRootTask, never()).resumeTopActivityUncheckedLocked(any(), any()); 2642 } 2643 2644 /** 2645 * Verifies the correct activity is returned when querying the top running activity. 2646 */ 2647 @Test testTopRunningActivity()2648 public void testTopRunningActivity() { 2649 final DisplayContent display = mRootWindowContainer.getDefaultDisplay(); 2650 final KeyguardController keyguard = mSupervisor.getKeyguardController(); 2651 final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); 2652 final ActivityRecord activity = rootTask.getTopNonFinishingActivity(); 2653 2654 // Create empty root task on top. 2655 final Task emptyRootTask = new TaskBuilder(mSupervisor).build(); 2656 2657 // Make sure the top running activity is not affected when keyguard is not locked. 2658 assertTopRunningActivity(activity, display); 2659 2660 // Check to make sure activity not reported when it cannot show on lock and lock is on. 2661 doReturn(true).when(keyguard).isKeyguardLocked(anyInt()); 2662 assertEquals(activity, display.topRunningActivity()); 2663 assertNull(display.topRunningActivity(true /* considerKeyguardState */)); 2664 2665 // Move root task with activity to top. 2666 rootTask.moveToFront("testRootTaskToFront"); 2667 assertEquals(rootTask, display.getFocusedRootTask()); 2668 assertEquals(activity, display.topRunningActivity()); 2669 assertNull(display.topRunningActivity(true /* considerKeyguardState */)); 2670 2671 // Add activity that should be shown on the keyguard. 2672 final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mAtm) 2673 .setTask(rootTask) 2674 .setActivityFlags(FLAG_SHOW_WHEN_LOCKED) 2675 .build(); 2676 2677 // Ensure the show when locked activity is returned. 2678 assertTopRunningActivity(showWhenLockedActivity, display); 2679 2680 // Move empty root task to front. The running activity in focusable root task which below 2681 // the empty root task should be returned. 2682 emptyRootTask.moveToFront("emptyRootTaskToFront"); 2683 assertEquals(rootTask, display.getFocusedRootTask()); 2684 assertTopRunningActivity(showWhenLockedActivity, display); 2685 } 2686 assertTopRunningActivity(ActivityRecord top, DisplayContent display)2687 private static void assertTopRunningActivity(ActivityRecord top, DisplayContent display) { 2688 assertEquals(top, display.topRunningActivity()); 2689 assertEquals(top, display.topRunningActivity(true /* considerKeyguardState */)); 2690 } 2691 2692 @Test testKeyguardGoingAwayWhileAodShown()2693 public void testKeyguardGoingAwayWhileAodShown() { 2694 mDisplayContent.getDisplayPolicy().setAwake(true); 2695 2696 final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin"); 2697 final ActivityRecord activity = appWin.mActivityRecord; 2698 2699 mAtm.mKeyguardController.setKeyguardShown(appWin.getDisplayId(), true /* keyguardShowing */, 2700 true /* aodShowing */); 2701 assertFalse(activity.isVisibleRequested()); 2702 2703 mAtm.mKeyguardController.keyguardGoingAway(appWin.getDisplayId(), 0 /* flags */); 2704 assertTrue(activity.isVisibleRequested()); 2705 } 2706 2707 @Test testRemoveRootTaskInWindowingModes()2708 public void testRemoveRootTaskInWindowingModes() { 2709 removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksInWindowingModes( 2710 WINDOWING_MODE_FULLSCREEN)); 2711 } 2712 2713 @Test testRemoveRootTaskWithActivityTypes()2714 public void testRemoveRootTaskWithActivityTypes() { 2715 removeRootTaskTests(() -> mRootWindowContainer.removeRootTasksWithActivityTypes( 2716 ACTIVITY_TYPE_STANDARD)); 2717 } 2718 2719 @SetupWindows(addWindows = W_INPUT_METHOD) 2720 @Test testImeChildWindowFocusWhenImeLayeringTargetChanges()2721 public void testImeChildWindowFocusWhenImeLayeringTargetChanges() { 2722 final WindowState imeChildWindow = 2723 createWindow(mImeWindow, TYPE_APPLICATION_ATTACHED_DIALOG, "imeChildWindow"); 2724 makeWindowVisibleAndDrawn(imeChildWindow, mImeWindow); 2725 assertTrue(imeChildWindow.canReceiveKeys()); 2726 mDisplayContent.setInputMethodWindowLocked(mImeWindow); 2727 2728 // Verify imeChildWindow can be focused window if the next IME target requests IME visible. 2729 final WindowState imeAppTarget = 2730 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); 2731 mDisplayContent.setImeLayeringTarget(imeAppTarget); 2732 spyOn(imeAppTarget); 2733 doReturn(true).when(imeAppTarget).isRequestedVisible(ime()); 2734 assertEquals(imeChildWindow, mDisplayContent.findFocusedWindow()); 2735 2736 // Verify imeChildWindow doesn't be focused window if the next IME target does not 2737 // request IME visible. 2738 final WindowState nextImeAppTarget = 2739 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget"); 2740 mDisplayContent.setImeLayeringTarget(nextImeAppTarget); 2741 assertNotEquals(imeChildWindow, mDisplayContent.findFocusedWindow()); 2742 } 2743 2744 @SetupWindows(addWindows = W_INPUT_METHOD) 2745 @Test testImeMenuDialogFocusWhenImeLayeringTargetChanges()2746 public void testImeMenuDialogFocusWhenImeLayeringTargetChanges() { 2747 final WindowState imeMenuDialog = 2748 createWindow(null, TYPE_INPUT_METHOD_DIALOG, "imeMenuDialog"); 2749 makeWindowVisibleAndDrawn(imeMenuDialog, mImeWindow); 2750 assertTrue(imeMenuDialog.canReceiveKeys()); 2751 mDisplayContent.setInputMethodWindowLocked(mImeWindow); 2752 2753 // Verify imeMenuDialog can be focused window if the next IME target requests IME visible. 2754 final WindowState imeAppTarget = 2755 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "imeAppTarget"); 2756 mDisplayContent.setImeLayeringTarget(imeAppTarget); 2757 spyOn(imeAppTarget); 2758 doReturn(true).when(imeAppTarget).isRequestedVisible(ime()); 2759 assertEquals(imeMenuDialog, mDisplayContent.findFocusedWindow()); 2760 2761 // Verify imeMenuDialog doesn't be focused window if the next IME target is closing. 2762 final WindowState nextImeAppTarget = 2763 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "nextImeAppTarget"); 2764 makeWindowVisibleAndDrawn(nextImeAppTarget); 2765 nextImeAppTarget.mActivityRecord.commitVisibility(false, false); 2766 mDisplayContent.setImeLayeringTarget(nextImeAppTarget); 2767 assertNotEquals(imeMenuDialog, mDisplayContent.findFocusedWindow()); 2768 } 2769 2770 @Test testKeepClearAreasMultipleWindows()2771 public void testKeepClearAreasMultipleWindows() { 2772 final WindowState w1 = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent, "w1"); 2773 final Rect rect1 = new Rect(0, 0, 10, 10); 2774 w1.setKeepClearAreas(Arrays.asList(rect1), Collections.emptyList()); 2775 final WindowState w2 = createWindow(null, TYPE_NOTIFICATION_SHADE, mDisplayContent, "w2"); 2776 final Rect rect2 = new Rect(10, 10, 20, 20); 2777 w2.setKeepClearAreas(Arrays.asList(rect2), Collections.emptyList()); 2778 2779 // No keep clear areas on display, because the windows are not visible 2780 assertEquals(Collections.emptySet(), mDisplayContent.getKeepClearAreas()); 2781 2782 makeWindowVisible(w1); 2783 2784 // The returned keep-clear areas contain the areas just from the visible window 2785 assertEquals(new ArraySet(Arrays.asList(rect1)), mDisplayContent.getKeepClearAreas()); 2786 2787 makeWindowVisible(w1, w2); 2788 2789 // The returned keep-clear areas contain the areas from all visible windows 2790 assertEquals(new ArraySet(Arrays.asList(rect1, rect2)), 2791 mDisplayContent.getKeepClearAreas()); 2792 } 2793 2794 @Test testHasAccessConsidersUserVisibilityForBackgroundVisibleUsers()2795 public void testHasAccessConsidersUserVisibilityForBackgroundVisibleUsers() { 2796 doReturn(true).when(() -> UserManager.isVisibleBackgroundUsersEnabled()); 2797 final int appId = 1234; 2798 final int userId1 = 11; 2799 final int userId2 = 12; 2800 final int uid1 = UserHandle.getUid(userId1, appId); 2801 final int uid2 = UserHandle.getUid(userId2, appId); 2802 final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo); 2803 final DisplayContent dc = createNewDisplay(displayInfo); 2804 int displayId = dc.getDisplayId(); 2805 doReturn(true).when(mWm.mUmInternal).isUserVisible(userId1, displayId); 2806 doReturn(false).when(mWm.mUmInternal).isUserVisible(userId2, displayId); 2807 2808 assertTrue(dc.hasAccess(uid1)); 2809 assertFalse(dc.hasAccess(uid2)); 2810 } 2811 2812 @Test testHasAccessIgnoresUserVisibilityForPrivateDisplay()2813 public void testHasAccessIgnoresUserVisibilityForPrivateDisplay() { 2814 doReturn(true).when(() -> UserManager.isVisibleBackgroundUsersEnabled()); 2815 final int appId = 1234; 2816 final int userId2 = 12; 2817 final int uid2 = UserHandle.getUid(userId2, appId); 2818 final DisplayInfo displayInfo = new DisplayInfo(mDisplayInfo); 2819 displayInfo.flags = FLAG_PRIVATE; 2820 displayInfo.ownerUid = uid2; 2821 final DisplayContent dc = createNewDisplay(displayInfo); 2822 int displayId = dc.getDisplayId(); 2823 2824 assertTrue(dc.hasAccess(uid2)); 2825 2826 verify(mWm.mUmInternal, never()).isUserVisible(userId2, displayId); 2827 } 2828 2829 @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM) 2830 @Test cameraCompatFreeformFlagEnabled_cameraCompatFreeformPolicyNotNull()2831 public void cameraCompatFreeformFlagEnabled_cameraCompatFreeformPolicyNotNull() { 2832 doReturn(true).when(() -> 2833 DesktopModeLaunchParamsModifier.canEnterDesktopMode(any())); 2834 2835 assertNotNull(createNewDisplay().mCameraCompatFreeformPolicy); 2836 } 2837 2838 @DisableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM) 2839 @Test cameraCompatFreeformFlagNotEnabled_cameraCompatFreeformPolicyIsNull()2840 public void cameraCompatFreeformFlagNotEnabled_cameraCompatFreeformPolicyIsNull() { 2841 doReturn(true).when(() -> 2842 DesktopModeLaunchParamsModifier.canEnterDesktopMode(any())); 2843 2844 assertNull(createNewDisplay().mCameraCompatFreeformPolicy); 2845 } 2846 2847 @EnableFlags(FLAG_CAMERA_COMPAT_FOR_FREEFORM) 2848 @DisableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) 2849 @Test desktopWindowingFlagNotEnabled_cameraCompatFreeformPolicyIsNull()2850 public void desktopWindowingFlagNotEnabled_cameraCompatFreeformPolicyIsNull() { 2851 assertNull(createNewDisplay().mCameraCompatFreeformPolicy); 2852 } 2853 removeRootTaskTests(Runnable runnable)2854 private void removeRootTaskTests(Runnable runnable) { 2855 final TaskDisplayArea taskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea(); 2856 final Task rootTask1 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, 2857 ACTIVITY_TYPE_STANDARD, ON_TOP); 2858 final Task rootTask2 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, 2859 ACTIVITY_TYPE_STANDARD, ON_TOP); 2860 final Task rootTask3 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, 2861 ACTIVITY_TYPE_STANDARD, ON_TOP); 2862 final Task rootTask4 = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, 2863 ACTIVITY_TYPE_STANDARD, ON_TOP); 2864 final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask1).build(); 2865 final Task task2 = new TaskBuilder(mSupervisor).setParentTask(rootTask2).build(); 2866 final Task task3 = new TaskBuilder(mSupervisor).setParentTask(rootTask3).build(); 2867 final Task task4 = new TaskBuilder(mSupervisor).setParentTask(rootTask4).build(); 2868 2869 // Reordering root tasks while removing root tasks. 2870 doAnswer(invocation -> { 2871 taskDisplayArea.positionChildAt(POSITION_TOP, rootTask3, false /*includingParents*/); 2872 return true; 2873 }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any()); 2874 2875 // Removing root tasks from the display while removing root tasks. 2876 doAnswer(invocation -> { 2877 taskDisplayArea.removeRootTask(rootTask2); 2878 return true; 2879 }).when(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any()); 2880 2881 runnable.run(); 2882 verify(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any()); 2883 verify(mSupervisor).removeTask(eq(task3), anyBoolean(), anyBoolean(), any()); 2884 verify(mSupervisor).removeTask(eq(task2), anyBoolean(), anyBoolean(), any()); 2885 verify(mSupervisor).removeTask(eq(task1), anyBoolean(), anyBoolean(), any()); 2886 } 2887 isOptionsPanelAtRight(int displayId)2888 private boolean isOptionsPanelAtRight(int displayId) { 2889 return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT; 2890 } 2891 verifySizes(DisplayContent displayContent, int expectedBaseWidth, int expectedBaseHeight, int expectedBaseDensity)2892 private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth, 2893 int expectedBaseHeight, int expectedBaseDensity) { 2894 assertEquals(expectedBaseWidth, displayContent.mBaseDisplayWidth); 2895 assertEquals(expectedBaseHeight, displayContent.mBaseDisplayHeight); 2896 assertEquals(expectedBaseDensity, displayContent.mBaseDisplayDensity); 2897 } 2898 verifySizes(DisplayContent displayContent, int expectedBaseWidth, int expectedBaseHeight, int expectedBaseDensity, float expectedBaseXDpi, float expectedBaseYDpi)2899 private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth, 2900 int expectedBaseHeight, int expectedBaseDensity, float expectedBaseXDpi, 2901 float expectedBaseYDpi) { 2902 assertEquals(expectedBaseWidth, displayContent.mBaseDisplayWidth); 2903 assertEquals(expectedBaseHeight, displayContent.mBaseDisplayHeight); 2904 assertEquals(expectedBaseDensity, displayContent.mBaseDisplayDensity); 2905 assertEquals(expectedBaseXDpi, displayContent.mBaseDisplayPhysicalXDpi, 1.0f /* delta */); 2906 assertEquals(expectedBaseYDpi, displayContent.mBaseDisplayPhysicalYDpi, 1.0f /* delta */); 2907 } 2908 updateFocusedWindow()2909 private void updateFocusedWindow() { 2910 mWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */); 2911 } 2912 performLayout(DisplayContent dc)2913 static void performLayout(DisplayContent dc) { 2914 dc.setLayoutNeeded(); 2915 dc.performLayout(true /* initial */, false /* updateImeWindows */); 2916 } 2917 2918 /** 2919 * Create DisplayContent that does not update display base/initial values from device to keep 2920 * the values set by test. 2921 */ createDisplayNoUpdateDisplayInfo()2922 private DisplayContent createDisplayNoUpdateDisplayInfo() { 2923 final DisplayContent displayContent = createNewDisplay(); 2924 doNothing().when(displayContent).updateDisplayInfo(any()); 2925 return displayContent; 2926 } 2927 assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop)2928 private void assertForAllWindowsOrder(List<WindowState> expectedWindowsBottomToTop) { 2929 final LinkedList<WindowState> actualWindows = new LinkedList<>(); 2930 2931 // Test forward traversal. 2932 mDisplayContent.forAllWindows(actualWindows::addLast, false /* traverseTopToBottom */); 2933 assertThat("bottomToTop", actualWindows, is(expectedWindowsBottomToTop)); 2934 2935 actualWindows.clear(); 2936 2937 // Test backward traversal. 2938 mDisplayContent.forAllWindows(actualWindows::addLast, true /* traverseTopToBottom */); 2939 assertThat("topToBottom", actualWindows, is(reverseList(expectedWindowsBottomToTop))); 2940 } 2941 getRotatedOrientation(DisplayContent dc)2942 static int getRotatedOrientation(DisplayContent dc) { 2943 return dc.mBaseDisplayWidth > dc.mBaseDisplayHeight 2944 ? SCREEN_ORIENTATION_PORTRAIT 2945 : SCREEN_ORIENTATION_LANDSCAPE; 2946 } 2947 reverseList(List<WindowState> list)2948 private static List<WindowState> reverseList(List<WindowState> list) { 2949 final ArrayList<WindowState> result = new ArrayList<>(list); 2950 Collections.reverse(result); 2951 return result; 2952 } 2953 updateDisplay(DisplayContent displayContent)2954 private void updateDisplay(DisplayContent displayContent) { 2955 CompletableFuture<Object> future = new CompletableFuture<>(); 2956 displayContent.requestDisplayUpdate(() -> future.complete(new Object())); 2957 try { 2958 future.get(15, TimeUnit.SECONDS); 2959 } catch (InterruptedException | ExecutionException | TimeoutException e) { 2960 throw new RuntimeException(e); 2961 } 2962 } 2963 } 2964