Reset embedded TaskFragment override when entering PIP

When an embedded activity is entering PIP when it is the only activity
in the Task, the whole Task will enter PIP.

When this happens, we want to reset all organizer override config so
that the activity will get the correct config. We don't want to remove
the TaskFragment because we can use it to get the windowing mode change
in organizer.

Besides that, we will disallow any further organizer override to the
TaskFragment in PIP.

Bug: 225371112
Test: atest WmTests:RootWindowContainerTests
Test: atest WmTests:TaskFragmentTest
Test: atest WmTests:TaskFragmentOrganizerControllerTest
Change-Id: I03bd679b77f7ed8a65ba5fc59723b83a4254442c
Merged-In: I03bd679b77f7ed8a65ba5fc59723b83a4254442c
This commit is contained in:
Chris Li
2022-04-08 13:40:25 +08:00
parent 721634a9ec
commit 103a6dc7f7
10 changed files with 330 additions and 11 deletions

View File

@@ -2481,6 +2481,12 @@ class ActivityStarter {
if (inTaskFragment == null) {
inTaskFragment = TaskFragment.fromTaskFragmentToken(
mOptions.getLaunchTaskFragmentToken(), mService);
if (inTaskFragment != null && inTaskFragment.isEmbeddedTaskFragmentInPip()) {
// Do not start activity in TaskFragment in a PIP Task.
Slog.w(TAG, "Can not start activity in TaskFragment in PIP: "
+ inTaskFragment);
inTaskFragment = null;
}
}
}

View File

@@ -190,6 +190,14 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
* @see #mFullConfiguration
*/
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
updateRequestedOverrideConfiguration(overrideConfiguration);
// Update full configuration of this container and all its children.
final ConfigurationContainer parent = getParent();
onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
}
/** Updates override configuration without recalculate full config. */
void updateRequestedOverrideConfiguration(Configuration overrideConfiguration) {
// Pre-compute this here, so we don't need to go through the entire Configuration when
// writing to proto (which has significant cost if we write a lot of empty configurations).
mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
@@ -199,9 +207,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
&& diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
}
// Update full configuration of this container and all its children.
final ConfigurationContainer parent = getParent();
onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
}
/**

View File

@@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.res.Configuration.EMPTY;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -2005,7 +2006,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// of the activity entering PIP
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
final boolean singleActivity = task.getChildCount() == 1;
// TODO: Does it make sense to only count non-finishing activities?
final boolean singleActivity = task.getActivityCount() == 1;
final Task rootTask;
if (singleActivity) {
rootTask = task;
@@ -2086,6 +2088,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// TODO(task-org): Figure-out more structured way to do this long term.
r.setWindowingMode(intermediateWindowingMode);
r.mWaitForEnteringPinnedMode = true;
rootTask.forAllTaskFragments(tf -> {
// When the Task is entering picture-in-picture, we should clear all override from
// the client organizer, so the PIP activity can get the correct config from the
// Task, and prevent conflict with the PipTaskOrganizer.
if (tf.isOrganizedTaskFragment()) {
tf.resetAdjacentTaskFragment();
tf.updateRequestedOverrideConfiguration(EMPTY);
}
});
rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
// Set the launch bounds for launch-into-pip Activity on the root task.
if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {

View File

@@ -1380,6 +1380,14 @@ class Task extends TaskFragment {
return getActivity(ActivityRecord::canBeTopRunning);
}
int getActivityCount() {
final int[] activityCount = new int[1];
forAllActivities(ar -> {
activityCount[0]++;
});
return activityCount[0];
}
/**
* Return true if any activities in this task belongs to input uid.
*/

View File

