From 87ed09ae425051c80ef7376d39b0bfcbf24df6b2 Mon Sep 17 00:00:00 2001 From: Hai Zhang Date: Mon, 22 Oct 2018 10:43:31 -0700 Subject: Add controller APIs for RoleManager. This change adds addRoleHolderFromController() and removeRoleHolderFromController() to RoleManager. These APIs are guarded by permission and intended to be called only by the controller service, which will only modify records inside RoleManager and won't affect the holder packages. Bug: 110557011 Test: build Change-Id: I15c6cde0b8c1c8c519ea521bc7bb6f97c7144ec4 --- api/system-current.txt | 5 +- core/java/android/app/role/IRoleManager.aidl | 4 + core/java/android/app/role/RoleManager.java | 132 ++++++++++++++++++--- .../server/role/RemoteRoleControllerService.java | 12 +- .../android/server/role/RoleManagerService.java | 33 ++++++ .../com/android/server/role/RoleUserState.java | 19 --- 6 files changed, 162 insertions(+), 43 deletions(-) diff --git a/api/system-current.txt b/api/system-current.txt index 7f5833022004..b37a32cd243a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -809,9 +809,12 @@ package android.app.role { public final class RoleManager { method public void addRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback); + method public boolean addRoleHolderFromController(java.lang.String, java.lang.String); method public void clearRoleHoldersAsUser(java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback); - method public java.util.Set getRoleHoldersAsUser(java.lang.String, android.os.UserHandle); + method public java.util.List getRoleHolders(java.lang.String); + method public java.util.List getRoleHoldersAsUser(java.lang.String, android.os.UserHandle); method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback); + method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String); field public static final java.lang.String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME"; } diff --git a/core/java/android/app/role/IRoleManager.aidl b/core/java/android/app/role/IRoleManager.aidl index 64f69c182f78..2cf13ec24141 100644 --- a/core/java/android/app/role/IRoleManager.aidl +++ b/core/java/android/app/role/IRoleManager.aidl @@ -36,4 +36,8 @@ interface IRoleManager { in IRoleManagerCallback callback); void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback); + + boolean addRoleHolderFromController(in String roleName, in String packageName); + + boolean removeRoleHolderFromController(in String roleName, in String packageName); } diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index f7c6dea21e85..ed27d9fe9fd4 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -22,19 +22,16 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; -import android.util.ArraySet; import com.android.internal.util.Preconditions; import java.util.List; -import java.util.Set; import java.util.concurrent.Executor; /** @@ -51,8 +48,8 @@ import java.util.concurrent.Executor; * role holders. To qualify for a role, an application must meet certain requirements, including * defining certain components in its manifest. These requirements can be found in the AndroidX * Libraries. Then the application will need user consent to become a role holder, which can be - * requested using {@link Activity#startActivityForResult(Intent, int)} with the {@code Intent} - * obtained from {@link #createRequestRoleIntent(String)}. + * requested using {@link android.app.Activity#startActivityForResult(Intent, int)} with the + * {@code Intent} obtained from {@link #createRequestRoleIntent(String)}. *

