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 android.server.wm; 18 19 import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 22 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; 23 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 24 import static android.server.wm.TaskFragmentOrganizerTestBase.assertEmptyTaskFragment; 25 import static android.server.wm.TaskFragmentOrganizerTestBase.getActivityToken; 26 import static android.server.wm.TaskFragmentOrganizerTestBase.startNewActivity; 27 import static android.server.wm.WindowManagerState.STATE_RESUMED; 28 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY; 29 import static android.server.wm.app30.Components.SDK_30_TEST_ACTIVITY; 30 import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CHANGE; 31 import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_CLOSE; 32 import static android.window.TaskFragmentOrganizer.TASK_FRAGMENT_TRANSIT_OPEN; 33 34 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 35 36 import static com.google.common.truth.Truth.assertThat; 37 import static com.google.common.truth.Truth.assertWithMessage; 38 39 import static org.junit.Assert.assertEquals; 40 import static org.junit.Assert.assertThrows; 41 import static org.junit.Assert.fail; 42 43 import android.app.Activity; 44 import android.content.ComponentName; 45 import android.content.Intent; 46 import android.graphics.Rect; 47 import android.os.Binder; 48 import android.os.IBinder; 49 import android.platform.test.annotations.Presubmit; 50 import android.server.wm.TaskFragmentOrganizerTestBase.BasicTaskFragmentOrganizer; 51 import android.server.wm.WindowContextTests.TestActivity; 52 import android.server.wm.WindowManagerState.Task; 53 import android.view.SurfaceControl; 54 import android.window.TaskAppearedInfo; 55 import android.window.TaskFragmentCreationParams; 56 import android.window.TaskFragmentInfo; 57 import android.window.TaskFragmentOrganizer; 58 import android.window.TaskOrganizer; 59 import android.window.WindowContainerToken; 60 import android.window.WindowContainerTransaction; 61 import android.window.WindowContainerTransactionCallback; 62 63 import androidx.annotation.NonNull; 64 import androidx.test.runner.AndroidJUnit4; 65 66 import com.android.compatibility.common.util.ApiTest; 67 68 import org.junit.After; 69 import org.junit.Before; 70 import org.junit.Test; 71 import org.junit.runner.RunWith; 72 73 import java.util.ArrayList; 74 import java.util.List; 75 76 /** 77 * Tests that verify the behavior of {@link TaskFragmentOrganizer} policy. 78 * 79 * Build/Install/Run: 80 * atest CtsWindowManagerDeviceTestCases:TaskFragmentOrganizerPolicyTest 81 */ 82 @RunWith(AndroidJUnit4.class) 83 @Presubmit 84 @android.server.wm.annotation.Group2 85 public class TaskFragmentOrganizerPolicyTest extends ActivityManagerTestBase { 86 87 private TaskOrganizer mTaskOrganizer; 88 private BasicTaskFragmentOrganizer mTaskFragmentOrganizer; 89 private final ArrayList<BasicTaskFragmentOrganizer> mOrganizers = new ArrayList<>(); 90 91 @Before 92 @Override setUp()93 public void setUp() throws Exception { 94 super.setUp(); 95 mTaskFragmentOrganizer = new BasicTaskFragmentOrganizer(); 96 mTaskFragmentOrganizer.registerOrganizer(); 97 mOrganizers.add(mTaskFragmentOrganizer); 98 } 99 100 @After tearDown()101 public void tearDown() { 102 for (TaskFragmentOrganizer organizer : mOrganizers) { 103 organizer.unregisterOrganizer(); 104 } 105 mOrganizers.clear(); 106 if (mTaskOrganizer != null) { 107 NestedShellPermission.run(() -> mTaskOrganizer.unregisterOrganizer()); 108 } 109 } 110 111 /** 112 * Verifies that performing {@link WindowContainerTransaction#createTaskFragment} will fail if 113 * the fragment token is not unique. 114 */ 115 @Test 116 @ApiTest(apis = { 117 "android.window.TaskFragmentOrganizer#applyTransaction", 118 "android.window.WindowContainerTransaction#createTaskFragment"}) testCreateTaskFragment_duplicatedFragmentToken_reportError()119 public void testCreateTaskFragment_duplicatedFragmentToken_reportError() { 120 final Activity activity = startNewActivity(); 121 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 122 mTaskFragmentOrganizer, activity); 123 final IBinder existingFragmentToken = taskFragmentInfo.getFragmentToken(); 124 final IBinder errorCallbackToken = new Binder(); 125 126 // Request to create another TaskFragment using the existing fragment token. 127 final TaskFragmentCreationParams params = mTaskFragmentOrganizer.generateTaskFragParams( 128 existingFragmentToken, getActivityToken(activity), new Rect(), 129 WINDOWING_MODE_UNDEFINED); 130 final WindowContainerTransaction wct = new WindowContainerTransaction() 131 .setErrorCallbackToken(errorCallbackToken) 132 .createTaskFragment(params); 133 134 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_OPEN, 135 false /* shouldApplyIndependently */); 136 mTaskFragmentOrganizer.waitForTaskFragmentError(); 137 138 assertThat(mTaskFragmentOrganizer.getThrowable()).isInstanceOf( 139 IllegalArgumentException.class); 140 assertThat(mTaskFragmentOrganizer.getErrorCallbackToken()).isEqualTo(errorCallbackToken); 141 } 142 143 /** 144 * Verifies that performing {@link WindowContainerTransaction#deleteTaskFragment} on 145 * non-organized TaskFragment will throw {@link SecurityException}. 146 */ 147 @Test 148 @ApiTest(apis = { 149 "android.window.TaskFragmentOrganizer#applyTransaction", 150 "android.window.WindowContainerTransaction#deleteTaskFragment"}) testDeleteTaskFragment_nonOrganizedTaskFragment_throwException()151 public void testDeleteTaskFragment_nonOrganizedTaskFragment_throwException() { 152 final Activity activity = startNewActivity(); 153 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 154 mTaskFragmentOrganizer, activity); 155 156 // Create another TaskFragmentOrganizer to request operation. 157 final TaskFragmentOrganizer anotherOrganizer = registerNewOrganizer(); 158 final WindowContainerTransaction wct = new WindowContainerTransaction() 159 .deleteTaskFragment(taskFragmentInfo.getFragmentToken()); 160 161 assertThrows(SecurityException.class, () -> anotherOrganizer.applyTransaction(wct, 162 TASK_FRAGMENT_TRANSIT_CLOSE, false /* shouldApplyIndependently */)); 163 } 164 165 /** 166 * Verifies that performing {@link WindowContainerTransaction#deleteTaskFragment} on organized 167 * TaskFragment is allowed. 168 */ 169 @Test 170 @ApiTest(apis = { 171 "android.window.TaskFragmentOrganizer#applyTransaction", 172 "android.window.WindowContainerTransaction#deleteTaskFragment"}) testDeleteTaskFragment_organizedTaskFragment()173 public void testDeleteTaskFragment_organizedTaskFragment() { 174 final Activity activity = startNewActivity(); 175 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 176 mTaskFragmentOrganizer, activity); 177 178 final WindowContainerTransaction wct = new WindowContainerTransaction() 179 .deleteTaskFragment(taskFragmentInfo.getFragmentToken()); 180 181 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_CLOSE, 182 false /* shouldApplyIndependently */); 183 } 184 185 /** 186 * Verifies that performing {@link WindowContainerTransaction#startActivityInTaskFragment} on 187 * non-organized TaskFragment will throw {@link SecurityException}. 188 */ 189 @Test 190 @ApiTest(apis = { 191 "android.window.TaskFragmentOrganizer#applyTransaction", 192 "android.window.WindowContainerTransaction#startActivityInTaskFragment"}) testStartActivityInTaskFragment_nonOrganizedTaskFragment_throwException()193 public void testStartActivityInTaskFragment_nonOrganizedTaskFragment_throwException() { 194 final Activity activity = startNewActivity(); 195 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 196 mTaskFragmentOrganizer, activity); 197 final IBinder fragmentToken = taskFragmentInfo.getFragmentToken(); 198 final IBinder callerToken = getActivityToken(activity); 199 final Intent intent = new Intent(mContext, TestActivity.class); 200 201 // Create another TaskFragmentOrganizer to request operation. 202 final TaskFragmentOrganizer anotherOrganizer = registerNewOrganizer(); 203 final WindowContainerTransaction wct = new WindowContainerTransaction() 204 .startActivityInTaskFragment(fragmentToken, callerToken, intent, 205 null /* activityOptions */); 206 207 assertThrows(SecurityException.class, () -> anotherOrganizer.applyTransaction(wct, 208 TASK_FRAGMENT_TRANSIT_OPEN, false /* shouldApplyIndependently */)); 209 } 210 211 /** 212 * Verifies that performing {@link WindowContainerTransaction#startActivityInTaskFragment} on 213 * organized TaskFragment is allowed. 214 */ 215 @Test 216 @ApiTest(apis = { 217 "android.window.TaskFragmentOrganizer#applyTransaction", 218 "android.window.WindowContainerTransaction#startActivityInTaskFragment"}) testStartActivityInTaskFragment_organizedTaskFragment()219 public void testStartActivityInTaskFragment_organizedTaskFragment() { 220 final Activity activity = startNewActivity(); 221 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 222 mTaskFragmentOrganizer, activity); 223 final IBinder fragmentToken = taskFragmentInfo.getFragmentToken(); 224 final IBinder callerToken = getActivityToken(activity); 225 final Intent intent = new Intent(mContext, TestActivity.class); 226 227 final WindowContainerTransaction wct = new WindowContainerTransaction() 228 .startActivityInTaskFragment(fragmentToken, callerToken, intent, 229 null /* activityOptions */); 230 231 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_OPEN, 232 false /* shouldApplyIndependently */); 233 } 234 235 /** 236 * Verifies that performing {@link WindowContainerTransaction#requestFocusOnTaskFragment} on 237 * non-organized TaskFragment will throw {@link SecurityException}. 238 */ 239 @Test 240 @ApiTest(apis = { 241 "android.window.TaskFragmentOrganizer#applyTransaction", 242 "android.window.WindowContainerTransaction#requestFocusOnTaskFragment"}) testRequestFocusOnTaskFragment_nonOrganizedTaskFragment_throwException()243 public void testRequestFocusOnTaskFragment_nonOrganizedTaskFragment_throwException() { 244 final Activity activity = startNewActivity(); 245 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 246 mTaskFragmentOrganizer, activity); 247 final IBinder fragmentToken = taskFragmentInfo.getFragmentToken(); 248 249 // Create another TaskFragmentOrganizer to request operation. 250 final TaskFragmentOrganizer anotherOrganizer = registerNewOrganizer(); 251 final WindowContainerTransaction wct = new WindowContainerTransaction() 252 .requestFocusOnTaskFragment(fragmentToken); 253 254 assertThrows(SecurityException.class, () -> anotherOrganizer.applyTransaction(wct, 255 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 256 } 257 258 /** 259 * Verifies that performing {@link WindowContainerTransaction#requestFocusOnTaskFragment} on 260 * organized TaskFragment is allowed. 261 */ 262 @Test 263 @ApiTest(apis = { 264 "android.window.TaskFragmentOrganizer#applyTransaction", 265 "android.window.WindowContainerTransaction#requestFocusOnTaskFragment"}) testRequestFocusOnTaskFragment_organizedTaskFragment()266 public void testRequestFocusOnTaskFragment_organizedTaskFragment() { 267 final Activity activity = startNewActivity(); 268 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 269 mTaskFragmentOrganizer, activity); 270 final IBinder fragmentToken = taskFragmentInfo.getFragmentToken(); 271 272 final WindowContainerTransaction wct = new WindowContainerTransaction() 273 .requestFocusOnTaskFragment(fragmentToken); 274 275 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_CHANGE, 276 false /* shouldApplyIndependently */); 277 } 278 279 /** 280 * Verifies that performing {@link WindowContainerTransaction#reparentActivityToTaskFragment} on 281 * non-organized TaskFragment will throw {@link SecurityException}. 282 */ 283 @Test 284 @ApiTest(apis = { 285 "android.window.TaskFragmentOrganizer#applyTransaction", 286 "android.window.WindowContainerTransaction#reparentActivityToTaskFragment"}) testReparentActivityToTaskFragment_nonOrganizedTaskFragment_throwException()287 public void testReparentActivityToTaskFragment_nonOrganizedTaskFragment_throwException() { 288 final Activity activity = startNewActivity(); 289 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 290 mTaskFragmentOrganizer, activity); 291 final IBinder fragmentToken = taskFragmentInfo.getFragmentToken(); 292 final IBinder activityToken = getActivityToken(activity); 293 294 // Create another TaskFragmentOrganizer to request operation. 295 final TaskFragmentOrganizer anotherOrganizer = registerNewOrganizer(); 296 final WindowContainerTransaction wct = new WindowContainerTransaction() 297 .reparentActivityToTaskFragment(fragmentToken, activityToken); 298 299 assertThrows(SecurityException.class, () -> anotherOrganizer.applyTransaction(wct, 300 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 301 } 302 303 /** 304 * Verifies that performing {@link WindowContainerTransaction#reparentActivityToTaskFragment} on 305 * organized TaskFragment is allowed. 306 */ 307 @Test 308 @ApiTest(apis = { 309 "android.window.TaskFragmentOrganizer#applyTransaction", 310 "android.window.WindowContainerTransaction#startActivityInTaskFragment"}) testReparentActivityToTaskFragment_organizedTaskFragment()311 public void testReparentActivityToTaskFragment_organizedTaskFragment() { 312 final Activity activity = startNewActivity(); 313 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 314 mTaskFragmentOrganizer, activity); 315 final IBinder fragmentToken = taskFragmentInfo.getFragmentToken(); 316 final IBinder activityToken = getActivityToken(activity); 317 318 final WindowContainerTransaction wct = new WindowContainerTransaction() 319 .reparentActivityToTaskFragment(fragmentToken, activityToken); 320 321 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_CHANGE, 322 false /* shouldApplyIndependently */); 323 } 324 325 /** 326 * Verifies that performing {@link WindowContainerTransaction#setAdjacentTaskFragments} on 327 * non-organized TaskFragment will throw {@link SecurityException}. 328 */ 329 @Test 330 @ApiTest(apis = { 331 "android.window.TaskFragmentOrganizer#applyTransaction", 332 "android.window.WindowContainerTransaction#setAdjacentTaskFragments"}) testSetAdjacentTaskFragments_nonOrganizedTaskFragment_throwException()333 public void testSetAdjacentTaskFragments_nonOrganizedTaskFragment_throwException() { 334 final Activity activity = startNewActivity(); 335 final TaskFragmentInfo taskFragmentInfo0 = createOrganizedTaskFragment( 336 mTaskFragmentOrganizer, activity); 337 final TaskFragmentInfo taskFragmentInfo1 = createOrganizedTaskFragment( 338 mTaskFragmentOrganizer, activity); 339 final IBinder fragmentToken0 = taskFragmentInfo0.getFragmentToken(); 340 final IBinder fragmentToken1 = taskFragmentInfo1.getFragmentToken(); 341 342 // Create another TaskFragmentOrganizer to request operation. 343 final TaskFragmentOrganizer anotherOrganizer = registerNewOrganizer(); 344 final WindowContainerTransaction wct = new WindowContainerTransaction() 345 .setAdjacentTaskFragments(fragmentToken0, fragmentToken1, null /* params */); 346 347 assertThrows(SecurityException.class, () -> anotherOrganizer.applyTransaction(wct, 348 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 349 } 350 351 /** 352 * Verifies that performing {@link WindowContainerTransaction#setAdjacentTaskFragments} on 353 * organized TaskFragment is allowed. 354 */ 355 @Test 356 @ApiTest(apis = { 357 "android.window.TaskFragmentOrganizer#applyTransaction", 358 "android.window.WindowContainerTransaction#setAdjacentTaskFragments"}) testSetAdjacentTaskFragments_organizedTaskFragment()359 public void testSetAdjacentTaskFragments_organizedTaskFragment() { 360 final Activity activity = startNewActivity(); 361 final TaskFragmentInfo taskFragmentInfo0 = createOrganizedTaskFragment( 362 mTaskFragmentOrganizer, activity); 363 final TaskFragmentInfo taskFragmentInfo1 = createOrganizedTaskFragment( 364 mTaskFragmentOrganizer, activity); 365 final IBinder fragmentToken0 = taskFragmentInfo0.getFragmentToken(); 366 final IBinder fragmentToken1 = taskFragmentInfo1.getFragmentToken(); 367 368 final WindowContainerTransaction wct = new WindowContainerTransaction() 369 .setAdjacentTaskFragments(fragmentToken0, fragmentToken1, null /* params */); 370 371 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_CHANGE, 372 false /* shouldApplyIndependently */); 373 } 374 375 /** 376 * Verifies that changing property on non-TaskFragment window will throw 377 * {@link SecurityException}. 378 */ 379 @Test 380 @ApiTest(apis = { 381 "android.window.TaskFragmentOrganizer#applyTransaction", 382 "android.window.WindowContainerTransaction#setRelativeBounds", 383 "android.window.WindowContainerTransaction#setWindowingMode", 384 }) testSetProperty_nonTaskFragmentWindow_throwException()385 public void testSetProperty_nonTaskFragmentWindow_throwException() { 386 final WindowContainerToken taskToken = getFirstTaskToken(); 387 final WindowContainerTransaction wct0 = new WindowContainerTransaction() 388 .setRelativeBounds(taskToken, new Rect()); 389 390 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct0, 391 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 392 393 final WindowContainerTransaction wct1 = new WindowContainerTransaction() 394 .setWindowingMode(taskToken, WINDOWING_MODE_MULTI_WINDOW); 395 396 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct1, 397 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 398 } 399 400 /** 401 * Verifies that changing property on non-organized TaskFragment will throw 402 * {@link SecurityException}. 403 */ 404 @Test 405 @ApiTest(apis = { 406 "android.window.TaskFragmentOrganizer#applyTransaction", 407 "android.window.WindowContainerTransaction#setRelativeBounds", 408 "android.window.WindowContainerTransaction#setWindowingMode", 409 }) testSetProperty_nonOrganizedTaskFragment_throwException()410 public void testSetProperty_nonOrganizedTaskFragment_throwException() { 411 final Activity activity = startNewActivity(); 412 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 413 mTaskFragmentOrganizer, activity); 414 final WindowContainerToken taskFragmentToken = taskFragmentInfo.getToken(); 415 416 // Create another TaskFragmentOrganizer to request operation. 417 final TaskFragmentOrganizer anotherOrganizer = registerNewOrganizer(); 418 final WindowContainerTransaction wct0 = new WindowContainerTransaction() 419 .setRelativeBounds(taskFragmentToken, new Rect()); 420 421 assertThrows(SecurityException.class, () -> anotherOrganizer.applyTransaction(wct0, 422 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 423 424 final WindowContainerTransaction wct1 = new WindowContainerTransaction() 425 .setWindowingMode(taskFragmentToken, WINDOWING_MODE_MULTI_WINDOW); 426 427 assertThrows(SecurityException.class, () -> anotherOrganizer.applyTransaction(wct1, 428 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 429 } 430 431 /** 432 * Verifies that changing property on organized TaskFragment is allowed. 433 */ 434 @Test 435 @ApiTest(apis = { 436 "android.window.TaskFragmentOrganizer#applyTransaction", 437 "android.window.WindowContainerTransaction#setRelativeBounds", 438 "android.window.WindowContainerTransaction#setWindowingMode", 439 }) testSetProperty_organizedTaskFragment()440 public void testSetProperty_organizedTaskFragment() { 441 final Activity activity = startNewActivity(); 442 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 443 mTaskFragmentOrganizer, activity); 444 final WindowContainerToken taskFragmentToken = taskFragmentInfo.getToken(); 445 446 final WindowContainerTransaction wct0 = new WindowContainerTransaction() 447 .setRelativeBounds(taskFragmentToken, new Rect()); 448 449 mTaskFragmentOrganizer.applyTransaction(wct0, TASK_FRAGMENT_TRANSIT_CHANGE, 450 false /* shouldApplyIndependently */); 451 452 final WindowContainerTransaction wct1 = new WindowContainerTransaction() 453 .setWindowingMode(taskFragmentToken, WINDOWING_MODE_MULTI_WINDOW); 454 455 mTaskFragmentOrganizer.applyTransaction(wct1, TASK_FRAGMENT_TRANSIT_CHANGE, 456 false /* shouldApplyIndependently */); 457 } 458 459 /** 460 * Verifies that the following {@link WindowContainerTransaction} operations are not allowed on 461 * organized TaskFragment. 462 */ 463 @Test 464 @ApiTest(apis = { 465 "android.window.TaskFragmentOrganizer#applyTransaction", 466 "android.window.WindowContainerTransaction#setBounds", 467 "android.window.WindowContainerTransaction#setAppBounds", 468 "android.window.WindowContainerTransaction#setScreenSizeDp", 469 "android.window.WindowContainerTransaction#setSmallestScreenWidthDp", 470 }) testSetProperty_unsupportedChange_throwException()471 public void testSetProperty_unsupportedChange_throwException() { 472 final Activity activity = startNewActivity(); 473 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 474 mTaskFragmentOrganizer, activity); 475 final WindowContainerToken taskFragmentToken = taskFragmentInfo.getToken(); 476 477 final WindowContainerTransaction wct0 = new WindowContainerTransaction() 478 .setBounds(taskFragmentToken, new Rect()); 479 480 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct0, 481 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 482 483 final WindowContainerTransaction wct1 = new WindowContainerTransaction() 484 .setAppBounds(taskFragmentToken, new Rect()); 485 486 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct1, 487 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 488 489 final WindowContainerTransaction wct2 = new WindowContainerTransaction() 490 .setScreenSizeDp(taskFragmentToken, 100, 200); 491 492 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct2, 493 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 494 495 final WindowContainerTransaction wct3 = new WindowContainerTransaction() 496 .setSmallestScreenWidthDp(taskFragmentToken, 100); 497 498 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct3, 499 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 500 } 501 502 /** 503 * Verifies that config changes with the following 504 * {@link WindowContainerTransaction.Change#getChangeMask()} are disallowed on organized 505 * TaskFragment. 506 */ 507 @Test 508 @ApiTest(apis = {"android.window.TaskFragmentOrganizer#applyTransaction", 509 "android.window.WindowContainerTransaction#scheduleFinishEnterPip", 510 "android.window.WindowContainerTransaction#setBoundsChangeTransaction", 511 "android.window.WindowContainerTransaction#setFocusable", 512 "android.window.WindowContainerTransaction#setHidden", 513 }) testApplyChange_unsupportedChangeMask_throwException()514 public void testApplyChange_unsupportedChangeMask_throwException() { 515 final Activity activity = startNewActivity(); 516 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 517 mTaskFragmentOrganizer, activity); 518 final WindowContainerToken token = taskFragmentInfo.getToken(); 519 520 final WindowContainerTransaction wct0 = new WindowContainerTransaction() 521 .scheduleFinishEnterPip(token, new Rect(0, 0, 100, 100)); 522 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct0, 523 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 524 525 final WindowContainerTransaction wct1 = new WindowContainerTransaction() 526 .setBoundsChangeTransaction(token, new SurfaceControl.Transaction()); 527 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct1, 528 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 529 530 final WindowContainerTransaction wct3 = new WindowContainerTransaction() 531 .setFocusable(token, false /* focusable */); 532 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct3, 533 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 534 535 final WindowContainerTransaction wct4 = new WindowContainerTransaction() 536 .setHidden(token, false /* hidden */); 537 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct4, 538 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 539 } 540 541 /** 542 * Verifies that performing {@link WindowContainerTransaction#reparent} from 543 * TaskFragmentOrganizer will throw {@link SecurityException}. 544 */ 545 @Test 546 @ApiTest(apis = { 547 "android.window.TaskFragmentOrganizer#applyTransaction", 548 "android.window.WindowContainerTransaction#reparent"}) testDisallowOperation_reparent()549 public void testDisallowOperation_reparent() { 550 final Activity activity = startNewActivity(); 551 final TaskFragmentInfo taskFragmentInfo0 = createOrganizedTaskFragment( 552 mTaskFragmentOrganizer, activity); 553 final TaskFragmentInfo taskFragmentInfo1 = createOrganizedTaskFragment( 554 mTaskFragmentOrganizer, activity); 555 final WindowContainerToken taskFragmentToken0 = taskFragmentInfo0.getToken(); 556 final WindowContainerToken taskFragmentToken1 = taskFragmentInfo1.getToken(); 557 558 final WindowContainerTransaction wct = new WindowContainerTransaction() 559 .reparent(taskFragmentToken0, taskFragmentToken1, true /* onTop */); 560 561 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct, 562 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 563 } 564 565 /** 566 * Verifies that performing {@link WindowContainerTransaction#reorder} from 567 * TaskFragmentOrganizer will throw {@link SecurityException}. 568 */ 569 @Test 570 @ApiTest(apis = { 571 "android.window.TaskFragmentOrganizer#applyTransaction", 572 "android.window.WindowContainerTransaction#reorder"}) testDisallowOperation_reorder()573 public void testDisallowOperation_reorder() { 574 final Activity activity = startNewActivity(); 575 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 576 mTaskFragmentOrganizer, activity); 577 final WindowContainerToken taskFragmentToken = taskFragmentInfo.getToken(); 578 579 final WindowContainerTransaction wct = new WindowContainerTransaction() 580 .reorder(taskFragmentToken, true /* onTop */); 581 582 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct, 583 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 584 } 585 586 /** 587 * Verifies that performing {@link WindowContainerTransaction#setLaunchRoot} from 588 * TaskFragmentOrganizer will throw {@link SecurityException}. 589 */ 590 @Test 591 @ApiTest(apis = { 592 "android.window.TaskFragmentOrganizer#applyTransaction", 593 "android.window.WindowContainerTransaction#setLaunchRoot"}) testDisallowOperation_setLaunchRoot()594 public void testDisallowOperation_setLaunchRoot() { 595 final Activity activity = startNewActivity(); 596 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 597 mTaskFragmentOrganizer, activity); 598 final WindowContainerToken taskFragmentToken = taskFragmentInfo.getToken(); 599 600 final WindowContainerTransaction wct = new WindowContainerTransaction() 601 .setLaunchRoot(taskFragmentToken, null /* windowingModes */, 602 null /* activityTypes */); 603 604 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct, 605 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 606 } 607 608 /** 609 * Verifies that performing {@link WindowContainerTransaction#setLaunchAdjacentFlagRoot} from 610 * TaskFragmentOrganizer will throw {@link SecurityException}. 611 */ 612 @Test 613 @ApiTest(apis = { 614 "android.window.TaskFragmentOrganizer#applyTransaction", 615 "android.window.WindowContainerTransaction#setLaunchAdjacentFlagRoot"}) testDisallowOperation_setLaunchAdjacentFlagRoot()616 public void testDisallowOperation_setLaunchAdjacentFlagRoot() { 617 final Activity activity = startNewActivity(); 618 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 619 mTaskFragmentOrganizer, activity); 620 final WindowContainerToken taskFragmentToken = taskFragmentInfo.getToken(); 621 622 final WindowContainerTransaction wct = new WindowContainerTransaction() 623 .setLaunchAdjacentFlagRoot(taskFragmentToken); 624 625 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct, 626 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 627 } 628 629 /** 630 * Verifies that performing {@link WindowContainerTransaction#clearLaunchAdjacentFlagRoot} from 631 * TaskFragmentOrganizer will throw {@link SecurityException}. 632 */ 633 @Test 634 @ApiTest(apis = { 635 "android.window.TaskFragmentOrganizer#applyTransaction", 636 "android.window.WindowContainerTransaction#clearLaunchAdjacentFlagRoot"}) testDisallowOperation_clearLaunchAdjacentFlagRoot()637 public void testDisallowOperation_clearLaunchAdjacentFlagRoot() { 638 final Activity activity = startNewActivity(); 639 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 640 mTaskFragmentOrganizer, activity); 641 final WindowContainerToken taskFragmentToken = taskFragmentInfo.getToken(); 642 643 final WindowContainerTransaction wct = new WindowContainerTransaction() 644 .clearLaunchAdjacentFlagRoot(taskFragmentToken); 645 646 assertThrows(SecurityException.class, () -> mTaskFragmentOrganizer.applyTransaction(wct, 647 TASK_FRAGMENT_TRANSIT_CHANGE, false /* shouldApplyIndependently */)); 648 } 649 650 /** 651 * Verifies the behavior to start Activity in a new created Task in TaskFragment is forbidden. 652 */ 653 @Test testStartActivityFromAnotherProcessInNewTask_ThrowException()654 public void testStartActivityFromAnotherProcessInNewTask_ThrowException() { 655 final Activity activity = startNewActivity(); 656 final IBinder ownerToken = getActivityToken(activity); 657 final TaskFragmentCreationParams params = mTaskFragmentOrganizer.generateTaskFragParams( 658 ownerToken); 659 final IBinder taskFragToken = params.getFragmentToken(); 660 final Intent intent = new Intent() 661 .setComponent(LAUNCHING_ACTIVITY) 662 .addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK); 663 final IBinder errorCallbackToken = new Binder(); 664 665 WindowContainerTransaction wct = new WindowContainerTransaction() 666 .setErrorCallbackToken(errorCallbackToken) 667 .createTaskFragment(params) 668 .startActivityInTaskFragment(taskFragToken, ownerToken, intent, 669 null /* activityOptions */); 670 671 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_OPEN, 672 false /* shouldApplyIndependently */); 673 mTaskFragmentOrganizer.waitForTaskFragmentError(); 674 675 assertThat(mTaskFragmentOrganizer.getThrowable()).isInstanceOf(SecurityException.class); 676 assertThat(mTaskFragmentOrganizer.getErrorCallbackToken()).isEqualTo(errorCallbackToken); 677 678 // Activity must be launched on a new task instead. 679 waitAndAssertActivityLaunchOnTask(LAUNCHING_ACTIVITY); 680 } 681 682 /** 683 * Verifies the behavior of starting an Activity of another app in TaskFragment is not 684 * allowed without permissions. 685 */ 686 @Test testStartAnotherAppActivityInTaskFragment()687 public void testStartAnotherAppActivityInTaskFragment() { 688 final Activity activity = startNewActivity(); 689 final IBinder ownerToken = getActivityToken(activity); 690 final TaskFragmentCreationParams params = 691 mTaskFragmentOrganizer.generateTaskFragParams(ownerToken); 692 final IBinder taskFragToken = params.getFragmentToken(); 693 final WindowContainerTransaction wct = new WindowContainerTransaction() 694 .createTaskFragment(params) 695 .startActivityInTaskFragment(taskFragToken, ownerToken, 696 new Intent().setComponent(SDK_30_TEST_ACTIVITY), 697 null /* activityOptions */); 698 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_OPEN, 699 false /* shouldApplyIndependently */); 700 mTaskFragmentOrganizer.waitForTaskFragmentCreated(); 701 702 // Launching an activity of another app in TaskFragment should report error. 703 mTaskFragmentOrganizer.waitForTaskFragmentError(); 704 assertThat(mTaskFragmentOrganizer.getThrowable()).isInstanceOf(SecurityException.class); 705 706 // Making sure activity is not launched on the TaskFragment 707 TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken); 708 assertEmptyTaskFragment(info, taskFragToken); 709 710 // Activity must be launched on a new task instead. 711 waitAndAssertActivityLaunchOnTask(SDK_30_TEST_ACTIVITY); 712 } 713 waitAndAssertActivityLaunchOnTask(ComponentName activityName)714 private void waitAndAssertActivityLaunchOnTask(ComponentName activityName) { 715 waitAndAssertResumedActivity(activityName, "Activity must be resumed."); 716 717 Task task = mWmState.getTaskByActivity(activityName); 718 assertWithMessage("Launching activity must be started on Task") 719 .that(task.getActivities()).contains(mWmState.getActivity(activityName)); 720 } 721 722 /** 723 * Verifies the behavior of starting an Activity of another app while activities of the host 724 * app are already embedded in TaskFragment. 725 */ 726 @Test testStartAnotherAppActivityWithEmbeddedTaskFragments()727 public void testStartAnotherAppActivityWithEmbeddedTaskFragments() { 728 final Activity activity = startNewActivity(); 729 final IBinder ownerToken = getActivityToken(activity); 730 final TaskFragmentCreationParams params = 731 mTaskFragmentOrganizer.generateTaskFragParams(ownerToken); 732 final IBinder taskFragToken = params.getFragmentToken(); 733 final WindowContainerTransaction wct = new WindowContainerTransaction() 734 .createTaskFragment(params) 735 .startActivityInTaskFragment(taskFragToken, ownerToken, 736 new Intent(getInstrumentation().getTargetContext(), 737 WindowMetricsActivityTests.MetricsActivity.class), 738 null /* activityOptions */); 739 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_OPEN, 740 false /* shouldApplyIndependently */); 741 mTaskFragmentOrganizer.waitForTaskFragmentCreated(); 742 mTaskFragmentOrganizer.waitForAndGetTaskFragmentInfo( 743 taskFragToken, info -> info.getActivities().size() == 1, 744 "getActivities from TaskFragment must contain 1 activities"); 745 746 activity.startActivity(new Intent().setComponent(SDK_30_TEST_ACTIVITY)); 747 748 waitAndAssertActivityState(SDK_30_TEST_ACTIVITY, STATE_RESUMED, 749 "Activity should be resumed."); 750 TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken); 751 assertEquals(1, info.getActivities().size()); 752 } 753 754 /** 755 * Verifies whether creating TaskFragment with non-resizeable {@link Activity} leads to 756 * {@link IllegalArgumentException} returned by 757 * {@link TaskFragmentOrganizer#onTaskFragmentError(IBinder, Throwable)}. 758 */ 759 @Test testCreateTaskFragmentWithNonResizeableActivity_ThrowException()760 public void testCreateTaskFragmentWithNonResizeableActivity_ThrowException() { 761 // Pass non-resizeable Activity's token to TaskFragmentCreationParams and tries to 762 // create a TaskFragment with the params. 763 final Activity activity = 764 startNewActivity(CompatChangeTests.NonResizeablePortraitActivity.class); 765 final IBinder ownerToken = getActivityToken(activity); 766 final TaskFragmentCreationParams params = 767 mTaskFragmentOrganizer.generateTaskFragParams(ownerToken); 768 final WindowContainerTransaction wct = new WindowContainerTransaction() 769 .createTaskFragment(params); 770 771 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_OPEN, 772 false /* shouldApplyIndependently */); 773 774 mTaskFragmentOrganizer.waitForTaskFragmentError(); 775 776 assertThat(mTaskFragmentOrganizer.getThrowable()) 777 .isInstanceOf(IllegalArgumentException.class); 778 } 779 780 /** 781 * Verifies that the TaskFragment hierarchy ops should still work while in lock task mode. 782 */ 783 @Test testApplyHierarchyOpsInLockTaskMode()784 public void testApplyHierarchyOpsInLockTaskMode() { 785 // Start an activity 786 final Activity activity = startNewActivity(); 787 788 try { 789 // Lock the task 790 runWithShellPermission(() -> { 791 mAtm.startSystemLockTaskMode(activity.getTaskId()); 792 }); 793 waitForOrFail("Task in app pinning mode", () -> { 794 return mAm.getLockTaskModeState() == LOCK_TASK_MODE_PINNED; 795 }); 796 797 // Create TaskFragment and reparent the activity 798 final IBinder ownerToken = getActivityToken(activity); 799 final TaskFragmentCreationParams params = 800 mTaskFragmentOrganizer.generateTaskFragParams(ownerToken); 801 final IBinder taskFragToken = params.getFragmentToken(); 802 WindowContainerTransaction wct = new WindowContainerTransaction() 803 .createTaskFragment(params) 804 .reparentActivityToTaskFragment(taskFragToken, ownerToken); 805 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_CHANGE, 806 false /* shouldApplyIndependently */); 807 808 // Verifies it works 809 mTaskFragmentOrganizer.waitForTaskFragmentCreated(); 810 TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken); 811 assertEquals(1, info.getActivities().size()); 812 813 // Delete the TaskFragment 814 wct = new WindowContainerTransaction().deleteTaskFragment(taskFragToken); 815 mTaskFragmentOrganizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_CLOSE, 816 false /* shouldApplyIndependently */); 817 818 // Verifies the TaskFragment NOT removed because the removal would also empty the task. 819 mTaskFragmentOrganizer.waitForTaskFragmentError(); 820 assertThat(mTaskFragmentOrganizer.getThrowable()).isInstanceOf( 821 IllegalStateException.class); 822 info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken); 823 assertEquals(1, info.getActivities().size()); 824 } finally { 825 runWithShellPermission(() -> { 826 mAtm.stopSystemLockTaskMode(); 827 }); 828 } 829 } 830 831 /** 832 * Verifies that {@link TaskFragmentOrganizer#applySyncTransaction} is not allowed. 833 */ 834 @Test 835 @ApiTest(apis = {"android.window.TaskFragmentOrganizer#applySyncTransaction"}) testApplySyncTransaction_disallowed()836 public void testApplySyncTransaction_disallowed() { 837 final Activity activity = startNewActivity(); 838 final TaskFragmentInfo taskFragmentInfo = createOrganizedTaskFragment( 839 mTaskFragmentOrganizer, activity); 840 841 final WindowContainerTransaction wct = new WindowContainerTransaction() 842 .deleteTaskFragment(taskFragmentInfo.getFragmentToken()); 843 final WindowContainerTransactionCallback callback = 844 new WindowContainerTransactionCallback() { 845 @Override 846 public void onTransactionReady(int id, @NonNull SurfaceControl.Transaction t) { 847 fail("Transaction shouldn't be executed"); 848 } 849 }; 850 851 assertThrows(SecurityException.class, 852 () -> mTaskFragmentOrganizer.applySyncTransaction(wct, callback)); 853 } 854 855 /** 856 * Creates and registers a {@link TaskFragmentOrganizer} that will be unregistered in 857 * {@link #tearDown()}. 858 */ registerNewOrganizer()859 private BasicTaskFragmentOrganizer registerNewOrganizer() { 860 final BasicTaskFragmentOrganizer organizer = new BasicTaskFragmentOrganizer(); 861 organizer.registerOrganizer(); 862 mOrganizers.add(organizer); 863 return organizer; 864 } 865 866 /** 867 * Registers a {@link TaskOrganizer} to get the {@link WindowContainerToken} of a Task. The 868 * organizer will be unregistered in {@link #tearDown()}. 869 */ getFirstTaskToken()870 private WindowContainerToken getFirstTaskToken() { 871 final List<TaskAppearedInfo> taskInfos = new ArrayList<>(); 872 // Register TaskOrganizer to obtain Task information. 873 NestedShellPermission.run(() -> { 874 mTaskOrganizer = new TaskOrganizer(); 875 taskInfos.addAll(mTaskOrganizer.registerOrganizer()); 876 }); 877 return taskInfos.get(0).getTaskInfo().getToken(); 878 } 879 880 /** 881 * Creates a TaskFragment organized by the given organizer. The TaskFragment will be removed 882 * when the organizer is unregistered. 883 */ createOrganizedTaskFragment( BasicTaskFragmentOrganizer organizer, Activity ownerActivity)884 private static TaskFragmentInfo createOrganizedTaskFragment( 885 BasicTaskFragmentOrganizer organizer, Activity ownerActivity) { 886 // Create a TaskFragment with a TaskFragmentOrganizer. 887 final TaskFragmentCreationParams params = organizer.generateTaskFragParams( 888 getActivityToken(ownerActivity)); 889 final WindowContainerTransaction wct = new WindowContainerTransaction() 890 .createTaskFragment(params); 891 organizer.applyTransaction(wct, TASK_FRAGMENT_TRANSIT_OPEN, 892 false /* shouldApplyIndependently */); 893 894 // Wait for TaskFragment's creation to obtain its info. 895 organizer.waitForTaskFragmentCreated(); 896 organizer.resetLatch(); 897 return organizer.getTaskFragmentInfo(params.getFragmentToken()); 898 } 899 } 900