1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 25 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 26 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 27 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 28 29 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; 30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; 33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 35 import static com.android.server.wm.ActivityRecord.State.RESUMED; 36 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION; 37 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST; 38 import static com.android.server.wm.WindowContainer.POSITION_TOP; 39 40 import static org.junit.Assert.assertEquals; 41 import static org.junit.Assert.assertFalse; 42 import static org.junit.Assert.assertNotEquals; 43 import static org.junit.Assert.assertNull; 44 import static org.junit.Assert.assertTrue; 45 import static org.mockito.ArgumentMatchers.anyInt; 46 import static org.mockito.Mockito.clearInvocations; 47 48 import android.content.res.Configuration; 49 import android.graphics.Color; 50 import android.graphics.Rect; 51 import android.os.Binder; 52 import android.platform.test.annotations.Presubmit; 53 import android.view.SurfaceControl; 54 import android.window.ITaskFragmentOrganizer; 55 import android.window.TaskFragmentAnimationParams; 56 import android.window.TaskFragmentInfo; 57 import android.window.TaskFragmentOrganizer; 58 59 import androidx.test.filters.MediumTest; 60 61 import org.junit.Before; 62 import org.junit.Test; 63 import org.junit.runner.RunWith; 64 import org.mockito.Mock; 65 import org.mockito.MockitoAnnotations; 66 67 /** 68 * Test class for {@link TaskFragment}. 69 * 70 * Build/Install/Run: 71 * atest WmTests:TaskFragmentTest 72 */ 73 @MediumTest 74 @Presubmit 75 @RunWith(WindowTestRunner.class) 76 public class TaskFragmentTest extends WindowTestsBase { 77 78 private TaskFragmentOrganizer mOrganizer; 79 private ITaskFragmentOrganizer mIOrganizer; 80 private TaskFragment mTaskFragment; 81 private SurfaceControl mLeash; 82 @Mock 83 private SurfaceControl.Transaction mTransaction; 84 85 @Before setup()86 public void setup() { 87 MockitoAnnotations.initMocks(this); 88 mOrganizer = new TaskFragmentOrganizer(Runnable::run); 89 mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizer.getOrganizerToken() 90 .asBinder()); 91 mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController 92 .registerOrganizer(mIOrganizer); 93 mTaskFragment = new TaskFragmentBuilder(mAtm) 94 .setCreateParentTask() 95 .setOrganizer(mOrganizer) 96 .setFragmentToken(new Binder()) 97 .build(); 98 mLeash = mTaskFragment.getSurfaceControl(); 99 spyOn(mTaskFragment); 100 doReturn(mTransaction).when(mTaskFragment).getSyncTransaction(); 101 doReturn(mTransaction).when(mTaskFragment).getPendingTransaction(); 102 } 103 104 @Test testOnConfigurationChanged()105 public void testOnConfigurationChanged() { 106 final Configuration parentConfig = mTaskFragment.getParent().getConfiguration(); 107 final Rect parentBounds = parentConfig.windowConfiguration.getBounds(); 108 parentConfig.smallestScreenWidthDp += 10; 109 final int parentSw = parentConfig.smallestScreenWidthDp; 110 final Rect bounds = new Rect(parentBounds); 111 bounds.inset(100, 100); 112 mTaskFragment.setBounds(bounds); 113 mTaskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 114 // Calculate its own sw with smaller bounds in multi-window mode. 115 assertNotEquals(parentSw, mTaskFragment.getConfiguration().smallestScreenWidthDp); 116 117 verify(mTransaction).setPosition(mLeash, bounds.left, bounds.top); 118 verify(mTransaction).setWindowCrop(mLeash, bounds.width(), bounds.height()); 119 120 mTaskFragment.setBounds(parentBounds); 121 mTaskFragment.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 122 // Inherit parent's sw in fullscreen mode. 123 assertEquals(parentSw, mTaskFragment.getConfiguration().smallestScreenWidthDp); 124 } 125 126 @Test testShouldStartChangeTransition_relativePositionChange()127 public void testShouldStartChangeTransition_relativePositionChange() { 128 final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, 129 ACTIVITY_TYPE_STANDARD); 130 task.setBoundsUnchecked(new Rect(0, 0, 1000, 1000)); 131 mTaskFragment = createTaskFragmentWithEmbeddedActivity(task, mOrganizer); 132 mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer); 133 final Rect startBounds = new Rect(0, 0, 500, 1000); 134 final Rect endBounds = new Rect(500, 0, 1000, 1000); 135 mTaskFragment.setRelativeEmbeddedBounds(startBounds); 136 mTaskFragment.recomputeConfiguration(); 137 doReturn(true).when(mTaskFragment).isVisible(); 138 doReturn(true).when(mTaskFragment).isVisibleRequested(); 139 140 // Do not resize, just change the relative position. 141 final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds()); 142 mTaskFragment.setRelativeEmbeddedBounds(endBounds); 143 mTaskFragment.recomputeConfiguration(); 144 spyOn(mDisplayContent.mTransitionController); 145 146 // For Shell transition, we don't want to take snapshot when the bounds are not resized 147 doReturn(true).when(mDisplayContent.mTransitionController) 148 .isShellTransitionsEnabled(); 149 assertFalse(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); 150 151 // For legacy transition, we want to request a change transition even if it is just relative 152 // bounds change. 153 doReturn(false).when(mDisplayContent.mTransitionController) 154 .isShellTransitionsEnabled(); 155 assertTrue(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); 156 } 157 158 @Test testStartChangeTransition_resetSurface()159 public void testStartChangeTransition_resetSurface() { 160 final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, 161 ACTIVITY_TYPE_STANDARD); 162 task.setBoundsUnchecked(new Rect(0, 0, 1000, 1000)); 163 mTaskFragment = createTaskFragmentWithEmbeddedActivity(task, mOrganizer); 164 doReturn(mTransaction).when(mTaskFragment).getSyncTransaction(); 165 doReturn(mTransaction).when(mTaskFragment).getPendingTransaction(); 166 mLeash = mTaskFragment.getSurfaceControl(); 167 mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer); 168 final Rect startBounds = new Rect(0, 0, 1000, 1000); 169 final Rect endBounds = new Rect(500, 500, 1000, 1000); 170 mTaskFragment.setRelativeEmbeddedBounds(startBounds); 171 mTaskFragment.recomputeConfiguration(); 172 doReturn(true).when(mTaskFragment).isVisible(); 173 doReturn(true).when(mTaskFragment).isVisibleRequested(); 174 175 clearInvocations(mTransaction); 176 final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds()); 177 mTaskFragment.deferOrganizedTaskFragmentSurfaceUpdate(); 178 mTaskFragment.setRelativeEmbeddedBounds(endBounds); 179 mTaskFragment.recomputeConfiguration(); 180 assertTrue(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); 181 mTaskFragment.initializeChangeTransition(startBounds); 182 mTaskFragment.continueOrganizedTaskFragmentSurfaceUpdate(); 183 184 // Surface reset when prepare transition. 185 verify(mTransaction).setPosition(mLeash, 0, 0); 186 verify(mTransaction).setWindowCrop(mLeash, 0, 0); 187 188 clearInvocations(mTransaction); 189 mTaskFragment.mSurfaceFreezer.unfreeze(mTransaction); 190 191 // Update surface after animation. 192 verify(mTransaction).setPosition(mLeash, 500, 500); 193 verify(mTransaction).setWindowCrop(mLeash, 500, 500); 194 } 195 196 @Test testStartChangeTransition_doNotFreezeWhenOnlyMoved()197 public void testStartChangeTransition_doNotFreezeWhenOnlyMoved() { 198 final Rect startBounds = new Rect(0, 0, 1000, 1000); 199 final Rect endBounds = new Rect(startBounds); 200 endBounds.offset(500, 0); 201 mTaskFragment.setBounds(startBounds); 202 doReturn(true).when(mTaskFragment).isVisible(); 203 doReturn(true).when(mTaskFragment).isVisibleRequested(); 204 205 clearInvocations(mTransaction); 206 mTaskFragment.setBounds(endBounds); 207 208 // No change transition, but update the organized surface position. 209 verify(mTaskFragment, never()).initializeChangeTransition(any(), any()); 210 verify(mTransaction).setPosition(mLeash, endBounds.left, endBounds.top); 211 } 212 213 @Test testNotOkToAnimate_doNotStartChangeTransition()214 public void testNotOkToAnimate_doNotStartChangeTransition() { 215 mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer); 216 final Rect startBounds = new Rect(0, 0, 1000, 1000); 217 final Rect endBounds = new Rect(500, 500, 1000, 1000); 218 mTaskFragment.setRelativeEmbeddedBounds(startBounds); 219 mTaskFragment.recomputeConfiguration(); 220 doReturn(true).when(mTaskFragment).isVisible(); 221 doReturn(true).when(mTaskFragment).isVisibleRequested(); 222 223 final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds()); 224 final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); 225 displayPolicy.screenTurnedOff(); 226 227 assertFalse(mTaskFragment.okToAnimate()); 228 229 mTaskFragment.setRelativeEmbeddedBounds(endBounds); 230 mTaskFragment.recomputeConfiguration(); 231 232 assertFalse(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); 233 } 234 235 /** 236 * Tests that when a {@link TaskFragmentInfo} is generated from a {@link TaskFragment}, an 237 * activity that has not yet been attached to a process because it is being initialized but 238 * belongs to the TaskFragmentOrganizer process is still reported in the TaskFragmentInfo. 239 */ 240 @Test testActivityStillReported_NotYetAssignedToProcess()241 public void testActivityStillReported_NotYetAssignedToProcess() { 242 mTaskFragment.addChild(new ActivityBuilder(mAtm).setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID) 243 .setProcessName(DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME).build()); 244 final ActivityRecord activity = mTaskFragment.getTopMostActivity(); 245 // Remove the process to simulate an activity that has not yet been attached to a process 246 activity.app = null; 247 final TaskFragmentInfo info = activity.getTaskFragment().getTaskFragmentInfo(); 248 assertEquals(1, info.getRunningActivityCount()); 249 assertEquals(1, info.getActivities().size()); 250 assertEquals(false, info.isEmpty()); 251 assertEquals(activity.token, info.getActivities().get(0)); 252 } 253 254 @Test testActivityVisibilityBehindTranslucentTaskFragment()255 public void testActivityVisibilityBehindTranslucentTaskFragment() { 256 // Having an activity covered by a translucent TaskFragment: 257 // Task 258 // - TaskFragment 259 // - Activity (Translucent) 260 // - Activity 261 ActivityRecord translucentActivity = new ActivityBuilder(mAtm) 262 .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).build(); 263 mTaskFragment.addChild(translucentActivity); 264 doReturn(true).when(mTaskFragment).isTranslucent(any()); 265 266 ActivityRecord activityBelow = new ActivityBuilder(mAtm).build(); 267 mTaskFragment.getTask().addChild(activityBelow, 0); 268 269 // Ensure the activity below is visible 270 mTaskFragment.getTask().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, 271 false /* preserveWindows */); 272 assertEquals(true, activityBelow.isVisibleRequested()); 273 } 274 275 @Test testMoveTaskToFront_supportsEnterPipOnTaskSwitchForAdjacentTaskFragment()276 public void testMoveTaskToFront_supportsEnterPipOnTaskSwitchForAdjacentTaskFragment() { 277 final Task bottomTask = createTask(mDisplayContent); 278 final ActivityRecord bottomActivity = createActivityRecord(bottomTask); 279 final Task topTask = createTask(mDisplayContent); 280 // First create primary TF, and then secondary TF, so that the secondary will be on the top. 281 final TaskFragment primaryTf = createTaskFragmentWithActivity(topTask); 282 final TaskFragment secondaryTf = createTaskFragmentWithActivity(topTask); 283 final ActivityRecord primaryActivity = primaryTf.getTopMostActivity(); 284 final ActivityRecord secondaryActivity = secondaryTf.getTopMostActivity(); 285 doReturn(true).when(primaryActivity).supportsPictureInPicture(); 286 doReturn(false).when(secondaryActivity).supportsPictureInPicture(); 287 288 primaryTf.setAdjacentTaskFragment(secondaryTf); 289 primaryActivity.setState(RESUMED, "test"); 290 secondaryActivity.setState(RESUMED, "test"); 291 292 assertEquals(topTask, bottomTask.getDisplayArea().getTopRootTask()); 293 294 // When moving Task to front, the resumed activity that supports PIP should support enter 295 // PIP on Task switch even if it is not the topmost in the Task. 296 bottomTask.moveTaskToFront(bottomTask, false /* noAnimation */, null /* options */, 297 null /* timeTracker */, "test"); 298 299 assertTrue(primaryActivity.supportsEnterPipOnTaskSwitch); 300 assertFalse(secondaryActivity.supportsEnterPipOnTaskSwitch); 301 } 302 303 @Test testEmbeddedTaskFragmentEnterPip_singleActivity_resetOrganizerOverrideConfig()304 public void testEmbeddedTaskFragmentEnterPip_singleActivity_resetOrganizerOverrideConfig() { 305 final Task task = createTask(mDisplayContent); 306 final TaskFragment taskFragment0 = createTaskFragmentWithEmbeddedActivity(task, mOrganizer); 307 final TaskFragment taskFragment1 = new TaskFragmentBuilder(mAtm) 308 .setParentTask(task) 309 .build(); 310 final ActivityRecord activity = taskFragment0.getTopMostActivity(); 311 final Rect taskFragmentBounds = new Rect(0, 0, 300, 1000); 312 task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 313 taskFragment0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 314 taskFragment0.setBounds(taskFragmentBounds); 315 taskFragment0.setAdjacentTaskFragment(taskFragment1); 316 taskFragment0.setCompanionTaskFragment(taskFragment1); 317 taskFragment0.setAnimationParams(new TaskFragmentAnimationParams.Builder() 318 .setAnimationBackgroundColor(Color.GREEN) 319 .build()); 320 321 assertEquals(taskFragmentBounds, activity.getBounds()); 322 assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode()); 323 assertEquals(taskFragment1, taskFragment0.getAdjacentTaskFragment()); 324 assertEquals(taskFragment1, taskFragment0.getCompanionTaskFragment()); 325 assertNotEquals(TaskFragmentAnimationParams.DEFAULT, taskFragment0.getAnimationParams()); 326 327 // Move activity to pinned root task. 328 mRootWindowContainer.moveActivityToPinnedRootTask(activity, 329 null /* launchIntoPipHostActivity */, "test"); 330 331 // Ensure taskFragment requested config is reset. 332 assertEquals(taskFragment0, activity.getOrganizedTaskFragment()); 333 assertEquals(task, activity.getTask()); 334 assertTrue(task.inPinnedWindowingMode()); 335 assertTrue(taskFragment0.inPinnedWindowingMode()); 336 final Rect taskBounds = task.getBounds(); 337 assertEquals(taskBounds, taskFragment0.getBounds()); 338 assertEquals(taskBounds, activity.getBounds()); 339 assertEquals(Configuration.EMPTY, taskFragment0.getRequestedOverrideConfiguration()); 340 assertNull(taskFragment0.getAdjacentTaskFragment()); 341 assertNull(taskFragment0.getCompanionTaskFragment()); 342 assertEquals(TaskFragmentAnimationParams.DEFAULT, taskFragment0.getAnimationParams()); 343 // Because the whole Task is entering PiP, no need to record for future reparent. 344 assertNull(activity.mLastTaskFragmentOrganizerBeforePip); 345 } 346 347 @Test testEmbeddedTaskFragmentEnterPip_multiActivities_notifyOrganizer()348 public void testEmbeddedTaskFragmentEnterPip_multiActivities_notifyOrganizer() { 349 final Task task = createTask(mDisplayContent); 350 final TaskFragment taskFragment0 = createTaskFragmentWithEmbeddedActivity(task, mOrganizer); 351 final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, mOrganizer); 352 final ActivityRecord activity0 = taskFragment0.getTopMostActivity(); 353 final ActivityRecord activity1 = taskFragment1.getTopMostActivity(); 354 activity0.setVisibility(true); 355 activity1.setVisibility(true); 356 spyOn(mAtm.mTaskFragmentOrganizerController); 357 358 // Move activity to pinned. 359 mRootWindowContainer.moveActivityToPinnedRootTask(activity0, 360 null /* launchIntoPipHostActivity */, "test"); 361 362 // Ensure taskFragment requested config is reset. 363 assertTrue(taskFragment0.mClearedTaskFragmentForPip); 364 assertFalse(taskFragment1.mClearedTaskFragmentForPip); 365 final TaskFragmentInfo info = taskFragment0.getTaskFragmentInfo(); 366 assertTrue(info.isTaskFragmentClearedForPip()); 367 assertTrue(info.isEmpty()); 368 369 // Notify organizer because the Task is still visible. 370 assertTrue(task.isVisibleRequested()); 371 verify(mAtm.mTaskFragmentOrganizerController) 372 .dispatchPendingInfoChangedEvent(taskFragment0); 373 // Make sure the organizer is recorded so that it can be reused when the activity is 374 // reparented back on exiting PiP. 375 assertEquals(mIOrganizer, activity0.mLastTaskFragmentOrganizerBeforePip); 376 } 377 378 @Test testEmbeddedActivityExitPip_notifyOrganizer()379 public void testEmbeddedActivityExitPip_notifyOrganizer() { 380 final Task task = createTask(mDisplayContent); 381 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 382 .setParentTask(task) 383 .setOrganizer(mOrganizer) 384 .setFragmentToken(new Binder()) 385 .createActivityCount(1) 386 .build(); 387 new TaskFragmentBuilder(mAtm) 388 .setParentTask(task) 389 .setOrganizer(mOrganizer) 390 .setFragmentToken(new Binder()) 391 .createActivityCount(1) 392 .build(); 393 final ActivityRecord activity = taskFragment.getTopMostActivity(); 394 mRootWindowContainer.moveActivityToPinnedRootTask(activity, 395 null /* launchIntoPipHostActivity */, "test"); 396 spyOn(mAtm.mTaskFragmentOrganizerController); 397 assertEquals(mIOrganizer, activity.mLastTaskFragmentOrganizerBeforePip); 398 399 // Move the activity back to its original Task. 400 activity.reparent(task, POSITION_TOP); 401 402 // Notify the organizer about the reparent. 403 verify(mAtm.mTaskFragmentOrganizerController).onActivityReparentedToTask(activity); 404 assertNull(activity.mLastTaskFragmentOrganizerBeforePip); 405 } 406 407 @Test testIsReadyToTransit()408 public void testIsReadyToTransit() { 409 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 410 .setCreateParentTask() 411 .setOrganizer(mOrganizer) 412 .setFragmentToken(new Binder()) 413 .build(); 414 final Task task = taskFragment.getTask(); 415 416 // Not ready when it is empty. 417 assertFalse(taskFragment.isReadyToTransit()); 418 419 // Ready when it is not empty. 420 final ActivityRecord activity = createActivityRecord(mDisplayContent); 421 doNothing().when(activity).setDropInputMode(anyInt()); 422 activity.reparent(taskFragment, WindowContainer.POSITION_TOP); 423 assertTrue(taskFragment.isReadyToTransit()); 424 425 // Ready when the Task is in PiP. 426 taskFragment.removeChild(activity); 427 task.setWindowingMode(WINDOWING_MODE_PINNED); 428 assertTrue(taskFragment.isReadyToTransit()); 429 430 // Ready when the TaskFragment is empty because of PiP, and the Task is invisible. 431 task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 432 taskFragment.mClearedTaskFragmentForPip = true; 433 assertTrue(taskFragment.isReadyToTransit()); 434 435 // Not ready if the task is still visible when the TaskFragment becomes empty. 436 doReturn(true).when(task).isVisibleRequested(); 437 assertFalse(taskFragment.isReadyToTransit()); 438 } 439 440 @Test testActivityHasOverlayOverUntrustedModeEmbedded()441 public void testActivityHasOverlayOverUntrustedModeEmbedded() { 442 final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, 443 ACTIVITY_TYPE_STANDARD); 444 final Task leafTask0 = new TaskBuilder(mSupervisor) 445 .setParentTask(rootTask) 446 .build(); 447 final TaskFragment organizedTf = new TaskFragmentBuilder(mAtm) 448 .createActivityCount(2) 449 .setParentTask(leafTask0) 450 .setFragmentToken(new Binder()) 451 .setOrganizer(mOrganizer) 452 .build(); 453 final ActivityRecord activity0 = organizedTf.getBottomMostActivity(); 454 final ActivityRecord activity1 = organizedTf.getTopMostActivity(); 455 // Bottom activity is untrusted embedding. Top activity is trusted embedded. 456 // Activity0 has overlay over untrusted mode embedded. 457 activity0.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID + 1; 458 activity1.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID; 459 doReturn(true).when(organizedTf).isAllowedToEmbedActivityInUntrustedMode(activity0); 460 461 assertTrue(activity0.hasOverlayOverUntrustedModeEmbedded()); 462 assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); 463 464 // Both activities are trusted embedded. 465 // None of the two has overlay over untrusted mode embedded. 466 activity0.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID; 467 468 assertFalse(activity0.hasOverlayOverUntrustedModeEmbedded()); 469 assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); 470 471 // Bottom activity is trusted embedding. Top activity is untrusted embedded. 472 // None of the two has overlay over untrusted mode embedded. 473 activity1.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID + 1; 474 475 assertFalse(activity0.hasOverlayOverUntrustedModeEmbedded()); 476 assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); 477 478 // There is an activity in a different leaf task on top of activity0 and activity1. 479 // None of the two has overlay over untrusted mode embedded because it is not the same Task. 480 final Task leafTask1 = new TaskBuilder(mSupervisor) 481 .setParentTask(rootTask) 482 .setOnTop(true) 483 .setCreateActivity(true) 484 .build(); 485 final ActivityRecord activity2 = leafTask1.getTopMostActivity(); 486 activity2.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID + 2; 487 488 assertFalse(activity0.hasOverlayOverUntrustedModeEmbedded()); 489 assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); 490 } 491 492 @Test testIsAllowedToBeEmbeddedInTrustedMode()493 public void testIsAllowedToBeEmbeddedInTrustedMode() { 494 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 495 .setCreateParentTask() 496 .createActivityCount(2) 497 .build(); 498 final ActivityRecord activity0 = taskFragment.getBottomMostActivity(); 499 final ActivityRecord activity1 = taskFragment.getTopMostActivity(); 500 501 // Allowed if all children activities are allowed. 502 doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0); 503 doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1); 504 505 assertTrue(taskFragment.isAllowedToBeEmbeddedInTrustedMode()); 506 507 // Disallowed if any child activity is not allowed. 508 doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0); 509 510 assertFalse(taskFragment.isAllowedToBeEmbeddedInTrustedMode()); 511 512 doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1); 513 514 assertFalse(taskFragment.isAllowedToBeEmbeddedInTrustedMode()); 515 } 516 517 @Test testIsAllowedToEmbedActivity()518 public void testIsAllowedToEmbedActivity() { 519 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 520 .setCreateParentTask() 521 .createActivityCount(1) 522 .build(); 523 final ActivityRecord activity = taskFragment.getTopMostActivity(); 524 525 // Not allow embedding activity if not a trusted host. 526 doReturn(false).when(taskFragment).isAllowedToEmbedActivityInUntrustedMode(any()); 527 doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(any(), anyInt()); 528 assertEquals(EMBEDDING_DISALLOWED_UNTRUSTED_HOST, 529 taskFragment.isAllowedToEmbedActivity(activity)); 530 531 // Not allow embedding activity if the TaskFragment is smaller than activity min dimension. 532 doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(any(), anyInt()); 533 doReturn(true).when(taskFragment).smallerThanMinDimension(any()); 534 assertEquals(EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION, 535 taskFragment.isAllowedToEmbedActivity(activity)); 536 } 537 538 @Test testIgnoreRequestedOrientationForActivityEmbeddingSplit()539 public void testIgnoreRequestedOrientationForActivityEmbeddingSplit() { 540 // Setup two activities in ActivityEmbedding split. 541 final Task task = createTask(mDisplayContent); 542 final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) 543 .setParentTask(task) 544 .createActivityCount(1) 545 .setOrganizer(mOrganizer) 546 .setFragmentToken(new Binder()) 547 .build(); 548 final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) 549 .setParentTask(task) 550 .createActivityCount(1) 551 .setOrganizer(mOrganizer) 552 .setFragmentToken(new Binder()) 553 .build(); 554 tf0.setAdjacentTaskFragment(tf1); 555 tf0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 556 tf1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 557 task.setBounds(0, 0, 1200, 1000); 558 tf0.setBounds(0, 0, 600, 1000); 559 tf1.setBounds(600, 0, 1200, 1000); 560 final ActivityRecord activity0 = tf0.getTopMostActivity(); 561 final ActivityRecord activity1 = tf1.getTopMostActivity(); 562 563 // Assert fixed orientation request is ignored for activity in ActivityEmbedding split. 564 activity0.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 565 566 assertFalse(activity0.isLetterboxedForFixedOrientationAndAspectRatio()); 567 assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation()); 568 569 activity1.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); 570 571 assertFalse(activity1.isLetterboxedForFixedOrientationAndAspectRatio()); 572 assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation()); 573 574 // Also verify the behavior on device that ignore orientation request. 575 mDisplayContent.setIgnoreOrientationRequest(true); 576 task.onConfigurationChanged(task.getParent().getConfiguration()); 577 578 assertFalse(activity0.isLetterboxedForFixedOrientationAndAspectRatio()); 579 assertFalse(activity1.isLetterboxedForFixedOrientationAndAspectRatio()); 580 assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation()); 581 582 tf0.setResumedActivity(activity0, "test"); 583 tf1.setResumedActivity(activity1, "test"); 584 mDisplayContent.mFocusedApp = activity1; 585 586 // Making the activity0 be the focused activity and ensure the focused app is updated. 587 activity0.moveFocusableActivityToTop("test"); 588 assertEquals(activity0, mDisplayContent.mFocusedApp); 589 590 // Moving activity1 to top and make both the two activities resumed. 591 activity1.moveFocusableActivityToTop("test"); 592 activity0.setState(RESUMED, "test"); 593 activity1.setState(RESUMED, "test"); 594 595 // Verifies that the focus app can be updated to an Activity in the adjacent TF 596 mAtm.setFocusedTask(task.mTaskId, activity0); 597 assertEquals(activity0, mDisplayContent.mFocusedApp); 598 } 599 600 @Test testIsVisibleWithAdjacent_reportOrientationUnspecified()601 public void testIsVisibleWithAdjacent_reportOrientationUnspecified() { 602 final Task task = createTask(mDisplayContent); 603 final TaskFragment tf0 = createTaskFragmentWithActivity(task); 604 final TaskFragment tf1 = createTaskFragmentWithActivity(task); 605 tf0.setAdjacentTaskFragment(tf1); 606 tf0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 607 tf1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 608 task.setBounds(0, 0, 1200, 1000); 609 tf0.setBounds(0, 0, 600, 1000); 610 tf1.setBounds(600, 0, 1200, 1000); 611 final ActivityRecord activity0 = tf0.getTopMostActivity(); 612 final ActivityRecord activity1 = tf1.getTopMostActivity(); 613 activity0.setVisibleRequested(true); 614 activity1.setVisibleRequested(true); 615 616 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf0.getOrientation(SCREEN_ORIENTATION_UNSET)); 617 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf1.getOrientation(SCREEN_ORIENTATION_UNSET)); 618 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, task.getOrientation(SCREEN_ORIENTATION_UNSET)); 619 620 activity0.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 621 622 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf0.getOrientation(SCREEN_ORIENTATION_UNSET)); 623 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf1.getOrientation(SCREEN_ORIENTATION_UNSET)); 624 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, task.getOrientation(SCREEN_ORIENTATION_UNSET)); 625 } 626 627 @Test testUpdateImeParentForActivityEmbedding()628 public void testUpdateImeParentForActivityEmbedding() { 629 // Setup two activities in ActivityEmbedding. 630 final Task task = createTask(mDisplayContent); 631 final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) 632 .setParentTask(task) 633 .createActivityCount(1) 634 .setOrganizer(mOrganizer) 635 .setFragmentToken(new Binder()) 636 .build(); 637 final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) 638 .setParentTask(task) 639 .createActivityCount(1) 640 .setOrganizer(mOrganizer) 641 .setFragmentToken(new Binder()) 642 .build(); 643 final ActivityRecord activity0 = tf0.getTopMostActivity(); 644 final ActivityRecord activity1 = tf1.getTopMostActivity(); 645 final WindowState win0 = createWindow(null, TYPE_BASE_APPLICATION, activity0, "win0"); 646 final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity1, "win1"); 647 doReturn(false).when(mDisplayContent).shouldImeAttachedToApp(); 648 649 mDisplayContent.setImeInputTarget(win0); 650 mDisplayContent.setImeLayeringTarget(win1); 651 652 // The ImeParent should be the display. 653 assertEquals(mDisplayContent.getImeContainer().getParent().getSurfaceControl(), 654 mDisplayContent.computeImeParent()); 655 } 656 657 @Test testIsolatedNavigation()658 public void testIsolatedNavigation() { 659 final Task task = createTask(mDisplayContent); 660 final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) 661 .setParentTask(task) 662 .createActivityCount(1) 663 .setOrganizer(mOrganizer) 664 .setFragmentToken(new Binder()) 665 .build(); 666 667 // Cannot be isolated if not embedded. 668 task.setIsolatedNav(true); 669 assertFalse(task.isIsolatedNav()); 670 671 // Ensure the TaskFragment is isolated once set. 672 tf0.setIsolatedNav(true); 673 assertTrue(tf0.isIsolatedNav()); 674 } 675 } 676