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