Merge "Fix exception when opening App info on work profile" into tm-dev am: 9b6a9578a1

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18989968

Change-Id: I844f7a09587441307542ffe6679463d569c39667
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Charles Chen
2022-07-07 02:16:56 +00:00
committed by Automerger Merge Worker
6 changed files with 180 additions and 56 deletions

View File

@@ -78,6 +78,10 @@ import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS;
import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY;
import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import android.annotation.NonNull;
@@ -131,6 +135,7 @@ import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityMetricsLogger.LaunchingState;
import com.android.server.wm.LaunchParamsController.LaunchParams;
import com.android.server.wm.TaskFragment.EmbeddingCheckResult;
import java.io.PrintWriter;
import java.text.DateFormat;
@@ -2038,12 +2043,6 @@ class ActivityStarter {
}
}
if (mInTaskFragment != null && !canEmbedActivity(mInTaskFragment, r, newTask, targetTask)) {
Slog.e(TAG, "Permission denied: Cannot embed " + r + " to " + mInTaskFragment.getTask()
+ " targetTask= " + targetTask);
return START_PERMISSION_DENIED;
}
// Do not start the activity if target display's DWPC does not allow it.
// We can't return fatal error code here because it will crash the caller of
// startActivity() if they don't catch the exception. We don't expect 3P apps to make
@@ -2070,19 +2069,21 @@ class ActivityStarter {
}
/**
* Return {@code true} if an activity can be embedded to the TaskFragment.
* Returns whether embedding of {@code starting} is allowed.
*
* @param taskFragment the TaskFragment for embedding.
* @param starting the starting activity.
* @param newTask whether the starting activity is going to be launched on a new task.
* @param targetTask the target task for launching activity, which could be different from
* the one who hosting the embedding.
*/
private boolean canEmbedActivity(@NonNull TaskFragment taskFragment,
@NonNull ActivityRecord starting, boolean newTask, Task targetTask) {
@VisibleForTesting
@EmbeddingCheckResult
static int canEmbedActivity(@NonNull TaskFragment taskFragment,
@NonNull ActivityRecord starting, @NonNull Task targetTask) {
final Task hostTask = taskFragment.getTask();
// Not allowed embedding a separate task or without host task.
if (hostTask == null || newTask || targetTask != hostTask) {
return false;
if (hostTask == null || targetTask != hostTask) {
return EMBEDDING_DISALLOWED_NEW_TASK;
}
return taskFragment.isAllowedToEmbedActivity(starting);
@@ -2894,19 +2895,16 @@ class ActivityStarter {
mIntentDelivered = true;
}
/** Places {@link #mStartActivity} in {@code task} or an embedded {@link TaskFragment}. */
private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
TaskFragment newParent = task;
if (mInTaskFragment != null) {
// TODO(b/234351413): remove remaining embedded Task logic.
// mInTaskFragment is created and added to the leaf task by task fragment organizer's
// request. If the task was resolved and different than mInTaskFragment, reparent the
// task to mInTaskFragment for embedding.
if (mInTaskFragment.getTask() != task) {
if (shouldReparentInTaskFragment(task)) {
task.reparent(mInTaskFragment, POSITION_TOP);
}
} else {
int embeddingCheckResult = canEmbedActivity(mInTaskFragment, mStartActivity, task);
if (embeddingCheckResult == EMBEDDING_ALLOWED) {
newParent = mInTaskFragment;
} else {
// Start mStartActivity to task instead if it can't be embedded to mInTaskFragment.
sendCanNotEmbedActivityError(mInTaskFragment, embeddingCheckResult);
}
} else {
TaskFragment candidateTf = mAddingToTaskFragment != null ? mAddingToTaskFragment : null;
@@ -2918,20 +2916,12 @@ class ActivityStarter {
}
}
if (candidateTf != null && candidateTf.isEmbedded()
&& canEmbedActivity(candidateTf, mStartActivity, false /* newTask */, task)) {
&& canEmbedActivity(candidateTf, mStartActivity, task) == EMBEDDING_ALLOWED) {
// Use the embedded TaskFragment of the top activity as the new parent if the
// activity can be embedded.
newParent = candidateTf;
}
}
// Start Activity to the Task if mStartActivity's min dimensions are not satisfied.
if (newParent.isEmbedded() && newParent.smallerThanMinDimension(mStartActivity)) {
reason += " - MinimumDimensionViolation";
mService.mWindowOrganizerController.sendMinimumDimensionViolation(
newParent, mStartActivity.getMinDimensions(), mRequest.errorCallbackToken,
reason);
newParent = task;
}
if (mStartActivity.getTaskFragment() == null
|| mStartActivity.getTaskFragment() == newParent) {
newParent.addChild(mStartActivity, POSITION_TOP);
@@ -2940,16 +2930,41 @@ class ActivityStarter {
}
}
private boolean shouldReparentInTaskFragment(Task task) {
// The task has not been embedded. We should reparent the task to TaskFragment.
if (!task.isEmbedded()) {
return true;
/**
* Notifies the client side that {@link #mStartActivity} cannot be embedded to
* {@code taskFragment}.
*/
private void sendCanNotEmbedActivityError(TaskFragment taskFragment,
@EmbeddingCheckResult int result) {
final String errMsg;
switch(result) {
case EMBEDDING_DISALLOWED_NEW_TASK: {
errMsg = "Cannot embed " + mStartActivity + " that launched on another task"
+ ",mLaunchMode=" + mLaunchMode
+ ",mLaunchFlag=" + Integer.toHexString(mLaunchFlags);
break;
}
case EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION: {
errMsg = "Cannot embed " + mStartActivity
+ ". TaskFragment's bounds:" + taskFragment.getBounds()
+ ", minimum dimensions:" + mStartActivity.getMinDimensions();
break;
}
case EMBEDDING_DISALLOWED_UNTRUSTED_HOST: {
errMsg = "The app:" + mCallingUid + "is not trusted to " + mStartActivity;
break;
}
default:
errMsg = "Unhandled embed result:" + result;
}
if (taskFragment.isOrganized()) {
mService.mWindowOrganizerController.sendTaskFragmentOperationFailure(
taskFragment.getTaskFragmentOrganizer(), mRequest.errorCallbackToken,
new SecurityException(errMsg));
} else {
// If the taskFragment is not organized, just dump error message as warning logs.
Slog.w(TAG, errMsg);
}
WindowContainer<?> parent = task.getParent();
// If the Activity is going to launch on top of embedded Task in the same TaskFragment,
// we don't need to reparent the Task. Otherwise, the embedded Task should reparent to
// another TaskFragment.
return parent.asTaskFragment() != mInTaskFragment;
}
private int adjustLaunchFlagsToDocumentMode(ActivityRecord r, boolean launchSingleInstance,

View File

@@ -139,6 +139,45 @@ class TaskFragment extends WindowContainer<WindowContainer> {
/** Set to false to disable the preview that is shown while a new activity is being started. */
static final boolean SHOW_APP_STARTING_PREVIEW = true;
/**
* An embedding check result of {@link #isAllowedToEmbedActivity(ActivityRecord)} or
* {@link ActivityStarter#canEmbedActivity(TaskFragment, ActivityRecord, Task)}:
* indicate that an Activity can be embedded successfully.
*/
static final int EMBEDDING_ALLOWED = 0;
/**
* An embedding check result of {@link #isAllowedToEmbedActivity(ActivityRecord)} or
* {@link ActivityStarter#canEmbedActivity(TaskFragment, ActivityRecord, Task)}:
* indicate that an Activity can't be embedded because either the Activity does not allow
* untrusted embedding, and the embedding host app is not trusted.
*/
static final int EMBEDDING_DISALLOWED_UNTRUSTED_HOST = 1;
/**
* An embedding check result of {@link #isAllowedToEmbedActivity(ActivityRecord)} or
* {@link ActivityStarter#canEmbedActivity(TaskFragment, ActivityRecord, Task)}:
* indicate that an Activity can't be embedded because this taskFragment's bounds are
* {@link #smallerThanMinDimension(ActivityRecord)}.
*/
static final int EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION = 2;
/**
* An embedding check result of
* {@link ActivityStarter#canEmbedActivity(TaskFragment, ActivityRecord, Task)}:
* indicate that an Activity can't be embedded because the Activity is started on a new task.
*/
static final int EMBEDDING_DISALLOWED_NEW_TASK = 3;
/**
* Embedding check results of {@link #isAllowedToEmbedActivity(ActivityRecord)} or
* {@link ActivityStarter#canEmbedActivity(TaskFragment, ActivityRecord, Task)}.
*/
@IntDef(prefix = {"EMBEDDING_"}, value = {
EMBEDDING_ALLOWED,
EMBEDDING_DISALLOWED_UNTRUSTED_HOST,
EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION,
EMBEDDING_DISALLOWED_NEW_TASK,
})
@interface EmbeddingCheckResult {}
/**
* Indicate that the minimal width/height should use the default value.
*
@@ -520,20 +559,29 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return false;
}
boolean isAllowedToEmbedActivity(@NonNull ActivityRecord a) {
@EmbeddingCheckResult
int isAllowedToEmbedActivity(@NonNull ActivityRecord a) {
return isAllowedToEmbedActivity(a, mTaskFragmentOrganizerUid);
}
/**
* Checks if the organized task fragment is allowed to have the specified activity, which is
* allowed if an activity allows embedding in untrusted mode, or if the trusted mode can be
* enabled.
* @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
* allowed if an activity allows embedding in untrusted mode, if the trusted mode can be
* enabled, or if the organized task fragment bounds are not
* {@link #smallerThanMinDimension(ActivityRecord)}.
*
* @param uid uid of the TaskFragment organizer.
* @see #isAllowedToEmbedActivityInTrustedMode(ActivityRecord)
*/
boolean isAllowedToEmbedActivity(@NonNull ActivityRecord a, int uid) {
return isAllowedToEmbedActivityInUntrustedMode(a)
|| isAllowedToEmbedActivityInTrustedMode(a, uid);
@EmbeddingCheckResult
int isAllowedToEmbedActivity(@NonNull ActivityRecord a, int uid) {
if (!isAllowedToEmbedActivityInUntrustedMode(a)
&& !isAllowedToEmbedActivityInTrustedMode(a, uid)) {
return EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
} else if (smallerThanMinDimension(a)) {
return EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
}
return EMBEDDING_ALLOWED;
}
boolean smallerThanMinDimension(@NonNull ActivityRecord activity) {
@@ -550,9 +598,8 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
final int minWidth = minDimensions.x;
final int minHeight = minDimensions.y;
final boolean smaller = taskFragBounds.width() < minWidth
return taskFragBounds.width() < minWidth
|| taskFragBounds.height() < minHeight;
return smaller;
}
/**
@@ -609,7 +656,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
// The system is trusted to embed other apps securely and for all users.
return UserHandle.getAppId(uid) == SYSTEM_UID
// Activities from the same UID can be embedded freely by the host.
|| uid == a.getUid();
|| a.isUid(uid);
}
/**

View File

@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.window.TaskFragmentOrganizer.putExceptionInBundle;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.WindowOrganizerController.configurationsAreEqualForOrganizer;
import android.annotation.IntDef;
@@ -235,7 +236,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
+ " is not in a task belong to the organizer app.");
return;
}
if (!task.isAllowedToEmbedActivity(activity, mOrganizerUid)) {
if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED) {
Slog.d(TAG, "Reparent activity=" + activity.token
+ " is not allowed to be embedded.");
return;

View File

@@ -44,6 +44,7 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANI
import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -756,7 +757,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
if (!parent.isAllowedToEmbedActivity(activity)) {
if (parent.isAllowedToEmbedActivity(activity) != EMBEDDING_ALLOWED) {
final Throwable exception = new SecurityException(
"The task fragment is not trusted to embed the given activity.");
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
@@ -988,7 +989,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
/** A helper method to send minimum dimension violation error to the client. */
void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions,
private void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions,
IBinder errorCallbackToken, String reason) {
if (taskFragment == null || taskFragment.getTaskFragmentOrganizer() == null) {
return;
@@ -1582,7 +1583,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
// 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));
newParentTF.isAllowedToEmbedActivity(activity) == EMBEDDING_ALLOWED);
if (isEmbeddingDisallowed) {
final Throwable exception = new SecurityException(
"The new parent is not trusted to embed the activities.");

View File

@@ -37,6 +37,7 @@ import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED;
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.content.pm.ActivityInfo.FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
@@ -52,6 +53,11 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityStarter.canEmbedActivity;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
@@ -59,6 +65,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -87,6 +94,7 @@ import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.service.voice.IVoiceInteractionSession;
import android.util.Pair;
import android.util.Size;
import android.view.Gravity;
import android.window.TaskFragmentOrganizerToken;
@@ -1172,6 +1180,7 @@ public class ActivityStarterTests extends WindowTestsBase {
null /* inTask */, taskFragment);
assertFalse(taskFragment.hasChild());
assertNotNull("Target record must be started on Task.", targetRecord.getParent().asTask());
}
@Test
@@ -1342,6 +1351,58 @@ public class ActivityStarterTests extends WindowTestsBase {
any());
}
@Test
public void testCanEmbedActivity() {
final Size minDimensions = new Size(1000, 1000);
final WindowLayout windowLayout = new WindowLayout(0, 0, 0, 0, 0,
minDimensions.getWidth(), minDimensions.getHeight());
final ActivityRecord starting = new ActivityBuilder(mAtm)
.setUid(UNIMPORTANT_UID)
.setWindowLayout(windowLayout)
.build();
// Task fragment hasn't attached to a task yet. Start activity to a new task.
TaskFragment taskFragment = new TaskFragmentBuilder(mAtm).build();
final Task task = new TaskBuilder(mSupervisor).build();
assertEquals(EMBEDDING_DISALLOWED_NEW_TASK,
canEmbedActivity(taskFragment, starting, task));
// Starting activity is going to be started on a task different from task fragment's parent
// task. Start activity to a new task.
task.addChild(taskFragment, POSITION_TOP);
final Task newTask = new TaskBuilder(mSupervisor).build();
assertEquals(EMBEDDING_DISALLOWED_NEW_TASK,
canEmbedActivity(taskFragment, starting, newTask));
// Make task fragment bounds exceed task bounds.
final Rect taskBounds = task.getBounds();
taskFragment.setBounds(taskBounds.left, taskBounds.top, taskBounds.right + 1,
taskBounds.bottom + 1);
assertEquals(EMBEDDING_DISALLOWED_UNTRUSTED_HOST,
canEmbedActivity(taskFragment, starting, task));
taskFragment.setBounds(taskBounds);
starting.info.flags |= FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
assertEquals(EMBEDDING_ALLOWED, canEmbedActivity(taskFragment, starting, task));
starting.info.flags &= ~FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING;
// Set task fragment's uid as the same as starting activity's uid.
taskFragment.setTaskFragmentOrganizer(mock(TaskFragmentOrganizerToken.class),
UNIMPORTANT_UID, "test");
assertEquals(EMBEDDING_ALLOWED, canEmbedActivity(taskFragment, starting, task));
// Make task fragment bounds smaller than starting activity's minimum dimensions
taskFragment.setBounds(0, 0, minDimensions.getWidth() - 1, minDimensions.getHeight() - 1);
assertEquals(EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION,
canEmbedActivity(taskFragment, starting, task));
}
private static void startActivityInner(ActivityStarter starter, ActivityRecord target,
ActivityRecord source, ActivityOptions options, Task inTask,
TaskFragment inTaskFragment) {

View File

@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
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.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static com.android.server.wm.testing.Assert.assertThrows;
@@ -531,7 +532,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
mWindowOrganizerController.mLaunchTaskFragments
.put(mFragmentToken, mTaskFragment);
mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token);
doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity);
doReturn(EMBEDDING_ALLOWED).when(mTaskFragment).isAllowedToEmbedActivity(activity);
clearInvocations(mAtm.mRootWindowContainer);
mAtm.getWindowOrganizerController().applyTransaction(mTransaction);
@@ -921,7 +922,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
.setOrganizer(mOrganizer)
.setBounds(mTaskFragBounds)
.build();
doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity);
mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
clearInvocations(mAtm.mRootWindowContainer);
@@ -956,7 +956,6 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
.setOrganizer(mOrganizer)
.setBounds(mTaskFragBounds)
.build();
doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity);
mWindowOrganizerController.mLaunchTaskFragments.put(oldFragToken, oldTaskFrag);
mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
clearInvocations(mAtm.mRootWindowContainer);