• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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