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