Merge changes from topic "presubmit-am-dec5ac638a7e44c68413c7f552c67cd0" into tm-mainline-prod
* changes:
[automerge] Reset embedded TaskFragment override when entering PIP 2p: 103a6dc7f7
Reset embedded TaskFragment override when entering PIP
This commit is contained in:
@@ -2481,6 +2481,12 @@ class ActivityStarter {
|
|||||||
if (inTaskFragment == null) {
|
if (inTaskFragment == null) {
|
||||||
inTaskFragment = TaskFragment.fromTaskFragmentToken(
|
inTaskFragment = TaskFragment.fromTaskFragmentToken(
|
||||||
mOptions.getLaunchTaskFragmentToken(), mService);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -190,6 +190,14 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
|
|||||||
* @see #mFullConfiguration
|
* @see #mFullConfiguration
|
||||||
*/
|
*/
|
||||||
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
|
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
|
// 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).
|
// writing to proto (which has significant cost if we write a lot of empty configurations).
|
||||||
mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
|
mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
|
||||||
@@ -199,9 +207,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
|
|||||||
&& diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
|
&& diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
|
||||||
mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||||
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
|
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
|
||||||
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
|
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.os.Trace.TRACE_TAG_WINDOW_MANAGER;
|
||||||
import static android.view.Display.DEFAULT_DISPLAY;
|
import static android.view.Display.DEFAULT_DISPLAY;
|
||||||
import static android.view.Display.INVALID_DISPLAY;
|
import static android.view.Display.INVALID_DISPLAY;
|
||||||
@@ -2005,7 +2006,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
|||||||
// of the activity entering PIP
|
// of the activity entering PIP
|
||||||
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE);
|
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;
|
final Task rootTask;
|
||||||
if (singleActivity) {
|
if (singleActivity) {
|
||||||
rootTask = task;
|
rootTask = task;
|
||||||
@@ -2086,6 +2088,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
|
|||||||
// TODO(task-org): Figure-out more structured way to do this long term.
|
// TODO(task-org): Figure-out more structured way to do this long term.
|
||||||
r.setWindowingMode(intermediateWindowingMode);
|
r.setWindowingMode(intermediateWindowingMode);
|
||||||
r.mWaitForEnteringPinnedMode = true;
|
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);
|
rootTask.setWindowingMode(WINDOWING_MODE_PINNED);
|
||||||
// Set the launch bounds for launch-into-pip Activity on the root task.
|
// Set the launch bounds for launch-into-pip Activity on the root task.
|
||||||
if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {
|
if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) {
|
||||||
|
|||||||
@@ -1380,6 +1380,14 @@ class Task extends TaskFragment {
|
|||||||
return getActivity(ActivityRecord::canBeTopRunning);
|
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.
|
* Return true if any activities in this task belongs to input uid.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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.
|
// Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment.
|
||||||
if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
|
if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
|
||||||
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
|
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
|
||||||
@@ -2317,6 +2317,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
|||||||
mMinHeight = minHeight;
|
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() {
|
boolean shouldRemoveSelfOnLastChildRemoval() {
|
||||||
return !mCreatedByOrganizer || mIsRemovalRequested;
|
return !mCreatedByOrganizer || mIsRemovalRequested;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2761,7 +2761,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
|
|||||||
boolean canStartChangeTransition() {
|
boolean canStartChangeTransition() {
|
||||||
return !mWmService.mDisableTransitionAnimation && mDisplayContent != null
|
return !mWmService.mDisableTransitionAnimation && mDisplayContent != null
|
||||||
&& getSurfaceControl() != null && !mDisplayContent.inTransition()
|
&& getSurfaceControl() != null && !mDisplayContent.inTransition()
|
||||||
&& isVisible() && isVisibleRequested() && okToAnimate();
|
&& isVisible() && isVisibleRequested() && okToAnimate()
|
||||||
|
// Pip animation will be handled by PipTaskOrganizer.
|
||||||
|
&& !inPinnedWindowingMode() && getParent() != null
|
||||||
|
&& !getParent().inPinnedWindowingMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -677,15 +677,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
}
|
}
|
||||||
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
|
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
|
||||||
final IBinder fragmentToken = hop.getContainer();
|
final IBinder fragmentToken = hop.getContainer();
|
||||||
if (!mLaunchTaskFragments.containsKey(fragmentToken)) {
|
final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
|
||||||
|
if (tf == null) {
|
||||||
final Throwable exception = new IllegalArgumentException(
|
final Throwable exception = new IllegalArgumentException(
|
||||||
"Not allowed to operate with invalid fragment token");
|
"Not allowed to operate with invalid fragment token");
|
||||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||||
break;
|
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 Intent activityIntent = hop.getActivityIntent();
|
||||||
final Bundle activityOptions = hop.getLaunchOptions();
|
final Bundle activityOptions = hop.getLaunchOptions();
|
||||||
final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken);
|
|
||||||
final int result = mService.getActivityStartController()
|
final int result = mService.getActivityStartController()
|
||||||
.startActivityInTaskFragment(tf, activityIntent, activityOptions,
|
.startActivityInTaskFragment(tf, activityIntent, activityOptions,
|
||||||
hop.getCallingActivity(), caller.mUid, caller.mPid);
|
hop.getCallingActivity(), caller.mUid, caller.mPid);
|
||||||
@@ -707,6 +713,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||||
break;
|
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)) {
|
if (!parent.isAllowedToEmbedActivity(activity)) {
|
||||||
final Throwable exception = new SecurityException(
|
final Throwable exception = new SecurityException(
|
||||||
"The task fragment is not trusted to embed the given activity.");
|
"The task fragment is not trusted to embed the given activity.");
|
||||||
@@ -730,6 +742,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||||
break;
|
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 */);
|
tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */);
|
||||||
effects |= TRANSACT_EFFECTS_LIFECYCLE;
|
effects |= TRANSACT_EFFECTS_LIFECYCLE;
|
||||||
|
|
||||||
@@ -1092,6 +1111,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
|
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
|
||||||
+ " organizer root1=" + root1 + " root2=" + root2);
|
+ " 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());
|
root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether());
|
||||||
return TRANSACT_EFFECTS_LIFECYCLE;
|
return TRANSACT_EFFECTS_LIFECYCLE;
|
||||||
}
|
}
|
||||||
@@ -1105,6 +1128,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
private int applyWindowContainerChange(WindowContainer wc,
|
private int applyWindowContainerChange(WindowContainer wc,
|
||||||
WindowContainerTransaction.Change c) {
|
WindowContainerTransaction.Change c) {
|
||||||
sanitizeWindowContainer(wc);
|
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);
|
int effects = applyChanges(wc, c);
|
||||||
|
|
||||||
@@ -1420,21 +1447,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// The ownerActivity has to belong to the same app as the target Task.
|
// The ownerActivity has to belong to the same app as the target Task.
|
||||||
if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
|
final Task ownerTask = ownerActivity.getTask();
|
||||||
|| ownerActivity.getTask().effectiveUid != caller.mUid) {
|
if (ownerTask.effectiveUid != ownerActivity.getUid()
|
||||||
|
|| ownerTask.effectiveUid != caller.mUid) {
|
||||||
final Throwable exception =
|
final Throwable exception =
|
||||||
new SecurityException("Not allowed to operate with the ownerToken while "
|
new SecurityException("Not allowed to operate with the ownerToken while "
|
||||||
+ "the root activity of the target task belong to the different app");
|
+ "the root activity of the target task belong to the different app");
|
||||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||||
return;
|
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,
|
final TaskFragment taskFragment = new TaskFragment(mService,
|
||||||
creationParams.getFragmentToken(), true /* createdByOrganizer */);
|
creationParams.getFragmentToken(), true /* createdByOrganizer */);
|
||||||
// Set task fragment organizer immediately, since it might have to be notified about further
|
// Set task fragment organizer immediately, since it might have to be notified about further
|
||||||
// actions.
|
// actions.
|
||||||
taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(),
|
taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(),
|
||||||
ownerActivity.getUid(), ownerActivity.info.processName);
|
ownerActivity.getUid(), ownerActivity.info.processName);
|
||||||
ownerActivity.getTask().addChild(taskFragment, POSITION_TOP);
|
ownerTask.addChild(taskFragment, POSITION_TOP);
|
||||||
taskFragment.setWindowingMode(creationParams.getWindowingMode());
|
taskFragment.setWindowingMode(creationParams.getWindowingMode());
|
||||||
taskFragment.setBounds(creationParams.getInitialBounds());
|
taskFragment.setBounds(creationParams.getInitialBounds());
|
||||||
mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
|
mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
|
||||||
@@ -1467,6 +1501,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
return;
|
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()) {
|
while (oldParent.hasChild()) {
|
||||||
oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP);
|
oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP);
|
||||||
}
|
}
|
||||||
@@ -1482,6 +1522,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||||
return 0;
|
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);
|
mLaunchTaskFragments.removeAt(index);
|
||||||
taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
|
taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
|
||||||
return TRANSACT_EFFECTS_LIFECYCLE;
|
return TRANSACT_EFFECTS_LIFECYCLE;
|
||||||
|
|||||||
@@ -300,6 +300,81 @@ public class RootWindowContainerTests extends WindowTestsBase {
|
|||||||
assertTrue(firstActivity.mRequestForceTransition);
|
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) {
|
private static void ensureTaskPlacement(Task task, ActivityRecord... activities) {
|
||||||
final ArrayList<ActivityRecord> taskActivities = new ArrayList<>();
|
final ArrayList<ActivityRecord> taskActivities = new ArrayList<>();
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
|
|
||||||
package com.android.server.wm;
|
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.doReturn;
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||||
import static com.android.server.wm.WindowContainer.POSITION_TOP;
|
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.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
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.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
@@ -434,6 +439,107 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
|||||||
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
|
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
|
@Test
|
||||||
public void testDeferPendingTaskFragmentEventsOfInvisibleTask() {
|
public void testDeferPendingTaskFragmentEventsOfInvisibleTask() {
|
||||||
// Task - TaskFragment - Activity.
|
// Task - TaskFragment - Activity.
|
||||||
@@ -643,4 +749,20 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
|||||||
fail();
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.server.wm;
|
package com.android.server.wm;
|
||||||
|
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
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 android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||||
|
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
|
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.junit.Assert.assertTrue;
|
||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
|
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.platform.test.annotations.Presubmit;
|
import android.platform.test.annotations.Presubmit;
|
||||||
@@ -206,6 +208,39 @@ public class TaskFragmentTest extends WindowTestsBase {
|
|||||||
assertFalse(secondaryActivity.supportsEnterPipOnTaskSwitch);
|
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
|
@Test
|
||||||
public void testActivityHasOverlayOverUntrustedModeEmbedded() {
|
public void testActivityHasOverlayOverUntrustedModeEmbedded() {
|
||||||
final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
|
final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
|
||||||
|
|||||||
Reference in New Issue
Block a user