@@ -339,7 +339,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
}
private void resetAdjacentTaskFragment() {
void resetAdjacentTaskFragment() {
// Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment.
if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
@@ -2317,6 +2317,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mMinHeight = minHeight;
}
/**
* Whether this is an embedded TaskFragment in PIP Task. We don't allow any client config
* override for such TaskFragment to prevent flight with PipTaskOrganizer.
*/
boolean isEmbeddedTaskFragmentInPip() {
return isOrganizedTaskFragment() && getTask() != null && getTask().inPinnedWindowingMode();
}
boolean shouldRemoveSelfOnLastChildRemoval() {
return !mCreatedByOrganizer || mIsRemovalRequested;
}

View File

@@ -2761,7 +2761,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
boolean canStartChangeTransition() {
return !mWmService.mDisableTransitionAnimation && mDisplayContent != null
&& getSurfaceControl() != null && !mDisplayContent.inTransition()
&& isVisible() && isVisibleRequested() && okToAnimate();
&& isVisible() && isVisibleRequested() && okToAnimate()
// Pip animation will be handled by PipTaskOrganizer.
&& !inPinnedWindowingMode() && getParent() != null
&& !getParent().inPinnedWindowingMode();
}
/**

View File

@@ -677,15 +677,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
final IBinder fragmentToken = hop.getContainer();
if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
if (tf == null) {
final Throwable exception = new IllegalArgumentException(
"Not allowed to operate with invalid fragment token");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
if (tf.isEmbeddedTaskFragmentInPip()) {
final Throwable exception = new IllegalArgumentException(
"Not allowed to start activity in PIP TaskFragment");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
final Intent activityIntent = hop.getActivityIntent();
final Bundle activityOptions = hop.getLaunchOptions();
final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
final int result = mService.getActivityStartController()
.startActivityInTaskFragment(tf, activityIntent, activityOptions,
hop.getCallingActivity(), caller.mUid, caller.mPid);
@@ -707,6 +713,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
if (parent.isEmbeddedTaskFragmentInPip()) {
final Throwable exception = new IllegalArgumentException(
"Not allowed to reparent activity to PIP TaskFragment");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
if (!parent.isAllowedToEmbedActivity(activity)) {
final Throwable exception = new SecurityException(
"The task fragment is not trusted to embed the given activity.");
@@ -730,6 +742,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
if (tf1.isEmbeddedTaskFragmentInPip()
|| (tf2 != null && tf2.isEmbeddedTaskFragmentInPip())) {
final Throwable exception = new IllegalArgumentException(
"Not allowed to set adjacent on TaskFragment in PIP Task");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
@@ -1092,6 +1111,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+ " organizer root1=" + root1 + " root2=" + root2);
}
if (root1.isEmbeddedTaskFragmentInPip() || root2.isEmbeddedTaskFragmentInPip()) {
Slog.e(TAG, "Attempt to set adjacent TaskFragment in PIP Task");
return 0;
}
root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether());
return TRANSACT_EFFECTS_LIFECYCLE;
}
@@ -1105,6 +1128,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
private int applyWindowContainerChange(WindowContainer wc,
WindowContainerTransaction.Change c) {
sanitizeWindowContainer(wc);
if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbeddedTaskFragmentInPip()) {
// No override from organizer for embedded TaskFragment in a PIP Task.
return 0;
}
int effects = applyChanges(wc, c);
@@ -1420,21 +1447,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return;
}
// The ownerActivity has to belong to the same app as the target Task.
if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
|| ownerActivity.getTask().effectiveUid != caller.mUid) {
final Task ownerTask = ownerActivity.getTask();
if (ownerTask.effectiveUid != ownerActivity.getUid()
|| ownerTask.effectiveUid != caller.mUid) {
final Throwable exception =
new SecurityException("Not allowed to operate with the ownerToken while "
+ "the root activity of the target task belong to the different app");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return;
}
if (ownerTask.inPinnedWindowingMode()) {
final Throwable exception = new IllegalArgumentException(
"Not allowed to create TaskFragment in PIP Task");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return;
}
final TaskFragment taskFragment = new TaskFragment(mService,
creationParams.getFragmentToken(), true /* createdByOrganizer */);
// Set task fragment organizer immediately, since it might have to be notified about further
// actions.
taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(),
ownerActivity.getUid(), ownerActivity.info.processName);
ownerActivity.getTask().addChild(taskFragment, POSITION_TOP);
ownerTask.addChild(taskFragment, POSITION_TOP);
taskFragment.setWindowingMode(creationParams.getWindowingMode());
taskFragment.setBounds(creationParams.getInitialBounds());
mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
@@ -1467,6 +1501,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
return;
}
}
if (newParentTF.isEmbeddedTaskFragmentInPip() || oldParent.isEmbeddedTaskFragmentInPip()) {
final Throwable exception = new SecurityException(
"Not allow to reparent in TaskFragment in PIP Task.");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return;
}
while (oldParent.hasChild()) {
oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP);
}
@@ -1482,6 +1522,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return 0;
}
if (taskFragment.isEmbeddedTaskFragmentInPip()) {
final Throwable exception = new IllegalArgumentException(
"Not allowed to delete TaskFragment in PIP Task");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return 0;
}
mLaunchTaskFragments.removeAt(index);
taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
return TRANSACT_EFFECTS_LIFECYCLE;

