Merge "Allow TaskFragmentOrganizer to apply WCT without permission" into sc-v2-dev
This commit is contained in:
@@ -3256,8 +3256,8 @@ package android.window {
|
||||
|
||||
public class WindowOrganizer {
|
||||
ctor public WindowOrganizer();
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
|
||||
method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void applyTransaction(@NonNull android.window.WindowContainerTransaction);
|
||||
method @RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true) public int applySyncTransaction(@NonNull android.window.WindowContainerTransaction, @NonNull android.window.WindowContainerTransactionCallback);
|
||||
method @RequiresPermission(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true) public void applyTransaction(@NonNull android.window.WindowContainerTransaction);
|
||||
}
|
||||
|
||||
@UiContext public abstract class WindowProviderService extends android.app.Service {
|
||||
|
||||
@@ -265,6 +265,7 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
|
||||
}
|
||||
};
|
||||
|
||||
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
|
||||
private IDisplayAreaOrganizerController getController() {
|
||||
try {
|
||||
return getWindowOrganizerController().getDisplayAreaOrganizerController();
|
||||
@@ -272,5 +273,4 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -120,6 +120,19 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
|
||||
public void onTaskFragmentError(
|
||||
@NonNull IBinder errorCallbackToken, @NonNull Throwable exception) {}
|
||||
|
||||
@Override
|
||||
public void applyTransaction(@NonNull WindowContainerTransaction t) {
|
||||
t.setTaskFragmentOrganizer(mInterface);
|
||||
super.applyTransaction(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int applySyncTransaction(@NonNull WindowContainerTransaction t,
|
||||
@NonNull WindowContainerTransactionCallback callback) {
|
||||
t.setTaskFragmentOrganizer(mInterface);
|
||||
return super.applySyncTransaction(t, callback);
|
||||
}
|
||||
|
||||
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
|
||||
@Override
|
||||
public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentInfo) {
|
||||
|
||||
@@ -290,6 +290,7 @@ public class TaskOrganizer extends WindowOrganizer {
|
||||
}
|
||||
};
|
||||
|
||||
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
|
||||
private ITaskOrganizerController getController() {
|
||||
try {
|
||||
return getWindowOrganizerController().getTaskOrganizerController();
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a collection of operations on some WindowContainers that should be applied all at
|
||||
@@ -52,12 +53,16 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
@Nullable
|
||||
private IBinder mErrorCallbackToken;
|
||||
|
||||
@Nullable
|
||||
private ITaskFragmentOrganizer mTaskFragmentOrganizer;
|
||||
|
||||
public WindowContainerTransaction() {}
|
||||
|
||||
private WindowContainerTransaction(Parcel in) {
|
||||
in.readMap(mChanges, null /* loader */);
|
||||
in.readList(mHierarchyOps, null /* loader */);
|
||||
mErrorCallbackToken = in.readStrongBinder();
|
||||
mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder());
|
||||
}
|
||||
|
||||
private Change getOrCreateChange(IBinder token) {
|
||||
@@ -473,7 +478,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
final HierarchyOp hierarchyOp =
|
||||
new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN)
|
||||
.setContainer(oldParent.asBinder())
|
||||
.setReparentContainer(newParent.asBinder())
|
||||
.setReparentContainer(newParent != null ? newParent.asBinder() : null)
|
||||
.build();
|
||||
mHierarchyOps.add(hierarchyOp);
|
||||
return this;
|
||||
@@ -496,6 +501,23 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link TaskFragmentOrganizer} that applies this {@link WindowContainerTransaction}.
|
||||
* When this is set, the server side will not check for the permission of
|
||||
* {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, but will ensure this WCT only
|
||||
* contains operations that are allowed for this organizer, such as modifying TaskFragments that
|
||||
* are organized by this organizer.
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
WindowContainerTransaction setTaskFragmentOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
|
||||
if (mTaskFragmentOrganizer != null) {
|
||||
throw new IllegalStateException("Can't set multiple organizers for one transaction.");
|
||||
}
|
||||
mTaskFragmentOrganizer = organizer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges another WCT into this one.
|
||||
* @param transfer When true, this will transfer everything from other potentially leaving
|
||||
@@ -519,7 +541,17 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
}
|
||||
if (mErrorCallbackToken != null && other.mErrorCallbackToken != null && mErrorCallbackToken
|
||||
!= other.mErrorCallbackToken) {
|
||||
throw new IllegalArgumentException("Can't merge two WCT with different error token");
|
||||
throw new IllegalArgumentException("Can't merge two WCTs with different error token");
|
||||
}
|
||||
final IBinder taskFragmentOrganizerAsBinder = mTaskFragmentOrganizer != null
|
||||
? mTaskFragmentOrganizer.asBinder()
|
||||
: null;
|
||||
final IBinder otherTaskFragmentOrganizerAsBinder = other.mTaskFragmentOrganizer != null
|
||||
? other.mTaskFragmentOrganizer.asBinder()
|
||||
: null;
|
||||
if (!Objects.equals(taskFragmentOrganizerAsBinder, otherTaskFragmentOrganizerAsBinder)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Can't merge two WCTs from different TaskFragmentOrganizers");
|
||||
}
|
||||
mErrorCallbackToken = mErrorCallbackToken != null
|
||||
? mErrorCallbackToken
|
||||
@@ -547,11 +579,21 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
return mErrorCallbackToken;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@Nullable
|
||||
public ITaskFragmentOrganizer getTaskFragmentOrganizer() {
|
||||
return mTaskFragmentOrganizer;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public String toString() {
|
||||
return "WindowContainerTransaction { changes = " + mChanges + " hops = " + mHierarchyOps
|
||||
+ " errorCallbackToken=" + mErrorCallbackToken + " }";
|
||||
return "WindowContainerTransaction {"
|
||||
+ " changes = " + mChanges
|
||||
+ " hops = " + mHierarchyOps
|
||||
+ " errorCallbackToken=" + mErrorCallbackToken
|
||||
+ " taskFragmentOrganizer=" + mTaskFragmentOrganizer
|
||||
+ " }";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -560,6 +602,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
||||
dest.writeMap(mChanges);
|
||||
dest.writeList(mHierarchyOps);
|
||||
dest.writeStrongBinder(mErrorCallbackToken);
|
||||
dest.writeStrongInterface(mTaskFragmentOrganizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -36,9 +36,16 @@ public class WindowOrganizer {
|
||||
|
||||
/**
|
||||
* Apply multiple WindowContainer operations at once.
|
||||
*
|
||||
* Note that using this API requires the caller to hold
|
||||
* {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, unless the caller is using
|
||||
* {@link TaskFragmentOrganizer}, in which case it is allowed to change TaskFragment that is
|
||||
* created by itself.
|
||||
*
|
||||
* @param t The transaction to apply.
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
|
||||
@RequiresPermission(value = android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
|
||||
conditional = true)
|
||||
public void applyTransaction(@NonNull WindowContainerTransaction t) {
|
||||
try {
|
||||
if (!t.isEmpty()) {
|
||||
@@ -51,6 +58,12 @@ public class WindowOrganizer {
|
||||
|
||||
/**
|
||||
* Apply multiple WindowContainer operations at once.
|
||||
*
|
||||
* Note that using this API requires the caller to hold
|
||||
* {@link android.Manifest.permission#MANAGE_ACTIVITY_TASKS}, unless the caller is using
|
||||
* {@link TaskFragmentOrganizer}, in which case it is allowed to change TaskFragment that is
|
||||
* created by itself.
|
||||
*
|
||||
* @param t The transaction to apply.
|
||||
* @param callback This transaction will use the synchronization scheme described in
|
||||
* BLASTSyncEngine.java. The SurfaceControl transaction containing the effects of this
|
||||
@@ -58,7 +71,8 @@ public class WindowOrganizer {
|
||||
* @return An ID for the sync operation which will later be passed to transactionReady callback.
|
||||
* This lets the caller differentiate overlapping sync operations.
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
|
||||
@RequiresPermission(value = android.Manifest.permission.MANAGE_ACTIVITY_TASKS,
|
||||
conditional = true)
|
||||
public int applySyncTransaction(@NonNull WindowContainerTransaction t,
|
||||
@NonNull WindowContainerTransactionCallback callback) {
|
||||
try {
|
||||
@@ -123,7 +137,6 @@ public class WindowOrganizer {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
|
||||
IWindowOrganizerController getWindowOrganizerController() {
|
||||
return IWindowOrganizerControllerSingleton.get();
|
||||
}
|
||||
|
||||
@@ -3445,7 +3445,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
|
||||
|
||||
@Override
|
||||
public IWindowOrganizerController getWindowOrganizerController() {
|
||||
enforceTaskPermission("getWindowOrganizerController()");
|
||||
return mWindowOrganizerController;
|
||||
}
|
||||
|
||||
|
||||
@@ -289,6 +289,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
mTaskFragmentOrganizerPid = pid;
|
||||
}
|
||||
|
||||
/** Whether this TaskFragment is organized by the given {@code organizer}. */
|
||||
boolean hasTaskFragmentOrganizer(ITaskFragmentOrganizer organizer) {
|
||||
return organizer != null && mTaskFragmentOrganizer != null
|
||||
&& organizer.asBinder().equals(mTaskFragmentOrganizer.asBinder());
|
||||
}
|
||||
|
||||
TaskFragment getAdjacentTaskFragment() {
|
||||
return mAdjacentTaskFragment;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.IDisplayAreaOrganizerController;
|
||||
import android.window.ITaskFragmentOrganizer;
|
||||
import android.window.ITaskFragmentOrganizerController;
|
||||
import android.window.ITaskOrganizerController;
|
||||
import android.window.ITransitionPlayer;
|
||||
@@ -110,7 +111,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
final TransitionController mTransitionController;
|
||||
/**
|
||||
* A Map which manages the relationship between
|
||||
* {@link TaskFragmentCreationParams.mFragmentToken fragmentToken} and {@link TaskFragment}
|
||||
* {@link TaskFragmentCreationParams#getFragmentToken()} and {@link TaskFragment}
|
||||
*/
|
||||
private final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
|
||||
|
||||
@@ -139,10 +140,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
|
||||
@Override
|
||||
public void applyTransaction(WindowContainerTransaction t) {
|
||||
enforceTaskPermission("applyTransaction()");
|
||||
if (t == null) {
|
||||
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
|
||||
throw new IllegalArgumentException("Null transaction passed to applyTransaction");
|
||||
}
|
||||
enforceTaskPermission("applyTransaction()", t);
|
||||
final CallerInfo caller = new CallerInfo();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
@@ -157,10 +158,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
@Override
|
||||
public int applySyncTransaction(WindowContainerTransaction t,
|
||||
IWindowContainerTransactionCallback callback) {
|
||||
enforceTaskPermission("applySyncTransaction()");
|
||||
if (t == null) {
|
||||
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
|
||||
}
|
||||
enforceTaskPermission("applySyncTransaction()", t);
|
||||
final CallerInfo caller = new CallerInfo();
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
@@ -620,7 +621,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
|
||||
final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
|
||||
final WindowContainer newParent = WindowContainer.fromBinder(hop.getNewParent());
|
||||
final WindowContainer newParent = hop.getNewParent() != null
|
||||
? WindowContainer.fromBinder(hop.getNewParent())
|
||||
: null;
|
||||
if (oldParent == null || !oldParent.isAttached()) {
|
||||
Slog.e(TAG, "Attempt to operate on unknown or detached container: "
|
||||
+ oldParent);
|
||||
@@ -906,6 +909,102 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
mService.enforceTaskPermission(func);
|
||||
}
|
||||
|
||||
private void enforceTaskPermission(String func, WindowContainerTransaction t) {
|
||||
if (t == null || t.getTaskFragmentOrganizer() == null) {
|
||||
enforceTaskPermission(func);
|
||||
return;
|
||||
}
|
||||
|
||||
// Apps may not have the permission to manage Tasks, but we are allowing apps to manage
|
||||
// TaskFragments belonging to their own Task.
|
||||
enforceOperationsAllowedForTaskFragmentOrganizer(func, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that the transaction only contains operations that are allowed for the
|
||||
* {@link WindowContainerTransaction#getTaskFragmentOrganizer()}.
|
||||
*/
|
||||
private void enforceOperationsAllowedForTaskFragmentOrganizer(
|
||||
String func, WindowContainerTransaction t) {
|
||||
final ITaskFragmentOrganizer organizer = t.getTaskFragmentOrganizer();
|
||||
|
||||
// Configuration changes
|
||||
final Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
|
||||
t.getChanges().entrySet().iterator();
|
||||
while (entries.hasNext()) {
|
||||
final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
|
||||
// Only allow to apply changes to TaskFragment that is created by this organizer.
|
||||
enforceTaskFragmentOrganized(func, WindowContainer.fromBinder(entry.getKey()),
|
||||
organizer);
|
||||
}
|
||||
|
||||
// Hierarchy changes
|
||||
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
|
||||
for (int i = hops.size() - 1; i >= 0; i--) {
|
||||
final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
|
||||
final int type = hop.getType();
|
||||
// Check for each type of the operations that are allowed for TaskFragmentOrganizer.
|
||||
switch (type) {
|
||||
case HIERARCHY_OP_TYPE_REORDER:
|
||||
case HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT:
|
||||
enforceTaskFragmentOrganized(func,
|
||||
WindowContainer.fromBinder(hop.getContainer()), organizer);
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
|
||||
enforceTaskFragmentOrganized(func,
|
||||
WindowContainer.fromBinder(hop.getContainer()), organizer);
|
||||
enforceTaskFragmentOrganized(func,
|
||||
WindowContainer.fromBinder(hop.getAdjacentRoot()),
|
||||
organizer);
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT:
|
||||
// We are allowing organizer to create TaskFragment. We will check the
|
||||
// ownerToken in #createTaskFragment, and trigger error callback if that is not
|
||||
// valid.
|
||||
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT:
|
||||
case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT:
|
||||
// We are allowing organizer to start/reparent activity to a TaskFragment it
|
||||
// created. Nothing to check here because the TaskFragment may not be created
|
||||
// yet, but will be created in the same transaction.
|
||||
break;
|
||||
case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
|
||||
enforceTaskFragmentOrganized(func,
|
||||
WindowContainer.fromBinder(hop.getContainer()), organizer);
|
||||
if (hop.getNewParent() != null) {
|
||||
enforceTaskFragmentOrganized(func,
|
||||
WindowContainer.fromBinder(hop.getNewParent()),
|
||||
organizer);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Other types of hierarchy changes are not allowed.
|
||||
String msg = "Permission Denial: " + func + " from pid="
|
||||
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
|
||||
+ " trying to apply a hierarchy change that is not allowed for"
|
||||
+ " TaskFragmentOrganizer=" + organizer;
|
||||
Slog.w(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceTaskFragmentOrganized(String func, @Nullable WindowContainer wc,
|
||||
ITaskFragmentOrganizer organizer) {
|
||||
if (wc == null) {
|
||||
Slog.e(TAG, "Attempt to operate on window that no longer exists");
|
||||
return;
|
||||
}
|
||||
|
||||
final TaskFragment tf = wc.asTaskFragment();
|
||||
if (tf == null || !tf.hasTaskFragmentOrganizer(organizer)) {
|
||||
String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid()
|
||||
+ ", uid=" + Binder.getCallingUid() + " trying to modify window container not"
|
||||
+ " belonging to the TaskFragmentOrganizer=" + organizer;
|
||||
Slog.w(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams) {
|
||||
final ActivityRecord ownerActivity =
|
||||
ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
|
||||
|
||||
@@ -22,6 +22,8 @@ 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.testing.Assert.assertThrows;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
@@ -29,7 +31,9 @@ import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
@@ -37,8 +41,12 @@ import android.os.RemoteException;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.ITaskFragmentOrganizer;
|
||||
import android.window.TaskFragmentCreationParams;
|
||||
import android.window.TaskFragmentInfo;
|
||||
import android.window.TaskFragmentOrganizer;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
import android.window.WindowContainerTransactionCallback;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
@@ -61,18 +69,24 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
private TaskFragment mTaskFragment;
|
||||
private TaskFragmentInfo mTaskFragmentInfo;
|
||||
private IBinder mFragmentToken;
|
||||
private WindowContainerTransaction mTransaction;
|
||||
private WindowContainerToken mFragmentWindowToken;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mController = mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
|
||||
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
|
||||
mIOrganizer = mOrganizer.getIOrganizer();
|
||||
mTaskFragment = mock(TaskFragment.class);
|
||||
mTaskFragmentInfo = mock(TaskFragmentInfo.class);
|
||||
mFragmentToken = new Binder();
|
||||
mTaskFragment =
|
||||
new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
|
||||
mTransaction = new WindowContainerTransaction();
|
||||
mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
|
||||
|
||||
spyOn(mController);
|
||||
spyOn(mOrganizer);
|
||||
spyOn(mTaskFragment);
|
||||
doReturn(mIOrganizer).when(mTaskFragment).getTaskFragmentOrganizer();
|
||||
doReturn(mTaskFragmentInfo).when(mTaskFragment).getTaskFragmentInfo();
|
||||
doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl();
|
||||
@@ -190,4 +204,156 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
|
||||
verify(mOrganizer).onTaskFragmentError(eq(errorCallbackToken), eq(exception));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowContainerTransaction_setTaskFragmentOrganizer() {
|
||||
mOrganizer.applyTransaction(mTransaction);
|
||||
|
||||
assertEquals(mIOrganizer, mTransaction.getTaskFragmentOrganizer());
|
||||
|
||||
mTransaction = new WindowContainerTransaction();
|
||||
mOrganizer.applySyncTransaction(
|
||||
mTransaction, mock(WindowContainerTransactionCallback.class));
|
||||
|
||||
assertEquals(mIOrganizer, mTransaction.getTaskFragmentOrganizer());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyTransaction_enforceConfigurationChangeOnOrganizedTaskFragment()
|
||||
throws RemoteException {
|
||||
mOrganizer.applyTransaction(mTransaction);
|
||||
|
||||
// Throw exception if the transaction is trying to change a window that is not organized by
|
||||
// the organizer.
|
||||
mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
|
||||
|
||||
assertThrows(SecurityException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
|
||||
// Allow transaction to change a TaskFragment created by the organizer.
|
||||
mTaskFragment.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
|
||||
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyTransaction_enforceHierarchyChange_reorder() throws RemoteException {
|
||||
mOrganizer.applyTransaction(mTransaction);
|
||||
|
||||
// Throw exception if the transaction is trying to change a window that is not organized by
|
||||
// the organizer.
|
||||
mTransaction.reorder(mFragmentWindowToken, true /* onTop */);
|
||||
|
||||
assertThrows(SecurityException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
|
||||
// Allow transaction to change a TaskFragment created by the organizer.
|
||||
mTaskFragment.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
|
||||
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyTransaction_enforceHierarchyChange_deleteTaskFragment()
|
||||
throws RemoteException {
|
||||
mOrganizer.applyTransaction(mTransaction);
|
||||
|
||||
// Throw exception if the transaction is trying to change a window that is not organized by
|
||||
// the organizer.
|
||||
mTransaction.deleteTaskFragment(mFragmentWindowToken);
|
||||
|
||||
assertThrows(SecurityException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
|
||||
// Allow transaction to change a TaskFragment created by the organizer.
|
||||
mTaskFragment.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
|
||||
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyTransaction_enforceHierarchyChange_setAdjacentRoots()
|
||||
throws RemoteException {
|
||||
final TaskFragment taskFragment2 =
|
||||
new TaskFragment(mAtm, new Binder(), true /* createdByOrganizer */);
|
||||
final WindowContainerToken token2 = taskFragment2.mRemoteToken.toWindowContainerToken();
|
||||
mOrganizer.applyTransaction(mTransaction);
|
||||
|
||||
// Throw exception if the transaction is trying to change a window that is not organized by
|
||||
// the organizer.
|
||||
mTransaction.setAdjacentRoots(mFragmentWindowToken, token2);
|
||||
|
||||
assertThrows(SecurityException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
|
||||
// Allow transaction to change a TaskFragment created by the organizer.
|
||||
mTaskFragment.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
|
||||
taskFragment2.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
|
||||
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyTransaction_enforceHierarchyChange_createTaskFragment() {
|
||||
mOrganizer.applyTransaction(mTransaction);
|
||||
|
||||
// Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
|
||||
mTransaction.createTaskFragment(mock(TaskFragmentCreationParams.class));
|
||||
mTransaction.startActivityInTaskFragment(
|
||||
mFragmentToken, new Intent(), null /* activityOptions */);
|
||||
mTransaction.reparentActivityToTaskFragment(mFragmentToken, mock(IBinder.class));
|
||||
|
||||
// It is expected to fail for the mock TaskFragmentCreationParams. It is ok as we are
|
||||
// testing the security check here.
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApplyTransaction_enforceHierarchyChange_reparentChildren()
|
||||
throws RemoteException {
|
||||
mOrganizer.applyTransaction(mTransaction);
|
||||
|
||||
// Throw exception if the transaction is trying to change a window that is not organized by
|
||||
// the organizer.
|
||||
mTransaction.reparentChildren(mFragmentWindowToken, null /* newParent */);
|
||||
|
||||
assertThrows(SecurityException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
|
||||
// Allow transaction to change a TaskFragment created by the organizer.
|
||||
mTaskFragment.setTaskFragmentOrganizer(mIOrganizer, 10 /* pid */);
|
||||
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user