summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt2
-rw-r--r--core/java/android/app/ActivityManagerInternal.java14
-rw-r--r--core/java/android/content/Context.java25
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java8
-rw-r--r--services/core/java/com/android/server/am/UserController.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java119
6 files changed, 170 insertions, 31 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index c46449c8bf3b..ea6ab0ce14a1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10823,7 +10823,7 @@ package android.content {
method public boolean bindIsolatedService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
method public abstract boolean bindService(@RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int);
method public boolean bindService(@NonNull @RequiresPermission android.content.Intent, int, @NonNull java.util.concurrent.Executor, @NonNull android.content.ServiceConnection);
- method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.INTERACT_ACROSS_PROFILES}) public boolean bindServiceAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
+ method @RequiresPermission(anyOf={"android.permission.INTERACT_ACROSS_USERS", "android.permission.INTERACT_ACROSS_USERS_FULL", android.Manifest.permission.INTERACT_ACROSS_PROFILES}, conditional=true) public boolean bindServiceAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.content.ServiceConnection, int, @NonNull android.os.UserHandle);
method @CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)") public abstract int checkCallingOrSelfPermission(@NonNull String);
method @CheckResult(suggest="#enforceCallingOrSelfUriPermission(Uri,int,String)") public abstract int checkCallingOrSelfUriPermission(android.net.Uri, int);
method @NonNull public int[] checkCallingOrSelfUriPermissions(@NonNull java.util.List<android.net.Uri>, int);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 324e1aea81e7..96487de5e119 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -71,6 +71,9 @@ public abstract class ActivityManagerInternal {
}
// Access modes for handleIncomingUser.
+ /**
+ * Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS}.
+ */
public static final int ALLOW_NON_FULL = 0;
/**
* Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
@@ -78,13 +81,18 @@ public abstract class ActivityManagerInternal {
* Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
*/
public static final int ALLOW_NON_FULL_IN_PROFILE = 1;
+ /**
+ * Allows access to a caller only if it has the full
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}.
+ */
public static final int ALLOW_FULL_ONLY = 2;
/**
* Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
- * or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} if in the same profile group.
- * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
+ * if in the same profile group.
+ * Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS} is required and suffices
+ * as in {@link #ALLOW_NON_FULL}.
*/
- public static final int ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE = 3;
+ public static final int ALLOW_PROFILES_OR_NON_FULL = 3;
/**
* Returns profile information in free form string in two separate strings.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2df6f9ae2bd6..0f24de6c54cb 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3601,10 +3601,18 @@ public abstract class Context {
* Binds to a service in the given {@code user} in the same manner as
* {@link #bindService(Intent, ServiceConnection, int)}.
*
- * <p>If the given {@code user} is in the same profile group and the target package is the
- * same as the caller, {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} is
- * sufficient. Otherwise, requires {@code android.Manifest.permission.INTERACT_ACROSS_USERS}
- * for interacting with other users.
+ * <p>Requires that one of the following conditions are met:
+ * <ul>
+ * <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}</li>
+ * <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS} and is the same
+ * package as the {@code service} (determined by its component's package) and the Android
+ * version is at least {@link android.os.Build.VERSION_CODES#S}</li>
+ * <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_USERS} and is in same
+ * profile group as the given {@code user}</li>
+ * <li>caller has {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} and is in same
+ * profile group as the given {@code user} and is the same package as the {@code service}
+ * </li>
+ * </ul>
*
* @param service Identifies the service to connect to. The Intent must
* specify an explicit component name.
@@ -3626,8 +3634,9 @@ public abstract class Context {
@SuppressWarnings("unused")
@RequiresPermission(anyOf = {
android.Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
android.Manifest.permission.INTERACT_ACROSS_PROFILES
- })
+ }, conditional = true)
public boolean bindServiceAsUser(
@NonNull @RequiresPermission Intent service, @NonNull ServiceConnection conn, int flags,
@NonNull UserHandle user) {
@@ -3640,7 +3649,11 @@ public abstract class Context {
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS)
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.INTERACT_ACROSS_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES
+ }, conditional = true)
@UnsupportedAppUsage(trackingBug = 136728678)
public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
Handler handler, UserHandle user) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7993936cd568..b92556bf9a20 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3472,11 +3472,9 @@ public final class ActiveServices {
}
private int getAllowMode(Intent service, @Nullable String callingPackage) {
- if (callingPackage == null || service.getComponent() == null) {
- return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
- }
- if (callingPackage.equals(service.getComponent().getPackageName())) {
- return ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
+ if (callingPackage != null && service.getComponent() != null
+ && callingPackage.equals(service.getComponent().getPackageName())) {
+ return ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL;
} else {
return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a7864b9607c8..028a0ec8648a 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -26,10 +26,10 @@ import static android.app.ActivityManager.USER_OP_ERROR_IS_SYSTEM;
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
import static android.app.ActivityManager.USER_OP_SUCCESS;
-import static android.app.ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
+import static android.app.ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL;
import static android.os.PowerWhitelistManager.REASON_BOOT_COMPLETED;
import static android.os.PowerWhitelistManager.REASON_LOCKED_BOOT_COMPLETED;
import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
@@ -2153,11 +2153,10 @@ class UserController implements Handler.Callback {
callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
// If the caller does not have either permission, they are always doomed.
allow = false;
- } else if (allowMode == ALLOW_NON_FULL) {
+ } else if (allowMode == ALLOW_NON_FULL || allowMode == ALLOW_PROFILES_OR_NON_FULL) {
// We are blanket allowing non-full access, you lucky caller!
allow = true;
- } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE
- || allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+ } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE) {
// We may or may not allow this depending on whether the two users are
// in the same profile.
allow = isSameProfileGroup;
@@ -2184,12 +2183,13 @@ class UserController implements Handler.Callback {
builder.append("; this requires ");
builder.append(INTERACT_ACROSS_USERS_FULL);
if (allowMode != ALLOW_FULL_ONLY) {
- if (allowMode == ALLOW_NON_FULL || isSameProfileGroup) {
+ if (allowMode == ALLOW_NON_FULL
+ || allowMode == ALLOW_PROFILES_OR_NON_FULL
+ || (allowMode == ALLOW_NON_FULL_IN_PROFILE && isSameProfileGroup)) {
builder.append(" or ");
builder.append(INTERACT_ACROSS_USERS);
}
- if (isSameProfileGroup
- && allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+ if (isSameProfileGroup && allowMode == ALLOW_PROFILES_OR_NON_FULL) {
builder.append(" or ");
builder.append(INTERACT_ACROSS_PROFILES);
}
@@ -2216,19 +2216,14 @@ class UserController implements Handler.Callback {
private boolean canInteractWithAcrossProfilesPermission(
int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid,
String callingPackage) {
- if (allowMode != ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+ if (allowMode != ALLOW_PROFILES_OR_NON_FULL) {
return false;
}
if (!isSameProfileGroup) {
return false;
}
- return PermissionChecker.PERMISSION_GRANTED
- == PermissionChecker.checkPermissionForPreflight(
- mInjector.getContext(),
- INTERACT_ACROSS_PROFILES,
- callingPid,
- callingUid,
- callingPackage);
+ return mInjector.checkPermissionForPreflight(INTERACT_ACROSS_PROFILES, callingPid,
+ callingUid, callingPackage);
}
int unsafeConvertIncomingUser(@UserIdInt int userId) {
@@ -3157,6 +3152,12 @@ class UserController implements Handler.Callback {
return mService.checkComponentPermission(permission, pid, uid, owningUid, exported);
}
+ boolean checkPermissionForPreflight(String permission, int pid, int uid, String pkg) {
+ return PermissionChecker.PERMISSION_GRANTED
+ == PermissionChecker.checkPermissionForPreflight(
+ getContext(), permission, pid, uid, pkg);
+ }
+
protected void startHomeActivity(@UserIdInt int userId, String reason) {
mService.mAtmInternal.startHomeActivity(userId, reason);
}
@@ -3234,7 +3235,7 @@ class UserController implements Handler.Callback {
mService.mAtmInternal.clearLockedTasks(reason);
}
- protected boolean isCallerRecents(int callingUid) {
+ boolean isCallerRecents(int callingUid) {
return mService.mAtmInternal.isCallerRecents(callingUid);
}
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index f1a63bcb0602..6818d1f6851b 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -16,6 +16,13 @@
package com.android.server.am;
+import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
+import static android.app.ActivityManagerInternal.ALLOW_PROFILES_OR_NON_FULL;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
@@ -64,6 +71,7 @@ import android.app.KeyguardManager;
import android.content.Context;
import android.content.IIntentReceiver;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo.UserInfoFlag;
import android.os.Binder;
@@ -617,6 +625,100 @@ public class UserControllerTest {
assertProfileLockedOrUnlockedAfterStopping(TEST_USER_ID1, /* expectLocking= */ true);
}
+ /** Tests handleIncomingUser() for a variety of permissions and situations. */
+ @Test
+ public void testHandleIncomingUser() throws Exception {
+ final UserInfo user1a = new UserInfo(111, "user1a", 0);
+ final UserInfo user1b = new UserInfo(112, "user1b", 0);
+ final UserInfo user2 = new UserInfo(113, "user2", 0);
+ // user1a and user2b are in the same profile group; user2 is in a different one.
+ user1a.profileGroupId = 5;
+ user1b.profileGroupId = 5;
+ user2.profileGroupId = 6;
+
+ final List<UserInfo> users = Arrays.asList(user1a, user1b, user2);
+ when(mInjector.mUserManagerMock.getUsers(false)).thenReturn(users);
+ mUserController.onSystemReady(); // To set the profileGroupIds in UserController.
+
+
+ // Has INTERACT_ACROSS_USERS_FULL.
+ when(mInjector.checkComponentPermission(
+ eq(INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt(), anyInt(), anyBoolean()))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mInjector.checkComponentPermission(
+ eq(INTERACT_ACROSS_USERS), anyInt(), anyInt(), anyInt(), anyBoolean()))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mInjector.checkPermissionForPreflight(
+ eq(INTERACT_ACROSS_PROFILES), anyInt(), anyInt(), any())).thenReturn(false);
+
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL, true);
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL_IN_PROFILE, true);
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_FULL_ONLY, true);
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL, true);
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL_IN_PROFILE, true);
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_FULL_ONLY, true);
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+
+ // Has INTERACT_ACROSS_USERS.
+ when(mInjector.checkComponentPermission(
+ eq(INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt(), anyInt(), anyBoolean()))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mInjector.checkComponentPermission(
+ eq(INTERACT_ACROSS_USERS), anyInt(), anyInt(), anyInt(), anyBoolean()))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+ when(mInjector.checkPermissionForPreflight(
+ eq(INTERACT_ACROSS_PROFILES), anyInt(), anyInt(), any())).thenReturn(false);
+
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL, true);
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL_IN_PROFILE, false);
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_FULL_ONLY, false);
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL, true);
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL_IN_PROFILE, true);
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_FULL_ONLY, false);
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_PROFILES_OR_NON_FULL, true);
+
+
+ // Has INTERACT_ACROSS_PROFILES.
+ when(mInjector.checkComponentPermission(
+ eq(INTERACT_ACROSS_USERS_FULL), anyInt(), anyInt(), anyInt(), anyBoolean()))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mInjector.checkComponentPermission(
+ eq(INTERACT_ACROSS_USERS), anyInt(), anyInt(), anyInt(), anyBoolean()))
+ .thenReturn(PackageManager.PERMISSION_DENIED);
+ when(mInjector.checkPermissionForPreflight(
+ eq(INTERACT_ACROSS_PROFILES), anyInt(), anyInt(), any())).thenReturn(true);
+
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL, false);
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_NON_FULL_IN_PROFILE, false);
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_FULL_ONLY, false);
+ checkHandleIncomingUser(user1a.id, user2.id, ALLOW_PROFILES_OR_NON_FULL, false);
+
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL, false);
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_NON_FULL_IN_PROFILE, false);
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_FULL_ONLY, false);
+ checkHandleIncomingUser(user1a.id, user1b.id, ALLOW_PROFILES_OR_NON_FULL, true);
+ }
+
+ private void checkHandleIncomingUser(int fromUser, int toUser, int allowMode, boolean pass) {
+ final int pid = 100;
+ final int uid = fromUser * UserHandle.PER_USER_RANGE + 34567 + fromUser;
+ final String name = "whatever";
+ final String pkg = "some.package";
+ final boolean allowAll = false;
+
+ if (pass) {
+ mUserController.handleIncomingUser(pid, uid, toUser, allowAll, allowMode, name, pkg);
+ } else {
+ assertThrows(SecurityException.class, () -> mUserController.handleIncomingUser(
+ pid, uid, toUser, allowAll, allowMode, name, pkg));
+ }
+ }
+
private void setUpAndStartUserInBackground(int userId) throws Exception {
setUpUser(userId, 0);
mUserController.startUser(userId, /* foreground= */ false);
@@ -784,6 +886,23 @@ public class UserControllerTest {
}
@Override
+ int checkComponentPermission(String permission, int pid, int uid, int owner, boolean exp) {
+ Log.i(TAG, "checkComponentPermission " + permission);
+ return PERMISSION_GRANTED;
+ }
+
+ @Override
+ boolean checkPermissionForPreflight(String permission, int pid, int uid, String pkg) {
+ Log.i(TAG, "checkPermissionForPreflight " + permission);
+ return true;
+ }
+
+ @Override
+ boolean isCallerRecents(int uid) {
+ return false;
+ }
+
+ @Override
WindowManagerService getWindowManager() {
return mWindowManagerMock;
}