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