View File

@@ -300,6 +300,81 @@ public class RootWindowContainerTests extends WindowTestsBase {
assertTrue(firstActivity.mRequestForceTransition);
}
/**
* When there is only one activity in the Task, and the activity is requesting to enter PIP, the
* whole Task should enter PIP.
*/
@Test
public void testSingleActivityTaskEnterPip() {
final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setTask(fullscreenTask)
.build();
final Task task = activity.getTask();
// Move activity to pinned root task.
mRootWindowContainer.moveActivityToPinnedRootTask(activity,
null /* launchIntoPipHostActivity */, "test");
// Ensure a task has moved over.
ensureTaskPlacement(task, activity);
assertTrue(task.inPinnedWindowingMode());
}
/**
* When there is only one activity in the Task, and the activity is requesting to enter PIP, the
* whole Task should enter PIP even if the activity is in a TaskFragment.
*/
@Test
public void testSingleActivityInTaskFragmentEnterPip() {
final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(fullscreenTask)
.createActivityCount(1)
.build();
final ActivityRecord activity = taskFragment.getTopMostActivity();
final Task task = activity.getTask();
// Move activity to pinned root task.
mRootWindowContainer.moveActivityToPinnedRootTask(activity,
null /* launchIntoPipHostActivity */, "test");
// Ensure a task has moved over.
ensureTaskPlacement(task, activity);
assertTrue(task.inPinnedWindowingMode());
}
/**
* When there is one TaskFragment with two activities in the Task, the activity requests to
* enter PIP, that activity will be move to PIP root task.
*/
@Test
public void testMultipleActivitiesInTaskFragmentEnterPip() {
final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
.setParentTask(fullscreenTask)
.createActivityCount(2)
.build();
final ActivityRecord firstActivity = taskFragment.getTopMostActivity();
final ActivityRecord secondActivity = taskFragment.getBottomMostActivity();
// Move first activity to pinned root task.
mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity,
null /* launchIntoPipHostActivity */, "test");
final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea();
final Task pinnedRootTask = taskDisplayArea.getRootPinnedTask();
// Ensure a task has moved over.
ensureTaskPlacement(pinnedRootTask, firstActivity);
ensureTaskPlacement(fullscreenTask, secondActivity);
assertTrue(pinnedRootTask.inPinnedWindowingMode());
assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode());
}
private static void ensureTaskPlacement(Task task, ActivityRecord... activities) {
final ArrayList<ActivityRecord> taskActivities = new ArrayList<>();

View File

@@ -16,6 +16,9 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -28,6 +31,8 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
@@ -434,6 +439,107 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
}
@Test
public void testTaskFragmentInPip_startActivityInTaskFragment() {
setupTaskFragmentInPip();
final ActivityRecord activity = mTaskFragment.getTopMostActivity();
final IBinder errorToken = new Binder();
spyOn(mAtm.getActivityStartController());
spyOn(mAtm.mWindowOrganizerController);
// Not allow to start activity in a TaskFragment that is in a PIP Task.
mTransaction.startActivityInTaskFragment(
mFragmentToken, activity.token, new Intent(), null /* activityOptions */)
.setErrorCallbackToken(errorToken);
mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
verify(mAtm.getActivityStartController(), never()).startActivityInTaskFragment(any(), any(),
any(), any(), anyInt(), anyInt());
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(errorToken), any(IllegalArgumentException.class));
}
@Test
public void testTaskFragmentInPip_reparentActivityToTaskFragment() {
setupTaskFragmentInPip();
final ActivityRecord activity = createActivityRecord(mDisplayContent);
final IBinder errorToken = new Binder();
spyOn(mAtm.mWindowOrganizerController);
// Not allow to reparent activity to a TaskFragment that is in a PIP Task.
mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token)
.setErrorCallbackToken(errorToken);
mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(errorToken), any(IllegalArgumentException.class));
assertNull(activity.getOrganizedTaskFragment());
}
@Test
public void testTaskFragmentInPip_setAdjacentTaskFragment() {
setupTaskFragmentInPip();
final IBinder errorToken = new Binder();
spyOn(mAtm.mWindowOrganizerController);
// Not allow to set adjacent on a TaskFragment that is in a PIP Task.
mTransaction.setAdjacentTaskFragments(mFragmentToken, null /* fragmentToken2 */,
null /* options */)
.setErrorCallbackToken(errorToken);
mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(errorToken), any(IllegalArgumentException.class));
verify(mTaskFragment, never()).setAdjacentTaskFragment(any(), anyBoolean());
}
@Test
public void testTaskFragmentInPip_createTaskFragment() {
mController.registerOrganizer(mIOrganizer);
final Task pipTask = createTask(mDisplayContent, WINDOWING_MODE_PINNED,
ACTIVITY_TYPE_STANDARD);
final ActivityRecord activity = createActivityRecord(pipTask);
final IBinder fragmentToken = new Binder();
final IBinder errorToken = new Binder();
spyOn(mAtm.mWindowOrganizerController);
// Not allow to create TaskFragment in a PIP Task.
createTaskFragmentFromOrganizer(mTransaction, activity, fragmentToken);
mTransaction.setErrorCallbackToken(errorToken);
mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(errorToken), any(IllegalArgumentException.class));
assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
}
@Test
public void testTaskFragmentInPip_deleteTaskFragment() {
setupTaskFragmentInPip();
final IBinder errorToken = new Binder();
spyOn(mAtm.mWindowOrganizerController);
// Not allow to delete a TaskFragment that is in a PIP Task.
mTransaction.deleteTaskFragment(mFragmentWindowToken)
.setErrorCallbackToken(errorToken);
mAtm.mWindowOrganizerController.applyTransaction(mTransaction);
verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer),
eq(errorToken), any(IllegalArgumentException.class));
assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(mFragmentToken));
}
@Test
public void testTaskFragmentInPip_setConfig() {
setupTaskFragmentInPip();
spyOn(mAtm.mWindowOrganizerController);
// Set bounds is ignored on a TaskFragment that is in a PIP Task.
mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
verify(mTaskFragment, never()).setBounds(any());
}
@Test
public void testDeferPendingTaskFragmentEventsOfInvisibleTask() {
// Task - TaskFragment - Activity.
@@ -643,4 +749,20 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
fail();
}
}
/** Setups an embedded TaskFragment in a PIP Task. */
private void setupTaskFragmentInPip() {
mOrganizer.applyTransaction(mTransaction);
mController.registerOrganizer(mIOrganizer);
mTaskFragment = new TaskFragmentBuilder(mAtm)
.setCreateParentTask()
.setFragmentToken(mFragmentToken)
.setOrganizer(mOrganizer)
.createActivityCount(1)
.build();
mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
mAtm.mWindowOrganizerController.mLaunchTaskFragments
.put(mFragmentToken, mTaskFragment);
mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED);
}
}

