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.Manifest.permission.EMBED_ANY_APP_IN_UNTRUSTED_MODE; 20 import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 22 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; 27 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 28 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 29 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; 30 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 31 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 32 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 33 34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; 35 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 36 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 37 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; 38 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 39 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; 40 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 41 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 43 import static com.android.server.wm.ActivityRecord.State.RESUMED; 44 import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_PARENT_TASK; 45 import static com.android.server.wm.TaskFragment.EMBEDDED_DIM_AREA_TASK_FRAGMENT; 46 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION; 47 import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST; 48 import static com.android.server.wm.WindowContainer.POSITION_TOP; 49 50 import static org.junit.Assert.assertEquals; 51 import static org.junit.Assert.assertFalse; 52 import static org.junit.Assert.assertNotEquals; 53 import static org.junit.Assert.assertNull; 54 import static org.junit.Assert.assertTrue; 55 import static org.mockito.ArgumentMatchers.anyInt; 56 import static org.mockito.Mockito.clearInvocations; 57 import static org.mockito.Mockito.mock; 58 59 import android.app.ActivityOptions; 60 import android.content.pm.SigningDetails; 61 import android.content.res.Configuration; 62 import android.graphics.Color; 63 import android.graphics.Rect; 64 import android.os.Binder; 65 import android.platform.test.annotations.Presubmit; 66 import android.view.SurfaceControl; 67 import android.view.View; 68 import android.window.ITaskFragmentOrganizer; 69 import android.window.TaskFragmentAnimationParams; 70 import android.window.TaskFragmentInfo; 71 import android.window.TaskFragmentOrganizer; 72 73 import androidx.test.filters.MediumTest; 74 75 import com.android.server.pm.pkg.AndroidPackage; 76 import com.android.window.flags.Flags; 77 78 import org.junit.Before; 79 import org.junit.Test; 80 import org.junit.runner.RunWith; 81 import org.mockito.Mock; 82 import org.mockito.MockitoAnnotations; 83 import org.mockito.MockitoSession; 84 85 import java.util.Collections; 86 import java.util.Set; 87 88 /** 89 * Test class for {@link TaskFragment}. 90 * 91 * Build/Install/Run: 92 * atest WmTests:TaskFragmentTest 93 */ 94 @MediumTest 95 @Presubmit 96 @RunWith(WindowTestRunner.class) 97 public class TaskFragmentTest extends WindowTestsBase { 98 99 private TaskFragmentOrganizer mOrganizer; 100 private ITaskFragmentOrganizer mIOrganizer; 101 private TaskFragment mTaskFragment; 102 private SurfaceControl mLeash; 103 @Mock 104 private SurfaceControl.Transaction mTransaction; 105 106 @Before setup()107 public void setup() { 108 MockitoAnnotations.initMocks(this); 109 mOrganizer = new TaskFragmentOrganizer(Runnable::run); 110 mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizer.getOrganizerToken() 111 .asBinder()); 112 registerTaskFragmentOrganizer(mIOrganizer); 113 mTaskFragment = new TaskFragmentBuilder(mAtm) 114 .setCreateParentTask() 115 .setOrganizer(mOrganizer) 116 .setFragmentToken(new Binder()) 117 .build(); 118 mLeash = mTaskFragment.getSurfaceControl(); 119 spyOn(mTaskFragment); 120 doReturn(mTransaction).when(mTaskFragment).getSyncTransaction(); 121 doReturn(mTransaction).when(mTaskFragment).getPendingTransaction(); 122 } 123 124 @Test testOnConfigurationChanged()125 public void testOnConfigurationChanged() { 126 final Configuration parentConfig = mTaskFragment.getParent().getConfiguration(); 127 final Rect parentBounds = parentConfig.windowConfiguration.getBounds(); 128 parentConfig.smallestScreenWidthDp += 10; 129 final int parentSw = parentConfig.smallestScreenWidthDp; 130 final Rect bounds = new Rect(parentBounds); 131 bounds.inset(100, 100); 132 mTaskFragment.setBounds(bounds); 133 mTaskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 134 // Calculate its own sw with smaller bounds in multi-window mode. 135 assertNotEquals(parentSw, mTaskFragment.getConfiguration().smallestScreenWidthDp); 136 137 verify(mTransaction).setPosition(mLeash, bounds.left, bounds.top); 138 verify(mTransaction).setWindowCrop(mLeash, bounds.width(), bounds.height()); 139 140 mTaskFragment.setBounds(parentBounds); 141 mTaskFragment.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 142 // Inherit parent's sw in fullscreen mode. 143 assertEquals(parentSw, mTaskFragment.getConfiguration().smallestScreenWidthDp); 144 } 145 146 @Test testShouldStartChangeTransition_relativePositionChange()147 public void testShouldStartChangeTransition_relativePositionChange() { 148 final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, 149 ACTIVITY_TYPE_STANDARD); 150 task.setBoundsUnchecked(new Rect(0, 0, 1000, 1000)); 151 mTaskFragment = createTaskFragmentWithEmbeddedActivity(task, mOrganizer); 152 mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer); 153 final Rect startBounds = new Rect(0, 0, 500, 1000); 154 final Rect endBounds = new Rect(500, 0, 1000, 1000); 155 mTaskFragment.setRelativeEmbeddedBounds(startBounds); 156 mTaskFragment.recomputeConfiguration(); 157 doReturn(true).when(mTaskFragment).isVisible(); 158 doReturn(true).when(mTaskFragment).isVisibleRequested(); 159 160 // Do not resize, just change the relative position. 161 final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds()); 162 mTaskFragment.setRelativeEmbeddedBounds(endBounds); 163 mTaskFragment.recomputeConfiguration(); 164 spyOn(mDisplayContent.mTransitionController); 165 166 // For Shell transition, we don't want to take snapshot when the bounds are not resized 167 doReturn(true).when(mDisplayContent.mTransitionController) 168 .isShellTransitionsEnabled(); 169 assertFalse(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); 170 171 // For legacy transition, we want to request a change transition even if it is just relative 172 // bounds change. 173 doReturn(false).when(mDisplayContent.mTransitionController) 174 .isShellTransitionsEnabled(); 175 assertTrue(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); 176 } 177 178 @Test testStartChangeTransition_resetSurface()179 public void testStartChangeTransition_resetSurface() { 180 final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, 181 ACTIVITY_TYPE_STANDARD); 182 task.setBoundsUnchecked(new Rect(0, 0, 1000, 1000)); 183 mTaskFragment = createTaskFragmentWithEmbeddedActivity(task, mOrganizer); 184 doReturn(mTransaction).when(mTaskFragment).getSyncTransaction(); 185 doReturn(mTransaction).when(mTaskFragment).getPendingTransaction(); 186 mLeash = mTaskFragment.getSurfaceControl(); 187 mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer); 188 final Rect startBounds = new Rect(0, 0, 1000, 1000); 189 final Rect endBounds = new Rect(500, 500, 1000, 1000); 190 mTaskFragment.setRelativeEmbeddedBounds(startBounds); 191 mTaskFragment.recomputeConfiguration(); 192 doReturn(true).when(mTaskFragment).isVisible(); 193 doReturn(true).when(mTaskFragment).isVisibleRequested(); 194 195 clearInvocations(mTransaction); 196 final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds()); 197 mTaskFragment.deferOrganizedTaskFragmentSurfaceUpdate(); 198 mTaskFragment.setRelativeEmbeddedBounds(endBounds); 199 mTaskFragment.recomputeConfiguration(); 200 assertTrue(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); 201 mTaskFragment.initializeChangeTransition(startBounds); 202 mTaskFragment.continueOrganizedTaskFragmentSurfaceUpdate(); 203 204 // Surface reset when prepare transition. 205 verify(mTransaction).setPosition(mLeash, 0, 0); 206 verify(mTransaction).setWindowCrop(mLeash, 0, 0); 207 208 clearInvocations(mTransaction); 209 mTaskFragment.mSurfaceFreezer.unfreeze(mTransaction); 210 211 // Update surface after animation. 212 verify(mTransaction).setPosition(mLeash, 500, 500); 213 verify(mTransaction).setWindowCrop(mLeash, 500, 500); 214 } 215 216 @Test testStartChangeTransition_doNotFreezeWhenOnlyMoved()217 public void testStartChangeTransition_doNotFreezeWhenOnlyMoved() { 218 final Rect startBounds = new Rect(0, 0, 1000, 1000); 219 final Rect endBounds = new Rect(startBounds); 220 endBounds.offset(500, 0); 221 mTaskFragment.setBounds(startBounds); 222 doReturn(true).when(mTaskFragment).isVisible(); 223 doReturn(true).when(mTaskFragment).isVisibleRequested(); 224 225 clearInvocations(mTransaction); 226 mTaskFragment.setBounds(endBounds); 227 228 // No change transition, but update the organized surface position. 229 verify(mTaskFragment, never()).initializeChangeTransition(any(), any()); 230 verify(mTransaction).setPosition(mLeash, endBounds.left, endBounds.top); 231 } 232 233 @Test testNotOkToAnimate_doNotStartChangeTransition()234 public void testNotOkToAnimate_doNotStartChangeTransition() { 235 mockSurfaceFreezerSnapshot(mTaskFragment.mSurfaceFreezer); 236 final Rect startBounds = new Rect(0, 0, 1000, 1000); 237 final Rect endBounds = new Rect(500, 500, 1000, 1000); 238 mTaskFragment.setRelativeEmbeddedBounds(startBounds); 239 mTaskFragment.recomputeConfiguration(); 240 doReturn(true).when(mTaskFragment).isVisible(); 241 doReturn(true).when(mTaskFragment).isVisibleRequested(); 242 243 final Rect relStartBounds = new Rect(mTaskFragment.getRelativeEmbeddedBounds()); 244 final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy(); 245 displayPolicy.screenTurnedOff(); 246 247 assertFalse(mTaskFragment.okToAnimate()); 248 249 mTaskFragment.setRelativeEmbeddedBounds(endBounds); 250 mTaskFragment.recomputeConfiguration(); 251 252 assertFalse(mTaskFragment.shouldStartChangeTransition(startBounds, relStartBounds)); 253 } 254 255 /** 256 * Tests that when a {@link TaskFragmentInfo} is generated from a {@link TaskFragment}, an 257 * activity that has not yet been attached to a process because it is being initialized but 258 * belongs to the TaskFragmentOrganizer process is still reported in the TaskFragmentInfo. 259 */ 260 @Test testActivityStillReported_NotYetAssignedToProcess()261 public void testActivityStillReported_NotYetAssignedToProcess() { 262 mTaskFragment.addChild(new ActivityBuilder(mAtm).setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID) 263 .setProcessName(DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME).build()); 264 final ActivityRecord activity = mTaskFragment.getTopMostActivity(); 265 // Remove the process to simulate an activity that has not yet been attached to a process 266 activity.app = null; 267 final TaskFragmentInfo info = activity.getTaskFragment().getTaskFragmentInfo(); 268 assertEquals(1, info.getRunningActivityCount()); 269 assertEquals(1, info.getActivities().size()); 270 assertEquals(false, info.isEmpty()); 271 assertEquals(activity.token, info.getActivities().get(0)); 272 } 273 274 @Test testActivityVisibilityBehindTranslucentTaskFragment()275 public void testActivityVisibilityBehindTranslucentTaskFragment() { 276 // Having an activity covered by a translucent TaskFragment: 277 // Task 278 // - TaskFragment 279 // - Activity (Translucent) 280 // - Activity 281 ActivityRecord translucentActivity = new ActivityBuilder(mAtm) 282 .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).build(); 283 mTaskFragment.addChild(translucentActivity); 284 doReturn(true).when(mTaskFragment).isTranslucent(any()); 285 286 ActivityRecord activityBelow = new ActivityBuilder(mAtm).build(); 287 mTaskFragment.getTask().addChild(activityBelow, 0); 288 289 // Ensure the activity below is visible 290 mTaskFragment.getTask().ensureActivitiesVisible(null /* starting */); 291 assertEquals(true, activityBelow.isVisibleRequested()); 292 } 293 294 @Test testFindTopNonFinishingActivity_ignoresLaunchedFromBubbleActivities()295 public void testFindTopNonFinishingActivity_ignoresLaunchedFromBubbleActivities() { 296 final ActivityOptions opts = ActivityOptions.makeBasic(); 297 opts.setTaskAlwaysOnTop(true); 298 opts.setLaunchedFromBubble(true); 299 ActivityRecord activity = new ActivityBuilder(mAtm) 300 .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build(); 301 mTaskFragment.addChild(activity); 302 303 assertNull(mTaskFragment.getTopNonFinishingActivity(true, false)); 304 } 305 306 @Test testFindTopNonFinishingActivity_includesLaunchedFromBubbleActivities()307 public void testFindTopNonFinishingActivity_includesLaunchedFromBubbleActivities() { 308 final ActivityOptions opts = ActivityOptions.makeBasic(); 309 opts.setTaskAlwaysOnTop(true); 310 opts.setLaunchedFromBubble(true); 311 ActivityRecord activity = new ActivityBuilder(mAtm) 312 .setUid(DEFAULT_TASK_FRAGMENT_ORGANIZER_UID).setActivityOptions(opts).build(); 313 mTaskFragment.addChild(activity); 314 315 assertEquals(mTaskFragment.getTopNonFinishingActivity(true, true), activity); 316 } 317 318 @Test testMoveTaskToFront_supportsEnterPipOnTaskSwitchForAdjacentTaskFragment()319 public void testMoveTaskToFront_supportsEnterPipOnTaskSwitchForAdjacentTaskFragment() { 320 final Task bottomTask = createTask(mDisplayContent); 321 final ActivityRecord bottomActivity = createActivityRecord(bottomTask); 322 final Task topTask = createTask(mDisplayContent); 323 // First create primary TF, and then secondary TF, so that the secondary will be on the top. 324 final TaskFragment primaryTf = createTaskFragmentWithActivity(topTask); 325 final TaskFragment secondaryTf = createTaskFragmentWithActivity(topTask); 326 final ActivityRecord primaryActivity = primaryTf.getTopMostActivity(); 327 final ActivityRecord secondaryActivity = secondaryTf.getTopMostActivity(); 328 doReturn(true).when(primaryActivity).supportsPictureInPicture(); 329 doReturn(false).when(secondaryActivity).supportsPictureInPicture(); 330 331 primaryTf.setAdjacentTaskFragment(secondaryTf); 332 primaryActivity.setState(RESUMED, "test"); 333 secondaryActivity.setState(RESUMED, "test"); 334 335 assertEquals(topTask, bottomTask.getDisplayArea().getTopRootTask()); 336 337 // When moving Task to front, the resumed activity that supports PIP should support enter 338 // PIP on Task switch even if it is not the topmost in the Task. 339 bottomTask.moveTaskToFront(bottomTask, false /* noAnimation */, null /* options */, 340 null /* timeTracker */, "test"); 341 342 assertTrue(primaryActivity.supportsEnterPipOnTaskSwitch); 343 assertFalse(secondaryActivity.supportsEnterPipOnTaskSwitch); 344 } 345 346 @Test testEmbeddedTaskFragmentEnterPip_singleActivity_resetOrganizerOverrideConfig()347 public void testEmbeddedTaskFragmentEnterPip_singleActivity_resetOrganizerOverrideConfig() { 348 final Task task = createTask(mDisplayContent); 349 final TaskFragment taskFragment0 = createTaskFragmentWithEmbeddedActivity(task, mOrganizer); 350 final TaskFragment taskFragment1 = new TaskFragmentBuilder(mAtm) 351 .setParentTask(task) 352 .build(); 353 final ActivityRecord activity = taskFragment0.getTopMostActivity(); 354 final Rect taskFragmentBounds = new Rect(0, 0, 300, 1000); 355 task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 356 taskFragment0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 357 taskFragment0.setBounds(taskFragmentBounds); 358 taskFragment0.setAdjacentTaskFragment(taskFragment1); 359 taskFragment0.setCompanionTaskFragment(taskFragment1); 360 taskFragment0.setAnimationParams(new TaskFragmentAnimationParams.Builder() 361 .setAnimationBackgroundColor(Color.GREEN) 362 .build()); 363 364 assertEquals(taskFragmentBounds, activity.getBounds()); 365 assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode()); 366 assertEquals(taskFragment1, taskFragment0.getAdjacentTaskFragment()); 367 assertEquals(taskFragment1, taskFragment0.getCompanionTaskFragment()); 368 assertNotEquals(TaskFragmentAnimationParams.DEFAULT, taskFragment0.getAnimationParams()); 369 370 // Move activity to pinned root task. 371 mRootWindowContainer.moveActivityToPinnedRootTask(activity, 372 null /* launchIntoPipHostActivity */, "test"); 373 374 // Ensure taskFragment requested config is reset. 375 assertEquals(taskFragment0, activity.getOrganizedTaskFragment()); 376 assertEquals(task, activity.getTask()); 377 assertTrue(task.inPinnedWindowingMode()); 378 assertTrue(taskFragment0.inPinnedWindowingMode()); 379 final Rect taskBounds = task.getBounds(); 380 assertEquals(taskBounds, taskFragment0.getBounds()); 381 assertEquals(taskBounds, activity.getBounds()); 382 assertEquals(Configuration.EMPTY, taskFragment0.getRequestedOverrideConfiguration()); 383 assertNull(taskFragment0.getAdjacentTaskFragment()); 384 assertNull(taskFragment0.getCompanionTaskFragment()); 385 assertEquals(TaskFragmentAnimationParams.DEFAULT, taskFragment0.getAnimationParams()); 386 // Because the whole Task is entering PiP, no need to record for future reparent. 387 assertNull(activity.mLastTaskFragmentOrganizerBeforePip); 388 } 389 390 @Test testEmbeddedTaskFragmentEnterPip_multiActivities_notifyOrganizer()391 public void testEmbeddedTaskFragmentEnterPip_multiActivities_notifyOrganizer() { 392 final Task task = createTask(mDisplayContent); 393 final TaskFragment taskFragment0 = createTaskFragmentWithEmbeddedActivity(task, mOrganizer); 394 final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, mOrganizer); 395 final ActivityRecord activity0 = taskFragment0.getTopMostActivity(); 396 final ActivityRecord activity1 = taskFragment1.getTopMostActivity(); 397 activity0.setVisibility(true); 398 activity1.setVisibility(true); 399 spyOn(mAtm.mTaskFragmentOrganizerController); 400 401 // Move activity to pinned. 402 mRootWindowContainer.moveActivityToPinnedRootTask(activity0, 403 null /* launchIntoPipHostActivity */, "test"); 404 405 // Ensure taskFragment requested config is reset. 406 assertTrue(taskFragment0.mClearedTaskFragmentForPip); 407 assertFalse(taskFragment1.mClearedTaskFragmentForPip); 408 final TaskFragmentInfo info = taskFragment0.getTaskFragmentInfo(); 409 assertTrue(info.isTaskFragmentClearedForPip()); 410 assertTrue(info.isEmpty()); 411 412 // Notify organizer because the Task is still visible. 413 assertTrue(task.isVisibleRequested()); 414 verify(mAtm.mTaskFragmentOrganizerController) 415 .dispatchPendingInfoChangedEvent(taskFragment0); 416 // Make sure the organizer is recorded so that it can be reused when the activity is 417 // reparented back on exiting PiP. 418 assertEquals(mIOrganizer, activity0.mLastTaskFragmentOrganizerBeforePip); 419 } 420 421 @Test testEmbeddedActivityExitPip_notifyOrganizer()422 public void testEmbeddedActivityExitPip_notifyOrganizer() { 423 final Task task = createTask(mDisplayContent); 424 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 425 .setParentTask(task) 426 .setOrganizer(mOrganizer) 427 .setFragmentToken(new Binder()) 428 .createActivityCount(1) 429 .build(); 430 new TaskFragmentBuilder(mAtm) 431 .setParentTask(task) 432 .setOrganizer(mOrganizer) 433 .setFragmentToken(new Binder()) 434 .createActivityCount(1) 435 .build(); 436 final ActivityRecord activity = taskFragment.getTopMostActivity(); 437 mRootWindowContainer.moveActivityToPinnedRootTask(activity, 438 null /* launchIntoPipHostActivity */, "test"); 439 spyOn(mAtm.mTaskFragmentOrganizerController); 440 assertEquals(mIOrganizer, activity.mLastTaskFragmentOrganizerBeforePip); 441 442 // Move the activity back to its original Task. 443 activity.reparent(task, POSITION_TOP); 444 445 // Notify the organizer about the reparent. 446 verify(mAtm.mTaskFragmentOrganizerController).onActivityReparentedToTask(activity); 447 assertNull(activity.mLastTaskFragmentOrganizerBeforePip); 448 } 449 450 @Test testIsReadyToTransit()451 public void testIsReadyToTransit() { 452 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 453 .setCreateParentTask() 454 .setOrganizer(mOrganizer) 455 .setFragmentToken(new Binder()) 456 .build(); 457 final Task task = taskFragment.getTask(); 458 459 // Not ready when it is empty. 460 assertFalse(taskFragment.isReadyToTransit()); 461 462 // Ready when it is not empty. 463 final ActivityRecord activity = createActivityRecord(mDisplayContent); 464 doNothing().when(activity).setDropInputMode(anyInt()); 465 activity.reparent(taskFragment, WindowContainer.POSITION_TOP); 466 assertTrue(taskFragment.isReadyToTransit()); 467 468 // Ready when the Task is in PiP. 469 taskFragment.removeChild(activity); 470 task.setWindowingMode(WINDOWING_MODE_PINNED); 471 assertTrue(taskFragment.isReadyToTransit()); 472 473 // Ready when the TaskFragment is empty because of PiP, and the Task is invisible. 474 task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 475 taskFragment.mClearedTaskFragmentForPip = true; 476 assertTrue(taskFragment.isReadyToTransit()); 477 478 // Not ready if the task is still visible when the TaskFragment becomes empty. 479 doReturn(true).when(task).isVisibleRequested(); 480 assertFalse(taskFragment.isReadyToTransit()); 481 482 // Ready if the mAllowTransitionWhenEmpty flag is true. 483 taskFragment.setAllowTransitionWhenEmpty(true); 484 assertTrue(taskFragment.isReadyToTransit()); 485 } 486 487 @Test testActivityHasOverlayOverUntrustedModeEmbedded()488 public void testActivityHasOverlayOverUntrustedModeEmbedded() { 489 final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, 490 ACTIVITY_TYPE_STANDARD); 491 final Task leafTask0 = new TaskBuilder(mSupervisor) 492 .setParentTask(rootTask) 493 .build(); 494 final TaskFragment organizedTf = new TaskFragmentBuilder(mAtm) 495 .createActivityCount(2) 496 .setParentTask(leafTask0) 497 .setFragmentToken(new Binder()) 498 .setOrganizer(mOrganizer) 499 .build(); 500 final ActivityRecord activity0 = organizedTf.getBottomMostActivity(); 501 final ActivityRecord activity1 = organizedTf.getTopMostActivity(); 502 // Bottom activity is untrusted embedding. Top activity is trusted embedded. 503 // Activity0 has overlay over untrusted mode embedded. 504 activity0.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID + 1; 505 activity1.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID; 506 doReturn(true).when(organizedTf).isAllowedToEmbedActivityInUntrustedMode(activity0); 507 508 assertTrue(activity0.hasOverlayOverUntrustedModeEmbedded()); 509 assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); 510 511 // Both activities are trusted embedded. 512 // None of the two has overlay over untrusted mode embedded. 513 activity0.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID; 514 515 assertFalse(activity0.hasOverlayOverUntrustedModeEmbedded()); 516 assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); 517 518 // Bottom activity is trusted embedding. Top activity is untrusted embedded. 519 // None of the two has overlay over untrusted mode embedded. 520 activity1.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID + 1; 521 522 assertFalse(activity0.hasOverlayOverUntrustedModeEmbedded()); 523 assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); 524 525 // There is an activity in a different leaf task on top of activity0 and activity1. 526 // None of the two has overlay over untrusted mode embedded because it is not the same Task. 527 final Task leafTask1 = new TaskBuilder(mSupervisor) 528 .setParentTask(rootTask) 529 .setOnTop(true) 530 .setCreateActivity(true) 531 .build(); 532 final ActivityRecord activity2 = leafTask1.getTopMostActivity(); 533 activity2.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID + 2; 534 535 assertFalse(activity0.hasOverlayOverUntrustedModeEmbedded()); 536 assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); 537 } 538 539 @Test testIsAllowedToBeEmbeddedInTrustedMode()540 public void testIsAllowedToBeEmbeddedInTrustedMode() { 541 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 542 .setCreateParentTask() 543 .createActivityCount(2) 544 .build(); 545 final ActivityRecord activity0 = taskFragment.getBottomMostActivity(); 546 final ActivityRecord activity1 = taskFragment.getTopMostActivity(); 547 548 // Allowed if all children activities are allowed. 549 doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0); 550 doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1); 551 552 assertTrue(taskFragment.isAllowedToBeEmbeddedInTrustedMode()); 553 554 // Disallowed if any child activity is not allowed. 555 doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity0); 556 557 assertFalse(taskFragment.isAllowedToBeEmbeddedInTrustedMode()); 558 559 doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(activity1); 560 561 assertFalse(taskFragment.isAllowedToBeEmbeddedInTrustedMode()); 562 } 563 564 @Test testIsAllowedToEmbedActivity()565 public void testIsAllowedToEmbedActivity() { 566 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 567 .setCreateParentTask() 568 .createActivityCount(1) 569 .build(); 570 final ActivityRecord activity = taskFragment.getTopMostActivity(); 571 572 // Not allow embedding activity if not a trusted host. 573 doReturn(false).when(taskFragment).isAllowedToEmbedActivityInUntrustedMode(any()); 574 doReturn(false).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(any(), anyInt()); 575 assertEquals(EMBEDDING_DISALLOWED_UNTRUSTED_HOST, 576 taskFragment.isAllowedToEmbedActivity(activity)); 577 578 // Not allow embedding activity if the TaskFragment is smaller than activity min dimension. 579 doReturn(true).when(taskFragment).isAllowedToEmbedActivityInTrustedMode(any(), anyInt()); 580 doReturn(true).when(taskFragment).smallerThanMinDimension(any()); 581 assertEquals(EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION, 582 taskFragment.isAllowedToEmbedActivity(activity)); 583 } 584 585 @Test testIsAllowedToEmbedActivityInTrustedModeByHostPackage()586 public void testIsAllowedToEmbedActivityInTrustedModeByHostPackage() { 587 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 588 .setCreateParentTask() 589 .createActivityCount(1) 590 .build(); 591 final ActivityRecord activity = taskFragment.getTopMostActivity(); 592 593 final String mockCert = "MOCKCERT"; 594 final AndroidPackage hostPackage = mock(AndroidPackage.class); 595 final SigningDetails signingDetails = mock(SigningDetails.class); 596 when(signingDetails.hasAncestorOrSelfWithDigest(any())) 597 .thenAnswer(invocation -> ((Set) invocation.getArgument(0)).contains(mockCert)); 598 doReturn(signingDetails).when(hostPackage).getSigningDetails(); 599 600 // Should return false when no certs are specified 601 assertFalse(taskFragment.isAllowedToEmbedActivityInTrustedModeByHostPackage( 602 activity, hostPackage)); 603 604 // Should return true when the cert is specified in <activity> 605 activity.info.setKnownActivityEmbeddingCerts(Set.of(mockCert)); 606 assertTrue(taskFragment.isAllowedToEmbedActivityInTrustedModeByHostPackage( 607 activity, hostPackage)); 608 609 // Should return false when the certs specified in <activity> doesn't match 610 activity.info.setKnownActivityEmbeddingCerts(Set.of("WRONGCERT")); 611 assertFalse(taskFragment.isAllowedToEmbedActivityInTrustedModeByHostPackage( 612 activity, hostPackage)); 613 614 // Should return true when the certs is specified in <application> 615 activity.info.setKnownActivityEmbeddingCerts(Collections.emptySet()); 616 activity.info.applicationInfo.setKnownActivityEmbeddingCerts(Set.of(mockCert)); 617 assertTrue(taskFragment.isAllowedToEmbedActivityInTrustedModeByHostPackage( 618 activity, hostPackage)); 619 620 // When the certs is specified in both <activity> and <application>, <activity> takes 621 // precedence 622 activity.info.setKnownActivityEmbeddingCerts(Set.of("WRONGCERT")); 623 activity.info.applicationInfo.setKnownActivityEmbeddingCerts(Set.of(mockCert)); 624 assertFalse(taskFragment.isAllowedToEmbedActivityInTrustedModeByHostPackage( 625 activity, hostPackage)); 626 } 627 628 @Test testIsAllowedToBeEmbeddedInTrustedMode_withManageActivityTasksPermission()629 public void testIsAllowedToBeEmbeddedInTrustedMode_withManageActivityTasksPermission() { 630 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 631 .setCreateParentTask() 632 .createActivityCount(1) 633 .build(); 634 final ActivityRecord activity = taskFragment.getTopMostActivity(); 635 636 // Not allow embedding activity if not a trusted host. 637 assertEquals(EMBEDDING_DISALLOWED_UNTRUSTED_HOST, 638 taskFragment.isAllowedToEmbedActivity(activity)); 639 640 MockitoSession session = 641 mockitoSession().spyStatic(ActivityTaskManagerService.class).startMocking(); 642 try { 643 doReturn(PERMISSION_GRANTED).when(() -> { 644 return ActivityTaskManagerService.checkPermission( 645 eq(MANAGE_ACTIVITY_TASKS), anyInt(), anyInt()); 646 }); 647 // With the MANAGE_ACTIVITY_TASKS permission, trusted embedding is always allowed. 648 assertTrue(taskFragment.isAllowedToBeEmbeddedInTrustedMode()); 649 } finally { 650 session.finishMocking(); 651 } 652 } 653 654 @Test testIsAllowedToEmbedActivityInUntrustedMode_withUntrustedEmbeddingAnyAppPermission( )655 public void testIsAllowedToEmbedActivityInUntrustedMode_withUntrustedEmbeddingAnyAppPermission( 656 ) { 657 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 658 .setCreateParentTask() 659 .createActivityCount(1) 660 .build(); 661 final ActivityRecord activity = taskFragment.getTopMostActivity(); 662 663 // Not allow embedding activity if not a trusted host. 664 assertEquals(EMBEDDING_DISALLOWED_UNTRUSTED_HOST, 665 taskFragment.isAllowedToEmbedActivity(activity)); 666 667 MockitoSession session = 668 mockitoSession() 669 .spyStatic(ActivityTaskManagerService.class) 670 .spyStatic(Flags.class) 671 .startMocking(); 672 try { 673 doReturn(PERMISSION_GRANTED).when(() -> { 674 return ActivityTaskManagerService.checkPermission( 675 eq(EMBED_ANY_APP_IN_UNTRUSTED_MODE), anyInt(), anyInt()); 676 }); 677 // With the EMBED_ANY_APP_IN_UNTRUSTED_MODE permission, untrusted embedding is always 678 // allowed, but it doesn't always allow trusted embedding. 679 doReturn(true).when(() -> Flags.untrustedEmbeddingAnyAppPermission()); 680 assertTrue(taskFragment.isAllowedToEmbedActivityInUntrustedMode(activity)); 681 assertFalse(taskFragment.isAllowedToEmbedActivityInTrustedMode(activity)); 682 683 // If the flag is disabled, the permission doesn't have effect. 684 doReturn(false).when(() -> Flags.untrustedEmbeddingAnyAppPermission()); 685 assertFalse(taskFragment.isAllowedToEmbedActivityInUntrustedMode(activity)); 686 assertFalse(taskFragment.isAllowedToEmbedActivityInTrustedMode(activity)); 687 } finally { 688 session.finishMocking(); 689 } 690 } 691 692 @Test testIgnoreRequestedOrientationForActivityEmbeddingSplit()693 public void testIgnoreRequestedOrientationForActivityEmbeddingSplit() { 694 // Setup two activities in ActivityEmbedding split. 695 final Task task = createTask(mDisplayContent); 696 final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) 697 .setParentTask(task) 698 .createActivityCount(1) 699 .setOrganizer(mOrganizer) 700 .setFragmentToken(new Binder()) 701 .build(); 702 final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) 703 .setParentTask(task) 704 .createActivityCount(1) 705 .setOrganizer(mOrganizer) 706 .setFragmentToken(new Binder()) 707 .build(); 708 tf0.setAdjacentTaskFragment(tf1); 709 tf0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 710 tf1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 711 task.setBounds(0, 0, 1200, 1000); 712 tf0.setBounds(0, 0, 600, 1000); 713 tf1.setBounds(600, 0, 1200, 1000); 714 final ActivityRecord activity0 = tf0.getTopMostActivity(); 715 final ActivityRecord activity1 = tf1.getTopMostActivity(); 716 717 // Assert fixed orientation request is ignored for activity in ActivityEmbedding split. 718 activity0.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 719 720 assertFalse(activity0.isLetterboxedForFixedOrientationAndAspectRatio()); 721 assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation()); 722 723 activity1.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT); 724 725 assertFalse(activity1.isLetterboxedForFixedOrientationAndAspectRatio()); 726 assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation()); 727 728 // Also verify the behavior on device that ignore orientation request. 729 mDisplayContent.setIgnoreOrientationRequest(true); 730 task.onConfigurationChanged(task.getParent().getConfiguration()); 731 732 assertFalse(activity0.isLetterboxedForFixedOrientationAndAspectRatio()); 733 assertFalse(activity1.isLetterboxedForFixedOrientationAndAspectRatio()); 734 assertEquals(SCREEN_ORIENTATION_UNSET, task.getOrientation()); 735 736 tf0.setResumedActivity(activity0, "test"); 737 tf1.setResumedActivity(activity1, "test"); 738 mDisplayContent.mFocusedApp = activity1; 739 740 // Making the activity0 be the focused activity and ensure the focused app is updated. 741 activity0.moveFocusableActivityToTop("test"); 742 assertEquals(activity0, mDisplayContent.mFocusedApp); 743 744 // Moving activity1 to top and make both the two activities resumed. 745 activity1.moveFocusableActivityToTop("test"); 746 activity0.setState(RESUMED, "test"); 747 activity1.setState(RESUMED, "test"); 748 749 // Verifies that the focus app can be updated to an Activity in the adjacent TF 750 mAtm.setFocusedTask(task.mTaskId, activity0); 751 assertEquals(activity0, mDisplayContent.mFocusedApp); 752 } 753 754 @Test testIsVisibleWithAdjacent_reportOrientationUnspecified()755 public void testIsVisibleWithAdjacent_reportOrientationUnspecified() { 756 final Task task = createTask(mDisplayContent); 757 final TaskFragment tf0 = createTaskFragmentWithActivity(task); 758 final TaskFragment tf1 = createTaskFragmentWithActivity(task); 759 tf0.setAdjacentTaskFragment(tf1); 760 tf0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 761 tf1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 762 task.setBounds(0, 0, 1200, 1000); 763 tf0.setBounds(0, 0, 600, 1000); 764 tf1.setBounds(600, 0, 1200, 1000); 765 final ActivityRecord activity0 = tf0.getTopMostActivity(); 766 final ActivityRecord activity1 = tf1.getTopMostActivity(); 767 activity0.setVisibleRequested(true); 768 activity1.setVisibleRequested(true); 769 770 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf0.getOrientation(SCREEN_ORIENTATION_UNSET)); 771 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf1.getOrientation(SCREEN_ORIENTATION_UNSET)); 772 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, task.getOrientation(SCREEN_ORIENTATION_UNSET)); 773 774 activity0.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 775 776 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf0.getOrientation(SCREEN_ORIENTATION_UNSET)); 777 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf1.getOrientation(SCREEN_ORIENTATION_UNSET)); 778 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, task.getOrientation(SCREEN_ORIENTATION_UNSET)); 779 } 780 781 @Test testGetOrientation_reportOverrideOrientation()782 public void testGetOrientation_reportOverrideOrientation() { 783 final Task task = createTask(mDisplayContent); 784 final TaskFragment tf = createTaskFragmentWithActivity(task); 785 final ActivityRecord activity = tf.getTopMostActivity(); 786 tf.setVisibleRequested(true); 787 tf.setOverrideOrientation(SCREEN_ORIENTATION_BEHIND); 788 789 // Should report the override orientation 790 assertEquals(SCREEN_ORIENTATION_BEHIND, tf.getOrientation(SCREEN_ORIENTATION_UNSET)); 791 792 // Should report the override orientation even if the activity requests a different value 793 activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 794 assertEquals(SCREEN_ORIENTATION_BEHIND, tf.getOrientation(SCREEN_ORIENTATION_UNSET)); 795 } 796 797 @Test testGetOrientation_reportOverrideOrientation_whenInvisible()798 public void testGetOrientation_reportOverrideOrientation_whenInvisible() { 799 final Task task = createTask(mDisplayContent); 800 final TaskFragment tf = createTaskFragmentWithActivity(task); 801 final ActivityRecord activity = tf.getTopMostActivity(); 802 tf.setVisibleRequested(false); 803 tf.setOverrideOrientation(SCREEN_ORIENTATION_BEHIND); 804 805 // Should report SCREEN_ORIENTATION_UNSPECIFIED for the override orientation when invisible 806 assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, tf.getOverrideOrientation()); 807 808 // Should report SCREEN_ORIENTATION_UNSET for the orientation 809 assertEquals(SCREEN_ORIENTATION_UNSET, tf.getOrientation(SCREEN_ORIENTATION_UNSET)); 810 811 // Should report SCREEN_ORIENTATION_UNSET even if the activity requests a different 812 // value 813 activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); 814 assertEquals(SCREEN_ORIENTATION_UNSET, tf.getOrientation(SCREEN_ORIENTATION_UNSET)); 815 } 816 817 @Test testUpdateImeParentForActivityEmbedding()818 public void testUpdateImeParentForActivityEmbedding() { 819 // Setup two activities in ActivityEmbedding. 820 final Task task = createTask(mDisplayContent); 821 final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) 822 .setParentTask(task) 823 .createActivityCount(1) 824 .setOrganizer(mOrganizer) 825 .setFragmentToken(new Binder()) 826 .build(); 827 final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) 828 .setParentTask(task) 829 .createActivityCount(1) 830 .setOrganizer(mOrganizer) 831 .setFragmentToken(new Binder()) 832 .build(); 833 final ActivityRecord activity0 = tf0.getTopMostActivity(); 834 final ActivityRecord activity1 = tf1.getTopMostActivity(); 835 final WindowState win0 = createWindow(null, TYPE_BASE_APPLICATION, activity0, "win0"); 836 final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity1, "win1"); 837 doReturn(false).when(mDisplayContent).shouldImeAttachedToApp(); 838 839 mDisplayContent.setImeInputTarget(win0); 840 mDisplayContent.setImeLayeringTarget(win1); 841 842 // The ImeParent should be the display. 843 assertEquals(mDisplayContent.getImeContainer().getParent().getSurfaceControl(), 844 mDisplayContent.computeImeParent()); 845 } 846 847 @Test testIsolatedNavigation()848 public void testIsolatedNavigation() { 849 final Task task = createTask(mDisplayContent); 850 final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) 851 .setParentTask(task) 852 .createActivityCount(1) 853 .setOrganizer(mOrganizer) 854 .setFragmentToken(new Binder()) 855 .build(); 856 857 // Cannot be isolated if not embedded. 858 task.setIsolatedNav(true); 859 assertFalse(task.isIsolatedNav()); 860 861 // Ensure the TaskFragment is isolated once set. 862 tf0.setIsolatedNav(true); 863 assertTrue(tf0.isIsolatedNav()); 864 } 865 866 @Test testGetDimBounds()867 public void testGetDimBounds() { 868 final Task task = mTaskFragment.getTask(); 869 final Rect taskBounds = task.getBounds(); 870 mTaskFragment.setBounds(taskBounds.left, taskBounds.top, taskBounds.left + 10, 871 taskBounds.top + 10); 872 final Rect taskFragmentBounds = mTaskFragment.getBounds(); 873 874 // Return Task bounds if dimming on parent Task. 875 final Rect dimBounds = new Rect(); 876 mTaskFragment.setEmbeddedDimArea(EMBEDDED_DIM_AREA_PARENT_TASK); 877 final Dimmer dimmer = mTaskFragment.getDimmer(); 878 spyOn(dimmer); 879 doReturn(taskBounds).when(dimmer).getDimBounds(); 880 mTaskFragment.getDimBounds(dimBounds); 881 assertEquals(taskBounds, dimBounds); 882 883 // Return TF bounds by default. 884 mTaskFragment.setEmbeddedDimArea(EMBEDDED_DIM_AREA_TASK_FRAGMENT); 885 mTaskFragment.getDimBounds(dimBounds); 886 assertEquals(taskFragmentBounds, dimBounds); 887 } 888 889 @Test testMoveFocusToAdjacentWindow()890 public void testMoveFocusToAdjacentWindow() { 891 // Setup two activities in ActivityEmbedding split. 892 final Task task = createTask(mDisplayContent); 893 final TaskFragment taskFragmentLeft = new TaskFragmentBuilder(mAtm) 894 .setParentTask(task) 895 .createActivityCount(2) 896 .setOrganizer(mOrganizer) 897 .setFragmentToken(new Binder()) 898 .build(); 899 final TaskFragment taskFragmentRight = new TaskFragmentBuilder(mAtm) 900 .setParentTask(task) 901 .createActivityCount(1) 902 .setOrganizer(mOrganizer) 903 .setFragmentToken(new Binder()) 904 .build(); 905 taskFragmentLeft.setAdjacentTaskFragment(taskFragmentRight); 906 taskFragmentLeft.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 907 taskFragmentRight.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 908 task.setBounds(0, 0, 1200, 1000); 909 taskFragmentLeft.setBounds(0, 0, 600, 1000); 910 taskFragmentRight.setBounds(600, 0, 1200, 1000); 911 final ActivityRecord appLeftTop = taskFragmentLeft.getTopMostActivity(); 912 final ActivityRecord appLeftBottom = taskFragmentLeft.getBottomMostActivity(); 913 final ActivityRecord appRightTop = taskFragmentRight.getTopMostActivity(); 914 appLeftTop.setVisibleRequested(true); 915 appRightTop.setVisibleRequested(true); 916 final WindowState winLeftTop = createAppWindow(appLeftTop, "winLeftTop"); 917 final WindowState winLeftBottom = createAppWindow(appLeftBottom, "winLeftBottom"); 918 final WindowState winRightTop = createAppWindow(appRightTop, "winRightTop"); 919 winLeftTop.setHasSurface(true); 920 winRightTop.setHasSurface(true); 921 922 taskFragmentLeft.setResumedActivity(appLeftTop, "test"); 923 taskFragmentRight.setResumedActivity(appRightTop, "test"); 924 appLeftTop.setState(RESUMED, "test"); 925 appRightTop.setState(RESUMED, "test"); 926 mDisplayContent.mFocusedApp = appRightTop; 927 928 // Make the appLeftTop be the focused activity and ensure the focused app is updated. 929 appLeftTop.moveFocusableActivityToTop("test"); 930 assertEquals(winLeftTop, mDisplayContent.mCurrentFocus); 931 932 // Send request from a non-focused window with valid direction. 933 assertFalse(mWm.moveFocusToAdjacentWindow(winLeftBottom, View.FOCUS_RIGHT)); 934 // The focus should remain the same. 935 assertEquals(winLeftTop, mDisplayContent.mCurrentFocus); 936 937 // Send request from the focused window with valid direction. 938 assertTrue(mWm.moveFocusToAdjacentWindow(winLeftTop, View.FOCUS_RIGHT)); 939 // The focus should change. 940 assertEquals(winRightTop, mDisplayContent.mCurrentFocus); 941 942 // Send request from the focused window with invalid direction. 943 assertFalse(mWm.moveFocusToAdjacentWindow(winRightTop, View.FOCUS_UP)); 944 // The focus should remain the same. 945 assertEquals(winRightTop, mDisplayContent.mCurrentFocus); 946 947 // Send request from the focused window with valid direction. 948 assertTrue(mWm.moveFocusToAdjacentWindow(winRightTop, View.FOCUS_BACKWARD)); 949 // The focus should change. 950 assertEquals(winLeftTop, mDisplayContent.mCurrentFocus); 951 952 if (Flags.embeddedActivityBackNavFlag()) { 953 // Move focus if the adjacent activity is more recently active. 954 doReturn(1L).when(appLeftTop).getLastWindowCreateTime(); 955 doReturn(2L).when(appRightTop).getLastWindowCreateTime(); 956 assertTrue(mWm.moveFocusToAdjacentEmbeddedWindow(winLeftTop)); 957 958 // Do not move the focus if the adjacent activity is less recently active. 959 doReturn(3L).when(appLeftTop).getLastWindowCreateTime(); 960 assertFalse(mWm.moveFocusToAdjacentEmbeddedWindow(winLeftTop)); 961 } 962 } 963 964 @Test testSetResumedActivity()965 public void testSetResumedActivity() { 966 // Setup two activities in ActivityEmbedding split. 967 final Task task = createTask(mDisplayContent); 968 final TaskFragment taskFragmentLeft = new TaskFragmentBuilder(mAtm) 969 .setParentTask(task) 970 .createActivityCount(1) 971 .build(); 972 final TaskFragment taskFragmentRight = new TaskFragmentBuilder(mAtm) 973 .setParentTask(task) 974 .createActivityCount(1) 975 .build(); 976 taskFragmentRight.setAdjacentTaskFragment(taskFragmentLeft); 977 taskFragmentLeft.setAdjacentTaskFragment(taskFragmentRight); 978 final ActivityRecord appLeftTop = taskFragmentLeft.getTopMostActivity(); 979 final ActivityRecord appRightTop = taskFragmentRight.getTopMostActivity(); 980 981 // Ensure the focused app is updated when the right activity resumed. 982 taskFragmentRight.setResumedActivity(appRightTop, "test"); 983 assertEquals(appRightTop, task.getDisplayContent().mFocusedApp); 984 985 // Ensure the focused app is updated when the left activity resumed. 986 taskFragmentLeft.setResumedActivity(appLeftTop, "test"); 987 assertEquals(appLeftTop, task.getDisplayContent().mFocusedApp); 988 } 989 990 @Test testShouldBeVisible_invisibleForEmptyTaskFragment()991 public void testShouldBeVisible_invisibleForEmptyTaskFragment() { 992 final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true) 993 .setWindowingMode(WINDOWING_MODE_FULLSCREEN).build(); 994 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 995 .setParentTask(task) 996 .build(); 997 998 // Empty taskFragment should be invisible 999 assertFalse(taskFragment.shouldBeVisible(null)); 1000 1001 // Should be invisible even if it is ACTIVITY_TYPE_HOME. 1002 when(taskFragment.getActivityType()).thenReturn(ACTIVITY_TYPE_HOME); 1003 assertFalse(taskFragment.shouldBeVisible(null)); 1004 } 1005 createAppWindow(ActivityRecord app, String name)1006 private WindowState createAppWindow(ActivityRecord app, String name) { 1007 final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name, 1008 0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow()); 1009 mWm.mWindowMap.put(win.mClient.asBinder(), win); 1010 return win; 1011 } 1012 } 1013