1 /* 2 * Copyright (C) 2020 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 20 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; 21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; 22 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 24 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 25 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 26 import static android.view.Surface.ROTATION_90; 27 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 28 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; 29 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; 30 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 31 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; 32 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; 33 import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; 34 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; 35 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; 36 37 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 38 import static com.android.server.wm.SizeCompatTests.prepareLimitedBounds; 39 import static com.android.server.wm.SizeCompatTests.prepareUnresizable; 40 import static com.android.server.wm.SizeCompatTests.rotateDisplay; 41 42 import static com.google.common.truth.Truth.assertThat; 43 44 import static org.mockito.ArgumentMatchers.any; 45 import static org.mockito.Mockito.doReturn; 46 import static org.mockito.Mockito.mock; 47 import static org.mockito.Mockito.never; 48 import static org.mockito.Mockito.verify; 49 import static org.mockito.Mockito.when; 50 51 import android.content.res.Configuration; 52 import android.graphics.Rect; 53 import android.os.Binder; 54 import android.platform.test.annotations.Presubmit; 55 import android.view.Display; 56 import android.window.IDisplayAreaOrganizer; 57 58 import androidx.test.filters.SmallTest; 59 60 import org.junit.Before; 61 import org.junit.Test; 62 import org.junit.runner.RunWith; 63 64 import java.util.ArrayList; 65 import java.util.List; 66 67 /** 68 * Tests for the Dual DisplayAreaGroup device behavior. 69 * 70 * Build/Install/Run: 71 * atest WmTests:DualDisplayAreaGroupPolicyTest 72 */ 73 @SmallTest 74 @Presubmit 75 @RunWith(WindowTestRunner.class) 76 public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { 77 private static final int FEATURE_FIRST_ROOT = FEATURE_VENDOR_FIRST; 78 private static final int FEATURE_FIRST_TASK_CONTAINER = FEATURE_DEFAULT_TASK_CONTAINER; 79 private static final int FEATURE_SECOND_ROOT = FEATURE_VENDOR_FIRST + 1; 80 private static final int FEATURE_SECOND_TASK_CONTAINER = FEATURE_VENDOR_FIRST + 2; 81 82 private DualDisplayContent mDisplay; 83 private DisplayAreaGroup mFirstRoot; 84 private DisplayAreaGroup mSecondRoot; 85 private TaskDisplayArea mFirstTda; 86 private TaskDisplayArea mSecondTda; 87 private Task mFirstTask; 88 private Task mSecondTask; 89 private ActivityRecord mFirstActivity; 90 private ActivityRecord mSecondActivity; 91 92 @Before setUp()93 public void setUp() { 94 // Let the Display to be created with the DualDisplay policy. 95 final DisplayAreaPolicy.Provider policyProvider = new DualDisplayTestPolicyProvider(); 96 doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider(); 97 98 // Display: 1920x1200 (landscape). First and second display are both 860x1200 (portrait). 99 mDisplay = new DualDisplayContent.Builder(mAtm, 1920, 1200).build(); 100 mFirstRoot = mDisplay.mFirstRoot; 101 mSecondRoot = mDisplay.mSecondRoot; 102 mFirstTda = mDisplay.getTaskDisplayArea(FEATURE_FIRST_TASK_CONTAINER); 103 mSecondTda = mDisplay.getTaskDisplayArea(FEATURE_SECOND_TASK_CONTAINER); 104 mFirstTask = new TaskBuilder(mSupervisor) 105 .setTaskDisplayArea(mFirstTda) 106 .setCreateActivity(true) 107 .build() 108 .getBottomMostTask(); 109 mSecondTask = new TaskBuilder(mSupervisor) 110 .setTaskDisplayArea(mSecondTda) 111 .setCreateActivity(true) 112 .build() 113 .getBottomMostTask(); 114 mFirstActivity = mFirstTask.getTopNonFinishingActivity(); 115 mSecondActivity = mSecondTask.getTopNonFinishingActivity(); 116 117 spyOn(mDisplay); 118 spyOn(mFirstRoot); 119 spyOn(mSecondRoot); 120 } 121 122 @Test testNotIgnoreOrientationRequest_differentOrientationFromDisplay_reversesRequest()123 public void testNotIgnoreOrientationRequest_differentOrientationFromDisplay_reversesRequest() { 124 mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); 125 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 126 127 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); 128 129 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); 130 assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); 131 132 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT); 133 134 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 135 assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); 136 } 137 138 @Test testNotIgnoreOrientationRequest_onlyRespectsFocusedTaskDisplayArea()139 public void testNotIgnoreOrientationRequest_onlyRespectsFocusedTaskDisplayArea() { 140 mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); 141 mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); 142 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 143 144 // Second TDA is not focused, so Display won't get the request 145 prepareUnresizable(mSecondActivity, SCREEN_ORIENTATION_LANDSCAPE); 146 147 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); 148 149 // First TDA is focused, so Display gets the request 150 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); 151 152 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); 153 } 154 155 @Test testIgnoreOrientationRequest_displayDoesNotReceiveOrientationChange()156 public void testIgnoreOrientationRequest_displayDoesNotReceiveOrientationChange() { 157 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 158 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 159 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 160 161 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); 162 163 verify(mFirstRoot).onDescendantOrientationChanged(any()); 164 verify(mDisplay, never()).onDescendantOrientationChanged(any()); 165 } 166 167 @Test testIgnoreOrientationRequest_displayReceiveOrientationChangeForNoSensor()168 public void testIgnoreOrientationRequest_displayReceiveOrientationChangeForNoSensor() { 169 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 170 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 171 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 172 173 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); 174 175 verify(mFirstRoot).onDescendantOrientationChanged(any()); 176 verify(mDisplay).onDescendantOrientationChanged(any()); 177 } 178 179 @Test testIgnoreOrientationRequest_displayReceiveOrientationChangeForLocked()180 public void testIgnoreOrientationRequest_displayReceiveOrientationChangeForLocked() { 181 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 182 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 183 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 184 185 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LOCKED); 186 187 verify(mFirstRoot).onDescendantOrientationChanged(any()); 188 verify(mDisplay).onDescendantOrientationChanged(any()); 189 } 190 191 @Test testLaunchPortraitApp_fillsDisplayAreaGroup()192 public void testLaunchPortraitApp_fillsDisplayAreaGroup() { 193 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 194 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 195 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 196 197 prepareLimitedBounds(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT, 198 false /* isUnresizable */); 199 final Rect dagBounds = new Rect(mFirstRoot.getBounds()); 200 final Rect taskBounds = new Rect(mFirstTask.getBounds()); 201 final Rect activityBounds = new Rect(mFirstActivity.getBounds()); 202 203 // DAG is portrait (860x1200), so Task and Activity fill DAG. 204 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 205 assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); 206 assertThat(taskBounds).isEqualTo(dagBounds); 207 assertThat(activityBounds).isEqualTo(taskBounds); 208 } 209 210 @Test testLaunchPortraitApp_sizeCompatAfterRotation()211 public void testLaunchPortraitApp_sizeCompatAfterRotation() { 212 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 213 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 214 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 215 216 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT); 217 final Rect dagBounds = new Rect(mFirstRoot.getBounds()); 218 final Rect activityBounds = new Rect(mFirstActivity.getBounds()); 219 220 rotateDisplay(mDisplay, ROTATION_90); 221 final Rect newDagBounds = new Rect(mFirstRoot.getBounds()); 222 final Rect newTaskBounds = new Rect(mFirstTask.getBounds()); 223 final Rect activitySizeCompatBounds = new Rect(mFirstActivity.getBounds()); 224 final Rect activityConfigBounds = 225 new Rect(mFirstActivity.getConfiguration().windowConfiguration.getBounds()); 226 227 // DAG is landscape (1200x860), no fixed orientation letterbox 228 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 229 assertThat(mFirstActivity.inSizeCompatMode()).isTrue(); 230 assertThat(newDagBounds.width()).isEqualTo(dagBounds.height()); 231 assertThat(newDagBounds.height()).isEqualTo(dagBounds.width()); 232 assertThat(newTaskBounds).isEqualTo(newDagBounds); 233 234 // Activity config bounds is unchanged, size compat bounds is (860x[860x860/1200=616]) 235 assertThat(mFirstActivity.getCompatScale()).isLessThan(1f); 236 assertThat(activityConfigBounds.width()).isEqualTo(activityBounds.width()); 237 assertThat(activityConfigBounds.height()).isEqualTo(activityBounds.height()); 238 assertThat(activitySizeCompatBounds.height()).isEqualTo(newTaskBounds.height()); 239 assertThat(activitySizeCompatBounds.width()).isEqualTo( 240 newTaskBounds.height() * newTaskBounds.height() / newTaskBounds.width()); 241 } 242 243 @Test testLaunchNoSensorApp_noSizeCompatAfterRotation()244 public void testLaunchNoSensorApp_noSizeCompatAfterRotation() { 245 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 246 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 247 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 248 249 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); 250 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 251 assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); 252 253 rotateDisplay(mDisplay, ROTATION_90); 254 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 255 assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); 256 } 257 258 @Test testLaunchLandscapeApp_activityIsLetterboxForFixedOrientationInDisplayAreaGroup()259 public void testLaunchLandscapeApp_activityIsLetterboxForFixedOrientationInDisplayAreaGroup() { 260 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 261 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 262 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 263 264 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); 265 final Rect dagBounds = new Rect(mFirstRoot.getBounds()); 266 final Rect taskBounds = new Rect(mFirstTask.getBounds()); 267 final Rect activityBounds = new Rect(mFirstActivity.getBounds()); 268 269 // DAG is portrait (860x1200), and activity is letterboxed for fixed orientation 270 // (860x[860x860/1200=616]). Task fills DAG. 271 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue(); 272 assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); 273 assertThat(taskBounds).isEqualTo(dagBounds); 274 assertThat(activityBounds.width()).isEqualTo(dagBounds.width()); 275 assertThat(activityBounds.height()) 276 .isEqualTo(dagBounds.width() * dagBounds.width() / dagBounds.height()); 277 } 278 279 @Test testLaunchNoSensorApp_activityIsNotLetterboxForFixedOrientationDisplayAreaGroup()280 public void testLaunchNoSensorApp_activityIsNotLetterboxForFixedOrientationDisplayAreaGroup() { 281 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 282 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 283 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 284 285 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); 286 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 287 } 288 289 @Test testLaunchLockedApp_activityIsNotLetterboxForFixedOrientationInDisplayAreaGroup()290 public void testLaunchLockedApp_activityIsNotLetterboxForFixedOrientationInDisplayAreaGroup() { 291 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 292 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 293 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 294 295 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LOCKED); 296 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 297 } 298 299 @Test testLaunchLandscapeApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation()300 public void testLaunchLandscapeApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() { 301 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 302 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 303 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 304 305 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_LANDSCAPE); 306 final Rect dagBounds = new Rect(mFirstRoot.getBounds()); 307 final Rect activityBounds = new Rect(mFirstActivity.getBounds()); 308 309 rotateDisplay(mDisplay, ROTATION_90); 310 final Rect newDagBounds = new Rect(mFirstRoot.getBounds()); 311 final Rect newTaskBounds = new Rect(mFirstTask.getBounds()); 312 final Rect newActivityBounds = new Rect(mFirstActivity.getBounds()); 313 314 // DAG is landscape (1200x860), no fixed orientation letterbox 315 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 316 assertThat(mFirstActivity.inSizeCompatMode()).isTrue(); 317 assertThat(newDagBounds.width()).isEqualTo(dagBounds.height()); 318 assertThat(newDagBounds.height()).isEqualTo(dagBounds.width()); 319 assertThat(newTaskBounds).isEqualTo(newDagBounds); 320 321 // Because we don't scale up, there is no size compat bounds and app bounds is the same as 322 // the previous bounds. 323 assertThat(mFirstActivity.hasSizeCompatBounds()).isFalse(); 324 assertThat(newActivityBounds.width()).isEqualTo(activityBounds.width()); 325 assertThat(newActivityBounds.height()).isEqualTo(activityBounds.height()); 326 } 327 328 @Test testLaunchNoSensorApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation()329 public void testLaunchNoSensorApp_fixedOrientationLetterboxBecomesSizeCompatAfterRotation() { 330 mFirstRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 331 mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); 332 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 333 334 prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_NOSENSOR); 335 336 rotateDisplay(mDisplay, ROTATION_90); 337 338 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 339 assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); 340 } 341 342 @Test testPlaceImeContainer_reparentToTargetDisplayAreaGroup()343 public void testPlaceImeContainer_reparentToTargetDisplayAreaGroup() { 344 setupImeWindow(); 345 final DisplayArea.Tokens imeContainer = mDisplay.getImeContainer(); 346 final WindowToken imeToken = tokenOfType(TYPE_INPUT_METHOD); 347 348 // By default, the ime container is attached to DC as defined in DAPolicy. 349 assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay); 350 assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer); 351 352 final WindowState firstActivityWin = 353 createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity, 354 "firstActivityWin"); 355 spyOn(firstActivityWin); 356 final WindowState secondActivityWin = 357 createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mSecondActivity, 358 "firstActivityWin"); 359 spyOn(secondActivityWin); 360 361 // firstActivityWin should be the target 362 doReturn(true).when(firstActivityWin).canBeImeTarget(); 363 doReturn(false).when(secondActivityWin).canBeImeTarget(); 364 365 WindowState imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */); 366 367 assertThat(imeTarget).isEqualTo(firstActivityWin); 368 verify(mFirstRoot).placeImeContainer(imeContainer); 369 assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mFirstRoot); 370 assertThat(imeContainer.getParent().asDisplayArea().mFeatureId) 371 .isEqualTo(FEATURE_IME_PLACEHOLDER); 372 assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isNull(); 373 assertThat(mFirstRoot.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer); 374 assertThat(mSecondRoot.findAreaForTokenInLayer(imeToken)).isNull(); 375 376 // secondActivityWin should be the target 377 doReturn(false).when(firstActivityWin).canBeImeTarget(); 378 doReturn(true).when(secondActivityWin).canBeImeTarget(); 379 380 imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */); 381 382 assertThat(imeTarget).isEqualTo(secondActivityWin); 383 verify(mSecondRoot).placeImeContainer(imeContainer); 384 assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mSecondRoot); 385 assertThat(imeContainer.getParent().asDisplayArea().mFeatureId) 386 .isEqualTo(FEATURE_IME_PLACEHOLDER); 387 assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isNull(); 388 assertThat(mFirstRoot.findAreaForTokenInLayer(imeToken)).isNull(); 389 assertThat(mSecondRoot.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer); 390 } 391 392 @Test testPlaceImeContainer_hidesImeWhenParentChanges()393 public void testPlaceImeContainer_hidesImeWhenParentChanges() { 394 setupImeWindow(); 395 final DisplayArea.Tokens imeContainer = mDisplay.getImeContainer(); 396 final WindowToken imeToken = tokenOfType(TYPE_INPUT_METHOD); 397 final WindowState firstActivityWin = 398 createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity, 399 "firstActivityWin"); 400 spyOn(firstActivityWin); 401 final WindowState secondActivityWin = 402 createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mSecondActivity, 403 "secondActivityWin"); 404 spyOn(secondActivityWin); 405 406 // firstActivityWin should be the target 407 doReturn(true).when(firstActivityWin).canBeImeTarget(); 408 doReturn(false).when(secondActivityWin).canBeImeTarget(); 409 410 WindowState imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */); 411 assertThat(imeTarget).isEqualTo(firstActivityWin); 412 verify(mFirstRoot).placeImeContainer(imeContainer); 413 414 // secondActivityWin should be the target 415 doReturn(false).when(firstActivityWin).canBeImeTarget(); 416 doReturn(true).when(secondActivityWin).canBeImeTarget(); 417 418 spyOn(mDisplay.mInputMethodWindow); 419 imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */); 420 421 assertThat(imeTarget).isEqualTo(secondActivityWin); 422 verify(mSecondRoot).placeImeContainer(imeContainer); 423 // verify hide() was called on InputMethodWindow. 424 verify(mDisplay.mInputMethodWindow).hide(false /* doAnimation */, false /* requestAnim */); 425 } 426 427 @Test testPlaceImeContainer_skipReparentForOrganizedImeContainer()428 public void testPlaceImeContainer_skipReparentForOrganizedImeContainer() { 429 setupImeWindow(); 430 final DisplayArea.Tokens imeContainer = mDisplay.getImeContainer(); 431 final WindowToken imeToken = tokenOfType(TYPE_INPUT_METHOD); 432 433 // By default, the ime container is attached to DC as defined in DAPolicy. 434 assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay); 435 assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer); 436 437 final WindowState firstActivityWin = 438 createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity, 439 "firstActivityWin"); 440 spyOn(firstActivityWin); 441 // firstActivityWin should be the target 442 doReturn(true).when(firstActivityWin).canBeImeTarget(); 443 444 // Main precondition for this test: organize the ImeContainer. 445 final IDisplayAreaOrganizer mockImeOrganizer = mock(IDisplayAreaOrganizer.class); 446 when(mockImeOrganizer.asBinder()).thenReturn(new Binder()); 447 imeContainer.setOrganizer(mockImeOrganizer); 448 449 WindowState imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */); 450 451 // The IME target must be updated but the don't reparent organized ImeContainers. 452 // See DisplayAreaOrganizer#FEATURE_IME. 453 assertThat(imeTarget).isEqualTo(firstActivityWin); 454 verify(mFirstRoot, never()).placeImeContainer(imeContainer); 455 456 // Clean up organizer. 457 imeContainer.setOrganizer(null); 458 } 459 460 @Test testResizableFixedOrientationApp_fixedOrientationLetterboxing()461 public void testResizableFixedOrientationApp_fixedOrientationLetterboxing() { 462 mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); 463 mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */); 464 465 // Launch portrait on first DAG 466 mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); 467 prepareLimitedBounds(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT, 468 false /* isUnresizable */); 469 470 // Display in landscape (as opposite to DAG), first DAG and activity in portrait 471 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); 472 assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); 473 assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); 474 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 475 assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); 476 477 // Launch portrait on second DAG 478 mDisplay.onLastFocusedTaskDisplayAreaChanged(mSecondTda); 479 prepareLimitedBounds(mSecondActivity, SCREEN_ORIENTATION_LANDSCAPE, 480 false /* isUnresizable */); 481 482 // Display in portrait (as opposite to DAG), first DAG and activity in landscape 483 assertThat(mDisplay.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_PORTRAIT); 484 assertThat(mSecondRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); 485 assertThat(mSecondActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); 486 assertThat(mSecondActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isFalse(); 487 assertThat(mSecondActivity.inSizeCompatMode()).isFalse(); 488 489 // First activity is letterboxed in portrait as requested. 490 assertThat(mFirstRoot.getConfiguration().orientation).isEqualTo(ORIENTATION_LANDSCAPE); 491 assertThat(mFirstActivity.getConfiguration().orientation).isEqualTo(ORIENTATION_PORTRAIT); 492 assertThat(mFirstActivity.isLetterboxedForFixedOrientationAndAspectRatio()).isTrue(); 493 assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); 494 495 } 496 setupImeWindow()497 private void setupImeWindow() { 498 final WindowState imeWindow = createWindow(null /* parent */, 499 TYPE_INPUT_METHOD, mDisplay, "mImeWindow"); 500 imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE; 501 mDisplay.mInputMethodWindow = imeWindow; 502 } 503 tokenOfType(int type)504 private WindowToken tokenOfType(int type) { 505 return new WindowToken.Builder(mWm, new Binder(), type) 506 .setDisplayContent(mDisplay).build(); 507 } 508 509 /** Display with two {@link DisplayAreaGroup}. Each of them take half of the screen. */ 510 static class DualDisplayContent extends TestDisplayContent { 511 final DisplayAreaGroup mFirstRoot; 512 final DisplayAreaGroup mSecondRoot; 513 final Rect mLastDisplayBounds; 514 515 /** Please use the {@link Builder} to create. */ DualDisplayContent(RootWindowContainer rootWindowContainer, Display display)516 DualDisplayContent(RootWindowContainer rootWindowContainer, 517 Display display) { 518 super(rootWindowContainer, display); 519 520 mFirstRoot = getGroupRoot(FEATURE_FIRST_ROOT); 521 mSecondRoot = getGroupRoot(FEATURE_SECOND_ROOT); 522 mLastDisplayBounds = new Rect(getBounds()); 523 updateDisplayAreaGroupBounds(); 524 } 525 getGroupRoot(int rootFeatureId)526 DisplayAreaGroup getGroupRoot(int rootFeatureId) { 527 DisplayArea da = getDisplayArea(rootFeatureId); 528 assertThat(da).isInstanceOf(DisplayAreaGroup.class); 529 return (DisplayAreaGroup) da; 530 } 531 getTaskDisplayArea(int tdaFeatureId)532 TaskDisplayArea getTaskDisplayArea(int tdaFeatureId) { 533 DisplayArea da = getDisplayArea(tdaFeatureId); 534 assertThat(da).isInstanceOf(TaskDisplayArea.class); 535 return (TaskDisplayArea) da; 536 } 537 getDisplayArea(int featureId)538 DisplayArea getDisplayArea(int featureId) { 539 final DisplayArea displayArea = 540 getItemFromDisplayAreas(da -> da.mFeatureId == featureId ? da : null); 541 assertThat(displayArea).isNotNull(); 542 return displayArea; 543 } 544 545 @Override onConfigurationChanged(Configuration newParentConfig)546 public void onConfigurationChanged(Configuration newParentConfig) { 547 super.onConfigurationChanged(newParentConfig); 548 549 final Rect curBounds = getBounds(); 550 if (mLastDisplayBounds != null && !mLastDisplayBounds.equals(curBounds)) { 551 mLastDisplayBounds.set(curBounds); 552 updateDisplayAreaGroupBounds(); 553 } 554 } 555 556 /** Updates first and second {@link DisplayAreaGroup} to take half of the screen. */ updateDisplayAreaGroupBounds()557 private void updateDisplayAreaGroupBounds() { 558 if (mFirstRoot == null || mSecondRoot == null) { 559 return; 560 } 561 562 final Rect bounds = mLastDisplayBounds; 563 Rect groupBounds1, groupBounds2; 564 if (bounds.width() >= bounds.height()) { 565 groupBounds1 = new Rect(bounds.left, bounds.top, 566 (bounds.right + bounds.left) / 2, bounds.bottom); 567 568 groupBounds2 = new Rect((bounds.right + bounds.left) / 2, bounds.top, 569 bounds.right, bounds.bottom); 570 } else { 571 groupBounds1 = new Rect(bounds.left, bounds.top, 572 bounds.right, (bounds.top + bounds.bottom) / 2); 573 574 groupBounds2 = new Rect(bounds.left, 575 (bounds.top + bounds.bottom) / 2, bounds.right, bounds.bottom); 576 } 577 mFirstRoot.setBounds(groupBounds1); 578 mSecondRoot.setBounds(groupBounds2); 579 } 580 581 static class Builder extends TestDisplayContent.Builder { 582 Builder(ActivityTaskManagerService service, int width, int height)583 Builder(ActivityTaskManagerService service, int width, int height) { 584 super(service, width, height); 585 } 586 587 @Override createInternal(Display display)588 TestDisplayContent createInternal(Display display) { 589 return new DualDisplayContent(mService.mRootWindowContainer, display); 590 } 591 build()592 DualDisplayContent build() { 593 return (DualDisplayContent) super.build(); 594 } 595 } 596 } 597 598 /** Policy to create a dual {@link DisplayAreaGroup} policy in test. */ 599 static class DualDisplayTestPolicyProvider implements DisplayAreaPolicy.Provider { 600 601 @Override instantiate(WindowManagerService wmService, DisplayContent content, RootDisplayArea root, DisplayArea.Tokens imeContainer)602 public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content, 603 RootDisplayArea root, DisplayArea.Tokens imeContainer) { 604 // Root 605 // Include FEATURE_WINDOWED_MAGNIFICATION because it will be used as the screen rotation 606 // layer 607 DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy = 608 new DisplayAreaPolicyBuilder.HierarchyBuilder(root) 609 .setImeContainer(imeContainer) 610 .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder( 611 wmService.mPolicy, 612 "WindowedMagnification", FEATURE_WINDOWED_MAGNIFICATION) 613 .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) 614 .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) 615 .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new) 616 .build()) 617 .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder( 618 wmService.mPolicy, 619 "ImePlaceholder", FEATURE_IME_PLACEHOLDER) 620 .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG) 621 .build()); 622 623 // First 624 final RootDisplayArea firstRoot = new DisplayAreaGroup(wmService, "FirstRoot", 625 FEATURE_FIRST_ROOT); 626 final TaskDisplayArea firstTaskDisplayArea = new TaskDisplayArea(content, wmService, 627 "FirstTaskDisplayArea", FEATURE_FIRST_TASK_CONTAINER); 628 final List<TaskDisplayArea> firstTdaList = new ArrayList<>(); 629 firstTdaList.add(firstTaskDisplayArea); 630 DisplayAreaPolicyBuilder.HierarchyBuilder firstHierarchy = 631 new DisplayAreaPolicyBuilder.HierarchyBuilder(firstRoot) 632 .setTaskDisplayAreas(firstTdaList) 633 .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder( 634 wmService.mPolicy, 635 "ImePlaceholder", FEATURE_IME_PLACEHOLDER) 636 .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG) 637 .build()); 638 639 // Second 640 final RootDisplayArea secondRoot = new DisplayAreaGroup(wmService, "SecondRoot", 641 FEATURE_SECOND_ROOT); 642 final TaskDisplayArea secondTaskDisplayArea = new TaskDisplayArea(content, wmService, 643 "SecondTaskDisplayArea", FEATURE_SECOND_TASK_CONTAINER); 644 final List<TaskDisplayArea> secondTdaList = new ArrayList<>(); 645 secondTdaList.add(secondTaskDisplayArea); 646 DisplayAreaPolicyBuilder.HierarchyBuilder secondHierarchy = 647 new DisplayAreaPolicyBuilder.HierarchyBuilder(secondRoot) 648 .setTaskDisplayAreas(secondTdaList) 649 .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder( 650 wmService.mPolicy, 651 "ImePlaceholder", FEATURE_IME_PLACEHOLDER) 652 .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG) 653 .build()); 654 655 return new DisplayAreaPolicyBuilder() 656 .setRootHierarchy(rootHierarchy) 657 .addDisplayAreaGroupHierarchy(firstHierarchy) 658 .addDisplayAreaGroupHierarchy(secondHierarchy) 659 .build(wmService); 660 } 661 } 662 } 663