1 /* 2 * Copyright (C) 2017 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.ActivityTaskManager.INVALID_TASK_ID; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 24 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 25 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 26 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 27 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 28 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; 29 import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; 30 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 31 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 32 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 33 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 34 import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED; 35 import static android.view.Surface.ROTATION_0; 36 import static android.view.Surface.ROTATION_90; 37 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; 38 39 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 40 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 41 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; 43 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 44 import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE; 45 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; 46 import static com.android.server.wm.TaskFragment.TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT; 47 48 import static com.google.common.truth.Truth.assertThat; 49 50 import static org.hamcrest.Matchers.not; 51 import static org.hamcrest.Matchers.sameInstance; 52 import static org.junit.Assert.assertEquals; 53 import static org.junit.Assert.assertFalse; 54 import static org.junit.Assert.assertNotEquals; 55 import static org.junit.Assert.assertNotNull; 56 import static org.junit.Assert.assertNull; 57 import static org.junit.Assert.assertTrue; 58 import static org.mockito.ArgumentMatchers.any; 59 import static org.mockito.ArgumentMatchers.anyBoolean; 60 import static org.mockito.ArgumentMatchers.anyInt; 61 import static org.mockito.ArgumentMatchers.anyString; 62 import static org.mockito.ArgumentMatchers.eq; 63 import static org.mockito.ArgumentMatchers.same; 64 import static org.mockito.Mockito.atLeast; 65 import static org.mockito.Mockito.clearInvocations; 66 import static org.mockito.Mockito.never; 67 68 import android.app.ActivityManager; 69 import android.app.ActivityOptions; 70 import android.app.TaskInfo; 71 import android.app.WindowConfiguration; 72 import android.content.ComponentName; 73 import android.content.Intent; 74 import android.content.pm.ActivityInfo; 75 import android.content.pm.ApplicationInfo; 76 import android.content.res.Configuration; 77 import android.graphics.Point; 78 import android.graphics.Rect; 79 import android.os.IBinder; 80 import android.platform.test.annotations.Presubmit; 81 import android.util.DisplayMetrics; 82 import android.util.Xml; 83 import android.view.Display; 84 import android.view.DisplayInfo; 85 import android.window.TaskFragmentOrganizer; 86 87 import androidx.test.filters.MediumTest; 88 89 import com.android.modules.utils.TypedXmlPullParser; 90 import com.android.modules.utils.TypedXmlSerializer; 91 92 import org.junit.Assert; 93 import org.junit.Before; 94 import org.junit.Test; 95 import org.junit.runner.RunWith; 96 import org.mockito.Mockito; 97 import org.xmlpull.v1.XmlPullParser; 98 import org.xmlpull.v1.XmlPullParserException; 99 100 import java.io.ByteArrayInputStream; 101 import java.io.ByteArrayOutputStream; 102 import java.io.IOException; 103 import java.io.InputStreamReader; 104 import java.io.Reader; 105 106 /** 107 * Test class for {@link Task}. 108 * 109 * Build/Install/Run: 110 * atest WmTests:TaskTests 111 */ 112 @MediumTest 113 @Presubmit 114 @RunWith(WindowTestRunner.class) 115 public class TaskTests extends WindowTestsBase { 116 117 private static final String TASK_TAG = "task"; 118 119 private Rect mParentBounds; 120 121 @Before setUp()122 public void setUp() throws Exception { 123 mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/); 124 removeGlobalMinSizeRestriction(); 125 } 126 127 @Test testRemoveContainer()128 public void testRemoveContainer() { 129 final Task rootTask = createTask(mDisplayContent); 130 final Task task = createTaskInRootTask(rootTask, 0 /* userId */); 131 final ActivityRecord activity = createActivityRecord(mDisplayContent, task); 132 133 task.remove(false /* withTransition */, "testRemoveContainer"); 134 // There is still an activity to be destroyed, so the task is not removed immediately. 135 assertNotNull(task.getParent()); 136 assertTrue(rootTask.hasChild()); 137 assertTrue(task.hasChild()); 138 assertTrue(activity.finishing); 139 140 activity.destroyed("testRemoveContainer"); 141 // Assert that the container was removed after the activity is destroyed. 142 assertNull(task.getParent()); 143 assertEquals(0, task.getChildCount()); 144 assertNull(activity.getParent()); 145 verify(mAtm.getLockTaskController(), atLeast(1)).clearLockedTask(task); 146 verify(mAtm.getLockTaskController(), atLeast(1)).clearLockedTask(rootTask); 147 } 148 149 @Test testRemoveContainer_multipleNestedTasks()150 public void testRemoveContainer_multipleNestedTasks() { 151 final Task rootTask = createTask(mDisplayContent); 152 rootTask.mCreatedByOrganizer = true; 153 final Task task1 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); 154 final Task task2 = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); 155 final ActivityRecord activity1 = createActivityRecord(task1); 156 final ActivityRecord activity2 = createActivityRecord(task2); 157 activity1.setVisible(false); 158 159 // All activities under the root task should be finishing. 160 rootTask.remove(true /* withTransition */, "test"); 161 assertTrue(activity1.finishing); 162 assertTrue(activity2.finishing); 163 164 // After all activities activities are destroyed, the root task should also be removed. 165 activity1.removeImmediately(); 166 activity2.removeImmediately(); 167 assertFalse(rootTask.isAttached()); 168 } 169 170 @Test testRemoveContainer_deferRemoval()171 public void testRemoveContainer_deferRemoval() { 172 final Task rootTask = createTask(mDisplayContent); 173 final Task task = createTaskInRootTask(rootTask, 0 /* userId */); 174 final ActivityRecord activity = createActivityRecord(mDisplayContent, task); 175 176 doReturn(true).when(task).shouldDeferRemoval(); 177 178 task.removeIfPossible(); 179 // For the case of deferred removal the task will still be connected to the its app token 180 // until the task window container is removed. 181 assertNotNull(task.getParent()); 182 assertNotEquals(0, task.getChildCount()); 183 assertNotNull(activity.getParent()); 184 185 task.removeImmediately(); 186 assertNull(task.getParent()); 187 assertEquals(0, task.getChildCount()); 188 assertNull(activity.getParent()); 189 } 190 191 @Test testReparent()192 public void testReparent() { 193 final Task taskController1 = createTask(mDisplayContent); 194 final Task task = createTaskInRootTask(taskController1, 0 /* userId */); 195 final Task taskController2 = createTask(mDisplayContent); 196 final Task task2 = createTaskInRootTask(taskController2, 0 /* userId */); 197 198 boolean gotException = false; 199 try { 200 task.reparent(taskController1, 0, false/* moveParents */, "testReparent"); 201 } catch (IllegalArgumentException e) { 202 gotException = true; 203 } 204 assertTrue("Should not be able to reparent to the same parent", gotException); 205 206 gotException = false; 207 try { 208 task.reparent(null, 0, false/* moveParents */, "testReparent"); 209 } catch (Exception e) { 210 gotException = true; 211 } 212 assertTrue("Should not be able to reparent to a task that doesn't exist", gotException); 213 214 task.reparent(taskController2, 0, false/* moveParents */, "testReparent"); 215 assertEquals(taskController2, task.getParent()); 216 assertEquals(0, task.getParent().mChildren.indexOf(task)); 217 assertEquals(1, task2.getParent().mChildren.indexOf(task2)); 218 } 219 220 @Test testReparent_BetweenDisplays()221 public void testReparent_BetweenDisplays() { 222 // Create first task on primary display. 223 final Task rootTask1 = createTask(mDisplayContent); 224 final Task task = createTaskInRootTask(rootTask1, 0 /* userId */); 225 assertEquals(mDisplayContent, rootTask1.getDisplayContent()); 226 227 // Create second display and put second task on it. 228 final DisplayContent dc = createNewDisplay(); 229 final Task rootTask2 = createTask(dc); 230 final Task task2 = createTaskInRootTask(rootTask2, 0 /* userId */); 231 // Reparent and check state 232 clearInvocations(task); // reset the number of onDisplayChanged for task. 233 task.reparent(rootTask2, 0, false /* moveParents */, "testReparent_BetweenDisplays"); 234 assertEquals(rootTask2, task.getParent()); 235 assertEquals(0, task.getParent().mChildren.indexOf(task)); 236 assertEquals(1, task2.getParent().mChildren.indexOf(task2)); 237 verify(task, times(1)).onDisplayChanged(any()); 238 } 239 240 @Test testBounds()241 public void testBounds() { 242 final Task rootTask1 = createTask(mDisplayContent); 243 final Task task = createTaskInRootTask(rootTask1, 0 /* userId */); 244 245 // Check that setting bounds also updates surface position 246 task.setWindowingMode(WINDOWING_MODE_FREEFORM); 247 Rect bounds = new Rect(10, 10, 100, 200); 248 task.setBounds(bounds); 249 assertEquals(new Point(bounds.left, bounds.top), task.getLastSurfacePosition()); 250 } 251 252 @Test testIsInTask()253 public void testIsInTask() { 254 final Task task1 = createTask(mDisplayContent); 255 final Task task2 = createTask(mDisplayContent); 256 final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task1); 257 final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task2); 258 assertEquals(activity1, task1.isInTask(activity1)); 259 assertNull(task1.isInTask(activity2)); 260 } 261 262 @Test testPerformClearTop()263 public void testPerformClearTop() { 264 final Task task = createTask(mDisplayContent); 265 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 266 final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); 267 // Detach from process so the activities can be removed from hierarchy when finishing. 268 activity1.detachFromProcess(); 269 activity2.detachFromProcess(); 270 int[] finishCount = new int[1]; 271 assertTrue(task.performClearTop(activity1, 0 /* launchFlags */, finishCount).finishing); 272 assertFalse(task.hasChild()); 273 // In real case, the task should be preserved for adding new activity. 274 assertTrue(task.isAttached()); 275 276 final ActivityRecord activityA = new ActivityBuilder(mAtm).setTask(task).build(); 277 final ActivityRecord activityB = new ActivityBuilder(mAtm).setTask(task).build(); 278 final ActivityRecord activityC = new ActivityBuilder(mAtm).setTask(task).build(); 279 activityA.setState(ActivityRecord.State.STOPPED, "test"); 280 activityB.setState(ActivityRecord.State.PAUSED, "test"); 281 activityC.setState(ActivityRecord.State.RESUMED, "test"); 282 doReturn(true).when(activityB).shouldBeVisibleUnchecked(); 283 doReturn(true).when(activityC).shouldBeVisibleUnchecked(); 284 activityA.getConfiguration().densityDpi += 100; 285 assertTrue(task.performClearTop(activityA, 0 /* launchFlags */, finishCount).finishing); 286 // The bottom activity should destroy directly without relaunch for config change. 287 assertEquals(ActivityRecord.State.DESTROYING, activityA.getState()); 288 verify(activityA, never()).startRelaunching(); 289 } 290 291 @Test testRemoveChildForOverlayTask()292 public void testRemoveChildForOverlayTask() { 293 final Task task = createTask(mDisplayContent); 294 final int taskId = task.mTaskId; 295 final ActivityRecord activity1 = createActivityRecord(mDisplayContent, task); 296 final ActivityRecord activity2 = createActivityRecord(mDisplayContent, task); 297 final ActivityRecord activity3 = createActivityRecord(mDisplayContent, task); 298 activity1.setTaskOverlay(true); 299 activity2.setTaskOverlay(true); 300 activity3.setTaskOverlay(true); 301 302 assertEquals(3, task.getChildCount()); 303 assertTrue(task.onlyHasTaskOverlayActivities(true)); 304 305 task.removeChild(activity1); 306 307 verify(task.mTaskSupervisor).removeTask(any(), anyBoolean(), anyBoolean(), anyString()); 308 assertEquals(2, task.getChildCount()); 309 task.forAllActivities((r) -> { 310 assertTrue(r.finishing); 311 }); 312 } 313 314 @Test testSwitchUser()315 public void testSwitchUser() { 316 final Task rootTask = createTask(mDisplayContent); 317 final Task childTask = createTaskInRootTask(rootTask, 0 /* userId */); 318 final Task leafTask1 = createTaskInRootTask(childTask, 10 /* userId */); 319 final Task leafTask2 = createTaskInRootTask(childTask, 0 /* userId */); 320 assertEquals(1, rootTask.getChildCount()); 321 assertEquals(leafTask2, childTask.getTopChild()); 322 323 doReturn(true).when(leafTask1).showToCurrentUser(); 324 rootTask.switchUser(10); 325 assertEquals(1, rootTask.getChildCount()); 326 assertEquals(leafTask1, childTask.getTopChild()); 327 } 328 329 @Test testEnsureActivitiesVisible()330 public void testEnsureActivitiesVisible() { 331 final Task rootTask = createTask(mDisplayContent); 332 final Task leafTask1 = createTaskInRootTask(rootTask, 0 /* userId */); 333 final Task leafTask2 = createTaskInRootTask(rootTask, 0 /* userId */); 334 final ActivityRecord activity1 = createActivityRecord(mDisplayContent, leafTask1); 335 final ActivityRecord activity2 = createActivityRecord(mDisplayContent, leafTask2); 336 337 // Check visibility of occluded tasks 338 doReturn(false).when(leafTask1).shouldBeVisible(any()); 339 doReturn(true).when(leafTask2).shouldBeVisible(any()); 340 rootTask.ensureActivitiesVisible( 341 null /* starting */ , 0 /* configChanges */, false /* preserveWindows */); 342 assertFalse(activity1.isVisible()); 343 assertTrue(activity2.isVisible()); 344 345 // Check visibility of not occluded tasks 346 doReturn(true).when(leafTask1).shouldBeVisible(any()); 347 doReturn(true).when(leafTask2).shouldBeVisible(any()); 348 rootTask.ensureActivitiesVisible( 349 null /* starting */ , 0 /* configChanges */, false /* preserveWindows */); 350 assertTrue(activity1.isVisible()); 351 assertTrue(activity2.isVisible()); 352 } 353 354 @Test testResolveNonResizableTaskWindowingMode()355 public void testResolveNonResizableTaskWindowingMode() { 356 // Test with no support non-resizable in multi window regardless the screen size. 357 mAtm.mSupportsNonResizableMultiWindow = -1; 358 359 final Task task = createTask(mDisplayContent); 360 Configuration parentConfig = task.getParent().getConfiguration(); 361 parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); 362 doReturn(false).when(task).isResizeable(); 363 WindowConfiguration requestedOverride = 364 task.getRequestedOverrideConfiguration().windowConfiguration; 365 WindowConfiguration resolvedOverride = 366 task.getResolvedOverrideConfiguration().windowConfiguration; 367 368 // The resolved override windowing mode of a non-resizeable task should be resolved as 369 // fullscreen even as a child of a freeform display. 370 requestedOverride.setWindowingMode(WINDOWING_MODE_UNDEFINED); 371 task.resolveOverrideConfiguration(parentConfig); 372 assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN); 373 374 // The resolved override windowing mode of a non-resizeable task should be resolved as 375 // fullscreen, even when requested as freeform windowing mode 376 requestedOverride.setWindowingMode(WINDOWING_MODE_FREEFORM); 377 task.resolveOverrideConfiguration(parentConfig); 378 assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN); 379 380 // The resolved override windowing mode of a non-resizeable task can be undefined as long 381 // as its parents is not in multi-window mode. 382 parentConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 383 requestedOverride.setWindowingMode(WINDOWING_MODE_UNDEFINED); 384 task.resolveOverrideConfiguration(parentConfig); 385 assertThat(resolvedOverride.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED); 386 } 387 388 @Test testHandlesOrientationChangeFromDescendant()389 public void testHandlesOrientationChangeFromDescendant() { 390 final Task rootTask = createTask(mDisplayContent, 391 WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); 392 final Task leafTask1 = createTaskInRootTask(rootTask, 0 /* userId */); 393 final Task leafTask2 = createTaskInRootTask(rootTask, 0 /* userId */); 394 leafTask1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_HOME); 395 leafTask2.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_STANDARD); 396 397 // We need to use an orientation that is not an exception for the 398 // ignoreOrientationRequest flag. 399 final int orientation = SCREEN_ORIENTATION_PORTRAIT; 400 401 assertEquals(leafTask2, rootTask.getTopChild()); 402 assertTrue(rootTask.handlesOrientationChangeFromDescendant(orientation)); 403 // Treat orientation request from home as handled. 404 assertTrue(leafTask1.handlesOrientationChangeFromDescendant(orientation)); 405 // Orientation request from standard activity in multi window will not be handled. 406 assertFalse(leafTask2.handlesOrientationChangeFromDescendant(orientation)); 407 } 408 409 @Test testAlwaysOnTop()410 public void testAlwaysOnTop() { 411 final Task task = createTask(mDisplayContent); 412 task.setAlwaysOnTop(true); 413 task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 414 assertTrue(task.isAlwaysOnTop()); 415 416 task.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, true /* set */); 417 assertFalse(task.isAlwaysOnTop()); 418 } 419 420 @Test testRestoreWindowedTask()421 public void testRestoreWindowedTask() throws Exception { 422 final Task expected = createTask(64); 423 expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100); 424 425 final byte[] serializedBytes = serializeToBytes(expected); 426 final Task actual = restoreFromBytes(serializedBytes); 427 assertEquals(expected.mTaskId, actual.mTaskId); 428 assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds); 429 } 430 431 /** Ensure we have no chance to modify the original intent. */ 432 @Test testCopyBaseIntentForTaskInfo()433 public void testCopyBaseIntentForTaskInfo() { 434 final Task task = createTask(1); 435 task.setTaskDescription(new ActivityManager.TaskDescription()); 436 final TaskInfo info = task.getTaskInfo(); 437 438 // The intent of info should be a copy so assert that they are different instances. 439 Assert.assertThat(info.baseIntent, not(sameInstance(task.getBaseIntent()))); 440 } 441 442 @Test testPropagateFocusedStateToRootTask()443 public void testPropagateFocusedStateToRootTask() { 444 final Task rootTask = createTask(mDefaultDisplay); 445 final Task leafTask = createTaskInRootTask(rootTask, 0 /* userId */); 446 447 final ActivityRecord activity = createActivityRecord(leafTask); 448 449 leafTask.getDisplayContent().setFocusedApp(activity); 450 451 assertTrue(leafTask.getTaskInfo().isFocused); 452 assertTrue(rootTask.getTaskInfo().isFocused); 453 454 leafTask.getDisplayContent().setFocusedApp(null); 455 456 assertFalse(leafTask.getTaskInfo().isFocused); 457 assertFalse(rootTask.getTaskInfo().isFocused); 458 } 459 460 @Test testReturnsToHomeRootTask()461 public void testReturnsToHomeRootTask() throws Exception { 462 final Task task = createTask(1); 463 spyOn(task); 464 doReturn(true).when(task).hasChild(); 465 assertFalse(task.returnsToHomeRootTask()); 466 task.intent = null; 467 assertFalse(task.returnsToHomeRootTask()); 468 task.intent = new Intent(); 469 assertFalse(task.returnsToHomeRootTask()); 470 task.intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME); 471 assertTrue(task.returnsToHomeRootTask()); 472 } 473 474 /** Ensures that empty bounds cause appBounds to inherit from parent. */ 475 @Test testAppBounds_EmptyBounds()476 public void testAppBounds_EmptyBounds() { 477 final Rect emptyBounds = new Rect(); 478 testRootTaskBoundsConfiguration(WINDOWING_MODE_FULLSCREEN, mParentBounds, emptyBounds, 479 mParentBounds); 480 } 481 482 /** Ensures that bounds on freeform root tasks are not clipped. */ 483 @Test testAppBounds_FreeFormBounds()484 public void testAppBounds_FreeFormBounds() { 485 final Rect freeFormBounds = new Rect(mParentBounds); 486 freeFormBounds.offset(10, 10); 487 testRootTaskBoundsConfiguration(WINDOWING_MODE_FREEFORM, mParentBounds, freeFormBounds, 488 freeFormBounds); 489 } 490 491 /** Ensures that fully contained bounds are not clipped. */ 492 @Test testAppBounds_ContainedBounds()493 public void testAppBounds_ContainedBounds() { 494 final Rect insetBounds = new Rect(mParentBounds); 495 insetBounds.inset(5, 5, 5, 5); 496 testRootTaskBoundsConfiguration( 497 WINDOWING_MODE_FREEFORM, mParentBounds, insetBounds, insetBounds); 498 } 499 500 @Test testFitWithinBounds()501 public void testFitWithinBounds() { 502 final Rect parentBounds = new Rect(10, 10, 200, 200); 503 TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); 504 Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FREEFORM, 505 ACTIVITY_TYPE_STANDARD, true /* onTop */); 506 Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); 507 final Configuration parentConfig = rootTask.getConfiguration(); 508 parentConfig.windowConfiguration.setBounds(parentBounds); 509 parentConfig.densityDpi = DisplayMetrics.DENSITY_DEFAULT; 510 511 // check top and left 512 Rect reqBounds = new Rect(-190, -190, 0, 0); 513 task.setBounds(reqBounds); 514 // Make sure part of it is exposed 515 assertTrue(task.getBounds().right > parentBounds.left); 516 assertTrue(task.getBounds().bottom > parentBounds.top); 517 // Should still be more-or-less in that corner 518 assertTrue(task.getBounds().left <= parentBounds.left); 519 assertTrue(task.getBounds().top <= parentBounds.top); 520 521 assertEquals(reqBounds.width(), task.getBounds().width()); 522 assertEquals(reqBounds.height(), task.getBounds().height()); 523 524 // check bottom and right 525 reqBounds = new Rect(210, 210, 400, 400); 526 task.setBounds(reqBounds); 527 // Make sure part of it is exposed 528 assertTrue(task.getBounds().left < parentBounds.right); 529 assertTrue(task.getBounds().top < parentBounds.bottom); 530 // Should still be more-or-less in that corner 531 assertTrue(task.getBounds().right >= parentBounds.right); 532 assertTrue(task.getBounds().bottom >= parentBounds.bottom); 533 534 assertEquals(reqBounds.width(), task.getBounds().width()); 535 assertEquals(reqBounds.height(), task.getBounds().height()); 536 } 537 538 /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */ 539 @Test testBoundsOnModeChangeFreeformToFullscreen()540 public void testBoundsOnModeChangeFreeformToFullscreen() { 541 DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay(); 542 Task rootTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true) 543 .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); 544 Task task = rootTask.getBottomMostTask(); 545 task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED); 546 DisplayInfo info = new DisplayInfo(); 547 display.mDisplay.getDisplayInfo(info); 548 final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight); 549 final Rect freeformBounds = new Rect(fullScreenBounds); 550 freeformBounds.inset((int) (freeformBounds.width() * 0.2), 551 (int) (freeformBounds.height() * 0.2)); 552 task.setBounds(freeformBounds); 553 554 assertEquals(freeformBounds, task.getBounds()); 555 556 // FULLSCREEN inherits bounds 557 rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 558 assertEquals(fullScreenBounds, task.getBounds()); 559 assertEquals(freeformBounds, task.mLastNonFullscreenBounds); 560 561 // FREEFORM restores bounds 562 rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); 563 assertEquals(freeformBounds, task.getBounds()); 564 } 565 566 @Test testTopActivityEligibleForUserAspectRatioButton()567 public void testTopActivityEligibleForUserAspectRatioButton() { 568 DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay(); 569 final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) 570 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); 571 final Task task = rootTask.getBottomMostTask(); 572 final ActivityRecord root = task.getTopNonFinishingActivity(); 573 spyOn(mWm.mLetterboxConfiguration); 574 spyOn(root); 575 spyOn(root.mLetterboxUiController); 576 577 doReturn(true).when(root.mLetterboxUiController) 578 .shouldEnableUserAspectRatioSettings(); 579 doReturn(false).when(root).inSizeCompatMode(); 580 doReturn(task).when(root).getOrganizedTask(); 581 582 // The button should be eligible to be displayed 583 assertTrue(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton); 584 585 // When shouldApplyUserMinAspectRatioOverride is disable the button is not enabled 586 doReturn(false).when(root.mLetterboxUiController) 587 .shouldEnableUserAspectRatioSettings(); 588 assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton); 589 doReturn(true).when(root.mLetterboxUiController) 590 .shouldEnableUserAspectRatioSettings(); 591 592 // When in size compat mode the button is not enabled 593 doReturn(true).when(root).inSizeCompatMode(); 594 assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton); 595 doReturn(false).when(root).inSizeCompatMode(); 596 } 597 598 /** 599 * Tests that a task with forced orientation has orientation-consistent bounds within the 600 * parent. 601 */ 602 @Test testFullscreenBoundsForcedOrientation()603 public void testFullscreenBoundsForcedOrientation() { 604 final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); 605 final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); 606 final DisplayContent display = new TestDisplayContent.Builder(mAtm, 607 fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); 608 assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); 609 // Fix the display orientation to landscape which is the natural rotation (0) for the test 610 // display. 611 final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); 612 dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); 613 dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); 614 615 final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) 616 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); 617 final Task task = rootTask.getBottomMostTask(); 618 final ActivityRecord root = task.getTopNonFinishingActivity(); 619 620 assertEquals(fullScreenBounds, task.getBounds()); 621 622 // Setting app to fixed portrait fits within parent 623 root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); 624 assertEquals(root, task.getRootActivity()); 625 assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation()); 626 // Portrait orientation is enforced on activity level. Task should fill fullscreen bounds. 627 assertThat(task.getBounds().height()).isLessThan(task.getBounds().width()); 628 assertEquals(fullScreenBounds, task.getBounds()); 629 630 // Top activity gets used 631 final ActivityRecord top = new ActivityBuilder(mAtm).setTask(task).setParentTask(rootTask) 632 .build(); 633 assertEquals(top, task.getTopNonFinishingActivity()); 634 top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 635 assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height()); 636 assertEquals(task.getBounds().width(), fullScreenBounds.width()); 637 638 // Setting app to unspecified restores 639 top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED); 640 assertEquals(fullScreenBounds, task.getBounds()); 641 642 // Setting app to fixed landscape and changing display 643 top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 644 // Fix the display orientation to portrait which is 90 degrees for the test display. 645 dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90); 646 647 // Fixed orientation request should be resolved on activity level. Task fills display 648 // bounds. 649 assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width()); 650 assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height()); 651 assertEquals(fullScreenBoundsPort, task.getBounds()); 652 653 // in FREEFORM, no constraint 654 final Rect freeformBounds = new Rect(display.getBounds()); 655 freeformBounds.inset((int) (freeformBounds.width() * 0.2), 656 (int) (freeformBounds.height() * 0.2)); 657 rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); 658 task.setBounds(freeformBounds); 659 assertEquals(freeformBounds, task.getBounds()); 660 661 // FULLSCREEN letterboxes bounds on activity level, no constraint on task level. 662 rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 663 rootTask.setBounds(null); 664 assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width()); 665 assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height()); 666 assertEquals(fullScreenBoundsPort, task.getBounds()); 667 668 // FREEFORM restores bounds as before 669 rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); 670 assertEquals(freeformBounds, task.getBounds()); 671 } 672 673 @Test testReportsOrientationRequestInLetterboxForOrientation()674 public void testReportsOrientationRequestInLetterboxForOrientation() { 675 final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); 676 final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920); 677 final DisplayContent display = new TestDisplayContent.Builder(mAtm, 678 fullScreenBounds.width(), fullScreenBounds.height()).setCanRotate(false).build(); 679 assertNotNull(mRootWindowContainer.getDisplayContent(display.mDisplayId)); 680 // Fix the display orientation to landscape which is the natural rotation (0) for the test 681 // display. 682 final DisplayRotation dr = display.mDisplayContent.getDisplayRotation(); 683 dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); 684 dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0); 685 686 final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) 687 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); 688 final Task task = rootTask.getBottomMostTask(); 689 ActivityRecord root = task.getTopNonFinishingActivity(); 690 691 assertEquals(fullScreenBounds, task.getBounds()); 692 693 // Setting app to fixed portrait fits within parent on activity level. Task fills parent. 694 root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); 695 assertThat(root.getBounds().width()).isLessThan(root.getBounds().height()); 696 assertEquals(task.getBounds(), fullScreenBounds); 697 698 assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getOrientation()); 699 } 700 701 @Test testIgnoresForcedOrientationWhenParentHandles()702 public void testIgnoresForcedOrientationWhenParentHandles() { 703 final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080); 704 DisplayContent display = new TestDisplayContent.Builder( 705 mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build(); 706 707 display.getRequestedOverrideConfiguration().orientation = 708 Configuration.ORIENTATION_LANDSCAPE; 709 display.onRequestedOverrideConfigurationChanged( 710 display.getRequestedOverrideConfiguration()); 711 Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true) 712 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build(); 713 Task task = rootTask.getBottomMostTask(); 714 ActivityRecord root = task.getTopNonFinishingActivity(); 715 716 final WindowContainer parentWindowContainer = 717 new WindowContainer(mSystemServicesTestRule.getWindowManagerService()); 718 spyOn(parentWindowContainer); 719 parentWindowContainer.setBounds(fullScreenBounds); 720 doReturn(parentWindowContainer).when(task).getParent(); 721 doReturn(display.getDefaultTaskDisplayArea()).when(task).getDisplayArea(); 722 doReturn(rootTask).when(task).getRootTask(); 723 doReturn(true).when(parentWindowContainer) 724 .handlesOrientationChangeFromDescendant(anyInt()); 725 726 // Setting app to fixed portrait fits within parent, but Task shouldn't adjust the 727 // bounds because its parent says it will handle it at a later time. 728 root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); 729 assertEquals(root, task.getRootActivity()); 730 assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation()); 731 assertEquals(fullScreenBounds, task.getBounds()); 732 } 733 734 @Test testComputeConfigResourceOverrides()735 public void testComputeConfigResourceOverrides() { 736 final Rect fullScreenBounds = new Rect(0, 0, 1080, 1920); 737 TestDisplayContent display = new TestDisplayContent.Builder( 738 mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build(); 739 final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); 740 final Configuration inOutConfig = new Configuration(); 741 final Configuration parentConfig = new Configuration(); 742 final Rect parentBounds = new Rect(0, 0, 250, 500); 743 final Rect parentAppBounds = new Rect(0, 0, 250, 480); 744 parentConfig.windowConfiguration.setBounds(parentBounds); 745 parentConfig.windowConfiguration.setAppBounds(parentAppBounds); 746 parentConfig.densityDpi = 400; 747 parentConfig.screenHeightDp = (parentBounds.bottom * 160) / parentConfig.densityDpi; // 200 748 parentConfig.screenWidthDp = (parentBounds.right * 160) / parentConfig.densityDpi; // 100 749 parentConfig.windowConfiguration.setRotation(ROTATION_0); 750 751 // By default, the input bounds will fill parent. 752 task.computeConfigResourceOverrides(inOutConfig, parentConfig); 753 754 assertEquals(parentConfig.screenHeightDp, inOutConfig.screenHeightDp); 755 assertEquals(parentConfig.screenWidthDp, inOutConfig.screenWidthDp); 756 assertEquals(parentAppBounds, inOutConfig.windowConfiguration.getAppBounds()); 757 assertEquals(Configuration.ORIENTATION_PORTRAIT, inOutConfig.orientation); 758 759 // If bounds are overridden, config properties should be made to match. Surface hierarchy 760 // will crop for policy. 761 inOutConfig.setToDefaults(); 762 final int longSide = 960; 763 final int shortSide = 540; 764 parentConfig.densityDpi = 192; 765 final Rect largerPortraitBounds = new Rect(0, 0, shortSide, longSide); 766 inOutConfig.windowConfiguration.setBounds(largerPortraitBounds); 767 task.computeConfigResourceOverrides(inOutConfig, parentConfig); 768 // The override bounds are beyond the parent, the out appBounds should not be intersected 769 // by parent appBounds. 770 assertEquals(largerPortraitBounds, inOutConfig.windowConfiguration.getAppBounds()); 771 assertEquals(800, inOutConfig.screenHeightDp); // 960/(192/160) = 800 772 assertEquals(450, inOutConfig.screenWidthDp); // 540/(192/160) = 450 773 774 inOutConfig.setToDefaults(); 775 // Landscape bounds. 776 final Rect largerLandscapeBounds = new Rect(0, 0, longSide, shortSide); 777 inOutConfig.windowConfiguration.setBounds(largerLandscapeBounds); 778 779 // Setup the display with a top stable inset. The later assertion will ensure the inset is 780 // excluded from screenHeightDp. 781 final int statusBarHeight = 100; 782 final DisplayInfo di = display.getDisplayInfo(); 783 display.getDisplayPolicy().getDecorInsetsInfo(di.rotation, 784 di.logicalWidth, di.logicalHeight).mConfigInsets.top = statusBarHeight; 785 786 // Without limiting to be inside the parent bounds, the out screen size should keep relative 787 // to the input bounds. 788 final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); 789 final ActivityRecord.CompatDisplayInsets compatInsets = 790 new ActivityRecord.CompatDisplayInsets( 791 display, activity, /* fixedOrientationBounds= */ null); 792 task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatInsets); 793 794 assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds()); 795 final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; 796 final int expectedHeightDp = (int) ((shortSide - statusBarHeight) / density + 0.5f); 797 assertEquals(expectedHeightDp, inOutConfig.screenHeightDp); 798 final int expectedWidthDp = (int) (longSide / density + 0.5f); 799 assertEquals(expectedWidthDp, inOutConfig.screenWidthDp); 800 assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation); 801 } 802 803 @Test testComputeConfigResourceLayoutOverrides()804 public void testComputeConfigResourceLayoutOverrides() { 805 final Rect fullScreenBounds = new Rect(0, 0, 1000, 2500); 806 TestDisplayContent display = new TestDisplayContent.Builder( 807 mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build(); 808 final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); 809 final Configuration inOutConfig = new Configuration(); 810 final Configuration parentConfig = new Configuration(); 811 final Rect nonLongBounds = new Rect(0, 0, 1000, 1250); 812 parentConfig.windowConfiguration.setBounds(fullScreenBounds); 813 parentConfig.windowConfiguration.setAppBounds(fullScreenBounds); 814 parentConfig.densityDpi = 400; 815 parentConfig.screenHeightDp = (fullScreenBounds.bottom * 160) / parentConfig.densityDpi; 816 parentConfig.screenWidthDp = (fullScreenBounds.right * 160) / parentConfig.densityDpi; 817 parentConfig.windowConfiguration.setRotation(ROTATION_0); 818 819 // Set BOTH screenW/H to an override value 820 inOutConfig.screenWidthDp = nonLongBounds.width() * 160 / parentConfig.densityDpi; 821 inOutConfig.screenHeightDp = nonLongBounds.height() * 160 / parentConfig.densityDpi; 822 task.computeConfigResourceOverrides(inOutConfig, parentConfig); 823 824 // screenLayout should honor override when both screenW/H are set. 825 assertTrue((inOutConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_NO) != 0); 826 } 827 828 @Test testComputeNestedConfigResourceOverrides()829 public void testComputeNestedConfigResourceOverrides() { 830 final Task task = new TaskBuilder(mSupervisor).build(); 831 assertTrue(task.getResolvedOverrideBounds().isEmpty()); 832 int origScreenH = task.getConfiguration().screenHeightDp; 833 Configuration rootTaskConfig = new Configuration(); 834 rootTaskConfig.setTo(task.getRootTask().getRequestedOverrideConfiguration()); 835 rootTaskConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); 836 837 // Set bounds on root task (not task) and verify that the task resource configuration 838 // changes despite it's override bounds being empty. 839 Rect bounds = new Rect(task.getRootTask().getBounds()); 840 bounds.bottom = (int) (bounds.bottom * 0.6f); 841 rootTaskConfig.windowConfiguration.setBounds(bounds); 842 task.getRootTask().onRequestedOverrideConfigurationChanged(rootTaskConfig); 843 assertNotEquals(origScreenH, task.getConfiguration().screenHeightDp); 844 } 845 846 @Test testFullScreenTaskNotAdjustedByMinimalSize()847 public void testFullScreenTaskNotAdjustedByMinimalSize() { 848 final Task fullscreenTask = new TaskBuilder(mSupervisor).build(); 849 final Rect originalTaskBounds = new Rect(fullscreenTask.getBounds()); 850 final ActivityInfo aInfo = new ActivityInfo(); 851 aInfo.windowLayout = new ActivityInfo.WindowLayout(0 /* width */, 0 /* widthFraction */, 852 0 /* height */, 0 /* heightFraction */, 0 /* gravity */, 853 originalTaskBounds.width() * 2 /* minWidth */, 854 originalTaskBounds.height() * 2 /* minHeight */); 855 fullscreenTask.setMinDimensions(aInfo); 856 fullscreenTask.onConfigurationChanged(fullscreenTask.getParent().getConfiguration()); 857 858 assertEquals(originalTaskBounds, fullscreenTask.getBounds()); 859 } 860 861 @Test testInsetDisregardedWhenFreeformOverlapsNavBar()862 public void testInsetDisregardedWhenFreeformOverlapsNavBar() { 863 TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); 864 Task rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_FULLSCREEN, 865 ACTIVITY_TYPE_STANDARD, true /* onTop */); 866 DisplayInfo displayInfo = new DisplayInfo(); 867 mAtm.mContext.getDisplay().getDisplayInfo(displayInfo); 868 final int displayHeight = displayInfo.logicalHeight; 869 final Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); 870 final Configuration inOutConfig = new Configuration(); 871 final Configuration parentConfig = new Configuration(); 872 final int longSide = 1200; 873 final int shortSide = 600; 874 parentConfig.densityDpi = 400; 875 parentConfig.screenHeightDp = 200; // 200 * 400 / 160 = 500px 876 parentConfig.screenWidthDp = 100; // 100 * 400 / 160 = 250px 877 parentConfig.windowConfiguration.setRotation(ROTATION_0); 878 879 final int longSideDp = 480; // longSide / density = 1200 / 400 * 160 880 final int shortSideDp = 240; // shortSide / density = 600 / 400 * 160 881 final int screenLayout = parentConfig.screenLayout 882 & (Configuration.SCREENLAYOUT_LONG_MASK | Configuration.SCREENLAYOUT_SIZE_MASK); 883 final int reducedScreenLayout = 884 Configuration.reduceScreenLayout(screenLayout, longSideDp, shortSideDp); 885 886 // Portrait bounds overlapping with navigation bar, without insets. 887 final Rect freeformBounds = new Rect(0, 888 displayHeight - 10 - longSide, 889 shortSide, 890 displayHeight - 10); 891 inOutConfig.windowConfiguration.setBounds(freeformBounds); 892 // Set to freeform mode to verify bug fix. 893 inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); 894 895 task.computeConfigResourceOverrides(inOutConfig, parentConfig); 896 897 // screenW/H should not be effected by parent since overridden and freeform 898 assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi, 899 inOutConfig.screenWidthDp); 900 assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi, 901 inOutConfig.screenHeightDp); 902 assertEquals(reducedScreenLayout, inOutConfig.screenLayout); 903 904 inOutConfig.setToDefaults(); 905 // Landscape bounds overlapping with navigtion bar, without insets. 906 freeformBounds.set(0, 907 displayHeight - 10 - shortSide, 908 longSide, 909 displayHeight - 10); 910 inOutConfig.windowConfiguration.setBounds(freeformBounds); 911 inOutConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); 912 913 task.computeConfigResourceOverrides(inOutConfig, parentConfig); 914 915 assertEquals(freeformBounds.width() * 160 / parentConfig.densityDpi, 916 inOutConfig.screenWidthDp); 917 assertEquals(freeformBounds.height() * 160 / parentConfig.densityDpi, 918 inOutConfig.screenHeightDp); 919 assertEquals(reducedScreenLayout, inOutConfig.screenLayout); 920 } 921 922 /** Ensures that the alias intent won't have target component resolved. */ 923 @Test testTaskIntentActivityAlias()924 public void testTaskIntentActivityAlias() { 925 final String aliasClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".aliasActivity"; 926 final String targetClassName = DEFAULT_COMPONENT_PACKAGE_NAME + ".targetActivity"; 927 final ComponentName aliasComponent = 928 new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, aliasClassName); 929 final ComponentName targetComponent = 930 new ComponentName(DEFAULT_COMPONENT_PACKAGE_NAME, targetClassName); 931 932 final Intent intent = new Intent(); 933 intent.setPackage(DEFAULT_COMPONENT_PACKAGE_NAME); 934 intent.setComponent(aliasComponent); 935 final ActivityInfo info = new ActivityInfo(); 936 info.applicationInfo = new ApplicationInfo(); 937 info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME; 938 info.targetActivity = targetClassName; 939 940 final Task task = new Task.Builder(mAtm) 941 .setTaskId(1) 942 .setActivityInfo(info) 943 .setIntent(intent) 944 .build(); 945 assertEquals("The alias activity component should be saved in task intent.", aliasClassName, 946 task.intent.getComponent().getClassName()); 947 948 ActivityRecord aliasActivity = new ActivityBuilder(mAtm).setComponent( 949 aliasComponent).setTargetActivity(targetClassName).build(); 950 assertEquals("Should be the same intent filter.", true, 951 task.isSameIntentFilter(aliasActivity)); 952 953 ActivityRecord targetActivity = new ActivityBuilder(mAtm).setComponent( 954 targetComponent).build(); 955 assertEquals("Should be the same intent filter.", true, 956 task.isSameIntentFilter(targetActivity)); 957 958 ActivityRecord defaultActivity = new ActivityBuilder(mAtm).build(); 959 assertEquals("Should not be the same intent filter.", false, 960 task.isSameIntentFilter(defaultActivity)); 961 } 962 963 /** Test that root activity index is reported correctly for several activities in the task. */ 964 @Test testFindRootIndex()965 public void testFindRootIndex() { 966 final Task task = getTestTask(); 967 // Add an extra activity on top of the root one 968 new ActivityBuilder(mAtm).setTask(task).build(); 969 970 assertEquals("The root activity in the task must be reported.", task.getChildAt(0), 971 task.getRootActivity( 972 true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); 973 } 974 975 /** 976 * Test that root activity index is reported correctly for several activities in the task when 977 * the activities on the bottom are finishing. 978 */ 979 @Test testFindRootIndex_finishing()980 public void testFindRootIndex_finishing() { 981 final Task task = getTestTask(); 982 // Add extra two activities and mark the two on the bottom as finishing. 983 final ActivityRecord activity0 = task.getBottomMostActivity(); 984 activity0.finishing = true; 985 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 986 activity1.finishing = true; 987 new ActivityBuilder(mAtm).setTask(task).build(); 988 989 assertEquals("The first non-finishing activity in the task must be reported.", 990 task.getChildAt(2), task.getRootActivity( 991 true /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); 992 } 993 994 /** 995 * Test that root activity index is reported correctly for several activities in the task when 996 * looking for the 'effective root'. 997 */ 998 @Test testFindRootIndex_effectiveRoot()999 public void testFindRootIndex_effectiveRoot() { 1000 final Task task = getTestTask(); 1001 // Add an extra activity on top of the root one 1002 new ActivityBuilder(mAtm).setTask(task).build(); 1003 1004 assertEquals("The root activity in the task must be reported.", 1005 task.getChildAt(0), task.getRootActivity( 1006 false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); 1007 } 1008 1009 /** 1010 * Test that root activity index is reported correctly when looking for the 'effective root' in 1011 * case when bottom activities are relinquishing task identity or finishing. 1012 */ 1013 @Test testFindRootIndex_effectiveRoot_finishingAndRelinquishing()1014 public void testFindRootIndex_effectiveRoot_finishingAndRelinquishing() { 1015 final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build(); 1016 final Task task = activity0.getTask(); 1017 // Add extra two activities. Mark the one on the bottom with "relinquishTaskIdentity" and 1018 // one above as finishing. 1019 activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; 1020 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 1021 activity1.finishing = true; 1022 new ActivityBuilder(mAtm).setTask(task).build(); 1023 1024 assertEquals("The first non-finishing activity and non-relinquishing task identity " 1025 + "must be reported.", task.getChildAt(2), task.getRootActivity( 1026 false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); 1027 } 1028 1029 /** 1030 * Test that root activity index is reported correctly when looking for the 'effective root' 1031 * for the case when there is only a single activity that also has relinquishTaskIdentity set. 1032 */ 1033 @Test testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity()1034 public void testFindRootIndex_effectiveRoot_relinquishingAndSingleActivity() { 1035 final Task task = getTestTask(); 1036 // Set relinquishTaskIdentity for the only activity in the task 1037 task.getBottomMostActivity().info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; 1038 1039 assertEquals("The root activity in the task must be reported.", 1040 task.getChildAt(0), task.getRootActivity( 1041 false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); 1042 } 1043 1044 /** 1045 * Test that the topmost activity index is reported correctly when looking for the 1046 * 'effective root' for the case when all activities have relinquishTaskIdentity set. 1047 */ 1048 @Test testFindRootIndex_effectiveRoot_relinquishingMultipleActivities()1049 public void testFindRootIndex_effectiveRoot_relinquishingMultipleActivities() { 1050 final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build(); 1051 final Task task = activity0.getTask(); 1052 // Set relinquishTaskIdentity for all activities in the task 1053 activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; 1054 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 1055 activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; 1056 1057 assertEquals("The topmost activity in the task must be reported.", 1058 task.getChildAt(task.getChildCount() - 1), task.getRootActivity( 1059 false /*ignoreRelinquishIdentity*/, true /*setToBottomIfNone*/)); 1060 } 1061 1062 /** Test that bottom-most activity is reported in {@link Task#getRootActivity()}. */ 1063 @Test testGetRootActivity()1064 public void testGetRootActivity() { 1065 final Task task = getTestTask(); 1066 // Add an extra activity on top of the root one 1067 new ActivityBuilder(mAtm).setTask(task).build(); 1068 1069 assertEquals("The root activity in the task must be reported.", 1070 task.getBottomMostActivity(), task.getRootActivity()); 1071 } 1072 1073 /** 1074 * Test that first non-finishing activity is reported in {@link Task#getRootActivity()}. 1075 */ 1076 @Test testGetRootActivity_finishing()1077 public void testGetRootActivity_finishing() { 1078 final Task task = getTestTask(); 1079 // Add an extra activity on top of the root one 1080 new ActivityBuilder(mAtm).setTask(task).build(); 1081 // Mark the root as finishing 1082 task.getBottomMostActivity().finishing = true; 1083 1084 assertEquals("The first non-finishing activity in the task must be reported.", 1085 task.getChildAt(1), task.getRootActivity()); 1086 } 1087 1088 /** 1089 * Test that relinquishTaskIdentity flag is ignored in {@link Task#getRootActivity()}. 1090 */ 1091 @Test testGetRootActivity_relinquishTaskIdentity()1092 public void testGetRootActivity_relinquishTaskIdentity() { 1093 final Task task = getTestTask(); 1094 // Mark the bottom-most activity with FLAG_RELINQUISH_TASK_IDENTITY. 1095 final ActivityRecord activity0 = task.getBottomMostActivity(); 1096 activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; 1097 // Add an extra activity on top of the root one. 1098 new ActivityBuilder(mAtm).setTask(task).build(); 1099 1100 assertEquals("The root activity in the task must be reported.", 1101 task.getBottomMostActivity(), task.getRootActivity()); 1102 } 1103 1104 /** 1105 * Test that no activity is reported in {@link Task#getRootActivity()} when all activities 1106 * in the task are finishing. 1107 */ 1108 @Test testGetRootActivity_allFinishing()1109 public void testGetRootActivity_allFinishing() { 1110 final Task task = getTestTask(); 1111 // Mark the bottom-most activity as finishing. 1112 final ActivityRecord activity0 = task.getBottomMostActivity(); 1113 activity0.finishing = true; 1114 // Add an extra activity on top of the root one and mark it as finishing 1115 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 1116 activity1.finishing = true; 1117 1118 assertNull("No activity must be reported if all are finishing", task.getRootActivity()); 1119 } 1120 1121 /** 1122 * Test that first non-finishing activity is the root of task. 1123 */ 1124 @Test testIsRootActivity()1125 public void testIsRootActivity() { 1126 final Task task = getTestTask(); 1127 // Mark the bottom-most activity as finishing. 1128 final ActivityRecord activity0 = task.getBottomMostActivity(); 1129 activity0.finishing = true; 1130 // Add an extra activity on top of the root one. 1131 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 1132 1133 assertFalse("Finishing activity must not be the root of task", activity0.isRootOfTask()); 1134 assertTrue("Non-finishing activity must be the root of task", activity1.isRootOfTask()); 1135 } 1136 1137 /** 1138 * Test that if all activities in the task are finishing, then the one on the bottom is the 1139 * root of task. 1140 */ 1141 @Test testIsRootActivity_allFinishing()1142 public void testIsRootActivity_allFinishing() { 1143 final Task task = getTestTask(); 1144 // Mark the bottom-most activity as finishing. 1145 final ActivityRecord activity0 = task.getBottomMostActivity(); 1146 activity0.finishing = true; 1147 // Add an extra activity on top of the root one and mark it as finishing 1148 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 1149 activity1.finishing = true; 1150 1151 assertTrue("Bottom activity must be the root of task", activity0.isRootOfTask()); 1152 assertFalse("Finishing activity on top must not be the root of task", 1153 activity1.isRootOfTask()); 1154 } 1155 1156 /** 1157 * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)}. 1158 */ 1159 @Test testGetTaskForActivity()1160 public void testGetTaskForActivity() { 1161 final Task task0 = getTestTask(); 1162 final ActivityRecord activity0 = task0.getBottomMostActivity(); 1163 1164 final Task task1 = getTestTask(); 1165 final ActivityRecord activity1 = task1.getBottomMostActivity(); 1166 1167 assertEquals(task0.mTaskId, 1168 ActivityRecord.getTaskForActivityLocked(activity0.token, false /* onlyRoot */)); 1169 assertEquals(task1.mTaskId, 1170 ActivityRecord.getTaskForActivityLocked(activity1.token, false /* onlyRoot */)); 1171 } 1172 1173 /** 1174 * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with finishing 1175 * activity. 1176 */ 1177 @Test testGetTaskForActivity_onlyRoot_finishing()1178 public void testGetTaskForActivity_onlyRoot_finishing() { 1179 final Task task = getTestTask(); 1180 // Make the current root activity finishing 1181 final ActivityRecord activity0 = task.getBottomMostActivity(); 1182 activity0.finishing = true; 1183 // Add an extra activity on top - this will be the new root 1184 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 1185 // Add one more on top 1186 final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); 1187 1188 assertEquals(task.mTaskId, 1189 ActivityRecord.getTaskForActivityLocked(activity0.token, true /* onlyRoot */)); 1190 assertEquals(task.mTaskId, 1191 ActivityRecord.getTaskForActivityLocked(activity1.token, true /* onlyRoot */)); 1192 assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, 1193 ActivityRecord.getTaskForActivityLocked(activity2.token, true /* onlyRoot */)); 1194 } 1195 1196 /** 1197 * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} with activity that 1198 * relinquishes task identity. 1199 */ 1200 @Test testGetTaskForActivity_onlyRoot_relinquishTaskIdentity()1201 public void testGetTaskForActivity_onlyRoot_relinquishTaskIdentity() { 1202 final ActivityRecord activity0 = new ActivityBuilder(mAtm).setCreateTask(true).build(); 1203 final Task task = activity0.getTask(); 1204 // Make the current root activity relinquish task identity 1205 activity0.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; 1206 // Add an extra activity on top - this will be the new root 1207 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 1208 // Add one more on top 1209 final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); 1210 1211 assertEquals(task.mTaskId, 1212 ActivityRecord.getTaskForActivityLocked(activity0.token, true /* onlyRoot */)); 1213 assertEquals(task.mTaskId, 1214 ActivityRecord.getTaskForActivityLocked(activity1.token, true /* onlyRoot */)); 1215 assertEquals("No task must be reported for activity that is above root", INVALID_TASK_ID, 1216 ActivityRecord.getTaskForActivityLocked(activity2.token, true /* onlyRoot */)); 1217 } 1218 1219 /** 1220 * Test {@link ActivityRecord#getTaskForActivityLocked(IBinder, boolean)} allowing non-root 1221 * entries. 1222 */ 1223 @Test testGetTaskForActivity_notOnlyRoot()1224 public void testGetTaskForActivity_notOnlyRoot() { 1225 final Task task = getTestTask(); 1226 // Mark the bottom-most activity as finishing. 1227 final ActivityRecord activity0 = task.getBottomMostActivity(); 1228 activity0.finishing = true; 1229 1230 // Add an extra activity on top of the root one and make it relinquish task identity 1231 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 1232 activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; 1233 1234 // Add one more activity on top 1235 final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); 1236 1237 assertEquals(task.mTaskId, 1238 ActivityRecord.getTaskForActivityLocked(activity0.token, false /* onlyRoot */)); 1239 assertEquals(task.mTaskId, 1240 ActivityRecord.getTaskForActivityLocked(activity1.token, false /* onlyRoot */)); 1241 assertEquals(task.mTaskId, 1242 ActivityRecord.getTaskForActivityLocked(activity2.token, false /* onlyRoot */)); 1243 } 1244 1245 /** 1246 * Test {@link Task#updateEffectiveIntent()}. 1247 */ 1248 @Test testUpdateEffectiveIntent()1249 public void testUpdateEffectiveIntent() { 1250 // Test simple case with a single activity. 1251 final Task task = getTestTask(); 1252 final ActivityRecord activity0 = task.getBottomMostActivity(); 1253 1254 spyOn(task); 1255 task.updateEffectiveIntent(); 1256 verify(task).setIntent(eq(activity0)); 1257 } 1258 1259 /** 1260 * Test {@link Task#updateEffectiveIntent()} with root activity marked as finishing. This 1261 * should make the task use the second activity when updating the intent. 1262 */ 1263 @Test testUpdateEffectiveIntent_rootFinishing()1264 public void testUpdateEffectiveIntent_rootFinishing() { 1265 // Test simple case with a single activity. 1266 final Task task = getTestTask(); 1267 final ActivityRecord activity0 = task.getBottomMostActivity(); 1268 // Mark the bottom-most activity as finishing. 1269 activity0.finishing = true; 1270 // Add an extra activity on top of the root one 1271 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 1272 1273 spyOn(task); 1274 task.updateEffectiveIntent(); 1275 verify(task).setIntent(eq(activity1)); 1276 } 1277 1278 /** 1279 * Test {@link Task#updateEffectiveIntent()} when all activities are finishing or 1280 * relinquishing task identity. In this case the root activity should still be used when 1281 * updating the intent (legacy behavior). 1282 */ 1283 @Test testUpdateEffectiveIntent_allFinishing()1284 public void testUpdateEffectiveIntent_allFinishing() { 1285 // Test simple case with a single activity. 1286 final Task task = getTestTask(); 1287 final ActivityRecord activity0 = task.getBottomMostActivity(); 1288 // Mark the bottom-most activity as finishing. 1289 activity0.finishing = true; 1290 // Add an extra activity on top of the root one and make it relinquish task identity 1291 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 1292 activity1.finishing = true; 1293 1294 // Task must still update the intent using the root activity (preserving legacy behavior). 1295 spyOn(task); 1296 task.updateEffectiveIntent(); 1297 verify(task).setIntent(eq(activity0)); 1298 } 1299 1300 /** 1301 * Test {@link Task#updateEffectiveIntent()} when activity with relinquishTaskIdentity but 1302 * another with different uid. This should make the task use the root activity when updating the 1303 * intent. 1304 */ 1305 @Test testUpdateEffectiveIntent_relinquishingWithDifferentUid()1306 public void testUpdateEffectiveIntent_relinquishingWithDifferentUid() { 1307 final ActivityRecord activity0 = new ActivityBuilder(mAtm) 1308 .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build(); 1309 final Task task = activity0.getTask(); 1310 1311 // Add an extra activity on top 1312 new ActivityBuilder(mAtm).setUid(11).setTask(task).build(); 1313 1314 spyOn(task); 1315 task.updateEffectiveIntent(); 1316 verify(task).setIntent(eq(activity0)); 1317 } 1318 1319 /** 1320 * Test {@link Task#updateEffectiveIntent()} with activities set as relinquishTaskIdentity. 1321 * This should make the task use the topmost activity when updating the intent. 1322 */ 1323 @Test testUpdateEffectiveIntent_relinquishingMultipleActivities()1324 public void testUpdateEffectiveIntent_relinquishingMultipleActivities() { 1325 final ActivityRecord activity0 = new ActivityBuilder(mAtm) 1326 .setActivityFlags(FLAG_RELINQUISH_TASK_IDENTITY).setCreateTask(true).build(); 1327 final Task task = activity0.getTask(); 1328 // Add an extra activity on top 1329 final ActivityRecord activity1 = new ActivityBuilder(mAtm).setTask(task).build(); 1330 activity1.info.flags |= FLAG_RELINQUISH_TASK_IDENTITY; 1331 1332 // Add an extra activity on top 1333 final ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(task).build(); 1334 1335 spyOn(task); 1336 task.updateEffectiveIntent(); 1337 verify(task).setIntent(eq(activity2)); 1338 } 1339 1340 @Test testSaveLaunchingStateWhenConfigurationChanged()1341 public void testSaveLaunchingStateWhenConfigurationChanged() { 1342 LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; 1343 spyOn(persister); 1344 1345 final Task task = getTestTask(); 1346 task.setHasBeenVisible(false); 1347 task.getDisplayContent().getDefaultTaskDisplayArea() 1348 .setWindowingMode(WINDOWING_MODE_FREEFORM); 1349 task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); 1350 1351 task.setHasBeenVisible(true); 1352 task.onConfigurationChanged(task.getParent().getConfiguration()); 1353 1354 verify(persister).saveTask(task, task.getDisplayContent()); 1355 } 1356 1357 @Test testSaveLaunchingStateWhenClearingParent()1358 public void testSaveLaunchingStateWhenClearingParent() { 1359 LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; 1360 spyOn(persister); 1361 1362 final Task task = getTestTask(); 1363 task.setHasBeenVisible(true); 1364 task.getDisplayContent() 1365 .getDefaultTaskDisplayArea() 1366 .setWindowingMode(WINDOWING_MODE_FREEFORM); 1367 task.getRootTask().setWindowingMode(WINDOWING_MODE_FREEFORM); 1368 final DisplayContent oldDisplay = task.getDisplayContent(); 1369 1370 LaunchParamsController.LaunchParams params = new LaunchParamsController.LaunchParams(); 1371 persister.getLaunchParams(task, null, params); 1372 assertEquals(WINDOWING_MODE_FREEFORM, params.mWindowingMode); 1373 1374 task.setHasBeenVisible(true); 1375 task.removeImmediately(); 1376 1377 verify(persister).saveTask(task, oldDisplay); 1378 1379 persister.getLaunchParams(task, null, params); 1380 assertEquals(WINDOWING_MODE_FREEFORM, params.mWindowingMode); 1381 } 1382 1383 @Test testNotSaveLaunchingStateNonFreeformDisplay()1384 public void testNotSaveLaunchingStateNonFreeformDisplay() { 1385 LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; 1386 spyOn(persister); 1387 1388 final Task task = getTestTask(); 1389 task.setHasBeenVisible(false); 1390 task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); 1391 1392 task.setHasBeenVisible(true); 1393 task.onConfigurationChanged(task.getParent().getConfiguration()); 1394 1395 Mockito.verify(persister, never()).saveTask(same(task), any()); 1396 } 1397 1398 @Test testNotSaveLaunchingStateWhenNotFullscreenOrFreeformWindow()1399 public void testNotSaveLaunchingStateWhenNotFullscreenOrFreeformWindow() { 1400 LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; 1401 spyOn(persister); 1402 1403 final Task task = getTestTask(); 1404 task.setHasBeenVisible(false); 1405 task.getDisplayContent().getDefaultTaskDisplayArea() 1406 .setWindowingMode(WINDOWING_MODE_FREEFORM); 1407 task.getRootTask().setWindowingMode(WINDOWING_MODE_PINNED); 1408 1409 task.setHasBeenVisible(true); 1410 task.onConfigurationChanged(task.getParent().getConfiguration()); 1411 1412 Mockito.verify(persister, never()).saveTask(same(task), any()); 1413 } 1414 1415 @Test testNotSaveLaunchingStateForNonLeafTask()1416 public void testNotSaveLaunchingStateForNonLeafTask() { 1417 LaunchParamsPersister persister = mAtm.mTaskSupervisor.mLaunchParamsPersister; 1418 spyOn(persister); 1419 1420 final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true) 1421 .setCreateParentTask(true).build().getRootTask(); 1422 task.setHasBeenVisible(false); 1423 task.getDisplayContent().getDefaultTaskDisplayArea() 1424 .setWindowingMode(WINDOWING_MODE_FREEFORM); 1425 task.getRootTask().setWindowingMode(WINDOWING_MODE_FULLSCREEN); 1426 1427 final Task leafTask = createTaskInRootTask(task, 0 /* userId */); 1428 1429 leafTask.setHasBeenVisible(true); 1430 task.setHasBeenVisible(true); 1431 task.onConfigurationChanged(task.getParent().getConfiguration()); 1432 1433 Mockito.verify(persister, never()).saveTask(same(task), any()); 1434 verify(persister).saveTask(same(leafTask), any()); 1435 } 1436 1437 @Test testNotSpecifyOrientationByFloatingTask()1438 public void testNotSpecifyOrientationByFloatingTask() { 1439 final Task task = new TaskBuilder(mSupervisor) 1440 .setCreateActivity(true).setCreateParentTask(true).build(); 1441 final ActivityRecord activity = task.getTopMostActivity(); 1442 final WindowContainer<?> parentContainer = task.getParent(); 1443 final TaskDisplayArea taskDisplayArea = task.getDisplayArea(); 1444 activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 1445 1446 assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parentContainer.getOrientation()); 1447 assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation()); 1448 1449 task.setWindowingMode(WINDOWING_MODE_PINNED); 1450 1451 // TDA returns the last orientation when child returns UNSET 1452 assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation()); 1453 assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation()); 1454 } 1455 1456 @Test testNotSpecifyOrientation_taskDisplayAreaNotFocused()1457 public void testNotSpecifyOrientation_taskDisplayAreaNotFocused() { 1458 final TaskDisplayArea firstTaskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); 1459 final TaskDisplayArea secondTaskDisplayArea = createTaskDisplayArea( 1460 mDisplayContent, mRootWindowContainer.mWmService, "TestTaskDisplayArea", 1461 FEATURE_VENDOR_FIRST); 1462 final Task firstRootTask = firstTaskDisplayArea.createRootTask( 1463 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 1464 final Task secondRootTask = secondTaskDisplayArea.createRootTask( 1465 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); 1466 final ActivityRecord firstActivity = new ActivityBuilder(mAtm) 1467 .setTask(firstRootTask).build(); 1468 final ActivityRecord secondActivity = new ActivityBuilder(mAtm) 1469 .setTask(secondRootTask).build(); 1470 firstActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 1471 secondActivity.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); 1472 1473 // Activity on TDA1 is focused 1474 mDisplayContent.setFocusedApp(firstActivity); 1475 1476 assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation()); 1477 assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation()); 1478 1479 // No focused app, TDA1 is still recorded as last focused. 1480 mDisplayContent.setFocusedApp(null); 1481 1482 assertEquals(SCREEN_ORIENTATION_LANDSCAPE, firstTaskDisplayArea.getOrientation()); 1483 assertEquals(SCREEN_ORIENTATION_UNSET, secondTaskDisplayArea.getOrientation()); 1484 1485 // Activity on TDA2 is focused 1486 mDisplayContent.setFocusedApp(secondActivity); 1487 1488 assertEquals(SCREEN_ORIENTATION_UNSET, firstTaskDisplayArea.getOrientation()); 1489 assertEquals(SCREEN_ORIENTATION_PORTRAIT, secondTaskDisplayArea.getOrientation()); 1490 } 1491 1492 @Test testTaskOrientationOnDisplayWindowingModeChange()1493 public void testTaskOrientationOnDisplayWindowingModeChange() { 1494 // Skip unnecessary operations to speed up the test. 1495 mAtm.deferWindowLayout(); 1496 final Task task = getTestTask(); 1497 final ActivityRecord activity = task.getTopMostActivity(); 1498 final DisplayContent display = task.getDisplayContent(); 1499 mWm.setWindowingMode(display.mDisplayId, WINDOWING_MODE_FREEFORM); 1500 1501 activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 1502 assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation()); 1503 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, display.getLastOrientation()); 1504 1505 mWm.setWindowingMode(display.mDisplayId, WINDOWING_MODE_FULLSCREEN); 1506 assertEquals(SCREEN_ORIENTATION_LANDSCAPE, task.getOrientation()); 1507 assertEquals(SCREEN_ORIENTATION_LANDSCAPE, display.getLastOrientation()); 1508 assertEquals(Configuration.ORIENTATION_LANDSCAPE, display.getConfiguration().orientation); 1509 } 1510 1511 @Test testGetNonNullDimmerOnUntrustedDisplays()1512 public void testGetNonNullDimmerOnUntrustedDisplays() { 1513 final DisplayInfo untrustedDisplayInfo = new DisplayInfo(mDisplayInfo); 1514 untrustedDisplayInfo.flags &= ~Display.FLAG_TRUSTED; 1515 final DisplayContent untrustedDisplay = createNewDisplay(untrustedDisplayInfo); 1516 final ActivityRecord activity = createActivityRecord(untrustedDisplay); 1517 activity.setOccludesParent(false); 1518 assertNotNull(activity.getTask().getDimmer()); 1519 } 1520 1521 @Test testResumeTask_doNotResumeTaskFragmentBehindTranslucent()1522 public void testResumeTask_doNotResumeTaskFragmentBehindTranslucent() { 1523 final Task task = createTask(mDisplayContent); 1524 final TaskFragment tfBehind = createTaskFragmentWithActivity(task); 1525 final TaskFragment tfFront = createTaskFragmentWithActivity(task); 1526 spyOn(tfFront); 1527 doReturn(true).when(tfFront).isTranslucent(any()); 1528 1529 // TaskFragment behind another translucent TaskFragment should not be resumed. 1530 assertEquals(TASK_FRAGMENT_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT, 1531 tfBehind.getVisibility(null /* starting */)); 1532 assertTrue(tfBehind.isFocusable()); 1533 assertFalse(tfBehind.canBeResumed(null /* starting */)); 1534 1535 spyOn(tfBehind); 1536 task.resumeTopActivityUncheckedLocked(null /* prev */, ActivityOptions.makeBasic(), 1537 false /* deferPause */); 1538 1539 verify(tfBehind, never()).resumeTopActivity(any(), any(), anyBoolean()); 1540 } 1541 1542 @Test testGetTaskFragment()1543 public void testGetTaskFragment() { 1544 final Task parentTask = createTask(mDisplayContent); 1545 final TaskFragment tf0 = createTaskFragmentWithActivity(parentTask); 1546 final TaskFragment tf1 = createTaskFragmentWithActivity(parentTask); 1547 1548 assertNull("Could not find it because there's no organized TaskFragment", 1549 parentTask.getTaskFragment(TaskFragment::isOrganizedTaskFragment)); 1550 1551 doReturn(true).when(tf0).isOrganizedTaskFragment(); 1552 1553 assertEquals("tf0 must be return because it's the organized TaskFragment.", 1554 tf0, parentTask.getTaskFragment(TaskFragment::isOrganizedTaskFragment)); 1555 } 1556 1557 @Test testReorderActivityToFront()1558 public void testReorderActivityToFront() { 1559 final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); 1560 final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); 1561 final ActivityRecord activity = task.getTopMostActivity(); 1562 1563 final TaskFragment fragment = createTaskFragmentWithEmbeddedActivity(task, organizer); 1564 final ActivityRecord embeddedActivity = fragment.getTopMostActivity(); 1565 task.moveActivityToFront(activity); 1566 assertEquals("Activity must be moved to front", activity, task.getTopMostActivity()); 1567 1568 doNothing().when(fragment).sendTaskFragmentInfoChanged(); 1569 task.moveActivityToFront(embeddedActivity); 1570 assertEquals("Activity must be moved to front", embeddedActivity, 1571 task.getTopMostActivity()); 1572 assertEquals("Activity must not be embedded", embeddedActivity, 1573 task.getTopChild()); 1574 } 1575 getTestTask()1576 private Task getTestTask() { 1577 final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); 1578 return task.getBottomMostTask(); 1579 } 1580 testRootTaskBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds, Rect expectedConfigBounds)1581 private void testRootTaskBoundsConfiguration(int windowingMode, Rect parentBounds, Rect bounds, 1582 Rect expectedConfigBounds) { 1583 1584 TaskDisplayArea taskDisplayArea = mAtm.mRootWindowContainer.getDefaultTaskDisplayArea(); 1585 Task rootTask = taskDisplayArea.createRootTask(windowingMode, ACTIVITY_TYPE_STANDARD, 1586 true /* onTop */); 1587 Task task = new TaskBuilder(mSupervisor).setParentTask(rootTask).build(); 1588 1589 final Configuration parentConfig = rootTask.getConfiguration(); 1590 parentConfig.windowConfiguration.setAppBounds(parentBounds); 1591 task.setBounds(bounds); 1592 1593 task.resolveOverrideConfiguration(parentConfig); 1594 // Assert that both expected and actual are null or are equal to each other 1595 assertEquals(expectedConfigBounds, 1596 task.getResolvedOverrideConfiguration().windowConfiguration.getAppBounds()); 1597 } 1598 serializeToBytes(Task r)1599 private byte[] serializeToBytes(Task r) throws Exception { 1600 try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { 1601 final TypedXmlSerializer serializer = Xml.newFastSerializer(); 1602 serializer.setOutput(os, "UTF-8"); 1603 serializer.startDocument(null, true); 1604 serializer.startTag(null, TASK_TAG); 1605 r.saveToXml(serializer); 1606 serializer.endTag(null, TASK_TAG); 1607 serializer.endDocument(); 1608 1609 os.flush(); 1610 return os.toByteArray(); 1611 } 1612 } 1613 restoreFromBytes(byte[] in)1614 private Task restoreFromBytes(byte[] in) throws IOException, XmlPullParserException { 1615 try (Reader reader = new InputStreamReader(new ByteArrayInputStream(in))) { 1616 final TypedXmlPullParser parser = Xml.newFastPullParser(); 1617 parser.setInput(reader); 1618 assertEquals(XmlPullParser.START_TAG, parser.next()); 1619 assertEquals(TASK_TAG, parser.getName()); 1620 return Task.restoreFromXml(parser, mAtm.mTaskSupervisor); 1621 } 1622 } 1623 createTask(int taskId)1624 private Task createTask(int taskId) { 1625 return new Task.Builder(mAtm) 1626 .setTaskId(taskId) 1627 .setIntent(new Intent()) 1628 .setRealActivity(ActivityBuilder.getDefaultComponent()) 1629 .setEffectiveUid(10050) 1630 .buildInner(); 1631 } 1632 } 1633