Revert^2 "Verify hierarchy change for cross-UID ActivityEmbedding"
Enforce safety of hierarchy operations. For untrusted embedding, we only allow bounds change within the parent's bounds. Reland ag/17157035 Bug: 197364677 Change-Id: I0092bfed615fba6196f96fccd2dee26a4ad26062
This commit is contained in:
@@ -518,7 +518,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
|| isAllowedToEmbedActivityInTrustedMode(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the organized task fragment is allowed to embed activity in untrusted mode.
|
||||
*/
|
||||
boolean isAllowedToEmbedActivityInUntrustedMode(@NonNull ActivityRecord a) {
|
||||
final WindowContainer parent = getParent();
|
||||
if (parent == null || !parent.getBounds().contains(getBounds())) {
|
||||
// Without full trust between the host and the embedded activity, we don't allow
|
||||
// TaskFragment to have bounds outside of the parent bounds.
|
||||
return false;
|
||||
}
|
||||
return (a.info.flags & FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING)
|
||||
== FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ package com.android.server.wm;
|
||||
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
|
||||
import static android.app.ActivityManager.isStartResultSuccessful;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION;
|
||||
import static android.window.WindowContainerTransaction.Change.CHANGE_BOUNDS_TRANSACTION_RECT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
|
||||
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
|
||||
@@ -680,7 +678,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
break;
|
||||
}
|
||||
}
|
||||
effects |= deleteTaskFragment(taskFragment, errorCallbackToken);
|
||||
effects |= deleteTaskFragment(taskFragment, organizer, errorCallbackToken);
|
||||
break;
|
||||
}
|
||||
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
|
||||
@@ -698,8 +696,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
.startActivityInTaskFragment(tf, activityIntent, activityOptions,
|
||||
hop.getCallingActivity(), caller.mUid, caller.mPid);
|
||||
if (!isStartResultSuccessful(result)) {
|
||||
sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(),
|
||||
errorCallbackToken,
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken,
|
||||
convertStartFailureToThrowable(result, activityIntent));
|
||||
} else {
|
||||
effects |= TRANSACT_EFFECTS_LIFECYCLE;
|
||||
@@ -709,13 +706,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: {
|
||||
final IBinder fragmentToken = hop.getNewParent();
|
||||
final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
|
||||
if (!mLaunchTaskFragments.containsKey(fragmentToken) || activity == null) {
|
||||
final TaskFragment parent = mLaunchTaskFragments.get(fragmentToken);
|
||||
if (parent == null || activity == null) {
|
||||
final Throwable exception = new IllegalArgumentException(
|
||||
"Not allowed to operate with invalid fragment token or activity.");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
break;
|
||||
}
|
||||
activity.reparent(mLaunchTaskFragments.get(fragmentToken), POSITION_TOP);
|
||||
if (!parent.isAllowedToEmbedActivity(activity)) {
|
||||
final Throwable exception = new SecurityException(
|
||||
"The task fragment is not trusted to embed the given activity.");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
break;
|
||||
}
|
||||
activity.reparent(parent, POSITION_TOP);
|
||||
effects |= TRANSACT_EFFECTS_LIFECYCLE;
|
||||
break;
|
||||
}
|
||||
@@ -877,12 +881,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
final WindowContainer newParent = hop.getNewParent() != null
|
||||
? WindowContainer.fromBinder(hop.getNewParent())
|
||||
: null;
|
||||
if (oldParent == null || !oldParent.isAttached()) {
|
||||
if (oldParent == null || oldParent.asTaskFragment() == null
|
||||
|| !oldParent.isAttached()) {
|
||||
Slog.e(TAG, "Attempt to operate on unknown or detached container: "
|
||||
+ oldParent);
|
||||
break;
|
||||
}
|
||||
reparentTaskFragment(oldParent, newParent, errorCallbackToken);
|
||||
reparentTaskFragment(oldParent.asTaskFragment(), newParent, organizer,
|
||||
errorCallbackToken);
|
||||
effects |= TRANSACT_EFFECTS_LIFECYCLE;
|
||||
break;
|
||||
}
|
||||
@@ -1238,7 +1244,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
mService.enforceTaskPermission(func);
|
||||
}
|
||||
|
||||
private void enforceTaskPermission(String func, WindowContainerTransaction t) {
|
||||
private void enforceTaskPermission(String func, @Nullable WindowContainerTransaction t) {
|
||||
if (t == null || t.getTaskFragmentOrganizer() == null) {
|
||||
enforceTaskPermission(func);
|
||||
return;
|
||||
@@ -1263,14 +1269,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
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.
|
||||
WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
|
||||
final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
|
||||
enforceTaskFragmentOrganized(func, wc, organizer);
|
||||
enforceTaskFragmentConfigChangeAllowed(func, wc, entry.getValue(), organizer);
|
||||
}
|
||||
|
||||
// TODO(b/197364677): Enforce safety of hierarchy operations in untrusted mode. E.g. one
|
||||
// could first change a trusted TF, and then start/reparent untrusted activity there.
|
||||
|
||||
// Hierarchy changes
|
||||
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
|
||||
for (int i = hops.size() - 1; i >= 0; i--) {
|
||||
@@ -1344,8 +1347,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
* Makes sure that SurfaceControl transactions and the ability to set bounds outside of the
|
||||
* parent bounds are not allowed for embedding without full trust between the host and the
|
||||
* target.
|
||||
* TODO(b/197364677): Allow SC transactions when the client-driven animations are protected from
|
||||
* tapjacking.
|
||||
*/
|
||||
private void enforceTaskFragmentConfigChangeAllowed(String func, @Nullable WindowContainer wc,
|
||||
WindowContainerTransaction.Change change, ITaskFragmentOrganizer organizer) {
|
||||
@@ -1353,35 +1354,48 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
Slog.e(TAG, "Attempt to operate on task fragment that no longer exists");
|
||||
return;
|
||||
}
|
||||
// Check if TaskFragment is embedded in fully trusted mode
|
||||
if (wc.asTaskFragment().isAllowedToBeEmbeddedInTrustedMode()) {
|
||||
// Fully trusted, no need to check further
|
||||
return;
|
||||
}
|
||||
|
||||
if (change == null) {
|
||||
return;
|
||||
}
|
||||
final int changeMask = change.getChangeMask();
|
||||
if ((changeMask & (CHANGE_BOUNDS_TRANSACTION | CHANGE_BOUNDS_TRANSACTION_RECT)) != 0) {
|
||||
if (changeMask != 0) {
|
||||
// None of the change should be requested from a TaskFragment organizer.
|
||||
String msg = "Permission Denial: " + func + " from pid="
|
||||
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
|
||||
+ " trying to apply SurfaceControl changes to TaskFragment in non-trusted "
|
||||
+ "embedding mode, TaskFragmentOrganizer=" + organizer;
|
||||
+ " trying to apply changes of " + changeMask + " to TaskFragment"
|
||||
+ " TaskFragmentOrganizer=" + organizer;
|
||||
Slog.w(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
// Check if TaskFragment is embedded in fully trusted mode.
|
||||
if (wc.asTaskFragment().isAllowedToBeEmbeddedInTrustedMode()) {
|
||||
// Fully trusted, no need to check further
|
||||
return;
|
||||
}
|
||||
final WindowContainer wcParent = wc.getParent();
|
||||
if (wcParent == null) {
|
||||
Slog.e(TAG, "Attempt to apply config change on task fragment that has no parent");
|
||||
return;
|
||||
}
|
||||
final Configuration requestedConfig = change.getConfiguration();
|
||||
final Configuration parentConfig = wcParent.getConfiguration();
|
||||
if (parentConfig.screenWidthDp < requestedConfig.screenWidthDp
|
||||
|| parentConfig.screenHeightDp < requestedConfig.screenHeightDp
|
||||
|| parentConfig.smallestScreenWidthDp < requestedConfig.smallestScreenWidthDp) {
|
||||
String msg = "Permission Denial: " + func + " from pid="
|
||||
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
|
||||
+ " trying to apply screen width/height greater than parent's for non-trusted"
|
||||
+ " host, TaskFragmentOrganizer=" + organizer;
|
||||
Slog.w(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
if (change.getWindowSetMask() == 0) {
|
||||
// Nothing else to check.
|
||||
// No bounds change.
|
||||
return;
|
||||
}
|
||||
WindowConfiguration requestedWindowConfig = change.getConfiguration().windowConfiguration;
|
||||
WindowContainer wcParent = wc.getParent();
|
||||
if (wcParent == null) {
|
||||
Slog.e(TAG, "Attempt to set bounds on task fragment that has no parent");
|
||||
return;
|
||||
}
|
||||
if (!wcParent.getBounds().contains(requestedWindowConfig.getBounds())) {
|
||||
final WindowConfiguration requestedWindowConfig = requestedConfig.windowConfiguration;
|
||||
final WindowConfiguration parentWindowConfig = parentConfig.windowConfiguration;
|
||||
if (!parentWindowConfig.getBounds().contains(requestedWindowConfig.getBounds())) {
|
||||
String msg = "Permission Denial: " + func + " from pid="
|
||||
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
|
||||
+ " trying to apply bounds outside of parent for non-trusted host,"
|
||||
@@ -1389,6 +1403,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
Slog.w(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
if (requestedWindowConfig.getAppBounds() != null
|
||||
&& parentWindowConfig.getAppBounds() != null
|
||||
&& !parentWindowConfig.getAppBounds().contains(
|
||||
requestedWindowConfig.getAppBounds())) {
|
||||
String msg = "Permission Denial: " + func + " from pid="
|
||||
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
|
||||
+ " trying to apply app bounds outside of parent for non-trusted host,"
|
||||
+ " TaskFragmentOrganizer=" + organizer;
|
||||
Slog.w(TAG, msg);
|
||||
throw new SecurityException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
|
||||
@@ -1414,7 +1439,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
|
||||
|| ownerActivity.getTask().effectiveUid != caller.mUid) {
|
||||
final Throwable exception =
|
||||
new IllegalArgumentException("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");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
return;
|
||||
@@ -1431,33 +1456,46 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
|
||||
mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
|
||||
}
|
||||
|
||||
void reparentTaskFragment(@NonNull WindowContainer oldParent,
|
||||
@Nullable WindowContainer newParent, @Nullable IBinder errorCallbackToken) {
|
||||
WindowContainer parent = newParent;
|
||||
if (parent == null && oldParent.asTaskFragment() != null) {
|
||||
parent = oldParent.asTaskFragment().getTask();
|
||||
void reparentTaskFragment(@NonNull TaskFragment oldParent, @Nullable WindowContainer newParent,
|
||||
@Nullable ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken) {
|
||||
final TaskFragment newParentTF;
|
||||
if (newParent == null) {
|
||||
// Use the old parent's parent if the caller doesn't specify the new parent.
|
||||
newParentTF = oldParent.getTask();
|
||||
} else {
|
||||
newParentTF = newParent.asTaskFragment();
|
||||
}
|
||||
if (parent == null) {
|
||||
if (newParentTF == null) {
|
||||
final Throwable exception =
|
||||
new IllegalArgumentException("Not allowed to operate with invalid container");
|
||||
sendTaskFragmentOperationFailure(oldParent.asTaskFragment().getTaskFragmentOrganizer(),
|
||||
errorCallbackToken, exception);
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
return;
|
||||
}
|
||||
if (newParentTF.getTaskFragmentOrganizer() != null) {
|
||||
// We are reparenting activities to a new embedded TaskFragment, this operation is only
|
||||
// allowed if the new parent is trusted by all reparent activities.
|
||||
final boolean isEmbeddingDisallowed = oldParent.forAllActivities(activity ->
|
||||
!newParentTF.isAllowedToEmbedActivity(activity));
|
||||
if (isEmbeddingDisallowed) {
|
||||
final Throwable exception = new SecurityException(
|
||||
"The new parent is not trusted to embed the activities.");
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (oldParent.hasChild()) {
|
||||
oldParent.getChildAt(0).reparent(parent, POSITION_TOP);
|
||||
oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP);
|
||||
}
|
||||
}
|
||||
|
||||
private int deleteTaskFragment(@NonNull TaskFragment taskFragment,
|
||||
@Nullable IBinder errorCallbackToken) {
|
||||
@Nullable ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken) {
|
||||
final int index = mLaunchTaskFragments.indexOfValue(taskFragment);
|
||||
if (index < 0) {
|
||||
final Throwable exception =
|
||||
new IllegalArgumentException("Not allowed to operate with invalid "
|
||||
+ "taskFragment");
|
||||
sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(),
|
||||
errorCallbackToken, exception);
|
||||
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
|
||||
return 0;
|
||||
}
|
||||
mLaunchTaskFragments.removeAt(index);
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.wm;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||
import static com.android.server.wm.WindowContainer.POSITION_TOP;
|
||||
import static com.android.server.wm.testing.Assert.assertThrows;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -249,19 +250,13 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
// the organizer.
|
||||
mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
|
||||
|
||||
assertThrows(SecurityException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
assertApplyTransactionDisallowed(mTransaction);
|
||||
|
||||
// Allow transaction to change a TaskFragment created by the organizer.
|
||||
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
|
||||
"Test:TaskFragmentOrganizer" /* processName */);
|
||||
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -272,19 +267,13 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
// the organizer.
|
||||
mTransaction.reorder(mFragmentWindowToken, true /* onTop */);
|
||||
|
||||
assertThrows(SecurityException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
assertApplyTransactionDisallowed(mTransaction);
|
||||
|
||||
// Allow transaction to change a TaskFragment created by the organizer.
|
||||
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
|
||||
"Test:TaskFragmentOrganizer" /* processName */);
|
||||
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -298,27 +287,21 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
// the organizer.
|
||||
mTransaction.deleteTaskFragment(mFragmentWindowToken);
|
||||
|
||||
assertThrows(SecurityException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
assertApplyTransactionDisallowed(mTransaction);
|
||||
|
||||
// Allow transaction to change a TaskFragment created by the organizer.
|
||||
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
|
||||
"Test:TaskFragmentOrganizer" /* processName */);
|
||||
clearInvocations(mAtm.mRootWindowContainer);
|
||||
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
|
||||
// No lifecycle update when the TaskFragment is not recorded.
|
||||
verify(mAtm.mRootWindowContainer, never()).resumeFocusedTasksTopActivities();
|
||||
|
||||
mAtm.mWindowOrganizerController.mLaunchTaskFragments
|
||||
.put(mFragmentToken, mTaskFragment);
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
|
||||
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
|
||||
}
|
||||
@@ -335,13 +318,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
// the organizer.
|
||||
mTransaction.setAdjacentRoots(mFragmentWindowToken, token2, false /* moveTogether */);
|
||||
|
||||
assertThrows(SecurityException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
assertApplyTransactionDisallowed(mTransaction);
|
||||
|
||||
// Allow transaction to change a TaskFragment created by the organizer.
|
||||
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
|
||||
@@ -350,7 +327,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
"Test:TaskFragmentOrganizer" /* processName */);
|
||||
clearInvocations(mAtm.mRootWindowContainer);
|
||||
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
|
||||
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
|
||||
}
|
||||
@@ -423,20 +400,14 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
// the organizer.
|
||||
mTransaction.reparentChildren(mFragmentWindowToken, null /* newParent */);
|
||||
|
||||
assertThrows(SecurityException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
assertApplyTransactionDisallowed(mTransaction);
|
||||
|
||||
// Allow transaction to change a TaskFragment created by the organizer.
|
||||
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
|
||||
"Test:TaskFragmentOrganizer" /* processName */);
|
||||
clearInvocations(mAtm.mRootWindowContainer);
|
||||
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
|
||||
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
|
||||
}
|
||||
@@ -454,6 +425,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
mAtm.mWindowOrganizerController.mLaunchTaskFragments
|
||||
.put(mFragmentToken, mTaskFragment);
|
||||
mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token);
|
||||
doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity);
|
||||
clearInvocations(mAtm.mRootWindowContainer);
|
||||
|
||||
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
|
||||
@@ -540,6 +512,66 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken));
|
||||
}
|
||||
|
||||
/**
|
||||
* For config change to untrusted embedded TaskFragment, we only allow bounds change within
|
||||
* its parent bounds.
|
||||
*/
|
||||
@Test
|
||||
public void testUntrustedEmbedding_configChange() throws RemoteException {
|
||||
mController.registerOrganizer(mIOrganizer);
|
||||
mOrganizer.applyTransaction(mTransaction);
|
||||
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
|
||||
"Test:TaskFragmentOrganizer" /* processName */);
|
||||
doReturn(false).when(mTaskFragment).isAllowedToBeEmbeddedInTrustedMode();
|
||||
final Task task = createTask(mDisplayContent);
|
||||
final Rect taskBounds = new Rect(task.getBounds());
|
||||
final Rect taskAppBounds = new Rect(task.getWindowConfiguration().getAppBounds());
|
||||
final int taskScreenWidthDp = task.getConfiguration().screenWidthDp;
|
||||
final int taskScreenHeightDp = task.getConfiguration().screenHeightDp;
|
||||
final int taskSmallestScreenWidthDp = task.getConfiguration().smallestScreenWidthDp;
|
||||
task.addChild(mTaskFragment, POSITION_TOP);
|
||||
|
||||
// Throw exception if the transaction is trying to change bounds of an untrusted outside of
|
||||
// its parent's.
|
||||
|
||||
// setBounds
|
||||
final Rect tfBounds = new Rect(taskBounds);
|
||||
tfBounds.right++;
|
||||
mTransaction.setBounds(mFragmentWindowToken, tfBounds);
|
||||
assertApplyTransactionDisallowed(mTransaction);
|
||||
|
||||
mTransaction.setBounds(mFragmentWindowToken, taskBounds);
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
|
||||
// setAppBounds
|
||||
final Rect tfAppBounds = new Rect(taskAppBounds);
|
||||
tfAppBounds.right++;
|
||||
mTransaction.setAppBounds(mFragmentWindowToken, tfAppBounds);
|
||||
assertApplyTransactionDisallowed(mTransaction);
|
||||
|
||||
mTransaction.setAppBounds(mFragmentWindowToken, taskAppBounds);
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
|
||||
// setScreenSizeDp
|
||||
mTransaction.setScreenSizeDp(mFragmentWindowToken, taskScreenWidthDp + 1,
|
||||
taskScreenHeightDp + 1);
|
||||
assertApplyTransactionDisallowed(mTransaction);
|
||||
|
||||
mTransaction.setScreenSizeDp(mFragmentWindowToken, taskScreenWidthDp, taskScreenHeightDp);
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
|
||||
// setSmallestScreenWidthDp
|
||||
mTransaction.setSmallestScreenWidthDp(mFragmentWindowToken, taskSmallestScreenWidthDp + 1);
|
||||
assertApplyTransactionDisallowed(mTransaction);
|
||||
|
||||
mTransaction.setSmallestScreenWidthDp(mFragmentWindowToken, taskSmallestScreenWidthDp);
|
||||
assertApplyTransactionAllowed(mTransaction);
|
||||
|
||||
// Any of the change mask is not allowed.
|
||||
mTransaction.setFocusable(mFragmentWindowToken, false);
|
||||
assertApplyTransactionDisallowed(mTransaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls
|
||||
* {@link WindowOrganizerController#applyTransaction} to apply the transaction,
|
||||
@@ -556,4 +588,24 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
|
||||
// Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
|
||||
wct.createTaskFragment(params);
|
||||
}
|
||||
|
||||
/** Asserts that applying the given transaction will throw a {@link SecurityException}. */
|
||||
private void assertApplyTransactionDisallowed(WindowContainerTransaction t) {
|
||||
assertThrows(SecurityException.class, () -> {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(t);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Asserts that applying the given transaction will not throw any exception. */
|
||||
private void assertApplyTransactionAllowed(WindowContainerTransaction t) {
|
||||
try {
|
||||
mAtm.getWindowOrganizerController().applyTransaction(t);
|
||||
} catch (RemoteException e) {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1264,6 +1264,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
|
||||
mOrganizer.getOrganizerToken(), DEFAULT_TASK_FRAGMENT_ORGANIZER_UID,
|
||||
DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME);
|
||||
}
|
||||
spyOn(taskFragment);
|
||||
return taskFragment;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user