package com.android.server.am;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
-import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
}
/** Check if placing task or activity on specified display is allowed. */
- boolean canPlaceEntityOnDisplay(int displayId, boolean resizeable) {
- return displayId == DEFAULT_DISPLAY || (mService.mSupportsMultiDisplay
- && (resizeable || displayConfigMatchesGlobal(displayId)));
+ boolean canPlaceEntityOnDisplay(int displayId, boolean resizeable, int callingPid,
+ int callingUid, ActivityInfo activityInfo) {
+ if (displayId == DEFAULT_DISPLAY) {
+ // No restrictions for the default display.
+ return true;
+ }
+ if (!mService.mSupportsMultiDisplay) {
+ // Can't launch on secondary displays if feature is not supported.
+ return false;
+ }
+ if (!resizeable && !displayConfigMatchesGlobal(displayId)) {
+ // Can't apply wrong configuration to non-resizeable activities.
+ return false;
+ }
+ if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
+ // Can't place activities to a display that has restricted launch rules.
+ // In this case the request should be made by explicitly adding target display id and
+ // by caller with corresponding permissions. See #isCallerAllowedToLaunchOnDisplay().
+ return false;
+ }
+ return true;
}
/**
// Check if someone tries to launch an activity on a private display with a different
// owner.
final int launchDisplayId = options.getLaunchDisplayId();
- if (launchDisplayId != INVALID_DISPLAY
- && !isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, launchDisplayId)) {
+ if (launchDisplayId != INVALID_DISPLAY && !isCallerAllowedToLaunchOnDisplay(callingPid,
+ callingUid, launchDisplayId, aInfo)) {
final String msg = "Permission Denial: starting " + intent.toString()
+ " from " + callerApp + " (pid=" + callingPid
+ ", uid=" + callingUid + ") with launchDisplayId="
}
/** Check if caller is allowed to launch activities on specified display. */
- boolean isCallerAllowedToLaunchOnDisplay(int callingPid, int callingUid, int launchDisplayId) {
+ boolean isCallerAllowedToLaunchOnDisplay(int callingPid, int callingUid, int launchDisplayId,
+ ActivityInfo aInfo) {
if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check: displayId=" + launchDisplayId
+ " callingPid=" + callingPid + " callingUid=" + callingUid);
+ if (callingPid == -1 && callingUid == -1) {
+ if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check: no caller info, skip check");
+ return true;
+ }
+
final ActivityDisplay activityDisplay = getActivityDisplayOrCreateLocked(launchDisplayId);
if (activityDisplay == null) {
Slog.w(TAG, "Launch on display check: display not found");
return false;
}
- // Check if the caller can manage activity stacks.
+ // Check if the caller has enough privileges to embed activities and launch to private
+ // displays.
final int startAnyPerm = mService.checkPermission(INTERNAL_SYSTEM_WINDOW, callingPid,
callingUid);
if (startAnyPerm == PERMISSION_GRANTED) {
}
if (activityDisplay.mDisplay.getType() == TYPE_VIRTUAL
- && activityDisplay.mDisplay.getOwnerUid() != SYSTEM_UID) {
+ && activityDisplay.mDisplay.getOwnerUid() != SYSTEM_UID
+ && activityDisplay.mDisplay.getOwnerUid() != aInfo.applicationInfo.uid) {
// Limit launching on virtual displays, because their contents can be read from Surface
// by apps that created them.
- if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
- + " disallow launch on virtual display for not-embedded activity");
- return false;
+ if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
+ if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ + " disallow launch on virtual display for not-embedded activity.");
+ return false;
+ }
}
if (!activityDisplay.isPrivate()) {
final TaskRecord sourceTask = mSourceRecord.getTask();
final ActivityStack sourceStack = mSourceRecord.getStack();
- // We only want to allow changing stack if the target task is not the top one,
- // otherwise we would move the launching task to the other side, rather than show
- // two side by side.
- final boolean moveStackAllowed = sourceStack.topTask() != sourceTask;
+ // We only want to allow changing stack in two cases:
+ // 1. If the target task is not the top one. Otherwise we would move the launching task to
+ // the other side, rather than show two side by side.
+ // 2. If activity is not allowed on target display.
+ final int targetDisplayId = mTargetStack != null ? mTargetStack.mDisplayId
+ : sourceStack.mDisplayId;
+ final boolean moveStackAllowed = sourceStack.topTask() != sourceTask
+ || !mStartActivity.canBeLaunchedOnDisplay(targetDisplayId);
if (moveStackAllowed) {
mTargetStack = getLaunchStack(mStartActivity, mLaunchFlags, mStartActivity.getTask(),
mOptions);
+ // If target stack is not found now - we can't just rely on the source stack, as it may
+ // be not suitable. Let's check other displays.
+ if (mTargetStack == null && targetDisplayId != sourceStack.mDisplayId) {
+ // Can't use target display, lets find a stack on the source display.
+ mTargetStack = mService.mStackSupervisor.getValidLaunchStackOnDisplay(
+ sourceStack.mDisplayId, mStartActivity);
+ }
+ if (mTargetStack == null) {
+ // There are no suitable stacks on the target and source display(s). Look on all
+ // displays.
+ mTargetStack = mService.mStackSupervisor.getNextValidLaunchStackLocked(
+ mStartActivity, -1 /* currentFocus */);
+ }
}
if (mTargetStack == null) {