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_MULTI_WINDOW; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 22 import static android.view.Display.DEFAULT_DISPLAY; 23 import static android.view.WindowManager.TRANSIT_CHANGE; 24 import static android.view.WindowManager.TRANSIT_CLOSE; 25 import static android.view.WindowManager.TRANSIT_NONE; 26 import static android.view.WindowManager.TRANSIT_OPEN; 27 import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; 28 import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT; 29 import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; 30 import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; 31 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; 32 import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS; 33 import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT; 34 import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE; 35 import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE; 36 import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE; 37 import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CLOSE; 38 import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_NONE; 39 import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_OPEN; 40 import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK; 41 import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED; 42 import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR; 43 import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED; 44 import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED; 45 import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED; 46 47 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 48 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 49 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 50 import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED; 51 import static com.android.server.wm.WindowContainer.POSITION_TOP; 52 import static com.android.server.wm.testing.Assert.assertThrows; 53 54 import static com.google.common.truth.Truth.assertWithMessage; 55 56 import static org.junit.Assert.assertEquals; 57 import static org.junit.Assert.assertFalse; 58 import static org.junit.Assert.assertNotEquals; 59 import static org.junit.Assert.assertNotNull; 60 import static org.junit.Assert.assertNull; 61 import static org.junit.Assert.assertTrue; 62 import static org.mockito.ArgumentMatchers.any; 63 import static org.mockito.ArgumentMatchers.anyBoolean; 64 import static org.mockito.ArgumentMatchers.anyInt; 65 import static org.mockito.ArgumentMatchers.eq; 66 import static org.mockito.Mockito.clearInvocations; 67 import static org.mockito.Mockito.doAnswer; 68 import static org.mockito.Mockito.mock; 69 import static org.mockito.Mockito.never; 70 import static org.mockito.Mockito.verify; 71 72 import android.annotation.NonNull; 73 import android.content.ComponentName; 74 import android.content.Intent; 75 import android.content.pm.ActivityInfo; 76 import android.content.res.Configuration; 77 import android.graphics.Color; 78 import android.graphics.Rect; 79 import android.net.Uri; 80 import android.os.Binder; 81 import android.os.Bundle; 82 import android.os.IBinder; 83 import android.os.RemoteException; 84 import android.platform.test.annotations.Presubmit; 85 import android.view.RemoteAnimationDefinition; 86 import android.view.SurfaceControl; 87 import android.window.ITaskFragmentOrganizer; 88 import android.window.TaskFragmentAnimationParams; 89 import android.window.TaskFragmentCreationParams; 90 import android.window.TaskFragmentInfo; 91 import android.window.TaskFragmentOperation; 92 import android.window.TaskFragmentOrganizer; 93 import android.window.TaskFragmentOrganizerToken; 94 import android.window.TaskFragmentParentInfo; 95 import android.window.TaskFragmentTransaction; 96 import android.window.WindowContainerToken; 97 import android.window.WindowContainerTransaction; 98 99 import androidx.test.filters.SmallTest; 100 101 import org.junit.Before; 102 import org.junit.Test; 103 import org.junit.runner.RunWith; 104 import org.mockito.ArgumentCaptor; 105 import org.mockito.Captor; 106 import org.mockito.Mock; 107 import org.mockito.MockitoAnnotations; 108 import org.mockito.stubbing.Answer; 109 110 import java.util.List; 111 112 /** 113 * Build/Install/Run: 114 * atest WmTests:TaskFragmentOrganizerControllerTest 115 */ 116 @SmallTest 117 @Presubmit 118 @RunWith(WindowTestRunner.class) 119 public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { 120 private static final int TASK_ID = 10; 121 122 private TaskFragmentOrganizerController mController; 123 private WindowOrganizerController mWindowOrganizerController; 124 private TransitionController mTransitionController; 125 private TaskFragmentOrganizer mOrganizer; 126 private TaskFragmentOrganizerToken mOrganizerToken; 127 private ITaskFragmentOrganizer mIOrganizer; 128 private TaskFragment mTaskFragment; 129 private IBinder mFragmentToken; 130 private WindowContainerTransaction mTransaction; 131 private WindowContainerToken mFragmentWindowToken; 132 private RemoteAnimationDefinition mDefinition; 133 private IBinder mErrorToken; 134 private Rect mTaskFragBounds; 135 136 @Mock 137 private TaskFragmentInfo mTaskFragmentInfo; 138 @Mock 139 private Task mTask; 140 @Captor 141 private ArgumentCaptor<TaskFragmentTransaction> mTransactionCaptor; 142 143 @Before setup()144 public void setup() throws RemoteException { 145 MockitoAnnotations.initMocks(this); 146 mWindowOrganizerController = mAtm.mWindowOrganizerController; 147 mTransitionController = mWindowOrganizerController.mTransitionController; 148 mController = mWindowOrganizerController.mTaskFragmentOrganizerController; 149 mOrganizer = new TaskFragmentOrganizer(Runnable::run); 150 mOrganizerToken = mOrganizer.getOrganizerToken(); 151 mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizerToken.asBinder()); 152 mFragmentToken = new Binder(); 153 mTaskFragment = 154 new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */); 155 mTransaction = new WindowContainerTransaction(); 156 mTransaction.setTaskFragmentOrganizer(mIOrganizer); 157 mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken(); 158 mDefinition = new RemoteAnimationDefinition(); 159 mErrorToken = new Binder(); 160 final Rect displayBounds = mDisplayContent.getBounds(); 161 mTaskFragBounds = new Rect(displayBounds.left, displayBounds.top, displayBounds.centerX(), 162 displayBounds.centerY()); 163 164 spyOn(mController); 165 spyOn(mOrganizer); 166 spyOn(mTaskFragment); 167 spyOn(mWindowOrganizerController); 168 spyOn(mTransitionController); 169 doReturn(mIOrganizer).when(mTaskFragment).getTaskFragmentOrganizer(); 170 doReturn(mTaskFragmentInfo).when(mTaskFragment).getTaskFragmentInfo(); 171 doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl(); 172 doReturn(mFragmentToken).when(mTaskFragment).getFragmentToken(); 173 doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration(); 174 175 // To prevent it from calling the real server. 176 doNothing().when(mOrganizer).applyTransaction(any(), anyInt(), anyBoolean()); 177 doNothing().when(mOrganizer).onTransactionHandled(any(), any(), anyInt(), anyBoolean()); 178 179 mController.registerOrganizer(mIOrganizer); 180 } 181 182 @Test testCallTaskFragmentCallbackWithoutRegister_throwsException()183 public void testCallTaskFragmentCallbackWithoutRegister_throwsException() { 184 mController.unregisterOrganizer(mIOrganizer); 185 186 doReturn(mTask).when(mTaskFragment).getTask(); 187 188 assertThrows(IllegalArgumentException.class, () -> mController 189 .onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment)); 190 191 assertThrows(IllegalArgumentException.class, () -> mController 192 .onTaskFragmentInfoChanged( 193 mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment)); 194 195 assertThrows(IllegalArgumentException.class, () -> mController 196 .onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment)); 197 } 198 199 @Test testOnTaskFragmentAppeared()200 public void testOnTaskFragmentAppeared() { 201 // No-op when the TaskFragment is not attached. 202 mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 203 mController.dispatchPendingEvents(); 204 205 verify(mOrganizer, never()).onTransactionReady(any()); 206 207 // Send callback when the TaskFragment is attached. 208 setupMockParent(mTaskFragment, mTask); 209 210 mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 211 mController.dispatchPendingEvents(); 212 213 assertTaskFragmentParentInfoChangedTransaction(mTask); 214 assertTaskFragmentAppearedTransaction(); 215 } 216 217 @Test testOnTaskFragmentInfoChanged()218 public void testOnTaskFragmentInfoChanged() { 219 setupMockParent(mTaskFragment, mTask); 220 221 // No-op if onTaskFragmentAppeared is not called yet. 222 mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(), 223 mTaskFragment); 224 mController.dispatchPendingEvents(); 225 226 verify(mOrganizer, never()).onTransactionReady(any()); 227 228 // Call onTaskFragmentAppeared first. 229 mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 230 mController.dispatchPendingEvents(); 231 232 verify(mOrganizer).onTransactionReady(any()); 233 234 // No callback if the info is not changed. 235 clearInvocations(mOrganizer); 236 doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any()); 237 doReturn(new Configuration()).when(mTaskFragmentInfo).getConfiguration(); 238 239 mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(), 240 mTaskFragment); 241 mController.dispatchPendingEvents(); 242 243 verify(mOrganizer, never()).onTransactionReady(any()); 244 245 // Trigger callback if the info is changed. 246 doReturn(false).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any()); 247 248 mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(), 249 mTaskFragment); 250 mController.dispatchPendingEvents(); 251 252 assertTaskFragmentInfoChangedTransaction(); 253 } 254 255 @Test testOnTaskFragmentVanished()256 public void testOnTaskFragmentVanished() { 257 mTaskFragment.mTaskFragmentAppearedSent = true; 258 mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 259 mController.dispatchPendingEvents(); 260 261 assertTrue(mTaskFragment.mTaskFragmentVanishedSent); 262 assertTaskFragmentVanishedTransaction(); 263 } 264 265 @Test testOnTaskFragmentVanished_clearUpRemaining()266 public void testOnTaskFragmentVanished_clearUpRemaining() { 267 setupMockParent(mTaskFragment, mTask); 268 269 // Not trigger onTaskFragmentAppeared. 270 mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 271 mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 272 mController.dispatchPendingEvents(); 273 274 assertTrue(mTaskFragment.mTaskFragmentVanishedSent); 275 assertTaskFragmentVanishedTransaction(); 276 277 // Not trigger onTaskFragmentInfoChanged. 278 // Call onTaskFragmentAppeared before calling onTaskFragmentInfoChanged. 279 mTaskFragment.mTaskFragmentVanishedSent = false; 280 mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 281 mController.dispatchPendingEvents(); 282 clearInvocations(mOrganizer); 283 doReturn(true).when(mTaskFragmentInfo).equalsForTaskFragmentOrganizer(any()); 284 mController.onTaskFragmentInfoChanged(mTaskFragment.getTaskFragmentOrganizer(), 285 mTaskFragment); 286 mController.onTaskFragmentVanished(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 287 mController.dispatchPendingEvents(); 288 289 assertTaskFragmentVanishedTransaction(); 290 } 291 292 @Test testOnTaskFragmentParentInfoChanged()293 public void testOnTaskFragmentParentInfoChanged() { 294 setupMockParent(mTaskFragment, mTask); 295 mTask.getTaskFragmentParentInfo().getConfiguration().smallestScreenWidthDp = 10; 296 297 mController.onTaskFragmentAppeared( 298 mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 299 mController.dispatchPendingEvents(); 300 301 assertTaskFragmentParentInfoChangedTransaction(mTask); 302 303 // No extra parent info changed callback if the info is not changed. 304 clearInvocations(mOrganizer); 305 306 mController.onTaskFragmentInfoChanged( 307 mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 308 mController.dispatchPendingEvents(); 309 310 verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture()); 311 final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); 312 final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); 313 assertEquals(1, changes.size()); 314 final TaskFragmentTransaction.Change change = changes.get(0); 315 assertEquals(TYPE_TASK_FRAGMENT_INFO_CHANGED, change.getType()); 316 317 // Trigger callback if the size is changed. 318 clearInvocations(mOrganizer); 319 mTask.getTaskFragmentParentInfo().getConfiguration().smallestScreenWidthDp = 100; 320 mController.onTaskFragmentInfoChanged( 321 mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 322 mController.dispatchPendingEvents(); 323 324 assertTaskFragmentParentInfoChangedTransaction(mTask); 325 326 // Trigger callback if the windowing mode is changed. 327 clearInvocations(mOrganizer); 328 mTask.getTaskFragmentParentInfo().getConfiguration().windowConfiguration 329 .setWindowingMode(WINDOWING_MODE_PINNED); 330 mController.onTaskFragmentInfoChanged( 331 mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 332 mController.dispatchPendingEvents(); 333 334 assertTaskFragmentParentInfoChangedTransaction(mTask); 335 } 336 337 @Test testOnTaskFragmentError()338 public void testOnTaskFragmentError() { 339 final Throwable exception = new IllegalArgumentException("Test exception"); 340 341 mController.onTaskFragmentError(mTaskFragment.getTaskFragmentOrganizer(), 342 mErrorToken, null /* taskFragment */, OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS, 343 exception); 344 mController.dispatchPendingEvents(); 345 346 assertTaskFragmentErrorTransaction(OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS, 347 exception.getClass()); 348 } 349 350 @Test testOnActivityReparentedToTask_activityInOrganizerProcess_useActivityToken()351 public void testOnActivityReparentedToTask_activityInOrganizerProcess_useActivityToken() { 352 // Make sure the activity pid/uid is the same as the organizer caller. 353 final int pid = Binder.getCallingPid(); 354 final int uid = Binder.getCallingUid(); 355 final ActivityRecord activity = createActivityRecord(mDisplayContent); 356 // Flush EVENT_APPEARED. 357 mController.dispatchPendingEvents(); 358 final Task task = activity.getTask(); 359 activity.info.applicationInfo.uid = uid; 360 doReturn(pid).when(activity).getPid(); 361 task.effectiveUid = uid; 362 363 // No need to notify organizer if it is not embedded. 364 mController.onActivityReparentedToTask(activity); 365 mController.dispatchPendingEvents(); 366 367 verify(mOrganizer, never()).onTransactionReady(any()); 368 369 // Notify organizer if it was embedded before entered Pip. 370 activity.mLastTaskFragmentOrganizerBeforePip = mIOrganizer; 371 mController.onActivityReparentedToTask(activity); 372 mController.dispatchPendingEvents(); 373 374 assertActivityReparentedToTaskTransaction(task.mTaskId, activity.intent, activity.token); 375 376 // Notify organizer if there is any embedded in the Task. 377 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 378 .setParentTask(task) 379 .setOrganizer(mOrganizer) 380 .build(); 381 taskFragment.setTaskFragmentOrganizer(mOrganizer.getOrganizerToken(), uid, 382 DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME); 383 activity.reparent(taskFragment, POSITION_TOP); 384 activity.mLastTaskFragmentOrganizerBeforePip = null; 385 // Flush EVENT_INFO_CHANGED. 386 mController.dispatchPendingEvents(); 387 388 // Clear invocations now because there will be another transaction for the TaskFragment 389 // change above, triggered by the reparent. We only want to test onActivityReparentedToTask 390 // here. 391 clearInvocations(mOrganizer); 392 mController.onActivityReparentedToTask(activity); 393 mController.dispatchPendingEvents(); 394 395 assertActivityReparentedToTaskTransaction(task.mTaskId, activity.intent, activity.token); 396 } 397 398 @Test testOnActivityReparentedToTask_activityNotInOrganizerProcess_useTemporaryToken()399 public void testOnActivityReparentedToTask_activityNotInOrganizerProcess_useTemporaryToken() { 400 final int pid = Binder.getCallingPid(); 401 final int uid = Binder.getCallingUid(); 402 final WindowProcessController organizerProc = mSystemServicesTestRule.addProcess( 403 "pkg.organizer", DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME, pid, uid); 404 mTaskFragment.setTaskFragmentOrganizer(mOrganizer.getOrganizerToken(), uid, 405 DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME); 406 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 407 final Task task = createTask(mDisplayContent); 408 task.addChild(mTaskFragment, POSITION_TOP); 409 final ActivityRecord activity = createActivityRecord(task); 410 // Flush EVENT_APPEARED. 411 mController.dispatchPendingEvents(); 412 413 // Make sure the activity belongs to the same app, but it is in a different pid. 414 activity.info.applicationInfo.uid = uid; 415 doReturn(pid + 1).when(activity).getPid(); 416 task.effectiveUid = uid; 417 418 // Notify organizer if it was embedded before entered Pip. 419 // Create a temporary token since the activity doesn't belong to the same process. 420 clearInvocations(mOrganizer); 421 activity.mLastTaskFragmentOrganizerBeforePip = mIOrganizer; 422 mController.onActivityReparentedToTask(activity); 423 mController.dispatchPendingEvents(); 424 425 // Allow organizer to reparent activity in other process using the temporary token. 426 verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture()); 427 final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); 428 final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); 429 assertFalse(changes.isEmpty()); 430 final TaskFragmentTransaction.Change change = changes.get(0); 431 assertEquals(TYPE_ACTIVITY_REPARENTED_TO_TASK, change.getType()); 432 assertEquals(task.mTaskId, change.getTaskId()); 433 assertIntentsEqualForOrganizer(activity.intent, change.getActivityIntent()); 434 assertNotEquals(activity.token, change.getActivityToken()); 435 mTransaction.reparentActivityToTaskFragment(mFragmentToken, change.getActivityToken()); 436 assertApplyTransactionAllowed(mTransaction); 437 438 assertEquals(mTaskFragment, activity.getTaskFragment()); 439 // The temporary token can only be used once. 440 assertNull(mController.getReparentActivityFromTemporaryToken(mIOrganizer, 441 change.getActivityToken())); 442 443 // The organizer process should also have visible state by the visible activity in a 444 // different process. 445 activity.setVisibleRequested(true); 446 activity.setState(ActivityRecord.State.RESUMED, "test"); 447 assertTrue(organizerProc.hasVisibleActivities()); 448 activity.setVisibleRequested(false); 449 activity.setState(ActivityRecord.State.STOPPED, "test"); 450 assertFalse(organizerProc.hasVisibleActivities()); 451 } 452 453 @Test testOnActivityReparentedToTask_untrustedEmbed_notReported()454 public void testOnActivityReparentedToTask_untrustedEmbed_notReported() { 455 final int pid = Binder.getCallingPid(); 456 final int uid = Binder.getCallingUid(); 457 mTaskFragment.setTaskFragmentOrganizer(mOrganizer.getOrganizerToken(), uid, 458 DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME); 459 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 460 final Task task = createTask(mDisplayContent); 461 task.addChild(mTaskFragment, POSITION_TOP); 462 final ActivityRecord activity = createActivityRecord(task); 463 // Flush EVENT_APPEARED. 464 mController.dispatchPendingEvents(); 465 466 // Make sure the activity is embedded in untrusted mode. 467 activity.info.applicationInfo.uid = uid + 1; 468 doReturn(pid + 1).when(activity).getPid(); 469 task.effectiveUid = uid; 470 doReturn(EMBEDDING_ALLOWED).when(task).isAllowedToEmbedActivity(activity, uid); 471 doReturn(false).when(task).isAllowedToEmbedActivityInTrustedMode(activity, uid); 472 doReturn(true).when(task).isAllowedToEmbedActivityInUntrustedMode(activity); 473 474 // Notify organizer if it was embedded before entered Pip. 475 // Create a temporary token since the activity doesn't belong to the same process. 476 clearInvocations(mOrganizer); 477 activity.mLastTaskFragmentOrganizerBeforePip = mIOrganizer; 478 mController.onActivityReparentedToTask(activity); 479 mController.dispatchPendingEvents(); 480 481 // Disallow organizer to reparent activity that is untrusted embedded. 482 verify(mOrganizer, never()).onTransactionReady(mTransactionCaptor.capture()); 483 } 484 485 @Test testOnActivityReparentedToTask_trimReportedIntent()486 public void testOnActivityReparentedToTask_trimReportedIntent() { 487 // Make sure the activity pid/uid is the same as the organizer caller. 488 final int pid = Binder.getCallingPid(); 489 final int uid = Binder.getCallingUid(); 490 final ActivityRecord activity = createActivityRecord(mDisplayContent); 491 final Task task = activity.getTask(); 492 activity.info.applicationInfo.uid = uid; 493 doReturn(pid).when(activity).getPid(); 494 task.effectiveUid = uid; 495 activity.mLastTaskFragmentOrganizerBeforePip = mIOrganizer; 496 497 // Test the Intent trim in #assertIntentTrimmed 498 activity.intent.setComponent(new ComponentName("TestPackage", "TestClass")) 499 .setPackage("TestPackage") 500 .setAction("TestAction") 501 .setData(mock(Uri.class)) 502 .putExtra("Test", 123) 503 .setFlags(10); 504 505 mController.onActivityReparentedToTask(activity); 506 mController.dispatchPendingEvents(); 507 508 assertActivityReparentedToTaskTransaction(task.mTaskId, activity.intent, activity.token); 509 } 510 511 @Test testRegisterRemoteAnimations()512 public void testRegisterRemoteAnimations() { 513 mController.registerRemoteAnimations(mIOrganizer, mDefinition); 514 515 assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer)); 516 517 mController.unregisterRemoteAnimations(mIOrganizer); 518 519 assertNull(mController.getRemoteAnimationDefinition(mIOrganizer)); 520 } 521 522 @Test testApplyTransaction_enforceConfigurationChangeOnOrganizedTaskFragment()523 public void testApplyTransaction_enforceConfigurationChangeOnOrganizedTaskFragment() { 524 // Throw exception if the transaction is trying to change a window that is not organized by 525 // the organizer. 526 mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100)); 527 528 assertApplyTransactionDisallowed(mTransaction); 529 530 // Allow transaction to change a TaskFragment created by the organizer. 531 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 532 "Test:TaskFragmentOrganizer" /* processName */); 533 534 assertApplyTransactionAllowed(mTransaction); 535 } 536 537 @Test testApplyTransaction_enforceHierarchyChange_deleteTaskFragment()538 public void testApplyTransaction_enforceHierarchyChange_deleteTaskFragment() { 539 doReturn(true).when(mTaskFragment).isAttached(); 540 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 541 542 // Throw exception if the transaction is trying to change a window that is not organized by 543 // the organizer. 544 mTransaction.deleteTaskFragment(mFragmentToken); 545 546 assertApplyTransactionDisallowed(mTransaction); 547 548 // Allow transaction to change a TaskFragment created by the organizer. 549 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 550 "Test:TaskFragmentOrganizer" /* processName */); 551 clearInvocations(mAtm.mRootWindowContainer); 552 553 assertApplyTransactionAllowed(mTransaction); 554 verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities(); 555 } 556 557 @Test testApplyTransaction_enforceHierarchyChange_createTaskFragment()558 public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() { 559 final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent); 560 561 // Allow organizer to create TaskFragment and start/reparent activity to TaskFragment. 562 createTaskFragmentFromOrganizer(mTransaction, ownerActivity, mFragmentToken); 563 mTransaction.startActivityInTaskFragment( 564 mFragmentToken, null /* callerToken */, new Intent(), null /* activityOptions */); 565 mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class)); 566 mTransaction.setAdjacentTaskFragments(mFragmentToken, mock(IBinder.class), 567 null /* options */); 568 mTransaction.clearAdjacentTaskFragments(mFragmentToken); 569 assertApplyTransactionAllowed(mTransaction); 570 571 // Successfully created a TaskFragment. 572 final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment( 573 mFragmentToken); 574 assertNotNull(taskFragment); 575 assertEquals(ownerActivity.getTask(), taskFragment.getTask()); 576 } 577 578 @Test testApplyTransaction_enforceTaskFragmentOrganized_startActivityInTaskFragment()579 public void testApplyTransaction_enforceTaskFragmentOrganized_startActivityInTaskFragment() { 580 final Task task = createTask(mDisplayContent); 581 final ActivityRecord ownerActivity = createActivityRecord(task); 582 mTaskFragment = new TaskFragmentBuilder(mAtm) 583 .setParentTask(task) 584 .setFragmentToken(mFragmentToken) 585 .build(); 586 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 587 mTransaction.startActivityInTaskFragment( 588 mFragmentToken, ownerActivity.token, new Intent(), null /* activityOptions */); 589 mOrganizer.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_OPEN, 590 false /* shouldApplyIndependently */); 591 592 // Not allowed because TaskFragment is not organized by the caller organizer. 593 assertApplyTransactionDisallowed(mTransaction); 594 595 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 596 "Test:TaskFragmentOrganizer" /* processName */); 597 598 assertApplyTransactionAllowed(mTransaction); 599 } 600 601 @Test testApplyTransaction_enforceTaskFragmentOrganized_reparentActivityInTaskFragment()602 public void testApplyTransaction_enforceTaskFragmentOrganized_reparentActivityInTaskFragment() { 603 final Task task = createTask(mDisplayContent); 604 final ActivityRecord activity = createActivityRecord(task); 605 mTaskFragment = new TaskFragmentBuilder(mAtm) 606 .setParentTask(task) 607 .setFragmentToken(mFragmentToken) 608 .build(); 609 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 610 mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token); 611 mOrganizer.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE, 612 false /* shouldApplyIndependently */); 613 614 // Not allowed because TaskFragment is not organized by the caller organizer. 615 assertApplyTransactionDisallowed(mTransaction); 616 617 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 618 "Test:TaskFragmentOrganizer" /* processName */); 619 620 assertApplyTransactionAllowed(mTransaction); 621 } 622 623 @Test testApplyTransaction_enforceTaskFragmentOrganized_setAdjacentTaskFragments()624 public void testApplyTransaction_enforceTaskFragmentOrganized_setAdjacentTaskFragments() { 625 final Task task = createTask(mDisplayContent); 626 mTaskFragment = new TaskFragmentBuilder(mAtm) 627 .setParentTask(task) 628 .setFragmentToken(mFragmentToken) 629 .build(); 630 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 631 final IBinder fragmentToken2 = new Binder(); 632 final TaskFragment taskFragment2 = new TaskFragmentBuilder(mAtm) 633 .setParentTask(task) 634 .setFragmentToken(fragmentToken2) 635 .build(); 636 mWindowOrganizerController.mLaunchTaskFragments.put(fragmentToken2, taskFragment2); 637 mTransaction.setAdjacentTaskFragments(mFragmentToken, fragmentToken2, null /* params */); 638 mOrganizer.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE, 639 false /* shouldApplyIndependently */); 640 641 // Not allowed because TaskFragments are not organized by the caller organizer. 642 assertApplyTransactionDisallowed(mTransaction); 643 assertNull(mTaskFragment.getAdjacentTaskFragment()); 644 assertNull(taskFragment2.getAdjacentTaskFragment()); 645 646 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 647 "Test:TaskFragmentOrganizer" /* processName */); 648 649 // Not allowed because TaskFragment2 is not organized by the caller organizer. 650 assertApplyTransactionDisallowed(mTransaction); 651 assertNull(mTaskFragment.getAdjacentTaskFragment()); 652 assertNull(taskFragment2.getAdjacentTaskFragment()); 653 654 mTaskFragment.onTaskFragmentOrganizerRemoved(); 655 taskFragment2.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 656 "Test:TaskFragmentOrganizer" /* processName */); 657 658 // Not allowed because mTaskFragment is not organized by the caller organizer. 659 assertApplyTransactionDisallowed(mTransaction); 660 assertNull(mTaskFragment.getAdjacentTaskFragment()); 661 assertNull(taskFragment2.getAdjacentTaskFragment()); 662 663 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 664 "Test:TaskFragmentOrganizer" /* processName */); 665 666 assertApplyTransactionAllowed(mTransaction); 667 assertEquals(taskFragment2, mTaskFragment.getAdjacentTaskFragment()); 668 } 669 670 @Test testApplyTransaction_enforceTaskFragmentOrganized_clearAdjacentTaskFragments()671 public void testApplyTransaction_enforceTaskFragmentOrganized_clearAdjacentTaskFragments() { 672 final Task task = createTask(mDisplayContent); 673 mTaskFragment = new TaskFragmentBuilder(mAtm) 674 .setParentTask(task) 675 .setFragmentToken(mFragmentToken) 676 .build(); 677 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 678 final IBinder fragmentToken2 = new Binder(); 679 final TaskFragment taskFragment2 = new TaskFragmentBuilder(mAtm) 680 .setParentTask(task) 681 .setFragmentToken(fragmentToken2) 682 .build(); 683 mWindowOrganizerController.mLaunchTaskFragments.put(fragmentToken2, taskFragment2); 684 mTaskFragment.setAdjacentTaskFragment(taskFragment2); 685 686 mTransaction.clearAdjacentTaskFragments(mFragmentToken); 687 mOrganizer.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE, 688 false /* shouldApplyIndependently */); 689 690 // Not allowed because TaskFragment is not organized by the caller organizer. 691 assertApplyTransactionDisallowed(mTransaction); 692 assertEquals(taskFragment2, mTaskFragment.getAdjacentTaskFragment()); 693 694 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 695 "Test:TaskFragmentOrganizer" /* processName */); 696 697 assertApplyTransactionAllowed(mTransaction); 698 assertNull(mTaskFragment.getAdjacentTaskFragment()); 699 assertNull(taskFragment2.getAdjacentTaskFragment()); 700 } 701 702 @Test testApplyTransaction_enforceTaskFragmentOrganized_requestFocusOnTaskFragment()703 public void testApplyTransaction_enforceTaskFragmentOrganized_requestFocusOnTaskFragment() { 704 final Task task = createTask(mDisplayContent); 705 mTaskFragment = new TaskFragmentBuilder(mAtm) 706 .setParentTask(task) 707 .setFragmentToken(mFragmentToken) 708 .build(); 709 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 710 mTransaction.requestFocusOnTaskFragment(mFragmentToken); 711 mOrganizer.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE, 712 false /* shouldApplyIndependently */); 713 714 // Not allowed because TaskFragment is not organized by the caller organizer. 715 assertApplyTransactionDisallowed(mTransaction); 716 717 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 718 "Test:TaskFragmentOrganizer" /* processName */); 719 720 assertApplyTransactionAllowed(mTransaction); 721 } 722 723 @Test testApplyTransaction_enforceTaskFragmentOrganized_addTaskFragmentOperation()724 public void testApplyTransaction_enforceTaskFragmentOrganized_addTaskFragmentOperation() { 725 final Task task = createTask(mDisplayContent); 726 mTaskFragment = new TaskFragmentBuilder(mAtm) 727 .setParentTask(task) 728 .setFragmentToken(mFragmentToken) 729 .build(); 730 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 731 final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( 732 OP_TYPE_SET_ANIMATION_PARAMS) 733 .setAnimationParams(TaskFragmentAnimationParams.DEFAULT) 734 .build(); 735 mTransaction.addTaskFragmentOperation(mFragmentToken, operation); 736 mOrganizer.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE, 737 false /* shouldApplyIndependently */); 738 739 // Not allowed because TaskFragment is not organized by the caller organizer. 740 assertApplyTransactionDisallowed(mTransaction); 741 742 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 743 "Test:TaskFragmentOrganizer" /* processName */); 744 745 assertApplyTransactionAllowed(mTransaction); 746 } 747 748 @Test testAddTaskFragmentOperation()749 public void testAddTaskFragmentOperation() { 750 final Task task = createTask(mDisplayContent); 751 mTaskFragment = new TaskFragmentBuilder(mAtm) 752 .setParentTask(task) 753 .setOrganizer(mOrganizer) 754 .setFragmentToken(mFragmentToken) 755 .build(); 756 assertEquals(TaskFragmentAnimationParams.DEFAULT, mTaskFragment.getAnimationParams()); 757 758 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 759 final TaskFragmentAnimationParams animationParams = 760 new TaskFragmentAnimationParams.Builder() 761 .setAnimationBackgroundColor(Color.GREEN) 762 .build(); 763 final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( 764 OP_TYPE_SET_ANIMATION_PARAMS) 765 .setAnimationParams(animationParams) 766 .build(); 767 mTransaction.addTaskFragmentOperation(mFragmentToken, operation); 768 mOrganizer.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE, 769 false /* shouldApplyIndependently */); 770 assertApplyTransactionAllowed(mTransaction); 771 772 assertEquals(animationParams, mTaskFragment.getAnimationParams()); 773 assertEquals(Color.GREEN, mTaskFragment.getAnimationParams().getAnimationBackgroundColor()); 774 } 775 776 @Test testApplyTransaction_createTaskFragment_failForDifferentUid()777 public void testApplyTransaction_createTaskFragment_failForDifferentUid() { 778 final ActivityRecord activity = createActivityRecord(mDisplayContent); 779 final int uid = Binder.getCallingUid(); 780 final IBinder fragmentToken = new Binder(); 781 final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder( 782 mOrganizerToken, fragmentToken, activity.token).build(); 783 mTransaction.createTaskFragment(params); 784 785 // Fail to create TaskFragment when the task uid is different from caller. 786 activity.info.applicationInfo.uid = uid; 787 activity.getTask().effectiveUid = uid + 1; 788 assertApplyTransactionAllowed(mTransaction); 789 790 assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); 791 792 // Fail to create TaskFragment when the task uid is different from owner activity. 793 activity.info.applicationInfo.uid = uid + 1; 794 activity.getTask().effectiveUid = uid; 795 assertApplyTransactionAllowed(mTransaction); 796 797 assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); 798 799 // Successfully created a TaskFragment for same uid. 800 activity.info.applicationInfo.uid = uid; 801 activity.getTask().effectiveUid = uid; 802 assertApplyTransactionAllowed(mTransaction); 803 804 assertNotNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); 805 } 806 807 @Test testApplyTransaction_createTaskFragment_withPairedPrimaryFragmentToken()808 public void testApplyTransaction_createTaskFragment_withPairedPrimaryFragmentToken() { 809 final Task task = createTask(mDisplayContent); 810 mTaskFragment = new TaskFragmentBuilder(mAtm) 811 .setParentTask(task) 812 .setFragmentToken(mFragmentToken) 813 .createActivityCount(1) 814 .build(); 815 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 816 final ActivityRecord activityOnTop = createActivityRecord(task); 817 final int uid = Binder.getCallingUid(); 818 activityOnTop.info.applicationInfo.uid = uid; 819 activityOnTop.getTask().effectiveUid = uid; 820 final IBinder fragmentToken1 = new Binder(); 821 final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder( 822 mOrganizerToken, fragmentToken1, activityOnTop.token) 823 .setPairedPrimaryFragmentToken(mFragmentToken) 824 .build(); 825 mTransaction.setTaskFragmentOrganizer(mIOrganizer); 826 mTransaction.createTaskFragment(params); 827 assertApplyTransactionAllowed(mTransaction); 828 829 // Successfully created a TaskFragment. 830 final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment( 831 fragmentToken1); 832 assertNotNull(taskFragment); 833 // The new TaskFragment should be positioned right above the paired TaskFragment. 834 assertEquals(task.mChildren.indexOf(mTaskFragment) + 1, 835 task.mChildren.indexOf(taskFragment)); 836 // The top activity should remain on top. 837 assertEquals(task.mChildren.indexOf(taskFragment) + 1, 838 task.mChildren.indexOf(activityOnTop)); 839 } 840 841 @Test testApplyTransaction_createTaskFragment_overrideBounds()842 public void testApplyTransaction_createTaskFragment_overrideBounds() { 843 final Task task = createTask(mDisplayContent); 844 final ActivityRecord activityAtBottom = createActivityRecord(task); 845 final int uid = Binder.getCallingUid(); 846 activityAtBottom.info.applicationInfo.uid = uid; 847 activityAtBottom.getTask().effectiveUid = uid; 848 mTaskFragment = new TaskFragmentBuilder(mAtm) 849 .setParentTask(task) 850 .setFragmentToken(mFragmentToken) 851 .createActivityCount(1) 852 .build(); 853 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 854 final IBinder fragmentToken1 = new Binder(); 855 final Rect bounds = new Rect(100, 100, 500, 1000); 856 final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder( 857 mOrganizerToken, fragmentToken1, activityAtBottom.token) 858 .setPairedActivityToken(activityAtBottom.token) 859 .setInitialRelativeBounds(bounds) 860 .build(); 861 mTransaction.setTaskFragmentOrganizer(mIOrganizer); 862 mTransaction.createTaskFragment(params); 863 assertApplyTransactionAllowed(mTransaction); 864 865 // Successfully created a TaskFragment. 866 final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment( 867 fragmentToken1); 868 assertNotNull(taskFragment); 869 // The relative embedded bounds is updated to the initial requested bounds. 870 assertEquals(bounds, taskFragment.getRelativeEmbeddedBounds()); 871 } 872 873 @Test testApplyTransaction_createTaskFragment_withPairedActivityToken()874 public void testApplyTransaction_createTaskFragment_withPairedActivityToken() { 875 final Task task = createTask(mDisplayContent); 876 final ActivityRecord activityAtBottom = createActivityRecord(task); 877 final int uid = Binder.getCallingUid(); 878 activityAtBottom.info.applicationInfo.uid = uid; 879 activityAtBottom.getTask().effectiveUid = uid; 880 mTaskFragment = new TaskFragmentBuilder(mAtm) 881 .setParentTask(task) 882 .setFragmentToken(mFragmentToken) 883 .createActivityCount(1) 884 .build(); 885 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 886 final IBinder fragmentToken1 = new Binder(); 887 final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder( 888 mOrganizerToken, fragmentToken1, activityAtBottom.token) 889 .setPairedActivityToken(activityAtBottom.token) 890 .build(); 891 mTransaction.setTaskFragmentOrganizer(mIOrganizer); 892 mTransaction.createTaskFragment(params); 893 assertApplyTransactionAllowed(mTransaction); 894 895 // Successfully created a TaskFragment. 896 final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment( 897 fragmentToken1); 898 assertNotNull(taskFragment); 899 // The new TaskFragment should be positioned right above the paired activity. 900 assertEquals(task.mChildren.indexOf(activityAtBottom) + 1, 901 task.mChildren.indexOf(taskFragment)); 902 // The top TaskFragment should remain on top. 903 assertEquals(task.mChildren.indexOf(taskFragment) + 1, 904 task.mChildren.indexOf(mTaskFragment)); 905 } 906 907 @Test testApplyTransaction_reparentActivityToTaskFragment_triggerLifecycleUpdate()908 public void testApplyTransaction_reparentActivityToTaskFragment_triggerLifecycleUpdate() { 909 final Task task = createTask(mDisplayContent); 910 final ActivityRecord activity = createActivityRecord(task); 911 // Skip manipulate the SurfaceControl. 912 doNothing().when(activity).setDropInputMode(anyInt()); 913 mOrganizer.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE, 914 false /* shouldApplyIndependently */); 915 mTaskFragment = new TaskFragmentBuilder(mAtm) 916 .setParentTask(task) 917 .setFragmentToken(mFragmentToken) 918 .setOrganizer(mOrganizer) 919 .build(); 920 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 921 mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token); 922 doReturn(EMBEDDING_ALLOWED).when(mTaskFragment).isAllowedToEmbedActivity(activity); 923 clearInvocations(mAtm.mRootWindowContainer); 924 925 assertApplyTransactionAllowed(mTransaction); 926 927 verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities(); 928 } 929 930 @Test testApplyTransaction_requestFocusOnTaskFragment()931 public void testApplyTransaction_requestFocusOnTaskFragment() { 932 final Task task = createTask(mDisplayContent); 933 final IBinder token0 = new Binder(); 934 final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) 935 .setParentTask(task) 936 .setFragmentToken(token0) 937 .setOrganizer(mOrganizer) 938 .createActivityCount(1) 939 .build(); 940 final IBinder token1 = new Binder(); 941 final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) 942 .setParentTask(task) 943 .setFragmentToken(token1) 944 .setOrganizer(mOrganizer) 945 .createActivityCount(1) 946 .build(); 947 mWindowOrganizerController.mLaunchTaskFragments.put(token0, tf0); 948 mWindowOrganizerController.mLaunchTaskFragments.put(token1, tf1); 949 final ActivityRecord activity0 = tf0.getTopMostActivity(); 950 final ActivityRecord activity1 = tf1.getTopMostActivity(); 951 952 // No effect if the current focus is in a different Task. 953 final ActivityRecord activityInOtherTask = createActivityRecord(mDefaultDisplay); 954 mDisplayContent.setFocusedApp(activityInOtherTask); 955 mTransaction.requestFocusOnTaskFragment(token0); 956 assertApplyTransactionAllowed(mTransaction); 957 958 assertEquals(activityInOtherTask, mDisplayContent.mFocusedApp); 959 960 // No effect if there is no resumed activity in the request TaskFragment. 961 activity0.setState(ActivityRecord.State.PAUSED, "test"); 962 activity1.setState(ActivityRecord.State.RESUMED, "test"); 963 mDisplayContent.setFocusedApp(activity1); 964 assertApplyTransactionAllowed(mTransaction); 965 966 assertEquals(activity1, mDisplayContent.mFocusedApp); 967 968 // Set focus to the request TaskFragment when the current focus is in the same Task, and it 969 // has a resumed activity. 970 activity0.setState(ActivityRecord.State.RESUMED, "test"); 971 mDisplayContent.setFocusedApp(activity1); 972 assertApplyTransactionAllowed(mTransaction); 973 974 assertEquals(activity0, mDisplayContent.mFocusedApp); 975 } 976 977 @Test testApplyTransaction_finishActivity()978 public void testApplyTransaction_finishActivity() { 979 final ActivityRecord activity = createActivityRecord(mDisplayContent); 980 981 mTransaction.finishActivity(activity.token); 982 assertApplyTransactionAllowed(mTransaction); 983 984 assertTrue(activity.finishing); 985 } 986 987 @Test testApplyTransaction_skipTransactionForUnregisterOrganizer()988 public void testApplyTransaction_skipTransactionForUnregisterOrganizer() { 989 mController.unregisterOrganizer(mIOrganizer); 990 final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent); 991 final IBinder fragmentToken = new Binder(); 992 993 // Allow organizer to create TaskFragment and start/reparent activity to TaskFragment. 994 createTaskFragmentFromOrganizer(mTransaction, ownerActivity, fragmentToken); 995 assertApplyTransactionAllowed(mTransaction); 996 997 // Nothing should happen as the organizer is not registered. 998 assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); 999 1000 mController.registerOrganizer(mIOrganizer); 1001 assertApplyTransactionAllowed(mTransaction); 1002 1003 // Successfully created when the organizer is registered. 1004 assertNotNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); 1005 } 1006 1007 @Test testOnTransactionHandled_skipTransactionForUnregisterOrganizer()1008 public void testOnTransactionHandled_skipTransactionForUnregisterOrganizer() { 1009 mController.unregisterOrganizer(mIOrganizer); 1010 final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent); 1011 final IBinder fragmentToken = new Binder(); 1012 1013 // Allow organizer to create TaskFragment and start/reparent activity to TaskFragment. 1014 createTaskFragmentFromOrganizer(mTransaction, ownerActivity, fragmentToken); 1015 mController.onTransactionHandled(new Binder(), mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE, 1016 false /* shouldApplyIndependently */); 1017 1018 // Nothing should happen as the organizer is not registered. 1019 assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); 1020 } 1021 1022 @Test testOrganizerRemovedWithPendingEvents()1023 public void testOrganizerRemovedWithPendingEvents() { 1024 final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) 1025 .setCreateParentTask() 1026 .setOrganizer(mOrganizer) 1027 .setFragmentToken(mFragmentToken) 1028 .build(); 1029 final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) 1030 .setCreateParentTask() 1031 .setOrganizer(mOrganizer) 1032 .setFragmentToken(new Binder()) 1033 .build(); 1034 assertTrue(tf0.isOrganizedTaskFragment()); 1035 assertTrue(tf1.isOrganizedTaskFragment()); 1036 assertTrue(tf0.isAttached()); 1037 assertTrue(tf0.isAttached()); 1038 1039 // Mock the behavior that remove TaskFragment can trigger event dispatch. 1040 final Answer<Void> removeImmediately = invocation -> { 1041 invocation.callRealMethod(); 1042 mController.dispatchPendingEvents(); 1043 return null; 1044 }; 1045 doAnswer(removeImmediately).when(tf0).removeImmediately(); 1046 doAnswer(removeImmediately).when(tf1).removeImmediately(); 1047 1048 // Add pending events. 1049 mController.onTaskFragmentAppeared(mIOrganizer, tf0); 1050 mController.onTaskFragmentAppeared(mIOrganizer, tf1); 1051 1052 // Remove organizer. 1053 mController.unregisterOrganizer(mIOrganizer); 1054 mController.dispatchPendingEvents(); 1055 1056 // Nothing should happen after the organizer is removed. 1057 verify(mOrganizer, never()).onTransactionReady(any()); 1058 1059 // TaskFragments should be removed. 1060 assertFalse(tf0.isOrganizedTaskFragment()); 1061 assertFalse(tf1.isOrganizedTaskFragment()); 1062 assertFalse(tf0.isAttached()); 1063 assertFalse(tf0.isAttached()); 1064 } 1065 1066 @Test testTaskFragmentInPip_startActivityInTaskFragment()1067 public void testTaskFragmentInPip_startActivityInTaskFragment() { 1068 setupTaskFragmentInPip(); 1069 final ActivityRecord activity = mTaskFragment.getTopMostActivity(); 1070 spyOn(mAtm.getActivityStartController()); 1071 spyOn(mWindowOrganizerController); 1072 1073 // Not allow to start activity in a TaskFragment that is in a PIP Task. 1074 mTransaction.startActivityInTaskFragment( 1075 mFragmentToken, activity.token, new Intent(), null /* activityOptions */) 1076 .setErrorCallbackToken(mErrorToken); 1077 assertApplyTransactionAllowed(mTransaction); 1078 1079 verify(mAtm.getActivityStartController(), never()).startActivityInTaskFragment(any(), any(), 1080 any(), any(), anyInt(), anyInt(), any()); 1081 verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), 1082 eq(mErrorToken), eq(mTaskFragment), 1083 eq(OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT), 1084 any(IllegalArgumentException.class)); 1085 } 1086 1087 @Test testTaskFragmentInPip_reparentActivityToTaskFragment()1088 public void testTaskFragmentInPip_reparentActivityToTaskFragment() { 1089 setupTaskFragmentInPip(); 1090 final ActivityRecord activity = createActivityRecord(mDisplayContent); 1091 spyOn(mWindowOrganizerController); 1092 1093 // Not allow to reparent activity to a TaskFragment that is in a PIP Task. 1094 mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token) 1095 .setErrorCallbackToken(mErrorToken); 1096 assertApplyTransactionAllowed(mTransaction); 1097 1098 verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), 1099 eq(mErrorToken), eq(mTaskFragment), 1100 eq(OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT), 1101 any(IllegalArgumentException.class)); 1102 assertNull(activity.getOrganizedTaskFragment()); 1103 } 1104 1105 @Test testTaskFragmentInPip_setAdjacentTaskFragment()1106 public void testTaskFragmentInPip_setAdjacentTaskFragment() { 1107 setupTaskFragmentInPip(); 1108 spyOn(mWindowOrganizerController); 1109 1110 // Not allow to set adjacent on a TaskFragment that is in a PIP Task. 1111 mTransaction.setAdjacentTaskFragments(mFragmentToken, new Binder(), null /* options */) 1112 .setErrorCallbackToken(mErrorToken); 1113 assertApplyTransactionAllowed(mTransaction); 1114 1115 verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), 1116 eq(mErrorToken), eq(mTaskFragment), eq(OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS), 1117 any(IllegalArgumentException.class)); 1118 verify(mTaskFragment, never()).setAdjacentTaskFragment(any()); 1119 } 1120 1121 @Test testTaskFragmentInPip_createTaskFragment()1122 public void testTaskFragmentInPip_createTaskFragment() { 1123 final Task pipTask = createTask(mDisplayContent, WINDOWING_MODE_PINNED, 1124 ACTIVITY_TYPE_STANDARD); 1125 final ActivityRecord activity = createActivityRecord(pipTask); 1126 final IBinder fragmentToken = new Binder(); 1127 spyOn(mWindowOrganizerController); 1128 1129 // Not allow to create TaskFragment in a PIP Task. 1130 createTaskFragmentFromOrganizer(mTransaction, activity, fragmentToken); 1131 mTransaction.setErrorCallbackToken(mErrorToken); 1132 assertApplyTransactionAllowed(mTransaction); 1133 1134 verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), 1135 eq(mErrorToken), eq(null), eq(OP_TYPE_CREATE_TASK_FRAGMENT), 1136 any(IllegalArgumentException.class)); 1137 assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); 1138 } 1139 1140 @Test testTaskFragmentInPip_deleteTaskFragment()1141 public void testTaskFragmentInPip_deleteTaskFragment() { 1142 setupTaskFragmentInPip(); 1143 spyOn(mWindowOrganizerController); 1144 1145 // Not allow to delete a TaskFragment that is in a PIP Task. 1146 mTransaction.deleteTaskFragment(mFragmentToken) 1147 .setErrorCallbackToken(mErrorToken); 1148 assertApplyTransactionAllowed(mTransaction); 1149 1150 verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), 1151 eq(mErrorToken), eq(mTaskFragment), eq(OP_TYPE_DELETE_TASK_FRAGMENT), 1152 any(IllegalArgumentException.class)); 1153 assertNotNull(mWindowOrganizerController.getTaskFragment(mFragmentToken)); 1154 1155 // Allow organizer to delete empty TaskFragment for cleanup. 1156 final Task task = mTaskFragment.getTask(); 1157 mTaskFragment.removeChild(mTaskFragment.getTopMostActivity()); 1158 assertApplyTransactionAllowed(mTransaction); 1159 1160 assertNull(mWindowOrganizerController.getTaskFragment(mFragmentToken)); 1161 assertNull(task.getTopChild()); 1162 } 1163 1164 @Test testTaskFragmentInPip_setConfig()1165 public void testTaskFragmentInPip_setConfig() { 1166 setupTaskFragmentInPip(); 1167 spyOn(mWindowOrganizerController); 1168 1169 // Set relative bounds is ignored on a TaskFragment that is in a PIP Task. 1170 mTransaction.setRelativeBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100)); 1171 1172 assertApplyTransactionAllowed(mTransaction); 1173 1174 verify(mTaskFragment, never()).setRelativeEmbeddedBounds(any()); 1175 } 1176 1177 @Test testDeferPendingTaskFragmentEventsOfInvisibleTask()1178 public void testDeferPendingTaskFragmentEventsOfInvisibleTask() { 1179 final Task task = createTask(mDisplayContent); 1180 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 1181 .setParentTask(task) 1182 .setOrganizer(mOrganizer) 1183 .setFragmentToken(mFragmentToken) 1184 .build(); 1185 doReturn(false).when(task).shouldBeVisible(any()); 1186 1187 // Dispatch the initial event in the Task to update the Task visibility to the organizer. 1188 mController.onTaskFragmentAppeared(mIOrganizer, taskFragment); 1189 mController.dispatchPendingEvents(); 1190 verify(mOrganizer).onTransactionReady(any()); 1191 1192 // Verify that events were not sent when the Task is in background. 1193 clearInvocations(mOrganizer); 1194 final Rect bounds = new Rect(0, 0, 500, 1000); 1195 task.setBoundsUnchecked(bounds); 1196 mController.onTaskFragmentParentInfoChanged(mIOrganizer, task); 1197 mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); 1198 mController.dispatchPendingEvents(); 1199 verify(mOrganizer, never()).onTransactionReady(any()); 1200 1201 // Verify that the events were sent when the Task becomes visible. 1202 doReturn(true).when(task).shouldBeVisible(any()); 1203 task.lastActiveTime++; 1204 mController.dispatchPendingEvents(); 1205 verify(mOrganizer).onTransactionReady(any()); 1206 } 1207 1208 @Test testSendAllPendingTaskFragmentEventsWhenAnyTaskIsVisible()1209 public void testSendAllPendingTaskFragmentEventsWhenAnyTaskIsVisible() { 1210 // Invisible Task. 1211 final Task invisibleTask = createTask(mDisplayContent); 1212 final TaskFragment invisibleTaskFragment = new TaskFragmentBuilder(mAtm) 1213 .setParentTask(invisibleTask) 1214 .setOrganizer(mOrganizer) 1215 .setFragmentToken(mFragmentToken) 1216 .build(); 1217 doReturn(false).when(invisibleTask).shouldBeVisible(any()); 1218 1219 // Visible Task. 1220 final IBinder fragmentToken = new Binder(); 1221 final Task visibleTask = createTask(mDisplayContent); 1222 final TaskFragment visibleTaskFragment = new TaskFragmentBuilder(mAtm) 1223 .setParentTask(visibleTask) 1224 .setOrganizer(mOrganizer) 1225 .setFragmentToken(fragmentToken) 1226 .build(); 1227 doReturn(true).when(invisibleTask).shouldBeVisible(any()); 1228 1229 // Sending events 1230 invisibleTaskFragment.mTaskFragmentAppearedSent = true; 1231 visibleTaskFragment.mTaskFragmentAppearedSent = true; 1232 mController.onTaskFragmentInfoChanged(mIOrganizer, invisibleTaskFragment); 1233 mController.onTaskFragmentInfoChanged(mIOrganizer, visibleTaskFragment); 1234 mController.dispatchPendingEvents(); 1235 1236 // Verify that both events are sent. 1237 verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture()); 1238 final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); 1239 final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); 1240 1241 // There should be two Task info changed with two TaskFragment info changed. 1242 assertEquals(4, changes.size()); 1243 // Invisible Task info changed 1244 assertEquals(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED, changes.get(0).getType()); 1245 assertEquals(invisibleTask.mTaskId, changes.get(0).getTaskId()); 1246 // Invisible TaskFragment info changed 1247 assertEquals(TYPE_TASK_FRAGMENT_INFO_CHANGED, changes.get(1).getType()); 1248 assertEquals(invisibleTaskFragment.getFragmentToken(), 1249 changes.get(1).getTaskFragmentToken()); 1250 // Visible Task info changed 1251 assertEquals(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED, changes.get(2).getType()); 1252 assertEquals(visibleTask.mTaskId, changes.get(2).getTaskId()); 1253 // Visible TaskFragment info changed 1254 assertEquals(TYPE_TASK_FRAGMENT_INFO_CHANGED, changes.get(3).getType()); 1255 assertEquals(visibleTaskFragment.getFragmentToken(), changes.get(3).getTaskFragmentToken()); 1256 } 1257 1258 @Test testCanSendPendingTaskFragmentEventsAfterActivityResumed()1259 public void testCanSendPendingTaskFragmentEventsAfterActivityResumed() { 1260 final Task task = createTask(mDisplayContent); 1261 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 1262 .setParentTask(task) 1263 .setOrganizer(mOrganizer) 1264 .setFragmentToken(mFragmentToken) 1265 .createActivityCount(1) 1266 .build(); 1267 final ActivityRecord activity = taskFragment.getTopMostActivity(); 1268 doReturn(false).when(task).shouldBeVisible(any()); 1269 taskFragment.setResumedActivity(null, "test"); 1270 1271 // Dispatch the initial event in the Task to update the Task visibility to the organizer. 1272 mController.onTaskFragmentAppeared(mIOrganizer, taskFragment); 1273 mController.dispatchPendingEvents(); 1274 verify(mOrganizer).onTransactionReady(any()); 1275 1276 // Verify the info changed event is not sent because the Task is invisible 1277 clearInvocations(mOrganizer); 1278 final Rect bounds = new Rect(0, 0, 500, 1000); 1279 task.setBoundsUnchecked(bounds); 1280 mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); 1281 mController.dispatchPendingEvents(); 1282 verify(mOrganizer, never()).onTransactionReady(any()); 1283 1284 // Mock the task becomes visible, and activity resumed. Verify the info changed event is 1285 // sent. 1286 doReturn(true).when(task).shouldBeVisible(any()); 1287 taskFragment.setResumedActivity(activity, "test"); 1288 mController.dispatchPendingEvents(); 1289 verify(mOrganizer).onTransactionReady(any()); 1290 } 1291 1292 /** 1293 * Tests that a task fragment info changed event is still sent if the task is invisible only 1294 * when the info changed event is because of the last activity in a task finishing. 1295 */ 1296 @Test testLastPendingTaskFragmentInfoChangedEventOfInvisibleTaskSent()1297 public void testLastPendingTaskFragmentInfoChangedEventOfInvisibleTaskSent() { 1298 // Create a TaskFragment with an activity, all within a parent task 1299 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 1300 .setOrganizer(mOrganizer) 1301 .setFragmentToken(mFragmentToken) 1302 .setCreateParentTask() 1303 .createActivityCount(1) 1304 .build(); 1305 final Task parentTask = taskFragment.getTask(); 1306 final ActivityRecord activity = taskFragment.getTopNonFinishingActivity(); 1307 assertTrue(parentTask.shouldBeVisible(null)); 1308 1309 // Dispatch pending info changed event from creating the activity 1310 taskFragment.mTaskFragmentAppearedSent = true; 1311 mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); 1312 mController.dispatchPendingEvents(); 1313 1314 // Finish the activity and verify that the task is invisible 1315 activity.finishing = true; 1316 assertFalse(parentTask.shouldBeVisible(null)); 1317 1318 // Verify the info changed callback still occurred despite the task being invisible 1319 clearInvocations(mOrganizer); 1320 mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); 1321 mController.dispatchPendingEvents(); 1322 verify(mOrganizer).onTransactionReady(any()); 1323 } 1324 1325 /** 1326 * Tests that a task fragment info changed event is sent if the TaskFragment becomes empty 1327 * even if the Task is invisible. 1328 */ 1329 @Test testPendingTaskFragmentInfoChangedEvent_emptyTaskFragment()1330 public void testPendingTaskFragmentInfoChangedEvent_emptyTaskFragment() { 1331 // Create a TaskFragment with an activity, all within a parent task 1332 final Task task = createTask(mDisplayContent); 1333 final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) 1334 .setParentTask(task) 1335 .setOrganizer(mOrganizer) 1336 .setFragmentToken(mFragmentToken) 1337 .createActivityCount(1) 1338 .build(); 1339 final ActivityRecord embeddedActivity = taskFragment.getTopNonFinishingActivity(); 1340 // Add another activity in the Task so that it always contains a non-finishing activity. 1341 createActivityRecord(task); 1342 doReturn(false).when(task).shouldBeVisible(any()); 1343 1344 // Dispatch the initial event in the Task to update the Task visibility to the organizer. 1345 clearInvocations(mOrganizer); 1346 mController.onTaskFragmentAppeared(mIOrganizer, taskFragment); 1347 mController.dispatchPendingEvents(); 1348 verify(mOrganizer).onTransactionReady(any()); 1349 1350 // Verify the info changed event is not sent because the Task is invisible 1351 clearInvocations(mOrganizer); 1352 final Rect bounds = new Rect(0, 0, 500, 1000); 1353 task.setBoundsUnchecked(bounds); 1354 mController.onTaskFragmentInfoChanged(mIOrganizer, taskFragment); 1355 mController.dispatchPendingEvents(); 1356 verify(mOrganizer, never()).onTransactionReady(any()); 1357 1358 // Finish the embedded activity, and verify the info changed event is sent because the 1359 // TaskFragment is becoming empty. 1360 embeddedActivity.finishing = true; 1361 mController.dispatchPendingEvents(); 1362 verify(mOrganizer).onTransactionReady(any()); 1363 } 1364 1365 /** 1366 * When an embedded {@link TaskFragment} is removed, we should clean up the reference in the 1367 * {@link WindowOrganizerController}. 1368 */ 1369 @Test testTaskFragmentRemoved_cleanUpEmbeddedTaskFragment()1370 public void testTaskFragmentRemoved_cleanUpEmbeddedTaskFragment() { 1371 final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent); 1372 final IBinder fragmentToken = new Binder(); 1373 createTaskFragmentFromOrganizer(mTransaction, ownerActivity, fragmentToken); 1374 assertApplyTransactionAllowed(mTransaction); 1375 final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(fragmentToken); 1376 1377 assertNotNull(taskFragment); 1378 1379 taskFragment.removeImmediately(); 1380 1381 assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); 1382 } 1383 1384 /** 1385 * For config change to untrusted embedded TaskFragment, the bounds should be always within 1386 * its parent bounds. 1387 */ 1388 @Test testUntrustedEmbedding_configChange()1389 public void testUntrustedEmbedding_configChange() { 1390 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 1391 "Test:TaskFragmentOrganizer" /* processName */); 1392 doReturn(false).when(mTaskFragment).isAllowedToBeEmbeddedInTrustedMode(); 1393 final Task task = createTask(mDisplayContent); 1394 final Rect taskBounds = new Rect(task.getBounds()); 1395 task.addChild(mTaskFragment, POSITION_TOP); 1396 1397 // When set a relative bounds outside of its parent's, it is allowed, but the actual 1398 // TaskFragment bounds will be updated to be fit the parent's bounds. 1399 final Rect tfBounds = new Rect(taskBounds); 1400 tfBounds.right++; 1401 mTransaction.setRelativeBounds(mFragmentWindowToken, tfBounds); 1402 assertApplyTransactionAllowed(mTransaction); 1403 1404 assertEquals(tfBounds, mTaskFragment.getRelativeEmbeddedBounds()); 1405 assertEquals(taskBounds, mTaskFragment.getBounds()); 1406 } 1407 1408 // TODO(b/232871351): add test for minimum dimension violation in startActivityInTaskFragment 1409 @Test testMinDimensionViolation_ReparentActivityToTaskFragment()1410 public void testMinDimensionViolation_ReparentActivityToTaskFragment() { 1411 final Task task = createTask(mDisplayContent); 1412 final ActivityRecord activity = createActivityRecord(task); 1413 // Make minWidth/minHeight exceeds the TaskFragment bounds. 1414 activity.info.windowLayout = new ActivityInfo.WindowLayout( 1415 0, 0, 0, 0, 0, mTaskFragBounds.width() + 10, mTaskFragBounds.height() + 10); 1416 mTaskFragment = new TaskFragmentBuilder(mAtm) 1417 .setParentTask(task) 1418 .setFragmentToken(mFragmentToken) 1419 .setOrganizer(mOrganizer) 1420 .setBounds(mTaskFragBounds) 1421 .build(); 1422 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 1423 1424 // Reparent activity to mTaskFragment, which is smaller than activity's 1425 // minimum dimensions. 1426 mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token) 1427 .setErrorCallbackToken(mErrorToken); 1428 assertApplyTransactionAllowed(mTransaction); 1429 // The pending event will be dispatched on the handler (from requestTraversal). 1430 waitHandlerIdle(mWm.mAnimationHandler); 1431 1432 assertTaskFragmentErrorTransaction(OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT, 1433 SecurityException.class); 1434 } 1435 1436 @Test testMinDimensionViolation_setRelativeBounds()1437 public void testMinDimensionViolation_setRelativeBounds() { 1438 final Task task = createTask(mDisplayContent); 1439 mTaskFragment = new TaskFragmentBuilder(mAtm) 1440 .setParentTask(task) 1441 .createActivityCount(1) 1442 .setFragmentToken(mFragmentToken) 1443 .setOrganizer(mOrganizer) 1444 .setBounds(new Rect(0, 0, mTaskFragBounds.right * 2, mTaskFragBounds.bottom * 2)) 1445 .build(); 1446 final ActivityRecord activity = mTaskFragment.getTopMostActivity(); 1447 // Make minWidth/minHeight exceeds the TaskFragment bounds. 1448 activity.info.windowLayout = new ActivityInfo.WindowLayout( 1449 0, 0, 0, 0, 0, mTaskFragBounds.width() + 10, mTaskFragBounds.height() + 10); 1450 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 1451 clearInvocations(mAtm.mRootWindowContainer); 1452 1453 // Shrink the TaskFragment to mTaskFragBounds to make its bounds smaller than activity's 1454 // minimum dimensions. 1455 mTransaction.setRelativeBounds(mTaskFragment.mRemoteToken.toWindowContainerToken(), 1456 mTaskFragBounds) 1457 .setErrorCallbackToken(mErrorToken); 1458 assertApplyTransactionAllowed(mTransaction); 1459 1460 // When the requested bounds do not satisfy the min dimension, it will be reset to empty. 1461 assertWithMessage("setRelativeBounds must not be performed.") 1462 .that(mTaskFragment.getRelativeEmbeddedBounds()).isEqualTo(new Rect()); 1463 } 1464 1465 @Test testOnTransactionReady_invokeOnTransactionHandled()1466 public void testOnTransactionReady_invokeOnTransactionHandled() { 1467 final TaskFragmentTransaction transaction = new TaskFragmentTransaction(); 1468 mOrganizer.onTransactionReady(transaction); 1469 1470 // Organizer should always trigger #onTransactionHandled when receives #onTransactionReady 1471 verify(mOrganizer).onTransactionHandled(eq(transaction.getTransactionToken()), any(), 1472 anyInt(), anyBoolean()); 1473 verify(mOrganizer, never()).applyTransaction(any(), anyInt(), anyBoolean()); 1474 } 1475 1476 @Test testDispatchTransaction_deferTransitionReady()1477 public void testDispatchTransaction_deferTransitionReady() { 1478 setupMockParent(mTaskFragment, mTask); 1479 final ArgumentCaptor<IBinder> tokenCaptor = ArgumentCaptor.forClass(IBinder.class); 1480 final ArgumentCaptor<WindowContainerTransaction> wctCaptor = 1481 ArgumentCaptor.forClass(WindowContainerTransaction.class); 1482 doReturn(true).when(mTransitionController).isCollecting(); 1483 doReturn(10).when(mTransitionController).getCollectingTransitionId(); 1484 1485 mController.onTaskFragmentAppeared(mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment); 1486 mController.dispatchPendingEvents(); 1487 1488 // Defer transition when send TaskFragment transaction during transition collection. 1489 verify(mTransitionController).deferTransitionReady(); 1490 verify(mOrganizer).onTransactionHandled(tokenCaptor.capture(), wctCaptor.capture(), 1491 anyInt(), anyBoolean()); 1492 1493 final IBinder transactionToken = tokenCaptor.getValue(); 1494 final WindowContainerTransaction wct = wctCaptor.getValue(); 1495 wct.setTaskFragmentOrganizer(mIOrganizer); 1496 mController.onTransactionHandled(transactionToken, wct, TASK_FRAGMENT_TRANSIT_CHANGE, 1497 false /* shouldApplyIndependently */); 1498 1499 verify(mTransitionController).continueTransitionReady(); 1500 } 1501 1502 @Test testWindowOrganizerApplyTransaction_throwException()1503 public void testWindowOrganizerApplyTransaction_throwException() { 1504 // Not allow to use #applyTransaction(WindowContainerTransaction). 1505 assertThrows(RuntimeException.class, () -> mOrganizer.applyTransaction(mTransaction)); 1506 1507 // Allow to use the overload method. 1508 mOrganizer.applyTransaction(mTransaction, TASK_FRAGMENT_TRANSIT_CHANGE, 1509 false /* shouldApplyIndependently */); 1510 } 1511 1512 @Test testTaskFragmentTransitionType()1513 public void testTaskFragmentTransitionType() { 1514 // 1-1 relationship with WindowManager.TransitionType 1515 assertEquals(TRANSIT_NONE, TASK_FRAGMENT_TRANSIT_NONE); 1516 assertEquals(TRANSIT_OPEN, TASK_FRAGMENT_TRANSIT_OPEN); 1517 assertEquals(TRANSIT_CLOSE, TASK_FRAGMENT_TRANSIT_CLOSE); 1518 assertEquals(TRANSIT_CHANGE, TASK_FRAGMENT_TRANSIT_CHANGE); 1519 } 1520 1521 @Test testApplyTransaction_setRelativeBounds()1522 public void testApplyTransaction_setRelativeBounds() { 1523 final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, 1524 ACTIVITY_TYPE_STANDARD); 1525 mTaskFragment = new TaskFragmentBuilder(mAtm) 1526 .setParentTask(task) 1527 .setFragmentToken(mFragmentToken) 1528 .build(); 1529 final WindowContainerToken token = mTaskFragment.mRemoteToken.toWindowContainerToken(); 1530 final Rect relBounds = new Rect(0, 0, 100, 1000); 1531 mTransaction.setRelativeBounds(token, relBounds); 1532 1533 // Not allowed because TaskFragment is not organized by the caller organizer. 1534 assertApplyTransactionDisallowed(mTransaction); 1535 1536 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 1537 "Test:TaskFragmentOrganizer" /* processName */); 1538 1539 assertApplyTransactionAllowed(mTransaction); 1540 assertEquals(relBounds, mTaskFragment.getRelativeEmbeddedBounds()); 1541 assertEquals(relBounds, mTaskFragment.getBounds()); 1542 1543 // TaskFragment bounds should be updated to the same relative bounds. 1544 final Rect taskBounds = new Rect(200, 200, 700, 1500); 1545 task.setBoundsUnchecked(taskBounds); 1546 assertEquals(taskBounds, task.getBounds()); 1547 assertEquals(relBounds, mTaskFragment.getRelativeEmbeddedBounds()); 1548 assertEquals(mTaskFragment.translateRelativeBoundsToAbsoluteBounds(relBounds, taskBounds), 1549 mTaskFragment.getBounds()); 1550 assertEquals(new Rect(200, 200, 300, 1200), mTaskFragment.getBounds()); 1551 1552 // Set TaskFragment to fill Task 1553 mTransaction.setRelativeBounds(token, new Rect()); 1554 1555 assertApplyTransactionAllowed(mTransaction); 1556 assertEquals(new Rect(), mTaskFragment.getRelativeEmbeddedBounds()); 1557 assertEquals(taskBounds, mTaskFragment.getBounds()); 1558 } 1559 1560 @Test testUntrustedEmbedding_setRelativeBounds_adjustToTaskBounds()1561 public void testUntrustedEmbedding_setRelativeBounds_adjustToTaskBounds() { 1562 final Task task = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, 1563 ACTIVITY_TYPE_STANDARD); 1564 mTaskFragment = new TaskFragmentBuilder(mAtm) 1565 .setParentTask(task) 1566 .setFragmentToken(mFragmentToken) 1567 .build(); 1568 final WindowContainerToken token = mTaskFragment.mRemoteToken.toWindowContainerToken(); 1569 mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, 1570 "Test:TaskFragmentOrganizer" /* processName */); 1571 // Untrusted embedded 1572 doReturn(false).when(mTaskFragment).isAllowedToBeEmbeddedInTrustedMode(); 1573 1574 final Rect taskBounds = new Rect(0, 0, 500, 1000); 1575 task.setBoundsUnchecked(taskBounds); 1576 1577 final Rect taskFragmentBounds = new Rect(250, 0, 750, 1000); 1578 mTransaction.setRelativeBounds(token, taskFragmentBounds); 1579 1580 // Allow operation in case the Task is also resizing. 1581 // Adjust the relBounds to the intersection. 1582 assertApplyTransactionAllowed(mTransaction); 1583 // Relative bounds is correctly set. 1584 assertEquals(taskFragmentBounds, mTaskFragment.getRelativeEmbeddedBounds()); 1585 // The actual window bounds is adjusted to fit the Task bounds. 1586 assertEquals(new Rect(250, 0, 500, 1000), mTaskFragment.getBounds()); 1587 1588 // Adjust to the full requested bounds when the Task is resized. 1589 taskBounds.set(0, 0, 750, 1000); 1590 task.setBoundsUnchecked(taskBounds); 1591 assertEquals(taskFragmentBounds, mTaskFragment.getBounds()); 1592 } 1593 1594 @Test testApplyTransaction_reorderTaskFragmentToFront()1595 public void testApplyTransaction_reorderTaskFragmentToFront() { 1596 final Task task = createTask(mDisplayContent); 1597 // Create a TaskFragment. 1598 final IBinder token0 = new Binder(); 1599 final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) 1600 .setParentTask(task) 1601 .setFragmentToken(token0) 1602 .setOrganizer(mOrganizer) 1603 .createActivityCount(1) 1604 .build(); 1605 // Create another TaskFragment 1606 final IBinder token1 = new Binder(); 1607 final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) 1608 .setParentTask(task) 1609 .setFragmentToken(token1) 1610 .setOrganizer(mOrganizer) 1611 .createActivityCount(1) 1612 .build(); 1613 // Create a non-embedded Activity on top. 1614 final ActivityRecord topActivity = new ActivityBuilder(mAtm) 1615 .setTask(task) 1616 .build(); 1617 mWindowOrganizerController.mLaunchTaskFragments.put(token0, tf0); 1618 mWindowOrganizerController.mLaunchTaskFragments.put(token1, tf1); 1619 1620 // Reorder TaskFragment to front 1621 final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( 1622 OP_TYPE_REORDER_TO_FRONT).build(); 1623 mTransaction.addTaskFragmentOperation(token0, operation); 1624 assertApplyTransactionAllowed(mTransaction); 1625 1626 // Ensure the non-embedded activity still on top. 1627 assertEquals(topActivity, task.getTopChild().asActivityRecord()); 1628 1629 // Ensure the TaskFragment is moved to front. 1630 final TaskFragment frontMostTaskFragment = task.getTaskFragment(tf -> tf.asTask() == null); 1631 assertEquals(frontMostTaskFragment, tf0); 1632 } 1633 1634 /** 1635 * Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls 1636 * {@link WindowOrganizerController#applyTransaction(WindowContainerTransaction)} to apply the 1637 * transaction, 1638 */ createTaskFragmentFromOrganizer(WindowContainerTransaction wct, ActivityRecord ownerActivity, IBinder fragmentToken)1639 private void createTaskFragmentFromOrganizer(WindowContainerTransaction wct, 1640 ActivityRecord ownerActivity, IBinder fragmentToken) { 1641 final int uid = Binder.getCallingUid(); 1642 ownerActivity.info.applicationInfo.uid = uid; 1643 ownerActivity.getTask().effectiveUid = uid; 1644 final TaskFragmentCreationParams params = new TaskFragmentCreationParams.Builder( 1645 mOrganizerToken, fragmentToken, ownerActivity.token).build(); 1646 wct.setTaskFragmentOrganizer(mIOrganizer); 1647 1648 // Allow organizer to create TaskFragment and start/reparent activity to TaskFragment. 1649 wct.createTaskFragment(params); 1650 } 1651 1652 /** Asserts that applying the given transaction will throw a {@link SecurityException}. */ assertApplyTransactionDisallowed(WindowContainerTransaction t)1653 private void assertApplyTransactionDisallowed(WindowContainerTransaction t) { 1654 assertThrows(SecurityException.class, () -> 1655 mController.applyTransaction(t, TASK_FRAGMENT_TRANSIT_CHANGE, 1656 false /* shouldApplyIndependently */)); 1657 } 1658 1659 /** Asserts that applying the given transaction will not throw any exception. */ assertApplyTransactionAllowed(WindowContainerTransaction t)1660 private void assertApplyTransactionAllowed(WindowContainerTransaction t) { 1661 mController.applyTransaction(t, TASK_FRAGMENT_TRANSIT_CHANGE, 1662 false /* shouldApplyIndependently */); 1663 } 1664 1665 /** Asserts that there will be a transaction for TaskFragment appeared. */ assertTaskFragmentAppearedTransaction()1666 private void assertTaskFragmentAppearedTransaction() { 1667 verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture()); 1668 final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); 1669 final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); 1670 assertFalse(changes.isEmpty()); 1671 1672 // Use remove to verify multiple transaction changes. 1673 final TaskFragmentTransaction.Change change = changes.remove(0); 1674 assertEquals(TYPE_TASK_FRAGMENT_APPEARED, change.getType()); 1675 assertEquals(mTaskFragmentInfo, change.getTaskFragmentInfo()); 1676 assertEquals(mFragmentToken, change.getTaskFragmentToken()); 1677 } 1678 1679 /** Asserts that there will be a transaction for TaskFragment info changed. */ assertTaskFragmentInfoChangedTransaction()1680 private void assertTaskFragmentInfoChangedTransaction() { 1681 verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture()); 1682 final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); 1683 final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); 1684 assertFalse(changes.isEmpty()); 1685 1686 // Use remove to verify multiple transaction changes. 1687 final TaskFragmentTransaction.Change change = changes.remove(0); 1688 assertEquals(TYPE_TASK_FRAGMENT_INFO_CHANGED, change.getType()); 1689 assertEquals(mTaskFragmentInfo, change.getTaskFragmentInfo()); 1690 assertEquals(mFragmentToken, change.getTaskFragmentToken()); 1691 } 1692 1693 /** Asserts that there will be a transaction for TaskFragment vanished. */ assertTaskFragmentVanishedTransaction()1694 private void assertTaskFragmentVanishedTransaction() { 1695 verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture()); 1696 final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); 1697 final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); 1698 assertFalse(changes.isEmpty()); 1699 1700 // Use remove to verify multiple transaction changes. 1701 final TaskFragmentTransaction.Change change = changes.remove(0); 1702 assertEquals(TYPE_TASK_FRAGMENT_VANISHED, change.getType()); 1703 assertEquals(mTaskFragmentInfo, change.getTaskFragmentInfo()); 1704 assertEquals(mFragmentToken, change.getTaskFragmentToken()); 1705 } 1706 1707 /** Asserts that there will be a transaction for TaskFragment vanished. */ assertTaskFragmentParentInfoChangedTransaction(@onNull Task task)1708 private void assertTaskFragmentParentInfoChangedTransaction(@NonNull Task task) { 1709 verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture()); 1710 final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); 1711 final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); 1712 assertFalse(changes.isEmpty()); 1713 1714 // Use remove to verify multiple transaction changes. 1715 final TaskFragmentTransaction.Change change = changes.remove(0); 1716 assertEquals(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED, change.getType()); 1717 assertEquals(task.mTaskId, change.getTaskId()); 1718 assertEquals(task.getTaskFragmentParentInfo(), change.getTaskFragmentParentInfo()); 1719 } 1720 1721 /** Asserts that there will be a transaction for TaskFragment error. */ assertTaskFragmentErrorTransaction(@askFragmentOperation.OperationType int opType, @NonNull Class<?> exceptionClass)1722 private void assertTaskFragmentErrorTransaction(@TaskFragmentOperation.OperationType int opType, 1723 @NonNull Class<?> exceptionClass) { 1724 verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture()); 1725 final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); 1726 final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); 1727 assertFalse(changes.isEmpty()); 1728 1729 // Use remove to verify multiple transaction changes. 1730 final TaskFragmentTransaction.Change change = changes.remove(0); 1731 assertEquals(TYPE_TASK_FRAGMENT_ERROR, change.getType()); 1732 assertEquals(mErrorToken, change.getErrorCallbackToken()); 1733 final Bundle errorBundle = change.getErrorBundle(); 1734 assertEquals(opType, errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE)); 1735 assertEquals(exceptionClass, errorBundle.getSerializable( 1736 KEY_ERROR_CALLBACK_THROWABLE, Throwable.class).getClass()); 1737 } 1738 1739 /** Asserts that there will be a transaction for activity reparented to Task. */ assertActivityReparentedToTaskTransaction(int taskId, @NonNull Intent intent, @NonNull IBinder activityToken)1740 private void assertActivityReparentedToTaskTransaction(int taskId, @NonNull Intent intent, 1741 @NonNull IBinder activityToken) { 1742 verify(mOrganizer).onTransactionReady(mTransactionCaptor.capture()); 1743 final TaskFragmentTransaction transaction = mTransactionCaptor.getValue(); 1744 final List<TaskFragmentTransaction.Change> changes = transaction.getChanges(); 1745 assertFalse(changes.isEmpty()); 1746 1747 // Use remove to verify multiple transaction changes. 1748 final TaskFragmentTransaction.Change change = changes.remove(0); 1749 assertEquals(TYPE_ACTIVITY_REPARENTED_TO_TASK, change.getType()); 1750 assertEquals(taskId, change.getTaskId()); 1751 assertIntentsEqualForOrganizer(intent, change.getActivityIntent()); 1752 assertIntentTrimmed(change.getActivityIntent()); 1753 assertEquals(activityToken, change.getActivityToken()); 1754 } 1755 1756 /** Setups an embedded TaskFragment in a PIP Task. */ setupTaskFragmentInPip()1757 private void setupTaskFragmentInPip() { 1758 mTaskFragment = new TaskFragmentBuilder(mAtm) 1759 .setCreateParentTask() 1760 .setFragmentToken(mFragmentToken) 1761 .setOrganizer(mOrganizer) 1762 .createActivityCount(1) 1763 .build(); 1764 mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken(); 1765 mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); 1766 mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED); 1767 } 1768 1769 /** Setups the mock Task as the parent of the given TaskFragment. */ setupMockParent(TaskFragment taskFragment, Task mockParent)1770 private static void setupMockParent(TaskFragment taskFragment, Task mockParent) { 1771 doReturn(mockParent).when(taskFragment).getTask(); 1772 doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true)) 1773 .when(mockParent).getTaskFragmentParentInfo(); 1774 1775 // Task needs to be visible 1776 mockParent.lastActiveTime = 100; 1777 doReturn(true).when(mockParent).shouldBeVisible(any()); 1778 } 1779 assertIntentsEqualForOrganizer(@onNull Intent expected, @NonNull Intent actual)1780 private static void assertIntentsEqualForOrganizer(@NonNull Intent expected, 1781 @NonNull Intent actual) { 1782 assertEquals(expected.getComponent(), actual.getComponent()); 1783 assertEquals(expected.getPackage(), actual.getPackage()); 1784 assertEquals(expected.getAction(), actual.getAction()); 1785 } 1786 assertIntentTrimmed(@onNull Intent intent)1787 private static void assertIntentTrimmed(@NonNull Intent intent) { 1788 assertNull(intent.getData()); 1789 assertNull(intent.getExtras()); 1790 assertEquals(0, intent.getFlags()); 1791 } 1792 } 1793