Merge "Verify hierarchy change for cross-UID ActivityEmbedding" into tm-dev
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user