1 /* 2 * Copyright (C) 2018 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.os.Process.NOBODY_UID; 20 import static android.view.Display.DEFAULT_DISPLAY; 21 import static android.view.Surface.ROTATION_0; 22 import static android.view.Surface.ROTATION_90; 23 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; 26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 28 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 29 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; 30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 34 import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING; 35 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED; 36 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING; 37 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED; 38 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; 39 import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING; 40 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE; 41 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE; 42 import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; 43 44 import static com.google.common.truth.Truth.assertThat; 45 46 import static org.junit.Assert.assertEquals; 47 import static org.junit.Assert.assertFalse; 48 import static org.junit.Assert.assertNotEquals; 49 import static org.junit.Assert.assertNotNull; 50 import static org.junit.Assert.assertNull; 51 import static org.junit.Assert.assertTrue; 52 import static org.mockito.ArgumentMatchers.anyString; 53 54 import android.app.ActivityManager; 55 import android.app.ActivityManagerInternal; 56 import android.app.ActivityOptions; 57 import android.app.servertransaction.ActivityConfigurationChangeItem; 58 import android.app.servertransaction.ClientTransaction; 59 import android.app.servertransaction.PauseActivityItem; 60 import android.content.ComponentName; 61 import android.content.pm.ActivityInfo; 62 import android.content.res.Configuration; 63 import android.content.res.Resources; 64 import android.graphics.Rect; 65 import android.platform.test.annotations.Presubmit; 66 import android.util.MergedConfiguration; 67 import android.util.MutableBoolean; 68 import android.view.IRemoteAnimationFinishedCallback; 69 import android.view.IRemoteAnimationRunner.Stub; 70 import android.view.RemoteAnimationAdapter; 71 import android.view.RemoteAnimationTarget; 72 73 import androidx.test.filters.MediumTest; 74 75 import com.android.internal.R; 76 import com.android.server.wm.utils.WmDisplayCutout; 77 78 import org.junit.Before; 79 import org.junit.Test; 80 import org.mockito.invocation.InvocationOnMock; 81 82 import java.util.concurrent.TimeUnit; 83 84 /** 85 * Tests for the {@link ActivityRecord} class. 86 * 87 * Build/Install/Run: 88 * atest WmTests:ActivityRecordTests 89 */ 90 @MediumTest 91 @Presubmit 92 public class ActivityRecordTests extends ActivityTestsBase { 93 private TestActivityStack mStack; 94 private TaskRecord mTask; 95 private ActivityRecord mActivity; 96 97 @Before setUp()98 public void setUp() throws Exception { 99 mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build(); 100 mTask = mStack.getChildAt(0); 101 mActivity = mTask.getTopActivity(); 102 103 doReturn(false).when(mService).isBooting(); 104 doReturn(true).when(mService).isBooted(); 105 } 106 107 @Test testStackCleanupOnClearingTask()108 public void testStackCleanupOnClearingTask() { 109 mActivity.setTask(null); 110 assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1); 111 } 112 113 @Test testStackCleanupOnActivityRemoval()114 public void testStackCleanupOnActivityRemoval() { 115 mTask.removeActivity(mActivity); 116 assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1); 117 } 118 119 @Test testStackCleanupOnTaskRemoval()120 public void testStackCleanupOnTaskRemoval() { 121 mStack.removeTask(mTask, null /*reason*/, REMOVE_TASK_MODE_MOVING); 122 // Stack should be gone on task removal. 123 assertNull(mService.mRootActivityContainer.getStack(mStack.mStackId)); 124 } 125 126 @Test testNoCleanupMovingActivityInSameStack()127 public void testNoCleanupMovingActivityInSameStack() { 128 final TaskRecord newTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack) 129 .build(); 130 mActivity.reparent(newTask, 0, null /*reason*/); 131 assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0); 132 } 133 134 @Test testPausingWhenVisibleFromStopped()135 public void testPausingWhenVisibleFromStopped() throws Exception { 136 final MutableBoolean pauseFound = new MutableBoolean(false); 137 doAnswer((InvocationOnMock invocationOnMock) -> { 138 final ClientTransaction transaction = invocationOnMock.getArgument(0); 139 if (transaction.getLifecycleStateRequest() instanceof PauseActivityItem) { 140 pauseFound.value = true; 141 } 142 return null; 143 }).when(mActivity.app.getThread()).scheduleTransaction(any()); 144 145 mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); 146 147 // The activity is in the focused stack so it should be resumed. 148 mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); 149 assertTrue(mActivity.isState(RESUMED)); 150 assertFalse(pauseFound.value); 151 152 // Make the activity non focusable 153 mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped"); 154 doReturn(false).when(mActivity).isFocusable(); 155 156 // If the activity is not focusable, it should move to paused. 157 mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); 158 assertTrue(mActivity.isState(PAUSING)); 159 assertTrue(pauseFound.value); 160 161 // Make sure that the state does not change for current non-stopping states. 162 mActivity.setState(INITIALIZING, "testPausingWhenVisibleFromStopped"); 163 doReturn(true).when(mActivity).isFocusable(); 164 165 mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); 166 167 assertTrue(mActivity.isState(INITIALIZING)); 168 169 // Make sure the state does not change if we are not the current top activity. 170 mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind"); 171 172 final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build(); 173 mStack.mTranslucentActivityWaiting = topActivity; 174 mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */); 175 assertTrue(mActivity.isState(PAUSED)); 176 } 177 ensureActivityConfiguration()178 private void ensureActivityConfiguration() { 179 mActivity.ensureActivityConfiguration(0 /* globalChanges */, false /* preserveWindow */); 180 } 181 182 @Test testCanBeLaunchedOnDisplay()183 public void testCanBeLaunchedOnDisplay() { 184 mService.mSupportsMultiWindow = true; 185 final ActivityRecord activity = new ActivityBuilder(mService).build(); 186 187 // An activity can be launched on default display. 188 assertTrue(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY)); 189 // An activity cannot be launched on a non-existent display. 190 assertFalse(activity.canBeLaunchedOnDisplay(DEFAULT_DISPLAY + 1)); 191 } 192 193 @Test testRestartProcessIfVisible()194 public void testRestartProcessIfVisible() { 195 doNothing().when(mSupervisor).scheduleRestartTimeout(mActivity); 196 mActivity.visible = true; 197 mActivity.haveState = false; 198 mActivity.setState(ActivityStack.ActivityState.RESUMED, "testRestart"); 199 prepareFixedAspectRatioUnresizableActivity(); 200 201 final Rect originalOverrideBounds = new Rect(mActivity.getBounds()); 202 setupDisplayAndParentSize(600, 1200); 203 // The visible activity should recompute configuration according to the last parent bounds. 204 mService.restartActivityProcessIfVisible(mActivity.appToken); 205 206 assertEquals(ActivityStack.ActivityState.RESTARTING_PROCESS, mActivity.getState()); 207 assertNotEquals(originalOverrideBounds, mActivity.getBounds()); 208 } 209 210 @Test testsApplyOptionsLocked()211 public void testsApplyOptionsLocked() { 212 ActivityOptions activityOptions = ActivityOptions.makeBasic(); 213 214 // Set and apply options for ActivityRecord. Pending options should be cleared 215 mActivity.updateOptionsLocked(activityOptions); 216 mActivity.applyOptionsLocked(); 217 assertNull(mActivity.pendingOptions); 218 219 // Set options for two ActivityRecords in same Task. Apply one ActivityRecord options. 220 // Pending options should be cleared for both ActivityRecords 221 ActivityRecord activity2 = new ActivityBuilder(mService).setTask(mTask).build(); 222 activity2.updateOptionsLocked(activityOptions); 223 mActivity.updateOptionsLocked(activityOptions); 224 mActivity.applyOptionsLocked(); 225 assertNull(mActivity.pendingOptions); 226 assertNull(activity2.pendingOptions); 227 228 // Set options for two ActivityRecords in separate Tasks. Apply one ActivityRecord options. 229 // Pending options should be cleared for only ActivityRecord that was applied 230 TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build(); 231 activity2 = new ActivityBuilder(mService).setTask(task2).build(); 232 activity2.updateOptionsLocked(activityOptions); 233 mActivity.updateOptionsLocked(activityOptions); 234 mActivity.applyOptionsLocked(); 235 assertNull(mActivity.pendingOptions); 236 assertNotNull(activity2.pendingOptions); 237 } 238 239 @Test testNewOverrideConfigurationIncrementsSeq()240 public void testNewOverrideConfigurationIncrementsSeq() { 241 final Configuration newConfig = new Configuration(); 242 243 final int prevSeq = mActivity.getMergedOverrideConfiguration().seq; 244 mActivity.onRequestedOverrideConfigurationChanged(newConfig); 245 assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq); 246 } 247 248 @Test testNewParentConfigurationIncrementsSeq()249 public void testNewParentConfigurationIncrementsSeq() { 250 final Configuration newConfig = new Configuration( 251 mTask.getRequestedOverrideConfiguration()); 252 newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT 253 ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT; 254 255 final int prevSeq = mActivity.getMergedOverrideConfiguration().seq; 256 mTask.onRequestedOverrideConfigurationChanged(newConfig); 257 assertEquals(prevSeq + 1, mActivity.getMergedOverrideConfiguration().seq); 258 } 259 260 @Test testNotifiesSeqIncrementToAppToken()261 public void testNotifiesSeqIncrementToAppToken() { 262 final Configuration appWindowTokenRequestedOrientation = mock(Configuration.class); 263 mActivity.mAppWindowToken = mock(AppWindowToken.class); 264 doReturn(appWindowTokenRequestedOrientation).when(mActivity.mAppWindowToken) 265 .getRequestedOverrideConfiguration(); 266 267 final Configuration newConfig = new Configuration(); 268 newConfig.orientation = Configuration.ORIENTATION_PORTRAIT; 269 270 final int prevSeq = mActivity.getMergedOverrideConfiguration().seq; 271 mActivity.onRequestedOverrideConfigurationChanged(newConfig); 272 assertEquals(prevSeq + 1, appWindowTokenRequestedOrientation.seq); 273 verify(mActivity.mAppWindowToken).onMergedOverrideConfigurationChanged(); 274 } 275 276 @Test testSetsRelaunchReason_NotDragResizing()277 public void testSetsRelaunchReason_NotDragResizing() { 278 mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing"); 279 280 mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); 281 mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), 282 mActivity.getConfiguration())); 283 284 mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION; 285 final Configuration newConfig = new Configuration(mTask.getConfiguration()); 286 newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT 287 ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT; 288 mTask.onRequestedOverrideConfigurationChanged(newConfig); 289 290 mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; 291 292 ensureActivityConfiguration(); 293 294 assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE, 295 mActivity.mRelaunchReason); 296 } 297 298 @Test testSetsRelaunchReason_DragResizing()299 public void testSetsRelaunchReason_DragResizing() { 300 mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing"); 301 302 mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); 303 mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), 304 mActivity.getConfiguration())); 305 306 mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION; 307 final Configuration newConfig = new Configuration(mTask.getConfiguration()); 308 newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT 309 ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT; 310 mTask.onRequestedOverrideConfigurationChanged(newConfig); 311 312 doReturn(true).when(mTask.getTask()).isDragResizing(); 313 314 mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE; 315 316 ensureActivityConfiguration(); 317 318 assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE, 319 mActivity.mRelaunchReason); 320 } 321 322 @Test testSetsRelaunchReason_NonResizeConfigChanges()323 public void testSetsRelaunchReason_NonResizeConfigChanges() { 324 mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing"); 325 326 mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); 327 mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), 328 mActivity.getConfiguration())); 329 330 mActivity.info.configChanges &= ~ActivityInfo.CONFIG_FONT_SCALE; 331 final Configuration newConfig = new Configuration(mTask.getConfiguration()); 332 newConfig.fontScale = 5; 333 mTask.onRequestedOverrideConfigurationChanged(newConfig); 334 335 mActivity.mRelaunchReason = 336 ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; 337 338 ensureActivityConfiguration(); 339 340 assertEquals(ActivityTaskManagerService.RELAUNCH_REASON_NONE, 341 mActivity.mRelaunchReason); 342 } 343 344 @Test testSetRequestedOrientationUpdatesConfiguration()345 public void testSetRequestedOrientationUpdatesConfiguration() throws Exception { 346 mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing"); 347 348 mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); 349 mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), 350 mActivity.getConfiguration())); 351 352 mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION; 353 final Configuration newConfig = new Configuration(mActivity.getConfiguration()); 354 newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT 355 ? Configuration.ORIENTATION_LANDSCAPE 356 : Configuration.ORIENTATION_PORTRAIT; 357 358 // Mimic the behavior that display doesn't handle app's requested orientation. 359 doAnswer(invocation -> { 360 mTask.onConfigurationChanged(newConfig); 361 return null; 362 }).when(mActivity.mAppWindowToken).setOrientation(anyInt(), any(), any()); 363 364 final int requestedOrientation; 365 switch (newConfig.orientation) { 366 case Configuration.ORIENTATION_LANDSCAPE: 367 requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 368 break; 369 case Configuration.ORIENTATION_PORTRAIT: 370 requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 371 break; 372 default: 373 throw new IllegalStateException("Orientation in new config should be either" 374 + "landscape or portrait."); 375 } 376 mActivity.setRequestedOrientation(requestedOrientation); 377 378 final ActivityConfigurationChangeItem expected = 379 ActivityConfigurationChangeItem.obtain(newConfig); 380 verify(mService.getLifecycleManager()).scheduleTransaction(eq(mActivity.app.getThread()), 381 eq(mActivity.appToken), eq(expected)); 382 } 383 384 @Test testShouldMakeActive_deferredResume()385 public void testShouldMakeActive_deferredResume() { 386 mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing"); 387 388 mSupervisor.beginDeferResume(); 389 assertEquals(false, mActivity.shouldMakeActive(null /* activeActivity */)); 390 391 mSupervisor.endDeferResume(); 392 assertEquals(true, mActivity.shouldMakeActive(null /* activeActivity */)); 393 } 394 395 @Test testShouldResume_stackVisibility()396 public void testShouldResume_stackVisibility() { 397 mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing"); 398 spyOn(mStack); 399 400 doReturn(STACK_VISIBILITY_VISIBLE).when(mStack).getVisibility(null); 401 assertEquals(true, mActivity.shouldResumeActivity(null /* activeActivity */)); 402 403 doReturn(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT).when(mStack).getVisibility(null); 404 assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */)); 405 406 doReturn(STACK_VISIBILITY_INVISIBLE).when(mStack).getVisibility(null); 407 assertEquals(false, mActivity.shouldResumeActivity(null /* activeActivity */)); 408 } 409 410 @Test testPushConfigurationWhenLaunchTaskBehind()411 public void testPushConfigurationWhenLaunchTaskBehind() throws Exception { 412 mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing"); 413 414 final TestActivityStack stack = (TestActivityStack) new StackBuilder(mRootActivityContainer) 415 .build(); 416 try { 417 stack.setIsTranslucent(false); 418 assertFalse(mStack.shouldBeVisible(null /* starting */)); 419 420 mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration()); 421 mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(), 422 mActivity.getConfiguration())); 423 424 mActivity.mLaunchTaskBehind = true; 425 mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION; 426 final Configuration newConfig = new Configuration(mActivity.getConfiguration()); 427 newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT 428 ? Configuration.ORIENTATION_LANDSCAPE 429 : Configuration.ORIENTATION_PORTRAIT; 430 431 mTask.onConfigurationChanged(newConfig); 432 433 mActivity.ensureActivityConfiguration(0 /* globalChanges */, 434 false /* preserveWindow */, true /* ignoreStopState */); 435 436 final ActivityConfigurationChangeItem expected = 437 ActivityConfigurationChangeItem.obtain(newConfig); 438 verify(mService.getLifecycleManager()).scheduleTransaction( 439 eq(mActivity.app.getThread()), eq(mActivity.appToken), eq(expected)); 440 } finally { 441 stack.getDisplay().removeChild(stack); 442 } 443 } 444 445 @Test testShouldPauseWhenMakeClientVisible()446 public void testShouldPauseWhenMakeClientVisible() { 447 ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build(); 448 topActivity.changeWindowTranslucency(false); 449 mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing"); 450 mActivity.makeClientVisible(); 451 assertEquals(PAUSED, mActivity.getState()); 452 } 453 454 @Test testSizeCompatMode_FixedAspectRatioBoundsWithDecor()455 public void testSizeCompatMode_FixedAspectRatioBoundsWithDecor() { 456 setupDisplayContentForCompatDisplayInsets(); 457 final int decorHeight = 200; // e.g. The device has cutout. 458 final DisplayPolicy policy = setupDisplayAndParentSize(600, 800).getDisplayPolicy(); 459 doAnswer(invocationOnMock -> { 460 final int rotation = invocationOnMock.<Integer>getArgument(0); 461 final Rect insets = invocationOnMock.<Rect>getArgument(4); 462 if (rotation == ROTATION_0) { 463 insets.top = decorHeight; 464 } else if (rotation == ROTATION_90) { 465 insets.left = decorHeight; 466 } 467 return null; 468 }).when(policy).getNonDecorInsetsLw(anyInt() /* rotation */, anyInt() /* width */, 469 anyInt() /* height */, any() /* displayCutout */, any() /* outInsets */); 470 471 doReturn(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) 472 .when(mActivity.mAppWindowToken).getOrientationIgnoreVisibility(); 473 mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE; 474 mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1; 475 ensureActivityConfiguration(); 476 // The parent configuration doesn't change since the first resolved configuration, so the 477 // activity shouldn't be in the size compatibility mode. 478 assertFalse(mActivity.inSizeCompatMode()); 479 480 final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds(); 481 // Ensure the app bounds keep the declared aspect ratio. 482 assertEquals(appBounds.width(), appBounds.height()); 483 // The decor height should be a part of the effective bounds. 484 assertEquals(mActivity.getBounds().height(), appBounds.height() + decorHeight); 485 486 mTask.getConfiguration().windowConfiguration.setRotation(ROTATION_90); 487 mActivity.onConfigurationChanged(mTask.getConfiguration()); 488 // After changing orientation, the aspect ratio should be the same. 489 assertEquals(appBounds.width(), appBounds.height()); 490 // The decor height will be included in width. 491 assertEquals(mActivity.getBounds().width(), appBounds.width() + decorHeight); 492 } 493 494 @Test testSizeCompatMode_FixedScreenConfigurationWhenMovingToDisplay()495 public void testSizeCompatMode_FixedScreenConfigurationWhenMovingToDisplay() { 496 // Initialize different bounds on a new display. 497 final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP); 498 newDisplay.getWindowConfiguration().setAppBounds(new Rect(0, 0, 1000, 2000)); 499 newDisplay.getConfiguration().densityDpi = 300; 500 501 mTask.getConfiguration().densityDpi = 200; 502 prepareFixedAspectRatioUnresizableActivity(); 503 504 final Rect originalBounds = new Rect(mActivity.getBounds()); 505 final int originalDpi = mActivity.getConfiguration().densityDpi; 506 507 // Move the non-resizable activity to the new display. 508 mStack.reparent(newDisplay, true /* onTop */, false /* displayRemoved */); 509 ensureActivityConfiguration(); 510 511 assertEquals(originalBounds, mActivity.getBounds()); 512 assertEquals(originalDpi, mActivity.getConfiguration().densityDpi); 513 assertTrue(mActivity.inSizeCompatMode()); 514 } 515 516 @Test testSizeCompatMode_FixedScreenBoundsWhenDisplaySizeChanged()517 public void testSizeCompatMode_FixedScreenBoundsWhenDisplaySizeChanged() { 518 setupDisplayContentForCompatDisplayInsets(); 519 when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn( 520 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 521 mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds()); 522 mTask.getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT; 523 mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 524 mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE; 525 ensureActivityConfiguration(); 526 final Rect originalBounds = new Rect(mActivity.getBounds()); 527 528 // Change the size of current display. 529 setupDisplayAndParentSize(1000, 2000); 530 ensureActivityConfiguration(); 531 532 assertEquals(originalBounds, mActivity.getBounds()); 533 assertTrue(mActivity.inSizeCompatMode()); 534 } 535 536 @Test testSizeCompatMode_FixedScreenLayoutSizeBits()537 public void testSizeCompatMode_FixedScreenLayoutSizeBits() { 538 final int fixedScreenLayout = Configuration.SCREENLAYOUT_LONG_NO 539 | Configuration.SCREENLAYOUT_SIZE_NORMAL; 540 mTask.getConfiguration().screenLayout = fixedScreenLayout 541 | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR; 542 prepareFixedAspectRatioUnresizableActivity(); 543 544 // The initial configuration should inherit from parent. 545 assertEquals(mTask.getConfiguration().screenLayout, 546 mActivity.getConfiguration().screenLayout); 547 548 mTask.getConfiguration().screenLayout = Configuration.SCREENLAYOUT_LAYOUTDIR_RTL 549 | Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_LARGE; 550 mActivity.onConfigurationChanged(mTask.getConfiguration()); 551 552 // The size and aspect ratio bits don't change, but the layout direction should be updated. 553 assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_RTL, 554 mActivity.getConfiguration().screenLayout); 555 } 556 557 @Test testSizeCompatMode_ResetNonVisibleActivity()558 public void testSizeCompatMode_ResetNonVisibleActivity() { 559 final ActivityDisplay display = mStack.getDisplay(); 560 spyOn(display); 561 562 prepareFixedAspectRatioUnresizableActivity(); 563 mActivity.setState(STOPPED, "testSizeCompatMode"); 564 mActivity.visible = false; 565 mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY); 566 // Make the parent bounds to be different so the activity is in size compatibility mode. 567 mTask.getWindowConfiguration().setAppBounds(new Rect(0, 0, 600, 1200)); 568 569 // Simulate the display changes orientation. 570 doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION 571 | ActivityInfo.CONFIG_WINDOW_CONFIGURATION) 572 .when(display).getLastOverrideConfigurationChanges(); 573 mActivity.onConfigurationChanged(mTask.getConfiguration()); 574 // The override configuration should not change so it is still in size compatibility mode. 575 assertTrue(mActivity.inSizeCompatMode()); 576 577 // Simulate the display changes density. 578 doReturn(ActivityInfo.CONFIG_DENSITY).when(display).getLastOverrideConfigurationChanges(); 579 mService.mAmInternal = mock(ActivityManagerInternal.class); 580 mActivity.onConfigurationChanged(mTask.getConfiguration()); 581 // The override configuration should be reset and the activity's process will be killed. 582 assertFalse(mActivity.inSizeCompatMode()); 583 verify(mActivity).restartProcessIfVisible(); 584 mService.mH.runWithScissors(() -> { }, TimeUnit.SECONDS.toMillis(3)); 585 verify(mService.mAmInternal).killProcess( 586 eq(mActivity.app.mName), eq(mActivity.app.mUid), anyString()); 587 } 588 589 @Test testTakeOptions()590 public void testTakeOptions() { 591 ActivityOptions opts = ActivityOptions.makeRemoteAnimation( 592 new RemoteAnimationAdapter(new Stub() { 593 594 @Override 595 public void onAnimationStart(RemoteAnimationTarget[] apps, 596 IRemoteAnimationFinishedCallback finishedCallback) { 597 598 } 599 600 @Override 601 public void onAnimationCancelled() { 602 603 } 604 }, 0, 0)); 605 mActivity.updateOptionsLocked(opts); 606 assertNotNull(mActivity.takeOptionsLocked(true /* fromClient */)); 607 assertNotNull(mActivity.pendingOptions); 608 609 mActivity.updateOptionsLocked(ActivityOptions.makeBasic()); 610 assertNotNull(mActivity.takeOptionsLocked(false /* fromClient */)); 611 assertNull(mActivity.pendingOptions); 612 } 613 614 @Test testCanLaunchHomeActivityFromChooser()615 public void testCanLaunchHomeActivityFromChooser() { 616 ComponentName chooserComponent = ComponentName.unflattenFromString( 617 Resources.getSystem().getString(R.string.config_chooserActivity)); 618 ActivityRecord chooserActivity = new ActivityBuilder(mService).setComponent( 619 chooserComponent).build(); 620 assertThat(mActivity.canLaunchHomeActivity(NOBODY_UID, chooserActivity)).isTrue(); 621 } 622 623 /** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */ prepareFixedAspectRatioUnresizableActivity()624 private void prepareFixedAspectRatioUnresizableActivity() { 625 setupDisplayContentForCompatDisplayInsets(); 626 when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn( 627 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 628 mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE; 629 mActivity.info.maxAspectRatio = 1.5f; 630 ensureActivityConfiguration(); 631 } 632 setupDisplayContentForCompatDisplayInsets()633 private void setupDisplayContentForCompatDisplayInsets() { 634 final Rect displayBounds = mStack.getDisplay().getBounds(); 635 final DisplayContent displayContent = setupDisplayAndParentSize( 636 displayBounds.width(), displayBounds.height()); 637 doReturn(mock(DisplayPolicy.class)).when(displayContent).getDisplayPolicy(); 638 doReturn(mock(WmDisplayCutout.class)).when(displayContent) 639 .calculateDisplayCutoutForRotation(anyInt()); 640 } 641 setupDisplayAndParentSize(int width, int height)642 private DisplayContent setupDisplayAndParentSize(int width, int height) { 643 // The DisplayContent is already a mocked object. 644 final DisplayContent displayContent = mStack.getDisplay().mDisplayContent; 645 displayContent.mBaseDisplayWidth = width; 646 displayContent.mBaseDisplayHeight = height; 647 mTask.getWindowConfiguration().setAppBounds(0, 0, width, height); 648 mTask.getWindowConfiguration().setRotation(ROTATION_0); 649 return displayContent; 650 } 651 } 652