diff options
| -rw-r--r-- | api/system-current.txt | 1 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 4 | ||||
| -rw-r--r-- | data/etc/privapp-permissions-platform.xml | 1 | ||||
| -rw-r--r-- | packages/Shell/AndroidManifest.xml | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityRecord.java | 2 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityStackSupervisor.java | 51 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityStarter.java | 25 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/TaskRecord.java | 3 |
8 files changed, 70 insertions, 18 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index f7023acd52b4..1d7d9e316d22 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -22,6 +22,7 @@ package android { 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 8e88b67ac11a..db38384b8d38 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 e9e2e8acc1dc..1f552321390d 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -265,6 +265,7 @@ applications that come with the platform <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 e6d9a9a39280..244677508894 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 9a1d9282d9bc..410824e01e35 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 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo */ 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 d9b7d761a7d2..2ef220c82bbb 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D // 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } /** 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 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } 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 749583225bb2..f58c768300ac 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 @@ class ActivityStarter { 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 751ecef63728..5753fbc5a92d 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 @@ final class TaskRecord extends ConfigurationContainer implements TaskWindowConta */ 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 */); } /** |