View File

@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
@@ -31,6 +32,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.clearInvocations;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
@@ -206,6 +208,39 @@ public class TaskFragmentTest extends WindowTestsBase {
assertFalse(secondaryActivity.supportsEnterPipOnTaskSwitch);
}
@Test
public void testEmbeddedTaskFragmentEnterPip_resetOrganizerOverrideConfig() {
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
.setOrganizer(mOrganizer)
.setFragmentToken(new Binder())
.setCreateParentTask()
.createActivityCount(1)
.build();
final Task task = taskFragment.getTask();
final ActivityRecord activity = taskFragment.getTopMostActivity();
final Rect taskFragmentBounds = new Rect(0, 0, 300, 1000);
task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
taskFragment.setBounds(taskFragmentBounds);
assertEquals(taskFragmentBounds, activity.getBounds());
assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode());
// Move activity to pinned root task.
mRootWindowContainer.moveActivityToPinnedRootTask(activity,
null /* launchIntoPipHostActivity */, "test");
// Ensure taskFragment requested config is reset.
assertEquals(taskFragment, activity.getOrganizedTaskFragment());
assertEquals(task, activity.getTask());
assertTrue(task.inPinnedWindowingMode());
assertTrue(taskFragment.inPinnedWindowingMode());
final Rect taskBounds = task.getBounds();
assertEquals(taskBounds, taskFragment.getBounds());
assertEquals(taskBounds, activity.getBounds());
assertEquals(Configuration.EMPTY, taskFragment.getRequestedOverrideConfiguration());
}
@Test
public void testActivityHasOverlayOverUntrustedModeEmbedded() {
final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,