Merge "Add permission to launch activities on VD"
diff --git a/api/system-current.txt b/api/system-current.txt
index f7023ac..1d7d9e3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -22,6 +22,7 @@
field public static final java.lang.String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
field public static final java.lang.String ACCESS_WIFI_STATE = "android.permission.ACCESS_WIFI_STATE";
field public static final java.lang.String ACCOUNT_MANAGER = "android.permission.ACCOUNT_MANAGER";
+ field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final java.lang.String ADD_VOICEMAIL = "com.android.voicemail.permission.ADD_VOICEMAIL";
field public static final java.lang.String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE";
field public static final java.lang.String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK";
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8e88b67..db38384 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1868,6 +1868,10 @@
<permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows an application to embed other activities -->
+ <permission android:name="android.permission.ACTIVITY_EMBEDDING"
+ android:protectionLevel="signature|privileged" />
+
<!-- Allows an application to start any activity, regardless of permission
protection or exported state.
@hide -->
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index e9e2e8a..1f55232 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -265,6 +265,7 @@
<permission name="android.permission.DELETE_CACHE_FILES"/>
<permission name="android.permission.DELETE_PACKAGES"/>
<permission name="android.permission.DUMP"/>
+ <permission name="android.permission.ACTIVITY_EMBEDDING"/>
<permission name="android.permission.FORCE_STOP_PACKAGES"/>
<permission name="android.permission.GET_APP_OPS_STATS"/>
<permission name="android.permission.INSTALL_LOCATION_PROVIDER"/>
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e6d9a9a..2446775 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -114,6 +114,7 @@
<uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
+ <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
<uses-permission android:name="android.permission.MANAGE_AUTO_FILL" />
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 9a1d928..410824e 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -1180,7 +1180,7 @@
*/
boolean canBeLaunchedOnDisplay(int displayId) {
return service.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
- supportsResizeableMultiWindow());
+ supportsResizeableMultiWindow(), launchedFromPid, launchedFromUid, info);
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d9b7d76..2ef220c 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,7 +17,6 @@
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;
@@ -476,9 +475,27 @@
}
/** 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;
}
/**
@@ -1633,8 +1650,8 @@
// 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="
@@ -1648,17 +1665,24 @@
}
/** 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) {
@@ -1668,12 +1692,15 @@
}
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()) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 7495832..f58c768 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1833,13 +1833,30 @@
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) {
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 751ecef..5753fbc 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1581,7 +1581,8 @@
*/
boolean canBeLaunchedOnDisplay(int displayId) {
return mService.mStackSupervisor.canPlaceEntityOnDisplay(displayId,
- isResizeable(false /* checkSupportsPip */));
+ isResizeable(false /* checkSupportsPip */), -1 /* don't check PID */,
+ -1 /* don't check UID */, null /* activityInfo */);
}
/**