* Upon becoming a role holder, the application may be granted certain privileges that are role * specific. When the application loses its role, these privileges will also be revoked. @@ -89,6 +86,14 @@ public final class RoleManager { @SystemApi public static final String EXTRA_REQUEST_ROLE_NAME = "android.app.role.extra.REQUEST_ROLE_NAME"; + /** + * The permission required to manage records of role holders in {@link RoleManager} directly. + * + * @hide + */ + public static final String PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER = + "com.android.permissioncontroller.permission.MANAGE_ROLE_HOLDERS_FROM_CONTROLLER"; + @NonNull private final Context mContext; @@ -105,11 +110,13 @@ public final class RoleManager { } /** - * Returns an {@code Intent} suitable for passing to {@link Activity#startActivityForResult( - * Intent, int)} which prompts the user to grant a role to this application. + * Returns an {@code Intent} suitable for passing to + * {@link android.app.Activity#startActivityForResult(Intent, int)} which prompts the user to + * grant a role to this application. *

- * If the role is granted, the {@code resultCode} will be {@link Activity#RESULT_OK}, otherwise - * it will be {@link Activity#RESULT_CANCELED}. + * If the role is granted, the {@code resultCode} will be + * {@link android.app.Activity#RESULT_OK}, otherwise it will be + * {@link android.app.Activity#RESULT_CANCELED}. * * @param roleName the name of requested role * @@ -165,14 +172,37 @@ public final class RoleManager { /** * Get package names of the applications holding the role. *

- * Note: Using this API requires holding + * Note: Using this API requires holding + * {@code android.permission.MANAGE_ROLE_HOLDERS}. + * + * @param roleName the name of the role to get the role holder for + * + * @return a list of package names of the role holders, or an empty list if none. + * + * @throws IllegalArgumentException if the role name is {@code null} or empty. + * + * @see #getRoleHoldersAsUser(String, UserHandle) + * + * @hide + */ + @NonNull + @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) + @SystemApi + public List getRoleHolders(@NonNull String roleName) { + return getRoleHoldersAsUser(roleName, UserHandle.of(UserHandle.getCallingUserId())); + } + + /** + * Get package names of the applications holding the role. + *

+ * Note: Using this API requires holding * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user * {@code android.permission.INTERACT_ACROSS_USERS_FULL}. * * @param roleName the name of the role to get the role holder for * @param user the user to get the role holder for * - * @return the package name of the role holder, or {@code null} if none. + * @return a list of package names of the role holders, or an empty list if none. * * @throws IllegalArgumentException if the role name is {@code null} or empty. * @@ -185,23 +215,21 @@ public final class RoleManager { @NonNull @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) @SystemApi - public Set getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) { + public List getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkNotNull(user, "user cannot be null"); - List roleHolders; try { - roleHolders = mService.getRoleHoldersAsUser(roleName, user.getIdentifier()); + return mService.getRoleHoldersAsUser(roleName, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - return new ArraySet<>(roleHolders); } /** * Add a specific application to the holders of a role. If the role is exclusive, the previous * holder will be replaced. *

- * Note: Using this API requires holding + * Note: Using this API requires holding * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user * {@code android.permission.INTERACT_ACROSS_USERS_FULL}. * @@ -240,7 +268,7 @@ public final class RoleManager { /** * Remove a specific application from the holders of a role. *

- * Note: Using this API requires holding + * Note: Using this API requires holding * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user * {@code android.permission.INTERACT_ACROSS_USERS_FULL}. * @@ -279,7 +307,7 @@ public final class RoleManager { /** * Remove all holders of a role. *

- * Note: Using this API requires holding + * Note: Using this API requires holding * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user * {@code android.permission.INTERACT_ACROSS_USERS_FULL}. * @@ -312,6 +340,74 @@ public final class RoleManager { } } + /** + * Add a specific application to the holders of a role, only modifying records inside + * {@link RoleManager}. Should only be called from + * {@link android.rolecontrollerservice.RoleControllerService}. + *

+ * Note: Using this API requires holding + * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}. + * + * @param roleName the name of the role to add the role holder for + * @param packageName the package name of the application to add to the role holders + * + * @return whether the operation was successful, and will also be {@code true} if a matching + * role holder is already found. + * + * @throws IllegalArgumentException if the role name or package name is {@code null} or empty. + * + * @see #getRoleHolders(String) + * @see #removeRoleHolderFromController(String, String) + * + * @hide + */ + @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER) + @SystemApi + public boolean addRoleHolderFromController(@NonNull String roleName, + @NonNull String packageName) { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); + try { + return mService.addRoleHolderFromController(roleName, packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Remove a specific application from the holders of a role, only modifying records inside + * {@link RoleManager}. Should only be called from + * {@link android.rolecontrollerservice.RoleControllerService}. + *

+ * Note: Using this API requires holding + * {@link #PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER}. + * + * @param roleName the name of the role to remove the role holder for + * @param packageName the package name of the application to remove from the role holders + * + * @return whether the operation was successful, and will also be {@code true} if no matching + * role holder was found to remove. + * + * @throws IllegalArgumentException if the role name or package name is {@code null} or empty. + * + * @see #getRoleHolders(String) + * @see #addRoleHolderFromController(String, String) + * + * @hide + */ + @RequiresPermission(PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER) + @SystemApi + public boolean removeRoleHolderFromController(@NonNull String roleName, + @NonNull String packageName) { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); + try { + return mService.removeRoleHolderFromController(roleName, packageName); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private static class RoleManagerCallbackDelegate extends IRoleManagerCallback.Stub { @NonNull diff --git a/services/core/java/com/android/server/role/RemoteRoleControllerService.java b/services/core/java/com/android/server/role/RemoteRoleControllerService.java index c737e8bb8710..b670291ba94b 100644 --- a/services/core/java/com/android/server/role/RemoteRoleControllerService.java +++ b/services/core/java/com/android/server/role/RemoteRoleControllerService.java @@ -105,6 +105,9 @@ public class RemoteRoleControllerService { @NonNull private final Queue mPendingCalls = new ArrayDeque<>(); + @NonNull + private final Handler mMainHandler = Handler.getMain(); + @NonNull private final Runnable mUnbindRunnable = this::unbind; @@ -142,7 +145,7 @@ public class RemoteRoleControllerService { } public void enqueueCall(@NonNull Call call) { - Handler.getMain().post(PooledLambda.obtainRunnable(this::executeCall, call)); + mMainHandler.post(PooledLambda.obtainRunnable(this::executeCall, call)); } @MainThread @@ -158,7 +161,7 @@ public class RemoteRoleControllerService { @MainThread private void ensureBound() { - Handler.getMain().removeCallbacks(mUnbindRunnable); + mMainHandler.removeCallbacks(mUnbindRunnable); if (!mBound) { Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE); intent.setPackage(mContext.getPackageManager() @@ -169,9 +172,8 @@ public class RemoteRoleControllerService { } private void scheduleUnbind() { - Handler mainHandler = Handler.getMain(); - mainHandler.removeCallbacks(mUnbindRunnable); - mainHandler.postDelayed(mUnbindRunnable, UNBIND_DELAY_MILLIS); + mMainHandler.removeCallbacks(mUnbindRunnable); + mMainHandler.postDelayed(mUnbindRunnable, UNBIND_DELAY_MILLIS); } @MainThread diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index 5c9cef507b67..b7d2ce29563e 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -249,9 +249,42 @@ public class RoleManagerService extends SystemService { userId = handleIncomingUser(userId, "clearRoleHoldersAsUser"); getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, "clearRoleHoldersAsUser"); + getControllerService(userId).onClearRoleHolders(roleName, callback); } + @Override + public boolean addRoleHolderFromController(@NonNull String roleName, + @NonNull String packageName) { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); + getContext().enforceCallingOrSelfPermission( + RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER, + "addRoleHolderFromController"); + + int userId = UserHandle.getCallingUserId(); + synchronized (mLock) { + RoleUserState userState = getUserStateLocked(userId); + return userState.addRoleHolderLocked(roleName, packageName); + } + } + + @Override + public boolean removeRoleHolderFromController(@NonNull String roleName, + @NonNull String packageName) { + Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); + Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); + getContext().enforceCallingOrSelfPermission( + RoleManager.PERMISSION_MANAGE_ROLE_HOLDERS_FROM_CONTROLLER, + "removeRoleHolderFromController"); + + int userId = UserHandle.getCallingUserId(); + synchronized (mLock) { + RoleUserState userState = getUserStateLocked(userId); + return userState.removeRoleHolderLocked(roleName, packageName); + } + } + @CheckResult private int handleIncomingUser(@UserIdInt int userId, @NonNull String name) { return ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId, diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index bd5449151beb..caa7c2845ccb 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -173,25 +173,6 @@ public class RoleUserState { return true; } - /** - * Remove all holders of a role. - * - * @param roleName the name of the role to remove all its holders - * - * @return {@code false} only if the set of role holders is null, which should not happen and - * indicates an issue. - */ - @GuardedBy("RoleManagerService.mLock") - public boolean clearRoleHolderLocked(@NonNull String roleName) { - throwIfDestroyedLocked(); - ArraySet roleHolders = mRoles.get(roleName); - if (roleHolders == null) { - return false; - } - roleHolders.clear(); - return true; - } - /** * Schedule writing the state to file. */ -- cgit v1.2.3-59-g8ed1b