Merge "Verify hierarchy change for cross-UID ActivityEmbedding" into tm-dev

This commit is contained in:
Chris Li
2022-03-18 09:37:27 +00:00
committed by Android (Google) Code Review
4 changed files with 187 additions and 87 deletions

View File

@@ -518,7 +518,16 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|| isAllowedToEmbedActivityInTrustedMode(a); || isAllowedToEmbedActivityInTrustedMode(a);
} }
/**
* Checks if the organized task fragment is allowed to embed activity in untrusted mode.
*/
boolean isAllowedToEmbedActivityInUntrustedMode(@NonNull ActivityRecord a) { 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) return (a.info.flags & FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING)
== FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING; == FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
} }

View File

@@ -19,8 +19,6 @@ package com.android.server.wm;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.isStartResultSuccessful; import static android.app.ActivityManager.isStartResultSuccessful;
import static android.view.Display.DEFAULT_DISPLAY; 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_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_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -680,7 +678,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
break; break;
} }
} }
effects |= deleteTaskFragment(taskFragment, errorCallbackToken); effects |= deleteTaskFragment(taskFragment, organizer, errorCallbackToken);
break; break;
} }
case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: { case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: {
@@ -698,8 +696,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
.startActivityInTaskFragment(tf, activityIntent, activityOptions, .startActivityInTaskFragment(tf, activityIntent, activityOptions,
hop.getCallingActivity(), caller.mUid, caller.mPid); hop.getCallingActivity(), caller.mUid, caller.mPid);
if (!isStartResultSuccessful(result)) { if (!isStartResultSuccessful(result)) {
sendTaskFragmentOperationFailure(tf.getTaskFragmentOrganizer(), sendTaskFragmentOperationFailure(organizer, errorCallbackToken,
errorCallbackToken,
convertStartFailureToThrowable(result, activityIntent)); convertStartFailureToThrowable(result, activityIntent));
} else { } else {
effects |= TRANSACT_EFFECTS_LIFECYCLE; effects |= TRANSACT_EFFECTS_LIFECYCLE;
@@ -709,13 +706,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: { case HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT: {
final IBinder fragmentToken = hop.getNewParent(); final IBinder fragmentToken = hop.getNewParent();
final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer()); 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( final Throwable exception = new IllegalArgumentException(
"Not allowed to operate with invalid fragment token or activity."); "Not allowed to operate with invalid fragment token or activity.");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break; 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; effects |= TRANSACT_EFFECTS_LIFECYCLE;
break; break;
} }
@@ -877,12 +881,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final WindowContainer newParent = hop.getNewParent() != null final WindowContainer newParent = hop.getNewParent() != null
? WindowContainer.fromBinder(hop.getNewParent()) ? WindowContainer.fromBinder(hop.getNewParent())
: null; : 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: " Slog.e(TAG, "Attempt to operate on unknown or detached container: "
+ oldParent); + oldParent);
break; break;
} }
reparentTaskFragment(oldParent, newParent, errorCallbackToken); reparentTaskFragment(oldParent.asTaskFragment(), newParent, organizer,
errorCallbackToken);
effects |= TRANSACT_EFFECTS_LIFECYCLE; effects |= TRANSACT_EFFECTS_LIFECYCLE;
break; break;
} }
@@ -1238,7 +1244,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mService.enforceTaskPermission(func); mService.enforceTaskPermission(func);
} }
private void enforceTaskPermission(String func, WindowContainerTransaction t) { private void enforceTaskPermission(String func, @Nullable WindowContainerTransaction t) {
if (t == null || t.getTaskFragmentOrganizer() == null) { if (t == null || t.getTaskFragmentOrganizer() == null) {
enforceTaskPermission(func); enforceTaskPermission(func);
return; return;
@@ -1263,14 +1269,11 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
while (entries.hasNext()) { while (entries.hasNext()) {
final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next(); final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
// Only allow to apply changes to TaskFragment that is created by this organizer. // 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); enforceTaskFragmentOrganized(func, wc, organizer);
enforceTaskFragmentConfigChangeAllowed(func, wc, entry.getValue(), 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 // Hierarchy changes
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
for (int i = hops.size() - 1; i >= 0; i--) { 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 * 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 * parent bounds are not allowed for embedding without full trust between the host and the
* target. * target.
* TODO(b/197364677): Allow SC transactions when the client-driven animations are protected from
* tapjacking.
*/ */
private void enforceTaskFragmentConfigChangeAllowed(String func, @Nullable WindowContainer wc, private void enforceTaskFragmentConfigChangeAllowed(String func, @Nullable WindowContainer wc,
WindowContainerTransaction.Change change, ITaskFragmentOrganizer organizer) { 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"); Slog.e(TAG, "Attempt to operate on task fragment that no longer exists");
return; 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) { if (change == null) {
return; return;
} }
final int changeMask = change.getChangeMask(); 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=" String msg = "Permission Denial: " + func + " from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ " trying to apply SurfaceControl changes to TaskFragment in non-trusted " + " trying to apply changes of " + changeMask + " to TaskFragment"
+ "embedding mode, TaskFragmentOrganizer=" + organizer; + " 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); Slog.w(TAG, msg);
throw new SecurityException(msg); throw new SecurityException(msg);
} }
if (change.getWindowSetMask() == 0) { if (change.getWindowSetMask() == 0) {
// Nothing else to check. // No bounds change.
return; return;
} }
WindowConfiguration requestedWindowConfig = change.getConfiguration().windowConfiguration; final WindowConfiguration requestedWindowConfig = requestedConfig.windowConfiguration;
WindowContainer wcParent = wc.getParent(); final WindowConfiguration parentWindowConfig = parentConfig.windowConfiguration;
if (wcParent == null) { if (!parentWindowConfig.getBounds().contains(requestedWindowConfig.getBounds())) {
Slog.e(TAG, "Attempt to set bounds on task fragment that has no parent");
return;
}
if (!wcParent.getBounds().contains(requestedWindowConfig.getBounds())) {
String msg = "Permission Denial: " + func + " from pid=" String msg = "Permission Denial: " + func + " from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ " trying to apply bounds outside of parent for non-trusted host," + " trying to apply bounds outside of parent for non-trusted host,"
@@ -1389,6 +1403,17 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
Slog.w(TAG, msg); Slog.w(TAG, msg);
throw new SecurityException(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, void createTaskFragment(@NonNull TaskFragmentCreationParams creationParams,
@@ -1414,7 +1439,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid() if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid()
|| ownerActivity.getTask().effectiveUid != caller.mUid) { || ownerActivity.getTask().effectiveUid != caller.mUid) {
final Throwable exception = 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"); + "the root activity of the target task belong to the different app");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
return; return;
@@ -1431,33 +1456,46 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment); mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment);
} }
void reparentTaskFragment(@NonNull WindowContainer oldParent, void reparentTaskFragment(@NonNull TaskFragment oldParent, @Nullable WindowContainer newParent,
@Nullable WindowContainer newParent, @Nullable IBinder errorCallbackToken) { @Nullable ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken) {
WindowContainer parent = newParent; final TaskFragment newParentTF;
if (parent == null && oldParent.asTaskFragment() != null) { if (newParent == null) {
parent = oldParent.asTaskFragment().getTask(); // 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 = final Throwable exception =
new IllegalArgumentException("Not allowed to operate with invalid container"); new IllegalArgumentException("Not allowed to operate with invalid container");
sendTaskFragmentOperationFailure(oldParent.asTaskFragment().getTaskFragmentOrganizer(), sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
errorCallbackToken, exception);
return; 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()) { while (oldParent.hasChild()) {
oldParent.getChildAt(0).reparent(parent, POSITION_TOP); oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP);
} }
} }
private int deleteTaskFragment(@NonNull TaskFragment taskFragment, private int deleteTaskFragment(@NonNull TaskFragment taskFragment,
@Nullable IBinder errorCallbackToken) { @Nullable ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken) {
final int index = mLaunchTaskFragments.indexOfValue(taskFragment); final int index = mLaunchTaskFragments.indexOfValue(taskFragment);
if (index < 0) { if (index < 0) {
final Throwable exception = final Throwable exception =
new IllegalArgumentException("Not allowed to operate with invalid " new IllegalArgumentException("Not allowed to operate with invalid "
+ "taskFragment"); + "taskFragment");
sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(), sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
errorCallbackToken, exception);
return 0; return 0;
} }
mLaunchTaskFragments.removeAt(index); mLaunchTaskFragments.removeAt(index);

View File

@@ -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.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.testing.Assert.assertThrows; import static com.android.server.wm.testing.Assert.assertThrows;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -249,19 +250,13 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// the organizer. // the organizer.
mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100)); mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100));
assertThrows(SecurityException.class, () -> { assertApplyTransactionDisallowed(mTransaction);
try {
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
} catch (RemoteException e) {
fail();
}
});
// Allow transaction to change a TaskFragment created by the organizer. // Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */); "Test:TaskFragmentOrganizer" /* processName */);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
} }
@Test @Test
@@ -272,19 +267,13 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// the organizer. // the organizer.
mTransaction.reorder(mFragmentWindowToken, true /* onTop */); mTransaction.reorder(mFragmentWindowToken, true /* onTop */);
assertThrows(SecurityException.class, () -> { assertApplyTransactionDisallowed(mTransaction);
try {
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
} catch (RemoteException e) {
fail();
}
});
// Allow transaction to change a TaskFragment created by the organizer. // Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */); "Test:TaskFragmentOrganizer" /* processName */);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
} }
@Test @Test
@@ -298,27 +287,21 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// the organizer. // the organizer.
mTransaction.deleteTaskFragment(mFragmentWindowToken); mTransaction.deleteTaskFragment(mFragmentWindowToken);
assertThrows(SecurityException.class, () -> { assertApplyTransactionDisallowed(mTransaction);
try {
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
} catch (RemoteException e) {
fail();
}
});
// Allow transaction to change a TaskFragment created by the organizer. // Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */); "Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer); clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
// No lifecycle update when the TaskFragment is not recorded. // No lifecycle update when the TaskFragment is not recorded.
verify(mAtm.mRootWindowContainer, never()).resumeFocusedTasksTopActivities(); verify(mAtm.mRootWindowContainer, never()).resumeFocusedTasksTopActivities();
mAtm.mWindowOrganizerController.mLaunchTaskFragments mAtm.mWindowOrganizerController.mLaunchTaskFragments
.put(mFragmentToken, mTaskFragment); .put(mFragmentToken, mTaskFragment);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities(); verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
} }
@@ -335,13 +318,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// the organizer. // the organizer.
mTransaction.setAdjacentRoots(mFragmentWindowToken, token2, false /* moveTogether */); mTransaction.setAdjacentRoots(mFragmentWindowToken, token2, false /* moveTogether */);
assertThrows(SecurityException.class, () -> { assertApplyTransactionDisallowed(mTransaction);
try {
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
} catch (RemoteException e) {
fail();
}
});
// Allow transaction to change a TaskFragment created by the organizer. // Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
@@ -350,7 +327,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
"Test:TaskFragmentOrganizer" /* processName */); "Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer); clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities(); verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
} }
@@ -423,20 +400,14 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// the organizer. // the organizer.
mTransaction.reparentChildren(mFragmentWindowToken, null /* newParent */); mTransaction.reparentChildren(mFragmentWindowToken, null /* newParent */);
assertThrows(SecurityException.class, () -> { assertApplyTransactionDisallowed(mTransaction);
try {
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
} catch (RemoteException e) {
fail();
}
});
// Allow transaction to change a TaskFragment created by the organizer. // Allow transaction to change a TaskFragment created by the organizer.
mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */, mTaskFragment.setTaskFragmentOrganizer(mOrganizerToken, 10 /* uid */,
"Test:TaskFragmentOrganizer" /* processName */); "Test:TaskFragmentOrganizer" /* processName */);
clearInvocations(mAtm.mRootWindowContainer); clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction); assertApplyTransactionAllowed(mTransaction);
verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities(); verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities();
} }
@@ -454,6 +425,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mAtm.mWindowOrganizerController.mLaunchTaskFragments mAtm.mWindowOrganizerController.mLaunchTaskFragments
.put(mFragmentToken, mTaskFragment); .put(mFragmentToken, mTaskFragment);
mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token); mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token);
doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity);
clearInvocations(mAtm.mRootWindowContainer); clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction); mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
@@ -540,6 +512,66 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken)); 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 * Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls
* {@link WindowOrganizerController#applyTransaction} to apply the transaction, * {@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. // Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
wct.createTaskFragment(params); 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();
}
}
} }

View File

@@ -1264,6 +1264,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
mOrganizer.getOrganizerToken(), DEFAULT_TASK_FRAGMENT_ORGANIZER_UID, mOrganizer.getOrganizerToken(), DEFAULT_TASK_FRAGMENT_ORGANIZER_UID,
DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME); DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME);
} }
spyOn(taskFragment);
return taskFragment; return taskFragment;
} }
} }