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