Merge "Allow TaskFragmentOrganizer to apply WCT without permission" into sc-v2-dev am: 5849a2b5e3
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15264526 Change-Id: I8a431df7d6fb2004df6e32571fb08ce474a9cd92
This commit is contained in:
@@ -3256,8 +3256,8 @@ package android.window {
|
|||||||
|
|
||||||
public class WindowOrganizer {
|
public class WindowOrganizer {
|
||||||
ctor public 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(value=android.Manifest.permission.MANAGE_ACTIVITY_TASKS, conditional=true) 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 void applyTransaction(@NonNull android.window.WindowContainerTransaction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiContext public abstract class WindowProviderService extends android.app.Service {
|
@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() {
|
private IDisplayAreaOrganizerController getController() {
|
||||||
try {
|
try {
|
||||||
return getWindowOrganizerController().getDisplayAreaOrganizerController();
|
return getWindowOrganizerController().getDisplayAreaOrganizerController();
|
||||||
@@ -272,5 +273,4 @@ public class DisplayAreaOrganizer extends WindowOrganizer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -120,6 +120,19 @@ public class TaskFragmentOrganizer extends WindowOrganizer {
|
|||||||
public void onTaskFragmentError(
|
public void onTaskFragmentError(
|
||||||
@NonNull IBinder errorCallbackToken, @NonNull Throwable exception) {}
|
@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() {
|
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
|
||||||
@Override
|
@Override
|
||||||
public void onTaskFragmentAppeared(@NonNull TaskFragmentAppearedInfo taskFragmentInfo) {
|
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() {
|
private ITaskOrganizerController getController() {
|
||||||
try {
|
try {
|
||||||
return getWindowOrganizerController().getTaskOrganizerController();
|
return getWindowOrganizerController().getTaskOrganizerController();
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a collection of operations on some WindowContainers that should be applied all at
|
* 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
|
@Nullable
|
||||||
private IBinder mErrorCallbackToken;
|
private IBinder mErrorCallbackToken;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private ITaskFragmentOrganizer mTaskFragmentOrganizer;
|
||||||
|
|
||||||
public WindowContainerTransaction() {}
|
public WindowContainerTransaction() {}
|
||||||
|
|
||||||
private WindowContainerTransaction(Parcel in) {
|
private WindowContainerTransaction(Parcel in) {
|
||||||
in.readMap(mChanges, null /* loader */);
|
in.readMap(mChanges, null /* loader */);
|
||||||
in.readList(mHierarchyOps, null /* loader */);
|
in.readList(mHierarchyOps, null /* loader */);
|
||||||
mErrorCallbackToken = in.readStrongBinder();
|
mErrorCallbackToken = in.readStrongBinder();
|
||||||
|
mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Change getOrCreateChange(IBinder token) {
|
private Change getOrCreateChange(IBinder token) {
|
||||||
@@ -473,7 +478,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
|||||||
final HierarchyOp hierarchyOp =
|
final HierarchyOp hierarchyOp =
|
||||||
new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN)
|
new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_CHILDREN)
|
||||||
.setContainer(oldParent.asBinder())
|
.setContainer(oldParent.asBinder())
|
||||||
.setReparentContainer(newParent.asBinder())
|
.setReparentContainer(newParent != null ? newParent.asBinder() : null)
|
||||||
.build();
|
.build();
|
||||||
mHierarchyOps.add(hierarchyOp);
|
mHierarchyOps.add(hierarchyOp);
|
||||||
return this;
|
return this;
|
||||||
@@ -496,6 +501,23 @@ public final class WindowContainerTransaction implements Parcelable {
|
|||||||
return this;
|
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.
|
* Merges another WCT into this one.
|
||||||
* @param transfer When true, this will transfer everything from other potentially leaving
|
* @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
|
if (mErrorCallbackToken != null && other.mErrorCallbackToken != null && mErrorCallbackToken
|
||||||
!= other.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 = mErrorCallbackToken != null
|
||||||
? mErrorCallbackToken
|
? mErrorCallbackToken
|
||||||
@@ -547,11 +579,21 @@ public final class WindowContainerTransaction implements Parcelable {
|
|||||||
return mErrorCallbackToken;
|
return mErrorCallbackToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
@Nullable
|
||||||
|
public ITaskFragmentOrganizer getTaskFragmentOrganizer() {
|
||||||
|
return mTaskFragmentOrganizer;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "WindowContainerTransaction { changes = " + mChanges + " hops = " + mHierarchyOps
|
return "WindowContainerTransaction {"
|
||||||
+ " errorCallbackToken=" + mErrorCallbackToken + " }";
|
+ " changes = " + mChanges
|
||||||
|
+ " hops = " + mHierarchyOps
|
||||||
|
+ " errorCallbackToken=" + mErrorCallbackToken
|
||||||
|
+ " taskFragmentOrganizer=" + mTaskFragmentOrganizer
|
||||||
|
+ " }";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -560,6 +602,7 @@ public final class WindowContainerTransaction implements Parcelable {
|
|||||||
dest.writeMap(mChanges);
|
dest.writeMap(mChanges);
|
||||||
dest.writeList(mHierarchyOps);
|
dest.writeList(mHierarchyOps);
|
||||||
dest.writeStrongBinder(mErrorCallbackToken);
|
dest.writeStrongBinder(mErrorCallbackToken);
|
||||||
|
dest.writeStrongInterface(mTaskFragmentOrganizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -36,9 +36,16 @@ public class WindowOrganizer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply multiple WindowContainer operations at once.
|
* 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 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) {
|
public void applyTransaction(@NonNull WindowContainerTransaction t) {
|
||||||
try {
|
try {
|
||||||
if (!t.isEmpty()) {
|
if (!t.isEmpty()) {
|
||||||
@@ -51,6 +58,12 @@ public class WindowOrganizer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply multiple WindowContainer operations at once.
|
* 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 t The transaction to apply.
|
||||||
* @param callback This transaction will use the synchronization scheme described in
|
* @param callback This transaction will use the synchronization scheme described in
|
||||||
* BLASTSyncEngine.java. The SurfaceControl transaction containing the effects of this
|
* 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.
|
* @return An ID for the sync operation which will later be passed to transactionReady callback.
|
||||||
* This lets the caller differentiate overlapping sync operations.
|
* 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,
|
public int applySyncTransaction(@NonNull WindowContainerTransaction t,
|
||||||
@NonNull WindowContainerTransactionCallback callback) {
|
@NonNull WindowContainerTransactionCallback callback) {
|
||||||
try {
|
try {
|
||||||
@@ -123,7 +137,6 @@ public class WindowOrganizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
|
|
||||||
IWindowOrganizerController getWindowOrganizerController() {
|
IWindowOrganizerController getWindowOrganizerController() {
|
||||||
return IWindowOrganizerControllerSingleton.get();
|
return IWindowOrganizerControllerSingleton.get();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3445,7 +3445,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IWindowOrganizerController getWindowOrganizerController() {
|
public IWindowOrganizerController getWindowOrganizerController() {
|
||||||
enforceTaskPermission("getWindowOrganizerController()");
|
|
||||||
return mWindowOrganizerController;
|
return mWindowOrganizerController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -289,6 +289,12 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
|||||||
mTaskFragmentOrganizerPid = pid;
|
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() {
|
TaskFragment getAdjacentTaskFragment() {
|
||||||
return mAdjacentTaskFragment;
|
return mAdjacentTaskFragment;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import android.util.ArraySet;
|
|||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
import android.window.IDisplayAreaOrganizerController;
|
import android.window.IDisplayAreaOrganizerController;
|
||||||
|
import android.window.ITaskFragmentOrganizer;
|
||||||
import android.window.ITaskFragmentOrganizerController;
|
import android.window.ITaskFragmentOrganizerController;
|
||||||
import android.window.ITaskOrganizerController;
|
import android.window.ITaskOrganizerController;
|
||||||
import android.window.ITransitionPlayer;
|
import android.window.ITransitionPlayer;
|
||||||
@@ -110,7 +111,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
final TransitionController mTransitionController;
|
final TransitionController mTransitionController;
|
||||||
/**
|
/**
|
||||||
* A Map which manages the relationship between
|
* 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<>();
|
private final ArrayMap<IBinder, TaskFragment> mLaunchTaskFragments = new ArrayMap<>();
|
||||||
|
|
||||||
@@ -139,10 +140,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyTransaction(WindowContainerTransaction t) {
|
public void applyTransaction(WindowContainerTransaction t) {
|
||||||
enforceTaskPermission("applyTransaction()");
|
|
||||||
if (t == null) {
|
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 CallerInfo caller = new CallerInfo();
|
||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
@@ -157,10 +158,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
@Override
|
@Override
|
||||||
public int applySyncTransaction(WindowContainerTransaction t,
|
public int applySyncTransaction(WindowContainerTransaction t,
|
||||||
IWindowContainerTransactionCallback callback) {
|
IWindowContainerTransactionCallback callback) {
|
||||||
enforceTaskPermission("applySyncTransaction()");
|
|
||||||
if (t == null) {
|
if (t == null) {
|
||||||
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
|
throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
|
||||||
}
|
}
|
||||||
|
enforceTaskPermission("applySyncTransaction()", t);
|
||||||
final CallerInfo caller = new CallerInfo();
|
final CallerInfo caller = new CallerInfo();
|
||||||
final long ident = Binder.clearCallingIdentity();
|
final long ident = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
@@ -620,7 +621,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
break;
|
break;
|
||||||
case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
|
case HIERARCHY_OP_TYPE_REPARENT_CHILDREN:
|
||||||
final WindowContainer oldParent = WindowContainer.fromBinder(hop.getContainer());
|
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()) {
|
if (oldParent == null || !oldParent.isAttached()) {
|
||||||
Slog.e(TAG, "Attempt to operate on unknown or detached container: "
|
Slog.e(TAG, "Attempt to operate on unknown or detached container: "
|
||||||
+ oldParent);
|
+ oldParent);
|
||||||
@@ -906,6 +909,102 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
|||||||
mService.enforceTaskPermission(func);
|
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) {
|
void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams) {
|
||||||
final ActivityRecord ownerActivity =
|
final ActivityRecord ownerActivity =
|
||||||
ActivityRecord.forTokenLocked(creationParams.getOwnerToken());
|
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.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||||
import static com.android.server.wm.testing.Assert.assertThrows;
|
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.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.clearInvocations;
|
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.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Rect;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@@ -37,8 +41,12 @@ import android.os.RemoteException;
|
|||||||
import android.platform.test.annotations.Presubmit;
|
import android.platform.test.annotations.Presubmit;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
import android.window.ITaskFragmentOrganizer;
|
import android.window.ITaskFragmentOrganizer;
|
||||||
|
import android.window.TaskFragmentCreationParams;
|
||||||
import android.window.TaskFragmentInfo;
|
import android.window.TaskFragmentInfo;
|
||||||
import android.window.TaskFragmentOrganizer;
|
import android.window.TaskFragmentOrganizer;
|
||||||
|
import android.window.WindowContainerToken;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
import android.window.WindowContainerTransactionCallback;
|
||||||
|
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
@@ -61,18 +69,24 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
|||||||
private TaskFragment mTaskFragment;
|
private TaskFragment mTaskFragment;
|
||||||
private TaskFragmentInfo mTaskFragmentInfo;
|
private TaskFragmentInfo mTaskFragmentInfo;
|
||||||
private IBinder mFragmentToken;
|
private IBinder mFragmentToken;
|
||||||
|
private WindowContainerTransaction mTransaction;
|
||||||
|
private WindowContainerToken mFragmentWindowToken;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
mController = mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
|
mController = mWm.mAtmService.mWindowOrganizerController.mTaskFragmentOrganizerController;
|
||||||
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
|
mOrganizer = new TaskFragmentOrganizer(Runnable::run);
|
||||||
mIOrganizer = mOrganizer.getIOrganizer();
|
mIOrganizer = mOrganizer.getIOrganizer();
|
||||||
mTaskFragment = mock(TaskFragment.class);
|
|
||||||
mTaskFragmentInfo = mock(TaskFragmentInfo.class);
|
mTaskFragmentInfo = mock(TaskFragmentInfo.class);
|
||||||
mFragmentToken = new Binder();
|
mFragmentToken = new Binder();
|
||||||
|
mTaskFragment =
|
||||||
|
new TaskFragment(mAtm, mFragmentToken, true /* createdByOrganizer */);
|
||||||
|
mTransaction = new WindowContainerTransaction();
|
||||||
|
mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken();
|
||||||
|
|
||||||
spyOn(mController);
|
spyOn(mController);
|
||||||
spyOn(mOrganizer);
|
spyOn(mOrganizer);
|
||||||
|
spyOn(mTaskFragment);
|
||||||
doReturn(mIOrganizer).when(mTaskFragment).getTaskFragmentOrganizer();
|
doReturn(mIOrganizer).when(mTaskFragment).getTaskFragmentOrganizer();
|
||||||
doReturn(mTaskFragmentInfo).when(mTaskFragment).getTaskFragmentInfo();
|
doReturn(mTaskFragmentInfo).when(mTaskFragment).getTaskFragmentInfo();
|
||||||
doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl();
|
doReturn(new SurfaceControl()).when(mTaskFragment).getSurfaceControl();
|
||||||
@@ -190,4 +204,156 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
|||||||
|
|
||||||
verify(mOrganizer).onTaskFragmentError(eq(errorCallbackToken), eq(exception));
|
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