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_STANDARD; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 23 import static android.permission.flags.Flags.FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION; 24 import static android.view.Display.DEFAULT_DISPLAY; 25 import static android.view.InsetsSource.ID_IME; 26 import static android.view.Surface.ROTATION_0; 27 import static android.view.Surface.ROTATION_270; 28 import static android.view.Surface.ROTATION_90; 29 import static android.view.WindowInsets.Type.ime; 30 import static android.view.WindowInsets.Type.navigationBars; 31 import static android.view.WindowInsets.Type.statusBars; 32 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; 33 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 34 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 35 import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; 36 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; 37 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 38 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL; 39 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; 40 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; 41 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; 42 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 43 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; 44 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; 45 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; 46 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 47 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 48 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; 49 import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; 50 import static android.view.WindowManager.LayoutParams.TYPE_TOAST; 51 52 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 53 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow; 54 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 55 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; 56 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; 57 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 58 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 59 import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL; 60 import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; 61 import static com.android.server.wm.WindowContainer.SYNC_STATE_WAITING_FOR_DRAW; 62 63 import static com.google.common.truth.Truth.assertThat; 64 65 import static org.hamcrest.Matchers.is; 66 import static org.hamcrest.Matchers.not; 67 import static org.junit.Assert.assertEquals; 68 import static org.junit.Assert.assertFalse; 69 import static org.junit.Assert.assertNotEquals; 70 import static org.junit.Assert.assertNotNull; 71 import static org.junit.Assert.assertNull; 72 import static org.junit.Assert.assertThat; 73 import static org.junit.Assert.assertTrue; 74 import static org.junit.Assume.assumeTrue; 75 import static org.mockito.ArgumentMatchers.any; 76 import static org.mockito.ArgumentMatchers.anyBoolean; 77 import static org.mockito.ArgumentMatchers.anyInt; 78 import static org.mockito.ArgumentMatchers.anyLong; 79 import static org.mockito.ArgumentMatchers.anyString; 80 import static org.mockito.ArgumentMatchers.eq; 81 import static org.mockito.Mockito.atLeast; 82 import static org.mockito.Mockito.atMost; 83 import static org.mockito.Mockito.clearInvocations; 84 import static org.mockito.Mockito.doAnswer; 85 import static org.mockito.Mockito.reset; 86 import static org.mockito.Mockito.when; 87 88 import android.content.ContentResolver; 89 import android.content.res.CompatibilityInfo; 90 import android.content.res.Configuration; 91 import android.graphics.Matrix; 92 import android.graphics.Point; 93 import android.graphics.Rect; 94 import android.graphics.Region; 95 import android.os.Build; 96 import android.os.IBinder; 97 import android.os.InputConfig; 98 import android.os.RemoteException; 99 import android.platform.test.annotations.DisableFlags; 100 import android.platform.test.annotations.EnableFlags; 101 import android.platform.test.annotations.Presubmit; 102 import android.platform.test.annotations.RequiresFlagsEnabled; 103 import android.provider.Settings; 104 import android.util.ArraySet; 105 import android.util.MergedConfiguration; 106 import android.view.Gravity; 107 import android.view.IWindow; 108 import android.view.InputWindowHandle; 109 import android.view.InsetsSource; 110 import android.view.InsetsSourceControl; 111 import android.view.InsetsState; 112 import android.view.SurfaceControl; 113 import android.view.View; 114 import android.view.WindowInsets; 115 import android.view.WindowManager; 116 import android.view.WindowRelayoutResult; 117 import android.view.inputmethod.ImeTracker; 118 import android.window.ClientWindowFrames; 119 import android.window.ITaskFragmentOrganizer; 120 import android.window.TaskFragmentOrganizer; 121 122 import androidx.test.filters.SmallTest; 123 124 import com.android.server.inputmethod.InputMethodManagerInternal; 125 import com.android.server.testutils.StubTransaction; 126 import com.android.server.wm.SensitiveContentPackages.PackageInfo; 127 import com.android.window.flags.Flags; 128 129 import org.junit.After; 130 import org.junit.Test; 131 import org.junit.runner.RunWith; 132 133 import java.util.ArrayList; 134 import java.util.Arrays; 135 import java.util.Collections; 136 import java.util.LinkedList; 137 import java.util.List; 138 139 /** 140 * Tests for the {@link WindowState} class. 141 * 142 * <p> Build/Install/Run: 143 * atest WmTests:WindowStateTests 144 */ 145 @SmallTest 146 @Presubmit 147 @RunWith(WindowTestRunner.class) 148 public class WindowStateTests extends WindowTestsBase { 149 150 @After tearDown()151 public void tearDown() { 152 mWm.mSensitiveContentPackages.clearBlockedApps(); 153 } 154 155 @Test testIsParentWindowHidden()156 public void testIsParentWindowHidden() { 157 final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); 158 final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); 159 final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); 160 161 // parentWindow is initially set to hidden. 162 assertTrue(parentWindow.mHidden); 163 assertFalse(parentWindow.isParentWindowHidden()); 164 assertTrue(child1.isParentWindowHidden()); 165 assertTrue(child2.isParentWindowHidden()); 166 167 parentWindow.mHidden = false; 168 assertFalse(parentWindow.isParentWindowHidden()); 169 assertFalse(child1.isParentWindowHidden()); 170 assertFalse(child2.isParentWindowHidden()); 171 } 172 173 @Test testIsChildWindow()174 public void testIsChildWindow() { 175 final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); 176 final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); 177 final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); 178 final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow"); 179 180 assertFalse(parentWindow.isChildWindow()); 181 assertTrue(child1.isChildWindow()); 182 assertTrue(child2.isChildWindow()); 183 assertFalse(randomWindow.isChildWindow()); 184 } 185 186 @Test testHasChild()187 public void testHasChild() { 188 final WindowState win1 = createWindow(null, TYPE_APPLICATION, "win1"); 189 final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW, "win11"); 190 final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW, "win12"); 191 final WindowState win2 = createWindow(null, TYPE_APPLICATION, "win2"); 192 final WindowState win21 = createWindow(win2, FIRST_SUB_WINDOW, "win21"); 193 final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow"); 194 195 assertTrue(win1.hasChild(win11)); 196 assertTrue(win1.hasChild(win12)); 197 assertTrue(win2.hasChild(win21)); 198 199 assertFalse(win1.hasChild(win21)); 200 assertFalse(win1.hasChild(randomWindow)); 201 202 assertFalse(win2.hasChild(win11)); 203 assertFalse(win2.hasChild(win12)); 204 assertFalse(win2.hasChild(randomWindow)); 205 } 206 207 @Test testGetParentWindow()208 public void testGetParentWindow() { 209 final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow"); 210 final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1"); 211 final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2"); 212 213 assertNull(parentWindow.getParentWindow()); 214 assertEquals(parentWindow, child1.getParentWindow()); 215 assertEquals(parentWindow, child2.getParentWindow()); 216 } 217 218 @Test testOverlayWindowHiddenWhenSuspended()219 public void testOverlayWindowHiddenWhenSuspended() { 220 final WindowState overlayWindow = spy(createWindow(null, TYPE_APPLICATION_OVERLAY, 221 "overlayWindow")); 222 overlayWindow.setHiddenWhileSuspended(true); 223 verify(overlayWindow).hide(true /* doAnimation */, true /* requestAnim */); 224 overlayWindow.setHiddenWhileSuspended(false); 225 verify(overlayWindow).show(true /* doAnimation */, true /* requestAnim */); 226 } 227 228 @Test testGetTopParentWindow()229 public void testGetTopParentWindow() { 230 final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); 231 final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1"); 232 final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2"); 233 234 assertEquals(root, root.getTopParentWindow()); 235 assertEquals(root, child1.getTopParentWindow()); 236 assertEquals(child1, child2.getParentWindow()); 237 assertEquals(root, child2.getTopParentWindow()); 238 239 // Test case were child is detached from parent. 240 root.removeChild(child1); 241 assertEquals(child1, child1.getTopParentWindow()); 242 assertEquals(child1, child2.getParentWindow()); 243 } 244 245 @Test testIsOnScreen_hiddenByPolicy()246 public void testIsOnScreen_hiddenByPolicy() { 247 final WindowState window = createWindow(null, TYPE_APPLICATION, "window"); 248 window.setHasSurface(true); 249 assertTrue(window.isOnScreen()); 250 window.hide(false /* doAnimation */, false /* requestAnim */); 251 assertFalse(window.isOnScreen()); 252 253 // Verifies that a window without animation can be hidden even if its parent is animating. 254 window.show(false /* doAnimation */, false /* requestAnim */); 255 assertTrue(window.isVisibleByPolicy()); 256 window.getParent().startAnimation(mTransaction, mock(AnimationAdapter.class), 257 false /* hidden */, SurfaceAnimator.ANIMATION_TYPE_TOKEN_TRANSFORM); 258 window.mAttrs.windowAnimations = 0; 259 window.hide(true /* doAnimation */, true /* requestAnim */); 260 assertFalse(window.isSelfAnimating(0, SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION)); 261 assertFalse(window.isVisibleByPolicy()); 262 assertFalse(window.isOnScreen()); 263 264 // Verifies that a window with animation can be hidden after the hide animation is finished. 265 window.show(false /* doAnimation */, false /* requestAnim */); 266 window.mAttrs.windowAnimations = android.R.style.Animation_Dialog; 267 window.hide(true /* doAnimation */, true /* requestAnim */); 268 assertTrue(window.isSelfAnimating(0, SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION)); 269 assertTrue(window.isVisibleByPolicy()); 270 window.cancelAnimation(); 271 assertFalse(window.isVisibleByPolicy()); 272 } 273 274 @Test testCanBeImeTarget()275 public void testCanBeImeTarget() { 276 final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); 277 final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow"); 278 279 // Setting FLAG_NOT_FOCUSABLE prevents the window from being an IME target. 280 appWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; 281 imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; 282 283 // Make windows visible 284 appWindow.setHasSurface(true); 285 imeWindow.setHasSurface(true); 286 287 // Windows with FLAG_NOT_FOCUSABLE can't be IME targets 288 assertFalse(appWindow.canBeImeTarget()); 289 assertFalse(imeWindow.canBeImeTarget()); 290 291 // Add IME target flags 292 appWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM); 293 imeWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM); 294 295 // Visible app window with flags can be IME target while an IME window can never be an IME 296 // target regardless of its visibility or flags. 297 assertTrue(appWindow.canBeImeTarget()); 298 assertFalse(imeWindow.canBeImeTarget()); 299 300 // Verify PINNED windows can't be IME target. 301 int initialMode = appWindow.mActivityRecord.getWindowingMode(); 302 appWindow.mActivityRecord.setWindowingMode(WINDOWING_MODE_PINNED); 303 assertFalse(appWindow.canBeImeTarget()); 304 appWindow.mActivityRecord.setWindowingMode(initialMode); 305 306 // Verify that app window can still be IME target as long as it is visible (even if 307 // it is going to become invisible). 308 appWindow.mActivityRecord.setVisibleRequested(false); 309 assertTrue(appWindow.canBeImeTarget()); 310 311 // Make windows invisible 312 appWindow.hide(false /* doAnimation */, false /* requestAnim */); 313 imeWindow.hide(false /* doAnimation */, false /* requestAnim */); 314 315 // Invisible window can't be IME targets even if they have the right flags. 316 assertFalse(appWindow.canBeImeTarget()); 317 assertFalse(imeWindow.canBeImeTarget()); 318 319 // Simulate the window is in split screen root task. 320 final Task rootTask = createTask(mDisplayContent, 321 WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); 322 rootTask.setFocusable(false); 323 appWindow.mActivityRecord.reparent(rootTask, 0 /* position */, "test"); 324 325 // Make sure canBeImeTarget is false; 326 assertFalse(appWindow.canBeImeTarget()); 327 } 328 329 @Test testGetWindow()330 public void testGetWindow() { 331 final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); 332 final WindowState mediaChild = createWindow(root, TYPE_APPLICATION_MEDIA, "mediaChild"); 333 final WindowState mediaOverlayChild = createWindow(root, 334 TYPE_APPLICATION_MEDIA_OVERLAY, "mediaOverlayChild"); 335 final WindowState attachedDialogChild = createWindow(root, 336 TYPE_APPLICATION_ATTACHED_DIALOG, "attachedDialogChild"); 337 final WindowState subPanelChild = createWindow(root, 338 TYPE_APPLICATION_SUB_PANEL, "subPanelChild"); 339 final WindowState aboveSubPanelChild = createWindow(root, 340 TYPE_APPLICATION_ABOVE_SUB_PANEL, "aboveSubPanelChild"); 341 342 final LinkedList<WindowState> windows = new LinkedList<>(); 343 344 root.getWindow(w -> { 345 windows.addLast(w); 346 return false; 347 }); 348 349 // getWindow should have returned candidate windows in z-order. 350 assertEquals(aboveSubPanelChild, windows.pollFirst()); 351 assertEquals(subPanelChild, windows.pollFirst()); 352 assertEquals(attachedDialogChild, windows.pollFirst()); 353 assertEquals(root, windows.pollFirst()); 354 assertEquals(mediaOverlayChild, windows.pollFirst()); 355 assertEquals(mediaChild, windows.pollFirst()); 356 assertTrue(windows.isEmpty()); 357 } 358 359 @Test testDestroySurface()360 public void testDestroySurface() { 361 final WindowState win = createWindow(null, TYPE_APPLICATION, "win"); 362 win.mHasSurface = win.mAnimatingExit = true; 363 win.mWinAnimator.mSurfaceControl = mock(SurfaceControl.class); 364 win.onExitAnimationDone(); 365 366 assertFalse("Case 1 destroySurface no-op", 367 win.destroySurface(false /* cleanupOnResume */, false /* appStopped */)); 368 assertTrue(win.mHasSurface); 369 assertTrue(win.mDestroying); 370 371 assertFalse("Case 2 destroySurface no-op", 372 win.destroySurface(true /* cleanupOnResume */, false /* appStopped */)); 373 assertTrue(win.mHasSurface); 374 assertTrue(win.mDestroying); 375 376 assertTrue("Case 3 destroySurface destroys surface", 377 win.destroySurface(false /* cleanupOnResume */, true /* appStopped */)); 378 assertFalse(win.mDestroying); 379 assertFalse(win.mHasSurface); 380 } 381 382 @Test testPrepareWindowToDisplayDuringRelayout()383 public void testPrepareWindowToDisplayDuringRelayout() { 384 // Call prepareWindowToDisplayDuringRelayout for a window without FLAG_TURN_SCREEN_ON before 385 // calling setCurrentLaunchCanTurnScreenOn for windows with flag in the same activity. 386 final ActivityRecord activity = createActivityRecord(mDisplayContent); 387 final WindowState first = createWindow(null, TYPE_APPLICATION, activity, "first"); 388 final WindowState second = createWindow(null, TYPE_APPLICATION, activity, "second"); 389 390 testPrepareWindowToDisplayDuringRelayout(first, false /* expectedWakeupCalled */, 391 true /* expectedCurrentLaunchCanTurnScreenOn */); 392 testPrepareWindowToDisplayDuringRelayout(second, false /* expectedWakeupCalled */, 393 true /* expectedCurrentLaunchCanTurnScreenOn */); 394 395 // Call prepareWindowToDisplayDuringRelayout for two windows from the same activity, one of 396 // which has FLAG_TURN_SCREEN_ON. The first processed one should trigger the wakeup. 397 second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; 398 testPrepareWindowToDisplayDuringRelayout(first, true /* expectedWakeupCalled */, 399 false /* expectedCurrentLaunchCanTurnScreenOn */); 400 testPrepareWindowToDisplayDuringRelayout(second, false /* expectedWakeupCalled */, 401 false /* expectedCurrentLaunchCanTurnScreenOn */); 402 403 // Call prepareWindowToDisplayDuringRelayout for two window that have FLAG_TURN_SCREEN_ON 404 // from the same activity. Only one should trigger the wakeup. 405 activity.setCurrentLaunchCanTurnScreenOn(true); 406 first.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; 407 second.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; 408 409 testPrepareWindowToDisplayDuringRelayout(first, true /* expectedWakeupCalled */, 410 false /* expectedCurrentLaunchCanTurnScreenOn */); 411 testPrepareWindowToDisplayDuringRelayout(second, false /* expectedWakeupCalled */, 412 false /* expectedCurrentLaunchCanTurnScreenOn */); 413 414 // Without window flags, the state of ActivityRecord.canTurnScreenOn should still be able to 415 // turn on the screen. 416 activity.setCurrentLaunchCanTurnScreenOn(true); 417 first.mAttrs.flags &= ~WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; 418 doReturn(true).when(activity).canTurnScreenOn(); 419 420 testPrepareWindowToDisplayDuringRelayout(first, true /* expectedWakeupCalled */, 421 false /* expectedCurrentLaunchCanTurnScreenOn */); 422 423 // Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an 424 // activity. Both windows have the FLAG_TURNS_SCREEN_ON so both should call wakeup 425 final WindowToken windowToken = createTestWindowToken(FIRST_SUB_WINDOW, mDisplayContent); 426 final WindowState firstWindow = createWindow(null, TYPE_APPLICATION, windowToken, 427 "firstWindow"); 428 final WindowState secondWindow = createWindow(null, TYPE_APPLICATION, windowToken, 429 "secondWindow"); 430 firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; 431 secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; 432 433 final var powerManager = mWm.mPowerManager; 434 clearInvocations(powerManager); 435 firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); 436 verify(powerManager).wakeUp(anyLong(), anyInt(), anyString(), anyInt()); 437 438 clearInvocations(powerManager); 439 secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/); 440 verify(powerManager).wakeUp(anyLong(), anyInt(), anyString(), anyInt()); 441 } 442 testPrepareWindowToDisplayDuringRelayout(WindowState appWindow, boolean expectedWakeupCalled, boolean expectedCurrentLaunchCanTurnScreenOn)443 private void testPrepareWindowToDisplayDuringRelayout(WindowState appWindow, 444 boolean expectedWakeupCalled, boolean expectedCurrentLaunchCanTurnScreenOn) { 445 final var powerManager = mWm.mPowerManager; 446 clearInvocations(powerManager); 447 appWindow.prepareWindowToDisplayDuringRelayout(false /* wasVisible */); 448 449 if (expectedWakeupCalled) { 450 verify(powerManager).wakeUp(anyLong(), anyInt(), anyString(), anyInt()); 451 } else { 452 verify(powerManager, never()).wakeUp(anyLong(), anyInt(), anyString(), anyInt()); 453 } 454 // If wakeup is expected to be called, the currentLaunchCanTurnScreenOn should be false 455 // because the state will be consumed. 456 assertThat(appWindow.mActivityRecord.currentLaunchCanTurnScreenOn(), 457 is(expectedCurrentLaunchCanTurnScreenOn)); 458 } 459 460 @Test testCanAffectSystemUiFlags()461 public void testCanAffectSystemUiFlags() { 462 final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); 463 app.mActivityRecord.setVisible(true); 464 assertTrue(app.canAffectSystemUiFlags()); 465 app.mActivityRecord.setVisible(false); 466 assertFalse(app.canAffectSystemUiFlags()); 467 app.mActivityRecord.setVisible(true); 468 app.mAttrs.alpha = 0.0f; 469 assertFalse(app.canAffectSystemUiFlags()); 470 } 471 472 @Test testCanAffectSystemUiFlags_starting()473 public void testCanAffectSystemUiFlags_starting() { 474 final WindowState app = createWindow(null, TYPE_APPLICATION_STARTING, "app"); 475 app.mActivityRecord.setVisible(true); 476 app.mStartingData = new SnapshotStartingData(mWm, null, 0); 477 assertFalse(app.canAffectSystemUiFlags()); 478 app.mStartingData = new SplashScreenStartingData(mWm, 0, 0); 479 assertTrue(app.canAffectSystemUiFlags()); 480 } 481 482 @Test testCanAffectSystemUiFlags_disallow()483 public void testCanAffectSystemUiFlags_disallow() { 484 final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); 485 app.mActivityRecord.setVisible(true); 486 assertTrue(app.canAffectSystemUiFlags()); 487 app.getTask().setCanAffectSystemUiFlags(false); 488 assertFalse(app.canAffectSystemUiFlags()); 489 } 490 491 @SetupWindows(addWindows = { W_ACTIVITY, W_STATUS_BAR }) 492 @Test testVisibleWithInsetsProvider()493 public void testVisibleWithInsetsProvider() { 494 final WindowState statusBar = mStatusBarWindow; 495 final WindowState app = mAppWindow; 496 statusBar.mHasSurface = true; 497 assertTrue(statusBar.isVisible()); 498 final int statusBarId = InsetsSource.createId(null, 0, statusBars()); 499 mDisplayContent.getInsetsStateController() 500 .getOrCreateSourceProvider(statusBarId, statusBars()) 501 .setWindowContainer(statusBar, null /* frameProvider */, 502 null /* imeFrameProvider */); 503 mDisplayContent.getInsetsStateController().onBarControlTargetChanged( 504 app, null /* fakeTopControlling */, app, null /* fakeNavControlling */); 505 app.setRequestedVisibleTypes(0, statusBars()); 506 mDisplayContent.getInsetsStateController() 507 .getOrCreateSourceProvider(statusBarId, statusBars()) 508 .updateClientVisibility(app, null /* statsToken */); 509 waitUntilHandlersIdle(); 510 assertFalse(statusBar.isVisible()); 511 } 512 513 /** 514 * Verifies that the InsetsSourceProvider frame cannot be updated by WindowState before 515 * relayout is called. 516 */ 517 @SetupWindows(addWindows = { W_STATUS_BAR }) 518 @Test testUpdateSourceFrameBeforeRelayout()519 public void testUpdateSourceFrameBeforeRelayout() { 520 final WindowState statusBar = mStatusBarWindow; 521 statusBar.mHasSurface = true; 522 assertTrue(statusBar.isVisible()); 523 final int statusBarId = InsetsSource.createId(null, 0, statusBars()); 524 final var statusBarProvider = mDisplayContent.getInsetsStateController() 525 .getOrCreateSourceProvider(statusBarId, statusBars()); 526 statusBarProvider.setWindowContainer(statusBar, null /* frameProvider */, 527 null /* imeFrameProvider */); 528 529 statusBar.updateSourceFrame(new Rect(0, 0, 500, 200)); 530 assertTrue("InsetsSourceProvider frame should not be updated before relayout", 531 statusBarProvider.getSourceFrame().isEmpty()); 532 533 makeWindowVisible(statusBar); 534 statusBar.updateSourceFrame(new Rect(0, 0, 500, 100)); 535 assertEquals("InsetsSourceProvider frame should be updated after relayout", 536 new Rect(0, 0, 500, 100), statusBarProvider.getSourceFrame()); 537 } 538 539 @Test testIsSelfOrAncestorWindowAnimating()540 public void testIsSelfOrAncestorWindowAnimating() { 541 final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); 542 final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1"); 543 final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2"); 544 assertFalse(child2.isSelfOrAncestorWindowAnimatingExit()); 545 child2.mAnimatingExit = true; 546 assertTrue(child2.isSelfOrAncestorWindowAnimatingExit()); 547 child2.mAnimatingExit = false; 548 root.mAnimatingExit = true; 549 assertTrue(child2.isSelfOrAncestorWindowAnimatingExit()); 550 } 551 552 @Test testDeferredRemovalByAnimating()553 public void testDeferredRemovalByAnimating() { 554 final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow"); 555 makeWindowVisible(appWindow); 556 spyOn(appWindow.mWinAnimator); 557 doReturn(true).when(appWindow.mWinAnimator).getShown(); 558 final AnimationAdapter animation = mock(AnimationAdapter.class); 559 final ActivityRecord activity = appWindow.mActivityRecord; 560 activity.startAnimation(appWindow.getPendingTransaction(), 561 animation, false /* hidden */, SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION); 562 563 appWindow.removeIfPossible(); 564 assertTrue(appWindow.mAnimatingExit); 565 assertFalse(appWindow.mRemoved); 566 567 activity.cancelAnimation(); 568 assertFalse(appWindow.mAnimatingExit); 569 assertTrue(appWindow.mRemoved); 570 } 571 572 @Test testOnExitAnimationDone()573 public void testOnExitAnimationDone() { 574 final WindowState parent = createWindow(null, TYPE_APPLICATION, "parent"); 575 final WindowState child = createWindow(parent, TYPE_APPLICATION_PANEL, "child"); 576 final SurfaceControl.Transaction t = parent.getPendingTransaction(); 577 child.startAnimation(t, mock(AnimationAdapter.class), false /* hidden */, 578 SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION); 579 parent.mAnimatingExit = parent.mRemoveOnExit = parent.mWindowRemovalAllowed = true; 580 child.mAnimatingExit = child.mRemoveOnExit = child.mWindowRemovalAllowed = true; 581 final int[] numRemovals = new int[2]; 582 parent.registerWindowContainerListener(new WindowContainerListener() { 583 @Override 584 public void onRemoved() { 585 numRemovals[0]++; 586 } 587 }); 588 child.registerWindowContainerListener(new WindowContainerListener() { 589 @Override 590 public void onRemoved() { 591 numRemovals[1]++; 592 } 593 }); 594 spyOn(parent); 595 // parent onExitAnimationDone 596 // -> child onExitAnimationDone() -> no-op because isAnimating() 597 // -> parent destroySurface() 598 // -> parent removeImmediately() because mDestroying+mRemoveOnExit 599 // -> child removeImmediately() -> cancelAnimation() 600 // -> child onExitAnimationDone() 601 // -> child destroySurface() because animation is canceled 602 // -> child removeImmediately() -> no-op because mRemoved 603 parent.onExitAnimationDone(); 604 // There must be no additional destroySurface() of parent from its child. 605 verify(parent, atMost(1)).destroySurface(anyBoolean(), anyBoolean()); 606 assertEquals(1, numRemovals[0]); 607 assertEquals(1, numRemovals[1]); 608 } 609 610 @Test testLayoutSeqResetOnReparent()611 public void testLayoutSeqResetOnReparent() { 612 final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); 613 app.mLayoutSeq = 1; 614 mDisplayContent.mLayoutSeq = 1; 615 616 DisplayContent newDisplay = createNewDisplay(); 617 618 app.onDisplayChanged(newDisplay); 619 620 assertThat(app.mLayoutSeq, not(is(mDisplayContent.mLayoutSeq))); 621 } 622 623 @Test testDisplayIdUpdatedOnReparent()624 public void testDisplayIdUpdatedOnReparent() { 625 final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); 626 // fake a different display 627 app.mInputWindowHandle.setDisplayId(mDisplayContent.getDisplayId() + 1); 628 app.onDisplayChanged(mDisplayContent); 629 630 assertThat(app.mInputWindowHandle.getDisplayId(), is(mDisplayContent.getDisplayId())); 631 assertThat(app.getDisplayId(), is(mDisplayContent.getDisplayId())); 632 } 633 634 @Test testApplyWithNextDraw()635 public void testApplyWithNextDraw() { 636 final WindowState win = createWindow(null, TYPE_APPLICATION_OVERLAY, "app"); 637 final SurfaceControl.Transaction[] handledT = { null }; 638 // The normal case that the draw transaction is applied with finishing drawing. 639 win.applyWithNextDraw(t -> handledT[0] = t); 640 assertTrue(win.syncNextBuffer()); 641 final SurfaceControl.Transaction drawT = new StubTransaction(); 642 final SurfaceControl.Transaction currT = win.getSyncTransaction(); 643 clearInvocations(currT); 644 win.mWinAnimator.mLastHidden = true; 645 assertTrue(win.finishDrawing(drawT, Integer.MAX_VALUE)); 646 // The draw transaction should be merged to current transaction even if the state is hidden. 647 verify(currT).merge(eq(drawT)); 648 assertEquals(drawT, handledT[0]); 649 assertFalse(win.syncNextBuffer()); 650 651 // If the window is gone before reporting drawn, the sync state should be cleared. 652 win.applyWithNextDraw(t -> handledT[0] = t); 653 win.destroySurfaceUnchecked(); 654 assertFalse(win.syncNextBuffer()); 655 assertNotEquals(drawT, handledT[0]); 656 } 657 658 @Test testSeamlesslyRotateWindow()659 public void testSeamlesslyRotateWindow() { 660 final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); 661 final SurfaceControl.Transaction t = spy(StubTransaction.class); 662 663 makeWindowVisible(app); 664 app.mSurfaceControl = mock(SurfaceControl.class); 665 final Rect frame = app.getFrame(); 666 frame.set(10, 20, 60, 80); 667 app.updateSurfacePosition(t); 668 assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top)); 669 app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true /* requested */); 670 assertTrue(app.mSeamlesslyRotated); 671 672 // Verify we un-rotate the window state surface. 673 final Matrix matrix = new Matrix(); 674 // Un-rotate 90 deg. 675 matrix.setRotate(270); 676 // Translate it back to origin. 677 matrix.postTranslate(0, mDisplayInfo.logicalWidth); 678 verify(t).setMatrix(eq(app.mSurfaceControl), eq(matrix), any(float[].class)); 679 680 // Verify we update the position as well. 681 final float[] curSurfacePos = {app.mLastSurfacePosition.x, app.mLastSurfacePosition.y}; 682 matrix.mapPoints(curSurfacePos); 683 verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1])); 684 685 app.finishSeamlessRotation(t); 686 assertFalse(app.mSeamlesslyRotated); 687 assertNull(app.mPendingSeamlessRotate); 688 689 // Simulate the case with deferred layout and animation. 690 app.resetSurfacePositionForAnimationLeash(t); 691 clearInvocations(t); 692 mWm.mWindowPlacerLocked.deferLayout(); 693 app.updateSurfacePosition(t); 694 // Because layout is deferred, the position should keep the reset value. 695 assertTrue(app.mLastSurfacePosition.equals(0, 0)); 696 697 app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_270, true /* requested */); 698 // The last position must be updated so the surface can be unrotated properly. 699 assertTrue(app.mLastSurfacePosition.equals(frame.left, frame.top)); 700 matrix.setRotate(90); 701 matrix.postTranslate(mDisplayInfo.logicalHeight, 0); 702 curSurfacePos[0] = frame.left; 703 curSurfacePos[1] = frame.top; 704 matrix.mapPoints(curSurfacePos); 705 verify(t).setPosition(eq(app.mSurfaceControl), eq(curSurfacePos[0]), eq(curSurfacePos[1])); 706 } 707 708 @Test testVisibilityChangeSwitchUser()709 public void testVisibilityChangeSwitchUser() { 710 final WindowState window = createWindow(null, TYPE_APPLICATION, "app"); 711 window.mHasSurface = true; 712 spyOn(window); 713 doReturn(false).when(window).showForAllUsers(); 714 715 mWm.mCurrentUserId = 1; 716 window.switchUser(mWm.mCurrentUserId); 717 assertFalse(window.isVisible()); 718 assertFalse(window.isVisibleByPolicy()); 719 720 mWm.mCurrentUserId = 0; 721 window.switchUser(mWm.mCurrentUserId); 722 assertTrue(window.isVisible()); 723 assertTrue(window.isVisibleByPolicy()); 724 } 725 726 @Test testCompatOverrideScale()727 public void testCompatOverrideScale() { 728 final float overrideScale = 2; // 0.5x on client side. 729 final CompatModePackages cmp = mWm.mAtmService.mCompatModePackages; 730 spyOn(cmp); 731 doReturn(overrideScale).when(cmp).getCompatScale(anyString(), anyInt()); 732 final WindowState w = createWindow(null, TYPE_APPLICATION_OVERLAY, "win"); 733 final WindowState child = createWindow(w, TYPE_APPLICATION_PANEL, "child"); 734 735 assertTrue(w.hasCompatScale()); 736 assertTrue(child.hasCompatScale()); 737 738 makeWindowVisible(w, child); 739 w.setRequestedSize(100, 200); 740 child.setRequestedSize(50, 100); 741 child.mAttrs.width = child.mAttrs.height = 0; 742 w.mAttrs.x = w.mAttrs.y = 100; 743 w.mAttrs.width = w.mAttrs.height = WindowManager.LayoutParams.WRAP_CONTENT; 744 w.mAttrs.gravity = Gravity.TOP | Gravity.LEFT; 745 child.mAttrs.gravity = Gravity.CENTER; 746 DisplayContentTests.performLayout(mDisplayContent); 747 final Rect parentFrame = w.getFrame(); 748 final Rect childFrame = child.getFrame(); 749 750 // Frame on screen = 200x400 (200, 200 - 400, 600). Compat frame on client = 100x200. 751 final Rect unscaledCompatFrame = new Rect(w.getWindowFrames().mCompatFrame); 752 unscaledCompatFrame.scale(overrideScale); 753 assertEquals(parentFrame, unscaledCompatFrame); 754 755 // Frame on screen = 100x200 (250, 300 - 350, 500). Compat frame on client = 50x100. 756 unscaledCompatFrame.set(child.getWindowFrames().mCompatFrame); 757 unscaledCompatFrame.scale(overrideScale); 758 assertEquals(childFrame, unscaledCompatFrame); 759 760 // The position of child is relative to parent. So the local coordinates should be scaled. 761 final Point expectedChildPos = new Point( 762 (int) ((childFrame.left - parentFrame.left) / overrideScale), 763 (int) ((childFrame.top - parentFrame.top) / overrideScale)); 764 final Point childPos = new Point(); 765 child.transformFrameToSurfacePosition(childFrame.left, childFrame.top, childPos); 766 assertEquals(expectedChildPos, childPos); 767 768 // Surface should apply the scale. 769 final SurfaceControl.Transaction t = w.getPendingTransaction(); 770 w.prepareSurfaces(); 771 verify(t).setMatrix(w.mSurfaceControl, overrideScale, 0, 0, overrideScale); 772 // Child surface inherits parent's scale, so it doesn't need to scale. 773 verify(t, never()).setMatrix(any(), anyInt(), anyInt(), anyInt(), anyInt()); 774 775 // According to "dp * density / 160 = px", density is scaled and the size in dp is the same. 776 final Configuration winConfig = w.getConfiguration(); 777 final Configuration clientConfig = new Configuration(w.getConfiguration()); 778 CompatibilityInfo.scaleConfiguration(w.mInvGlobalScale, clientConfig); 779 780 assertEquals(winConfig.screenWidthDp, clientConfig.screenWidthDp); 781 assertEquals(winConfig.screenHeightDp, clientConfig.screenHeightDp); 782 assertEquals(winConfig.smallestScreenWidthDp, clientConfig.smallestScreenWidthDp); 783 assertEquals(winConfig.densityDpi, (int) (clientConfig.densityDpi * overrideScale)); 784 785 final Rect unscaledClientBounds = new Rect(clientConfig.windowConfiguration.getBounds()); 786 unscaledClientBounds.scale(overrideScale); 787 assertEquals(w.getWindowConfiguration().getBounds(), unscaledClientBounds); 788 789 // Child window without scale (e.g. different app) should apply inverse scale of parent. 790 doReturn(1f).when(cmp).getCompatScale(anyString(), anyInt()); 791 final WindowState child2 = createWindow(w, TYPE_APPLICATION_SUB_PANEL, "child2"); 792 makeWindowVisible(w, child2); 793 clearInvocations(t); 794 child2.prepareSurfaces(); 795 verify(t).setMatrix(child2.mSurfaceControl, w.mInvGlobalScale, 0, 0, w.mInvGlobalScale); 796 } 797 798 @SetupWindows(addWindows = { W_ABOVE_ACTIVITY, W_NOTIFICATION_SHADE }) 799 @Test testRequestDrawIfNeeded()800 public void testRequestDrawIfNeeded() { 801 final WindowState startingApp = createWindow(null /* parent */, 802 TYPE_BASE_APPLICATION, "startingApp"); 803 final WindowState startingWindow = createWindow(null /* parent */, 804 TYPE_APPLICATION_STARTING, startingApp.mToken, "starting"); 805 startingApp.mActivityRecord.mStartingWindow = startingWindow; 806 final WindowState keyguardHostWindow = mNotificationShadeWindow; 807 final WindowState allDrawnApp = mAppWindow; 808 allDrawnApp.mActivityRecord.allDrawn = true; 809 810 // The waiting list is used to ensure the content is ready when turning on screen. 811 final List<WindowState> outWaitingForDrawn = mDisplayContent.mWaitingForDrawn; 812 final List<WindowState> visibleWindows = Arrays.asList(mChildAppWindowAbove, 813 keyguardHostWindow, allDrawnApp, startingApp, startingWindow); 814 visibleWindows.forEach(w -> { 815 w.mHasSurface = true; 816 w.requestDrawIfNeeded(outWaitingForDrawn); 817 }); 818 819 // Keyguard host window should be always contained. The drawn app or app with starting 820 // window are unnecessary to draw. 821 assertEquals(Arrays.asList(keyguardHostWindow, startingWindow), outWaitingForDrawn); 822 823 // No need to wait for a window of invisible activity even if the window has surface. 824 final WindowState invisibleApp = mAppWindow; 825 invisibleApp.mActivityRecord.setVisibleRequested(false); 826 invisibleApp.mActivityRecord.allDrawn = false; 827 outWaitingForDrawn.clear(); 828 invisibleApp.requestDrawIfNeeded(outWaitingForDrawn); 829 assertTrue(outWaitingForDrawn.isEmpty()); 830 831 // Drawn state should not be changed for insets change if the window is not visible. 832 startingApp.mActivityRecord.setVisibleRequested(false); 833 makeWindowVisibleAndDrawn(startingApp); 834 startingApp.getConfiguration().orientation = 0; // Reset to be the same as last reported. 835 startingApp.getWindowFrames().setInsetsChanged(true); 836 startingApp.updateResizingWindowIfNeeded(); 837 assertTrue(mWm.mResizingWindows.contains(startingApp)); 838 assertTrue(startingApp.isDrawn()); 839 assertFalse(startingApp.getOrientationChanging()); 840 841 // Even if the display is frozen, invisible requested window should not be affected. 842 mWm.startFreezingDisplay(0, 0, mDisplayContent); 843 startingApp.getWindowFrames().setInsetsChanged(true); 844 startingApp.updateResizingWindowIfNeeded(); 845 assertTrue(startingApp.isDrawn()); 846 } 847 848 @SetupWindows(addWindows = W_ABOVE_ACTIVITY) 849 @Test testReportResizedWithRemoteException()850 public void testReportResizedWithRemoteException() { 851 final WindowState win = mChildAppWindowAbove; 852 makeWindowVisible(win, win.getParentWindow()); 853 win.mLayoutSeq = win.getDisplayContent().mLayoutSeq; 854 win.updateResizingWindowIfNeeded(); 855 856 assertThat(mWm.mResizingWindows).contains(win); 857 assertTrue(win.getOrientationChanging()); 858 859 mWm.mResizingWindows.remove(win); 860 spyOn(win.mClient); 861 try { 862 doThrow(new RemoteException("test")).when(win.mClient).resized(any() /* frames */, 863 anyBoolean() /* reportDraw */, any() /* mergedConfig */, 864 any() /* insetsState */, anyBoolean() /* forceLayout */, 865 anyBoolean() /* alwaysConsumeSystemBars */, anyInt() /* displayId */, 866 anyInt() /* seqId */, anyBoolean() /* dragResizing */, 867 any() /* activityWindowInfo */); 868 } catch (RemoteException ignored) { 869 } 870 win.reportResized(); 871 win.updateResizingWindowIfNeeded(); 872 873 // Even "resized" throws remote exception, it is still considered as reported. So the window 874 // shouldn't be resized again (which may block unfreeze in real case). 875 assertThat(mWm.mResizingWindows).doesNotContain(win); 876 assertFalse(win.getOrientationChanging()); 877 } 878 879 @Test testRequestResizeForBlastSync()880 public void testRequestResizeForBlastSync() { 881 final WindowState win = createWindow(null, TYPE_APPLICATION, "window"); 882 makeWindowVisible(win); 883 makeLastConfigReportedToClient(win, true /* visible */); 884 win.mLayoutSeq = win.getDisplayContent().mLayoutSeq; 885 win.reportResized(); 886 win.updateResizingWindowIfNeeded(); 887 assertThat(mWm.mResizingWindows).doesNotContain(win); 888 889 // Check that the window is in resizing if using blast sync. 890 final BLASTSyncEngine.SyncGroup syncGroup = mock(BLASTSyncEngine.SyncGroup.class); 891 syncGroup.mSyncMethod = BLASTSyncEngine.METHOD_BLAST; 892 win.mSyncGroup = syncGroup; 893 win.reportResized(); 894 win.prepareSync(); 895 assertEquals(SYNC_STATE_WAITING_FOR_DRAW, win.mSyncState); 896 win.updateResizingWindowIfNeeded(); 897 assertThat(mWm.mResizingWindows).contains(win); 898 899 // Don't re-add the window again if it's been reported to the client and still waiting on 900 // the client draw for blast sync. 901 win.reportResized(); 902 mWm.mResizingWindows.remove(win); 903 win.updateResizingWindowIfNeeded(); 904 assertThat(mWm.mResizingWindows).doesNotContain(win); 905 906 // Non blast sync doesn't require to force resizing, because it won't use syncSeqId. 907 // And if the window is already drawn, it can report sync finish immediately so that the 908 // sync group won't be blocked. 909 win.finishSync(mTransaction, syncGroup, false /* cancel */); 910 syncGroup.mSyncMethod = BLASTSyncEngine.METHOD_NONE; 911 win.mSyncGroup = syncGroup; 912 win.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN; 913 win.prepareSync(); 914 assertEquals(SYNC_STATE_WAITING_FOR_DRAW, win.mSyncState); 915 win.updateResizingWindowIfNeeded(); 916 assertThat(mWm.mResizingWindows).doesNotContain(win); 917 assertTrue(win.isSyncFinished(syncGroup)); 918 assertEquals(WindowContainer.SYNC_STATE_READY, win.mSyncState); 919 } 920 921 @Test testEmbeddedActivityResizing_clearAllDrawn()922 public void testEmbeddedActivityResizing_clearAllDrawn() { 923 final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); 924 registerTaskFragmentOrganizer( 925 ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); 926 final Task task = createTask(mDisplayContent); 927 final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer); 928 final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity(); 929 final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION, embeddedActivity, 930 "App window"); 931 doReturn(true).when(embeddedActivity).isVisible(); 932 embeddedActivity.setVisibleRequested(true); 933 makeWindowVisible(win); 934 win.mLayoutSeq = win.getDisplayContent().mLayoutSeq; 935 // Set the bounds twice: 936 // 1. To make sure there is no orientation change after #reportResized, which can also cause 937 // #clearAllDrawn. 938 // 2. Make #isLastConfigReportedToClient to be false after #reportResized, so it can process 939 // to check if we need redraw. 940 embeddedTf.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 941 embeddedTf.setBounds(0, 0, 1000, 2000); 942 win.reportResized(); 943 embeddedTf.setBounds(500, 0, 1000, 2000); 944 945 // Clear all drawn when the window config of embedded TaskFragment is changed. 946 win.updateResizingWindowIfNeeded(); 947 verify(embeddedActivity).clearAllDrawn(); 948 } 949 950 @Test testCantReceiveTouchWhenAppTokenHiddenRequested()951 public void testCantReceiveTouchWhenAppTokenHiddenRequested() { 952 final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); 953 win0.mActivityRecord.setVisibleRequested(false); 954 assertFalse(win0.canReceiveTouchInput()); 955 } 956 957 @Test testCantReceiveTouchWhenNotFocusable()958 public void testCantReceiveTouchWhenNotFocusable() { 959 final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0"); 960 final Task rootTask = win0.mActivityRecord.getRootTask(); 961 spyOn(rootTask); 962 when(rootTask.shouldIgnoreInput()).thenReturn(true); 963 assertFalse(win0.canReceiveTouchInput()); 964 } 965 testFlag(int flags, int test)966 private boolean testFlag(int flags, int test) { 967 return (flags & test) == test; 968 } 969 970 @Test testUpdateInputWindowHandle()971 public void testUpdateInputWindowHandle() { 972 final WindowState win = createWindow(null, TYPE_APPLICATION, "win"); 973 win.mAttrs.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; 974 win.mAttrs.flags = FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH; 975 final InputWindowHandle handle = new InputWindowHandle( 976 win.mInputWindowHandle.getInputApplicationHandle(), win.getDisplayId()); 977 final InputWindowHandleWrapper handleWrapper = new InputWindowHandleWrapper(handle); 978 final IBinder inputChannelToken = mock(IBinder.class); 979 win.mInputChannelToken = inputChannelToken; 980 981 mDisplayContent.getInputMonitor().populateInputWindowHandle(handleWrapper, win); 982 983 assertTrue(handleWrapper.isChanged()); 984 assertTrue(testFlag(handle.inputConfig, InputConfig.WATCH_OUTSIDE_TOUCH)); 985 assertFalse(testFlag(handle.inputConfig, InputConfig.PREVENT_SPLITTING)); 986 assertTrue(testFlag(handle.inputConfig, InputConfig.DISABLE_USER_ACTIVITY)); 987 // The window of standard resizable task should not use surface crop as touchable region. 988 assertFalse(handle.replaceTouchableRegionWithCrop); 989 assertEquals(inputChannelToken, handle.token); 990 assertEquals(win.mActivityRecord.getInputApplicationHandle(false /* update */), 991 handle.inputApplicationHandle); 992 993 final SurfaceControl sc = mock(SurfaceControl.class); 994 final SurfaceControl.Transaction transaction = mSystemServicesTestRule.mTransaction; 995 InputMonitor.setInputWindowInfoIfNeeded(transaction, sc, handleWrapper); 996 997 // The fields of input window handle are changed, so it must set input window info 998 // successfully. And then the changed flag should be reset. 999 verify(transaction).setInputWindowInfo(eq(sc), eq(handle)); 1000 assertFalse(handleWrapper.isChanged()); 1001 // Populate the same states again, the handle should not detect change. 1002 mDisplayContent.getInputMonitor().populateInputWindowHandle(handleWrapper, win); 1003 assertFalse(handleWrapper.isChanged()); 1004 1005 // Apply the no change handle, the invocation of setInputWindowInfo should be skipped. 1006 clearInvocations(transaction); 1007 InputMonitor.setInputWindowInfoIfNeeded(transaction, sc, handleWrapper); 1008 verify(transaction, never()).setInputWindowInfo(any(), any()); 1009 1010 // The rotated bounds have higher priority as the touchable region. 1011 final Rect rotatedBounds = new Rect(0, 0, 123, 456); 1012 doReturn(rotatedBounds).when(win.mToken).getFixedRotationTransformDisplayBounds(); 1013 mDisplayContent.getInputMonitor().populateInputWindowHandle(handleWrapper, win); 1014 assertEquals(rotatedBounds, handle.touchableRegion.getBounds()); 1015 1016 // Populate as an overlay to disable the input of window. 1017 InputMonitor.populateOverlayInputInfo(handleWrapper); 1018 // The overlay attributes should be set. 1019 assertTrue(handleWrapper.isChanged()); 1020 assertFalse(handleWrapper.isFocusable()); 1021 assertNull(handle.token); 1022 assertEquals(0L, handle.dispatchingTimeoutMillis); 1023 assertTrue(testFlag(handle.inputConfig, InputConfig.NO_INPUT_CHANNEL)); 1024 } 1025 1026 @DisableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX) 1027 @Test testTouchRegionUsesLetterboxBoundsIfTransformedBoundsAndLetterboxScrolling()1028 public void testTouchRegionUsesLetterboxBoundsIfTransformedBoundsAndLetterboxScrolling() { 1029 final WindowState win = createWindow(null, TYPE_APPLICATION, "win"); 1030 1031 // Transformed bounds used for size of touchable region if letterbox inner bounds are empty. 1032 final Rect transformedBounds = new Rect(0, 0, 300, 500); 1033 doReturn(transformedBounds).when(win.mToken).getFixedRotationTransformDisplayBounds(); 1034 1035 // Otherwise, touchable region should match letterbox inner bounds. 1036 final Rect letterboxInnerBounds = new Rect(30, 0, 270, 500); 1037 doAnswer(invocation -> { 1038 Rect rect = invocation.getArgument(0); 1039 rect.set(letterboxInnerBounds); 1040 return null; 1041 }).when(win.mActivityRecord).getLetterboxInnerBounds(any()); 1042 1043 Region outRegion = new Region(); 1044 win.getSurfaceTouchableRegion(outRegion, win.mAttrs); 1045 1046 // Because scrollingFromLetterbox flag is disabled and letterboxInnerBounds is not empty, 1047 // touchable region should match letterboxInnerBounds always. 1048 assertEquals(letterboxInnerBounds, outRegion.getBounds()); 1049 } 1050 1051 @DisableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX) 1052 @Test testTouchRegionUsesLetterboxBoundsIfNullTransformedBoundsAndLetterboxScrolling()1053 public void testTouchRegionUsesLetterboxBoundsIfNullTransformedBoundsAndLetterboxScrolling() { 1054 final WindowState win = createWindow(null, TYPE_APPLICATION, "win"); 1055 1056 // Fragment bounds used for size of touchable region if letterbox inner bounds are empty 1057 // and Transform bounds are null. 1058 doReturn(null).when(win.mToken).getFixedRotationTransformDisplayBounds(); 1059 final Rect fragmentBounds = new Rect(0, 0, 300, 500); 1060 final TaskFragment taskFragment = win.mActivityRecord.getTaskFragment(); 1061 doAnswer(invocation -> { 1062 Rect rect = invocation.getArgument(0); 1063 rect.set(fragmentBounds); 1064 return null; 1065 }).when(taskFragment).getDimBounds(any()); 1066 1067 // Otherwise, touchable region should match letterbox inner bounds. 1068 final Rect letterboxInnerBounds = new Rect(30, 0, 270, 500); 1069 doAnswer(invocation -> { 1070 Rect rect = invocation.getArgument(0); 1071 rect.set(letterboxInnerBounds); 1072 return null; 1073 }).when(win.mActivityRecord).getLetterboxInnerBounds(any()); 1074 1075 Region outRegion = new Region(); 1076 win.getSurfaceTouchableRegion(outRegion, win.mAttrs); 1077 1078 // Because scrollingFromLetterbox flag is disabled and letterboxInnerBounds is not empty, 1079 // touchable region should match letterboxInnerBounds always. 1080 assertEquals(letterboxInnerBounds, outRegion.getBounds()); 1081 } 1082 1083 @EnableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX) 1084 @Test testTouchRegionUsesTransformedBoundsIfLetterboxScrolling()1085 public void testTouchRegionUsesTransformedBoundsIfLetterboxScrolling() { 1086 final WindowState win = createWindow(null, TYPE_APPLICATION, "win"); 1087 1088 // Transformed bounds used for size of touchable region if letterbox inner bounds are empty. 1089 final Rect transformedBounds = new Rect(0, 0, 300, 500); 1090 doReturn(transformedBounds).when(win.mToken).getFixedRotationTransformDisplayBounds(); 1091 1092 // Otherwise, touchable region should match letterbox inner bounds. 1093 final Rect letterboxInnerBounds = new Rect(30, 0, 270, 500); 1094 doAnswer(invocation -> { 1095 Rect rect = invocation.getArgument(0); 1096 rect.set(letterboxInnerBounds); 1097 return null; 1098 }).when(win.mActivityRecord).getLetterboxInnerBounds(any()); 1099 1100 Region outRegion = new Region(); 1101 win.getSurfaceTouchableRegion(outRegion, win.mAttrs); 1102 1103 // Because scrollingFromLetterbox flag is enabled and transformedBounds are non-null, 1104 // touchable region should match transformedBounds. 1105 assertEquals(transformedBounds, outRegion.getBounds()); 1106 } 1107 1108 @Test testHasActiveVisibleWindow()1109 public void testHasActiveVisibleWindow() { 1110 final int uid = ActivityBuilder.DEFAULT_FAKE_UID; 1111 1112 final WindowState app = createWindow(null, TYPE_APPLICATION, "app", uid); 1113 app.mActivityRecord.setVisible(false); 1114 app.mActivityRecord.setVisibility(false); 1115 assertFalse(mAtm.hasActiveVisibleWindow(uid)); 1116 1117 app.mActivityRecord.setVisibility(true); 1118 assertTrue(mAtm.hasActiveVisibleWindow(uid)); 1119 1120 // Make the activity invisible and add a visible toast. The uid should have no active 1121 // visible window because toast can be misused by legacy app to bypass background check. 1122 app.mActivityRecord.setVisibility(false); 1123 final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay", uid); 1124 final WindowState toast = createWindow(null, TYPE_TOAST, app.mToken, "toast", uid); 1125 toast.onSurfaceShownChanged(true); 1126 assertFalse(mAtm.hasActiveVisibleWindow(uid)); 1127 1128 // Though starting window should belong to system. Make sure it is ignored to avoid being 1129 // allow-list unexpectedly, see b/129563343. 1130 final WindowState starting = 1131 createWindow(null, TYPE_APPLICATION_STARTING, app.mToken, "starting", uid); 1132 starting.onSurfaceShownChanged(true); 1133 assertFalse(mAtm.hasActiveVisibleWindow(uid)); 1134 1135 // Make the application overlay window visible. It should be a valid active visible window. 1136 overlay.onSurfaceShownChanged(true); 1137 assertTrue(mAtm.hasActiveVisibleWindow(uid)); 1138 1139 // The number of windows should be independent of the existence of uid state. 1140 mAtm.mActiveUids.onUidInactive(uid); 1141 mAtm.mActiveUids.onUidActive(uid, 0 /* any proc state */); 1142 assertTrue(mAtm.mActiveUids.hasNonAppVisibleWindow(uid)); 1143 } 1144 1145 @SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD }) 1146 @Test testNeedsRelativeLayeringToIme_notAttached()1147 public void testNeedsRelativeLayeringToIme_notAttached() { 1148 WindowState sameTokenWindow = createWindow(null, TYPE_BASE_APPLICATION, mAppWindow.mToken, 1149 "SameTokenWindow"); 1150 mDisplayContent.setImeLayeringTarget(mAppWindow); 1151 makeWindowVisible(mImeWindow); 1152 sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1153 assertTrue(sameTokenWindow.needsRelativeLayeringToIme()); 1154 sameTokenWindow.removeImmediately(); 1155 assertFalse(sameTokenWindow.needsRelativeLayeringToIme()); 1156 } 1157 1158 @SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD }) 1159 @Test testNeedsRelativeLayeringToIme_startingWindow()1160 public void testNeedsRelativeLayeringToIme_startingWindow() { 1161 WindowState sameTokenWindow = createWindow(null, TYPE_APPLICATION_STARTING, 1162 mAppWindow.mToken, "SameTokenWindow"); 1163 mDisplayContent.setImeLayeringTarget(mAppWindow); 1164 makeWindowVisible(mImeWindow); 1165 sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1166 assertFalse(sameTokenWindow.needsRelativeLayeringToIme()); 1167 } 1168 1169 @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD}) 1170 @Test testNeedsRelativeLayeringToIme_systemDialog()1171 public void testNeedsRelativeLayeringToIme_systemDialog() { 1172 WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY, 1173 mDisplayContent, 1174 "SystemDialog", true); 1175 mDisplayContent.setImeLayeringTarget(mAppWindow); 1176 mAppWindow.getTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1177 makeWindowVisible(mImeWindow); 1178 systemDialogWindow.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; 1179 assertTrue(systemDialogWindow.needsRelativeLayeringToIme()); 1180 } 1181 1182 @UseTestDisplay(addWindows = {W_INPUT_METHOD}) 1183 @Test testNeedsRelativeLayeringToIme_notificationShadeShouldNotHideSystemDialog()1184 public void testNeedsRelativeLayeringToIme_notificationShadeShouldNotHideSystemDialog() { 1185 WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY, 1186 mDisplayContent, 1187 "SystemDialog", true); 1188 mDisplayContent.setImeLayeringTarget(systemDialogWindow); 1189 makeWindowVisible(mImeWindow); 1190 WindowState notificationShade = createWindow(null, TYPE_NOTIFICATION_SHADE, 1191 mDisplayContent, "NotificationShade", true); 1192 notificationShade.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM; 1193 assertFalse(notificationShade.needsRelativeLayeringToIme()); 1194 } 1195 1196 @Test testSetFreezeInsetsState()1197 public void testSetFreezeInsetsState() { 1198 final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); 1199 spyOn(app); 1200 doReturn(true).when(app).isVisible(); 1201 1202 // Set freezing the insets state to make the window ignore to dispatch insets changed. 1203 final InsetsState expectedState = new InsetsState(app.getInsetsState(), 1204 true /* copySources */); 1205 app.freezeInsetsState(); 1206 assertEquals(expectedState, app.getFrozenInsetsState()); 1207 assertFalse(app.isReadyToDispatchInsetsState()); 1208 assertEquals(expectedState, app.getInsetsState()); 1209 mDisplayContent.getInsetsStateController().notifyInsetsChanged(); 1210 verify(app, never()).notifyInsetsChanged(); 1211 1212 // Unfreeze the insets state to make the window can dispatch insets changed. 1213 app.clearFrozenInsetsState(); 1214 assertTrue(app.isReadyToDispatchInsetsState()); 1215 mDisplayContent.getInsetsStateController().notifyInsetsChanged(); 1216 verify(app).notifyInsetsChanged(); 1217 1218 // Verify that invisible non-activity window won't dispatch insets changed. 1219 final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay"); 1220 makeWindowVisible(overlay); 1221 assertTrue(overlay.isReadyToDispatchInsetsState()); 1222 overlay.mHasSurface = false; 1223 assertFalse(overlay.isReadyToDispatchInsetsState()); 1224 mDisplayContent.getInsetsStateController().notifyInsetsChanged(); 1225 assertFalse(overlay.getWindowFrames().hasInsetsChanged()); 1226 } 1227 1228 @SetupWindows(addWindows = { W_INPUT_METHOD, W_ACTIVITY }) 1229 @Test testImeAlwaysReceivesVisibleNavigationBarInsets()1230 public void testImeAlwaysReceivesVisibleNavigationBarInsets() { 1231 final int navId = InsetsSource.createId(null, 0, navigationBars()); 1232 final InsetsSource navSource = new InsetsSource(navId, navigationBars()); 1233 mImeWindow.mAboveInsetsState.addSource(navSource); 1234 mAppWindow.mAboveInsetsState.addSource(navSource); 1235 1236 navSource.setVisible(false); 1237 assertTrue(mImeWindow.getInsetsState().isSourceOrDefaultVisible(navId, navigationBars())); 1238 assertFalse(mAppWindow.getInsetsState().isSourceOrDefaultVisible(navId, navigationBars())); 1239 1240 navSource.setVisible(true); 1241 assertTrue(mImeWindow.getInsetsState().isSourceOrDefaultVisible(navId, navigationBars())); 1242 assertTrue(mAppWindow.getInsetsState().isSourceOrDefaultVisible(navId, navigationBars())); 1243 } 1244 1245 @Test testAdjustImeInsetsVisibilityWhenSwitchingApps()1246 public void testAdjustImeInsetsVisibilityWhenSwitchingApps() { 1247 final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); 1248 final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2"); 1249 final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); 1250 spyOn(imeWindow); 1251 doReturn(true).when(imeWindow).isVisible(); 1252 mDisplayContent.mInputMethodWindow = imeWindow; 1253 1254 final InsetsStateController controller = mDisplayContent.getInsetsStateController(); 1255 controller.getImeSourceProvider().setWindowContainer(imeWindow, null, null); 1256 1257 // Simulate app requests IME with updating all windows Insets State when IME is above app. 1258 mDisplayContent.setImeLayeringTarget(app); 1259 mDisplayContent.setImeInputTarget(app); 1260 app.setRequestedVisibleTypes(ime(), ime()); 1261 assertTrue(mDisplayContent.shouldImeAttachedToApp()); 1262 controller.getImeSourceProvider().scheduleShowImePostLayout(app, ImeTracker.Token.empty()); 1263 controller.getImeSourceProvider().getSource().setVisible(true); 1264 controller.updateAboveInsetsState(false); 1265 1266 // Expect all app windows behind IME can receive IME insets visible. 1267 assertTrue(app.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); 1268 assertTrue(app2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); 1269 1270 // Simulate app plays closing transition to app2. 1271 app.mActivityRecord.commitVisibility(false, false); 1272 assertTrue(app.mActivityRecord.mLastImeShown); 1273 assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); 1274 1275 // Verify the IME insets is visible on app, but not for app2 during app task switching. 1276 assertTrue(app.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); 1277 assertFalse(app2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); 1278 } 1279 1280 @Test testAdjustImeInsetsVisibilityWhenSwitchingApps_toAppInMultiWindowMode()1281 public void testAdjustImeInsetsVisibilityWhenSwitchingApps_toAppInMultiWindowMode() { 1282 final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); 1283 final WindowState app2 = createWindow(null, WINDOWING_MODE_MULTI_WINDOW, 1284 ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app2"); 1285 final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow"); 1286 spyOn(imeWindow); 1287 doReturn(true).when(imeWindow).isVisible(); 1288 mDisplayContent.mInputMethodWindow = imeWindow; 1289 1290 final InsetsStateController controller = mDisplayContent.getInsetsStateController(); 1291 controller.getImeSourceProvider().setWindowContainer(imeWindow, null, null); 1292 1293 // Simulate app2 in multi-window mode is going to background to switch to the fullscreen 1294 // app which requests IME with updating all windows Insets State when IME is above app. 1295 app2.mActivityRecord.mImeInsetsFrozenUntilStartInput = true; 1296 mDisplayContent.setImeLayeringTarget(app); 1297 mDisplayContent.setImeInputTarget(app); 1298 app.setRequestedVisibleTypes(ime(), ime()); 1299 assertTrue(mDisplayContent.shouldImeAttachedToApp()); 1300 controller.getImeSourceProvider().scheduleShowImePostLayout(app, ImeTracker.Token.empty()); 1301 controller.getImeSourceProvider().getSource().setVisible(true); 1302 controller.updateAboveInsetsState(false); 1303 1304 // Expect app windows behind IME can receive IME insets visible, 1305 // but not for app2 in background. 1306 assertTrue(app.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); 1307 assertFalse(app2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); 1308 1309 // Simulate app plays closing transition to app2. 1310 // And app2 is now IME layering target but not yet to be the IME input target. 1311 mDisplayContent.setImeLayeringTarget(app2); 1312 app.mActivityRecord.commitVisibility(false, false); 1313 assertTrue(app.mActivityRecord.mLastImeShown); 1314 assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); 1315 1316 // Verify the IME insets is still visible on app, but not for app2 during task switching. 1317 assertTrue(app.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); 1318 assertFalse(app2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime())); 1319 } 1320 1321 @SetupWindows(addWindows = W_ACTIVITY) 1322 @Test testUpdateImeControlTargetWhenLeavingMultiWindow()1323 public void testUpdateImeControlTargetWhenLeavingMultiWindow() { 1324 WindowState app = createWindow(null, TYPE_BASE_APPLICATION, 1325 mAppWindow.mToken, "app"); 1326 mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController()); 1327 1328 spyOn(app); 1329 mDisplayContent.setImeInputTarget(mAppWindow); 1330 mDisplayContent.setImeLayeringTarget(mAppWindow); 1331 1332 // Simulate entering multi-window mode and verify if the IME control target is remote. 1333 app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1334 assertEquals(WINDOWING_MODE_MULTI_WINDOW, app.getWindowingMode()); 1335 assertEquals(mDisplayContent.mRemoteInsetsControlTarget, 1336 mDisplayContent.computeImeControlTarget()); 1337 1338 // Simulate exiting multi-window mode and verify if the IME control target changed 1339 // to the app window. 1340 spyOn(app.getDisplayContent()); 1341 app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); 1342 1343 // Expect updateImeParent will be invoked when the configuration of the IME control 1344 // target has changed. 1345 verify(app.getDisplayContent()).updateImeControlTarget(eq(true) /* updateImeParent */); 1346 assertEquals(mAppWindow, mDisplayContent.getImeTarget(IME_TARGET_CONTROL).getWindow()); 1347 } 1348 1349 @SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD, W_NOTIFICATION_SHADE }) 1350 @Test testNotificationShadeHasImeInsetsWhenMultiWindow()1351 public void testNotificationShadeHasImeInsetsWhenMultiWindow() { 1352 WindowState app = createWindow(null, TYPE_BASE_APPLICATION, 1353 mAppWindow.mToken, "app"); 1354 1355 // Simulate entering multi-window mode and windowing mode is multi-window. 1356 app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 1357 assertEquals(WINDOWING_MODE_MULTI_WINDOW, app.getWindowingMode()); 1358 1359 // Simulate notificationShade is shown and being IME layering target. 1360 mNotificationShadeWindow.setHasSurface(true); 1361 mNotificationShadeWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE; 1362 assertTrue(mNotificationShadeWindow.canBeImeTarget()); 1363 mDisplayContent.getInsetsStateController().getOrCreateSourceProvider(ID_IME, ime()) 1364 .setWindowContainer(mImeWindow, null, null); 1365 1366 mDisplayContent.computeImeTarget(true); 1367 assertEquals(mNotificationShadeWindow, mDisplayContent.getImeTarget(IME_TARGET_LAYERING)); 1368 mDisplayContent.getInsetsStateController().getRawInsetsState() 1369 .setSourceVisible(ID_IME, true); 1370 1371 // Verify notificationShade can still get IME insets even windowing mode is multi-window. 1372 InsetsState state = mNotificationShadeWindow.getInsetsState(); 1373 assertNotNull(state.peekSource(ID_IME)); 1374 assertTrue(state.isSourceOrDefaultVisible(ID_IME, ime())); 1375 } 1376 1377 @Test testRequestedVisibility()1378 public void testRequestedVisibility() { 1379 final WindowState app = createWindow(null, TYPE_APPLICATION, "app"); 1380 app.mActivityRecord.setVisible(false); 1381 app.mActivityRecord.setVisibility(false); 1382 assertFalse(app.isVisibleRequested()); 1383 1384 // It doesn't have a surface yet, but should still be visible requested. 1385 app.setHasSurface(false); 1386 app.mActivityRecord.setVisibility(true); 1387 1388 assertFalse(app.isVisible()); 1389 assertTrue(app.isVisibleRequested()); 1390 } 1391 1392 @Test testKeepClearAreas()1393 public void testKeepClearAreas() { 1394 final WindowState window = createWindow(null, TYPE_APPLICATION, "window"); 1395 makeWindowVisible(window); 1396 1397 final Rect keepClearArea1 = new Rect(0, 0, 10, 10); 1398 final Rect keepClearArea2 = new Rect(5, 10, 15, 20); 1399 final List<Rect> keepClearAreas = Arrays.asList(keepClearArea1, keepClearArea2); 1400 window.setKeepClearAreas(keepClearAreas, Collections.emptyList()); 1401 1402 // Test that the keep-clear rects are stored and returned 1403 final List<Rect> windowKeepClearAreas = new ArrayList(); 1404 window.getKeepClearAreas(windowKeepClearAreas, new ArrayList()); 1405 assertEquals(new ArraySet(keepClearAreas), new ArraySet(windowKeepClearAreas)); 1406 1407 // Test that keep-clear rects are overwritten 1408 window.setKeepClearAreas(Collections.emptyList(), Collections.emptyList()); 1409 windowKeepClearAreas.clear(); 1410 window.getKeepClearAreas(windowKeepClearAreas, new ArrayList()); 1411 assertEquals(0, windowKeepClearAreas.size()); 1412 1413 // Move the window position 1414 final SurfaceControl.Transaction t = spy(StubTransaction.class); 1415 window.mSurfaceControl = mock(SurfaceControl.class); 1416 final Rect frame = window.getFrame(); 1417 frame.set(10, 20, 60, 80); 1418 window.updateSurfacePosition(t); 1419 assertEquals(new Point(frame.left, frame.top), window.mLastSurfacePosition); 1420 1421 // Test that the returned keep-clear rects are translated to display space 1422 window.setKeepClearAreas(keepClearAreas, Collections.emptyList()); 1423 Rect expectedArea1 = new Rect(keepClearArea1); 1424 expectedArea1.offset(frame.left, frame.top); 1425 Rect expectedArea2 = new Rect(keepClearArea2); 1426 expectedArea2.offset(frame.left, frame.top); 1427 1428 windowKeepClearAreas.clear(); 1429 window.getKeepClearAreas(windowKeepClearAreas, new ArrayList()); 1430 assertEquals(new ArraySet(Arrays.asList(expectedArea1, expectedArea2)), 1431 new ArraySet(windowKeepClearAreas)); 1432 } 1433 1434 @Test testUnrestrictedKeepClearAreas()1435 public void testUnrestrictedKeepClearAreas() { 1436 final WindowState window = createWindow(null, TYPE_APPLICATION, "window"); 1437 makeWindowVisible(window); 1438 1439 final Rect keepClearArea1 = new Rect(0, 0, 10, 10); 1440 final Rect keepClearArea2 = new Rect(5, 10, 15, 20); 1441 final List<Rect> keepClearAreas = Arrays.asList(keepClearArea1, keepClearArea2); 1442 window.setKeepClearAreas(Collections.emptyList(), keepClearAreas); 1443 1444 // Test that the keep-clear rects are stored and returned 1445 final List<Rect> restrictedKeepClearAreas = new ArrayList(); 1446 final List<Rect> unrestrictedKeepClearAreas = new ArrayList(); 1447 window.getKeepClearAreas(restrictedKeepClearAreas, unrestrictedKeepClearAreas); 1448 assertEquals(Collections.emptySet(), new ArraySet(restrictedKeepClearAreas)); 1449 assertEquals(new ArraySet(keepClearAreas), new ArraySet(unrestrictedKeepClearAreas)); 1450 1451 // Test that keep-clear rects are overwritten 1452 window.setKeepClearAreas(Collections.emptyList(), Collections.emptyList()); 1453 unrestrictedKeepClearAreas.clear(); 1454 window.getKeepClearAreas(unrestrictedKeepClearAreas, new ArrayList()); 1455 assertEquals(0, unrestrictedKeepClearAreas.size()); 1456 1457 // Move the window position 1458 final SurfaceControl.Transaction t = spy(StubTransaction.class); 1459 window.mSurfaceControl = mock(SurfaceControl.class); 1460 final Rect frame = window.getFrame(); 1461 frame.set(10, 20, 60, 80); 1462 window.updateSurfacePosition(t); 1463 assertEquals(new Point(frame.left, frame.top), window.mLastSurfacePosition); 1464 1465 // Test that the returned keep-clear rects are translated to display space 1466 window.setKeepClearAreas(Collections.emptyList(), keepClearAreas); 1467 Rect expectedArea1 = new Rect(keepClearArea1); 1468 expectedArea1.offset(frame.left, frame.top); 1469 Rect expectedArea2 = new Rect(keepClearArea2); 1470 expectedArea2.offset(frame.left, frame.top); 1471 1472 unrestrictedKeepClearAreas.clear(); 1473 window.getKeepClearAreas(restrictedKeepClearAreas, unrestrictedKeepClearAreas); 1474 assertEquals(Collections.emptySet(), new ArraySet(restrictedKeepClearAreas)); 1475 assertEquals(new ArraySet(Arrays.asList(expectedArea1, expectedArea2)), 1476 new ArraySet(unrestrictedKeepClearAreas)); 1477 } 1478 1479 @Test testImeTargetChangeListener_OnImeInputTargetVisibilityChanged()1480 public void testImeTargetChangeListener_OnImeInputTargetVisibilityChanged() { 1481 final InputMethodManagerInternal immi = InputMethodManagerInternal.get(); 1482 spyOn(immi); 1483 1484 final WindowState imeTarget = createWindow(null /* parent */, TYPE_BASE_APPLICATION, 1485 createActivityRecord(mDisplayContent), "imeTarget"); 1486 1487 imeTarget.mActivityRecord.setVisibleRequested(true); 1488 makeWindowVisible(imeTarget); 1489 mDisplayContent.setImeInputTarget(imeTarget); 1490 waitHandlerIdle(mWm.mH); 1491 verify(immi).onImeInputTargetVisibilityChanged(imeTarget.mClient.asBinder(), 1492 true /* visibleAndNotRemoved */, mDisplayContent.getDisplayId()); 1493 reset(immi); 1494 1495 imeTarget.mActivityRecord.setVisibleRequested(false); 1496 waitHandlerIdle(mWm.mH); 1497 verify(immi).onImeInputTargetVisibilityChanged(imeTarget.mClient.asBinder(), 1498 false /* visibleAndNotRemoved */, mDisplayContent.getDisplayId()); 1499 reset(immi); 1500 1501 imeTarget.removeImmediately(); 1502 verify(immi).onImeInputTargetVisibilityChanged(imeTarget.mClient.asBinder(), 1503 false /* visibleAndNotRemoved */, mDisplayContent.getDisplayId()); 1504 } 1505 1506 @SetupWindows(addWindows = {W_INPUT_METHOD}) 1507 @Test testImeTargetChangeListener_OnImeTargetOverlayVisibilityChanged()1508 public void testImeTargetChangeListener_OnImeTargetOverlayVisibilityChanged() { 1509 final InputMethodManagerInternal immi = InputMethodManagerInternal.get(); 1510 spyOn(immi); 1511 1512 // Scenario 1: test addWindow/relayoutWindow to add Ime layering overlay window as visible. 1513 final WindowToken windowToken = createTestWindowToken(TYPE_APPLICATION_OVERLAY, 1514 mDisplayContent); 1515 final IWindow client = new TestIWindow(); 1516 final Session session = getTestSession(); 1517 final ClientWindowFrames outFrames = new ClientWindowFrames(); 1518 final MergedConfiguration outConfig = new MergedConfiguration(); 1519 final SurfaceControl outSurfaceControl = new SurfaceControl(); 1520 final InsetsState outInsetsState = new InsetsState(); 1521 final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array(); 1522 final WindowRelayoutResult outRelayoutResult = new WindowRelayoutResult(outFrames, 1523 outConfig, outSurfaceControl, outInsetsState, outControls); 1524 final WindowManager.LayoutParams params = new WindowManager.LayoutParams( 1525 TYPE_APPLICATION_OVERLAY); 1526 params.setTitle("imeLayeringTargetOverlay"); 1527 params.token = windowToken.token; 1528 params.flags = FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM; 1529 1530 mWm.addWindow(session, client, params, View.VISIBLE, DEFAULT_DISPLAY, 1531 0 /* userUd */, WindowInsets.Type.defaultVisible(), null, new InsetsState(), 1532 new InsetsSourceControl.Array(), new Rect(), new float[1]); 1533 mWm.relayoutWindow(session, client, params, 100, 200, View.VISIBLE, 0, 0, 0, 1534 outRelayoutResult); 1535 waitHandlerIdle(mWm.mH); 1536 1537 final WindowState imeLayeringTargetOverlay = mDisplayContent.getWindow( 1538 w -> w.mClient.asBinder() == client.asBinder()); 1539 assertThat(imeLayeringTargetOverlay.isVisible()).isTrue(); 1540 verify(immi, atLeast(1)) 1541 .setHasVisibleImeLayeringOverlay(true /* hasVisibleOverlay */, 1542 mDisplayContent.getDisplayId()); 1543 reset(immi); 1544 1545 // Scenario 2: test relayoutWindow to let the Ime layering target overlay window invisible. 1546 mWm.relayoutWindow(session, client, params, 100, 200, View.GONE, 0, 0, 0, 1547 outRelayoutResult); 1548 waitHandlerIdle(mWm.mH); 1549 1550 assertThat(imeLayeringTargetOverlay.isVisible()).isFalse(); 1551 verify(immi).setHasVisibleImeLayeringOverlay(false /* hasVisibleOverlay */, 1552 mDisplayContent.getDisplayId()); 1553 reset(immi); 1554 1555 // Scenario 3: test removeWindow to remove the Ime layering target overlay window. 1556 mWm.removeClientToken(session, client.asBinder()); 1557 waitHandlerIdle(mWm.mH); 1558 1559 verify(immi).setHasVisibleImeLayeringOverlay(false /* hasVisibleOverlay */, 1560 mDisplayContent.getDisplayId()); 1561 } 1562 1563 @Test testIsSecureLocked_flagSecureSet()1564 public void testIsSecureLocked_flagSecureSet() { 1565 WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window", 1566 1 /* ownerId */); 1567 window.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SECURE; 1568 1569 assertTrue(window.isSecureLocked()); 1570 } 1571 1572 @Test testIsSecureLocked_flagSecureNotSet()1573 public void testIsSecureLocked_flagSecureNotSet() { 1574 WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window", 1575 1 /* ownerId */); 1576 1577 assertFalse(window.isSecureLocked()); 1578 } 1579 1580 @Test testIsSecureLocked_disableSecureWindows()1581 public void testIsSecureLocked_disableSecureWindows() { 1582 assumeTrue(Build.IS_DEBUGGABLE); 1583 1584 WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window", 1585 1 /* ownerId */); 1586 window.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SECURE; 1587 ContentResolver cr = useFakeSettingsProvider(); 1588 1589 // isSecureLocked should return false when DISABLE_SECURE_WINDOWS is set to 1 1590 Settings.Secure.putString(cr, Settings.Secure.DISABLE_SECURE_WINDOWS, "1"); 1591 mWm.mSettingsObserver.onChange(false /* selfChange */, 1592 Settings.Secure.getUriFor(Settings.Secure.DISABLE_SECURE_WINDOWS)); 1593 assertFalse(window.isSecureLocked()); 1594 1595 // isSecureLocked should return true if DISABLE_SECURE_WINDOWS is set to 0. 1596 Settings.Secure.putString(cr, Settings.Secure.DISABLE_SECURE_WINDOWS, "0"); 1597 mWm.mSettingsObserver.onChange(false /* selfChange */, 1598 Settings.Secure.getUriFor(Settings.Secure.DISABLE_SECURE_WINDOWS)); 1599 assertTrue(window.isSecureLocked()); 1600 1601 // Disable secure windows again. 1602 Settings.Secure.putString(cr, Settings.Secure.DISABLE_SECURE_WINDOWS, "1"); 1603 mWm.mSettingsObserver.onChange(false /* selfChange */, 1604 Settings.Secure.getUriFor(Settings.Secure.DISABLE_SECURE_WINDOWS)); 1605 assertFalse(window.isSecureLocked()); 1606 1607 // isSecureLocked should return true if DISABLE_SECURE_WINDOWS is deleted. 1608 Settings.Secure.putString(cr, Settings.Secure.DISABLE_SECURE_WINDOWS, null); 1609 mWm.mSettingsObserver.onChange(false /* selfChange */, 1610 Settings.Secure.getUriFor(Settings.Secure.DISABLE_SECURE_WINDOWS)); 1611 assertTrue(window.isSecureLocked()); 1612 } 1613 1614 @Test 1615 @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION) testIsSecureLocked_sensitiveContentProtectionManagerEnabled()1616 public void testIsSecureLocked_sensitiveContentProtectionManagerEnabled() { 1617 String testPackage = "test"; 1618 int ownerId1 = 20; 1619 int ownerId2 = 21; 1620 final WindowState window1 = createWindow(null, TYPE_APPLICATION, "window1", ownerId1); 1621 final WindowState window2 = createWindow(null, TYPE_APPLICATION, "window2", ownerId2); 1622 1623 // Setting packagename for targeted feature 1624 window1.mAttrs.packageName = testPackage; 1625 window2.mAttrs.packageName = testPackage; 1626 1627 PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId1); 1628 ArraySet<PackageInfo> blockedPackages = new ArraySet(); 1629 blockedPackages.add(blockedPackage); 1630 mWm.mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedPackages); 1631 1632 assertTrue(window1.isSecureLocked()); 1633 assertFalse(window2.isSecureLocked()); 1634 } 1635 1636 @Test 1637 @RequiresFlagsEnabled(FLAG_SENSITIVE_NOTIFICATION_APP_PROTECTION) testIsSecureLocked_sensitiveContentBlockOrClearScreenCaptureForApp()1638 public void testIsSecureLocked_sensitiveContentBlockOrClearScreenCaptureForApp() { 1639 String testPackage = "test"; 1640 int ownerId = 20; 1641 final WindowState window = createWindow(null, TYPE_APPLICATION, "window", ownerId); 1642 window.mAttrs.packageName = testPackage; 1643 assertFalse(window.isSecureLocked()); 1644 1645 PackageInfo blockedPackage = new PackageInfo(testPackage, ownerId); 1646 ArraySet<PackageInfo> blockedPackages = new ArraySet(); 1647 blockedPackages.add(blockedPackage); 1648 mWm.mSensitiveContentPackages.addBlockScreenCaptureForApps(blockedPackages); 1649 assertTrue(window.isSecureLocked()); 1650 1651 mWm.mSensitiveContentPackages.removeBlockScreenCaptureForApps(blockedPackages); 1652 assertFalse(window.isSecureLocked()); 1653 } 1654 } 1655