/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.role; import android.Manifest; import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.UserHandleAware; import android.annotation.UserIdInt; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.Build; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; import android.permission.flags.Flags; import android.util.ArrayMap; import android.util.SparseArray; import androidx.annotation.RequiresApi; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.modules.utils.build.SdkLevel; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; import java.util.function.Consumer; /** * This class provides information about and manages roles. *
* A role is a unique name within the system associated with certain privileges. The list of * available roles might change with a system app update, so apps should not make assumption about * the availability of roles. Instead, they should always query if the role is available using * {@link #isRoleAvailable(String)} before trying to do anything with it. Some predefined role names * are available as constants in this class, and a list of possibly available roles can be found in * the AndroidX Role * library. *
* There can be multiple applications qualifying for a role, but only a subset of them can become * 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 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. */ @SystemService(Context.ROLE_SERVICE) public final class RoleManager { /** * The name of the assistant app role. * * @see android.service.voice.VoiceInteractionService */ public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; /** * The name of the browser role. * * @see Intent#CATEGORY_APP_BROWSER */ public static final String ROLE_BROWSER = "android.app.role.BROWSER"; /** * The name of the dialer role. * * @see Intent#ACTION_DIAL * @see android.telecom.InCallService */ public static final String ROLE_DIALER = "android.app.role.DIALER"; /** * The name of the SMS role. * * @see Intent#CATEGORY_APP_MESSAGING */ public static final String ROLE_SMS = "android.app.role.SMS"; /** * The name of the emergency role */ public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY"; /** * The name of the home role. * * @see Intent#CATEGORY_HOME */ public static final String ROLE_HOME = "android.app.role.HOME"; /** * The name of the call redirection role. *
* A call redirection app provides a means to re-write the phone number for an outgoing call to
* place the call through a call redirection service.
*
* @see android.telecom.CallRedirectionService
*/
public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION";
/**
* The name of the call screening and caller id role.
*
* @see android.telecom.CallScreeningService
*/
public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING";
/**
* The name of the notes role.
*
* @see Intent#ACTION_CREATE_NOTE
* @see Intent#EXTRA_USE_STYLUS_MODE
*/
public static final String ROLE_NOTES = "android.app.role.NOTES";
/**
* The name of the Wallet role.
*
* @see android.nfc.cardemulation.CardEmulation
*/
@FlaggedApi(Flags.FLAG_WALLET_ROLE_ENABLED)
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
public static final String ROLE_WALLET = "android.app.role.WALLET";
/**
* The name of the system wellbeing role.
*
* @hide
*/
@SystemApi
public static final String ROLE_SYSTEM_WELLBEING = "android.app.role.SYSTEM_WELLBEING";
/**
* The name of the system supervision role.
*
* @hide
*/
@SystemApi
public static final String ROLE_SYSTEM_SUPERVISION = "android.app.role.SYSTEM_SUPERVISION";
/**
* The name of the system activity recognizer role.
*
* @hide
*/
@SystemApi
public static final String ROLE_SYSTEM_ACTIVITY_RECOGNIZER =
"android.app.role.SYSTEM_ACTIVITY_RECOGNIZER";
/**
* The name of the device policy management role.
*
* @hide
*/
@SystemApi
public static final String ROLE_DEVICE_POLICY_MANAGEMENT =
"android.app.role.DEVICE_POLICY_MANAGEMENT";
/**
* The name of the financed device kiosk role.
*
* A financed device is a device purchased through a creditor and typically paid back under an
* installment plan.
* The creditor has the ability to lock a financed device in case of payment default.
*
* @hide
*/
@SystemApi
public static final String ROLE_FINANCED_DEVICE_KIOSK =
"android.app.role.FINANCED_DEVICE_KIOSK";
/**
* The name of the system call streaming role.
*
* @hide
*/
@SystemApi
public static final String ROLE_SYSTEM_CALL_STREAMING =
"android.app.role.SYSTEM_CALL_STREAMING";
/**
* @hide
*/
@IntDef(flag = true, value = { MANAGE_HOLDERS_FLAG_DONT_KILL_APP })
@Retention(RetentionPolicy.SOURCE)
public @interface ManageHoldersFlags {}
/**
* Flag parameter for {@link #addRoleHolderAsUser}, {@link #removeRoleHolderAsUser} and
* {@link #clearRoleHoldersAsUser} to indicate that apps should not be killed when changing
* their role holder status.
*
* @hide
*/
@SystemApi
public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1;
/**
* For apps targeting Android V and above, several methods are now user-handle-aware, which
* means they use the user contained within the context. For apps targeting an SDK version
* below this, the user of the calling process will be used.
*
* @hide
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public static final long ROLE_MANAGER_USER_HANDLE_AWARE = 303742236L;
/**
* The action used to request user approval of a role for an application.
*
* @hide
*/
public static final String ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE";
/**
* The permission required to manage records of role holders in {@link RoleManager} directly.
*
* @hide
*/
public static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER =
"com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER";
@NonNull
private final Context mContext;
@NonNull
private final IRoleManager mService;
@GuardedBy("mListenersLock")
@NonNull
private final SparseArray
* 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
*
* @return the {@code Intent} to prompt user to grant the role
*/
@NonNull
public Intent createRequestRoleIntent(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Intent intent = new Intent(ACTION_REQUEST_ROLE);
intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
intent.putExtra(Intent.EXTRA_ROLE_NAME, roleName);
return intent;
}
/**
* Check whether a role is available in the system.
*
* @param roleName the name of role to checking for
*
* @return whether the role is available in the system
*/
@UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public boolean isRoleAvailable(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
UserHandle user = getContextUserIfAppropriate();
try {
return mService.isRoleAvailableAsUser(roleName, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Check whether the calling application is holding a particular role.
*
* @param roleName the name of the role to check for
*
* @return whether the calling application is holding the role
*/
@UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public boolean isRoleHeld(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
UserHandle user = getContextUserIfAppropriate();
try {
return mService.isRoleHeldAsUser(roleName, mContext.getPackageName(),
user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Get package names of the applications holding the role.
*
* 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.
*
* @see #getRoleHoldersAsUser(String, UserHandle)
*
* @hide
*/
@NonNull
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
@UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public List
* 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 a list of package names of the role holders, or an empty list if none.
*
* @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
* @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
* @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, Consumer)
*
* @hide
*/
@NonNull
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
public List
* 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 add the role holder for
* @param packageName the package name of the application to add to the role holders
* @param flags optional behavior flags
* @param user the user to add the role holder for
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
* @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, Consumer)
*
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
@ManageHoldersFlags int flags, @NonNull UserHandle user,
@CallbackExecutor @NonNull Executor executor, @NonNull Consumer
* 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 remove the role holder for
* @param packageName the package name of the application to remove from the role holders
* @param flags optional behavior flags
* @param user the user to remove the role holder for
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
* @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, Consumer)
*
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
@ManageHoldersFlags int flags, @NonNull UserHandle user,
@CallbackExecutor @NonNull Executor executor, @NonNull Consumer
* 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 remove role holders for
* @param flags optional behavior flags
* @param user the user to remove role holders for
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
* @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
*
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
public void clearRoleHoldersAsUser(@NonNull String roleName, @ManageHoldersFlags int flags,
@NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
@NonNull Consumer
* Only roles describing default applications can be used with this method. They can have
* at most one holder.
*
* @param roleName the name of the default application role to get
*
* @return a package name of the role holder or {@code null} if not set.
*
* @see #setDefaultApplication(String, String, int, Executor, Consumer)
*
* @hide
*/
@Nullable
@RequiresPermission(Manifest.permission.MANAGE_DEFAULT_APPLICATIONS)
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@UserHandleAware
@SystemApi
public String getDefaultApplication(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
try {
return mService.getDefaultApplicationAsUser(
roleName, mContext.getUser().getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Set a specific application as the default application.
*
* Only roles describing default applications can be used with this method. They can have
* at most one holder.
*
* @param roleName the name of the default application role to set the role holder for
* @param packageName the package name of the application to set as the default application,
* or {@code null} to unset.
* @param flags optional behavior flags
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
* @see #getDefaultApplication(String)
*
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_DEFAULT_APPLICATIONS)
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@UserHandleAware
@SystemApi
public void setDefaultApplication(@NonNull String roleName, @Nullable String packageName,
@ManageHoldersFlags int flags, @CallbackExecutor @NonNull Executor executor,
@NonNull Consumer
* Note: Using this API requires holding
* {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
* {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
*
* @param executor the {@code Executor} to call the listener on.
* @param listener the listener to be added
* @param user the user to add the listener for
*
* @see #removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener, UserHandle)
*
* @hide
*/
@RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
@SuppressLint("SamShouldBeLast") // TODO(b/190240500): remove this
@SystemApi
public void addOnRoleHoldersChangedListenerAsUser(@CallbackExecutor @NonNull Executor executor,
@NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
Objects.requireNonNull(executor, "executor cannot be null");
Objects.requireNonNull(listener, "listener cannot be null");
Objects.requireNonNull(user, "user cannot be null");
int userId = user.getIdentifier();
synchronized (mListenersLock) {
ArrayMap
* Note: Using this API requires holding
* {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
* {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
*
* @param listener the listener to be removed
* @param user the user to remove the listener for
*
* @see #addOnRoleHoldersChangedListenerAsUser(Executor, OnRoleHoldersChangedListener,
* UserHandle)
*
* @hide
*/
@RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
@SuppressLint("SamShouldBeLast") // TODO(b/190240500): remove this
@SystemApi
public void removeOnRoleHoldersChangedListenerAsUser(
@NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
Objects.requireNonNull(listener, "listener cannot be null");
Objects.requireNonNull(user, "user cannot be null");
int userId = user.getIdentifier();
synchronized (mListenersLock) {
ArrayMap
* Only the shell is allowed to do this, the qualification for the shell role itself cannot be
* bypassed, and each role needs to explicitly allow bypassing qualification in its definition.
* The bypass state will not be persisted across reboot.
*
* @return whether role qualification should be bypassed
*
* @hide
*/
@RequiresApi(Build.VERSION_CODES.S)
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@SystemApi
public boolean isBypassingRoleQualification() {
try {
return mService.isBypassingRoleQualification();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Set whether role qualifications should be bypassed.
*
* Only the shell is allowed to do this, the qualification for the shell role itself cannot be
* bypassed, and each role needs to explicitly allow bypassing qualification in its definition.
* The bypass state will not be persisted across reboot.
*
* @param bypassRoleQualification whether role qualification should be bypassed
*
* @hide
*/
@RequiresApi(Build.VERSION_CODES.S)
@RequiresPermission(Manifest.permission.BYPASS_ROLE_QUALIFICATION)
@SystemApi
public void setBypassingRoleQualification(boolean bypassRoleQualification) {
try {
mService.setBypassingRoleQualification(bypassRoleQualification);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Check whether role currently enables fallback to default holder.
*
* This is based on the "None" holder being actively selected, in which case don't fallback.
*
* @param roleName the name of the role being queried
*
* @return whether fallback is enabled for the provided role
*
* @hide
*/
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED)
@UserHandleAware
@SystemApi
public boolean isRoleFallbackEnabled(@NonNull String roleName) {
try {
return mService.isRoleFallbackEnabledAsUser(roleName,
mContext.getUser().getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Set whether role should fallback to a default role holder.
*
* @param roleName the name of the role being queried.
* @param fallbackEnabled whether to enable fallback holders for this role.
*
* @hide
*/
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
@FlaggedApi(Flags.FLAG_SYSTEM_SERVER_ROLE_CONTROLLER_ENABLED)
@UserHandleAware
@SystemApi
public void setRoleFallbackEnabled(@NonNull String roleName, boolean fallbackEnabled) {
try {
mService.setRoleFallbackEnabledAsUser(roleName, fallbackEnabled,
mContext.getUser().getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Set the names of all the available roles. Should only be called from
* {@link android.app.role.RoleControllerService}.
*
* Note: Using this API requires holding
* {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
*
* @param roleNames the names of all the available roles
*
* @deprecated This is only usable by the role controller service, which is an internal
* implementation detail inside role.
*
* @hide
*/
@Deprecated
@RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
@UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public void setRoleNamesFromController(@NonNull List
* Note: Using this API requires holding
* {@link #PERMISSION_MANAGE_ROLES_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.
*
* @see #getRoleHolders(String)
* @see #removeRoleHolderFromController(String, String)
*
* @deprecated This is only usable by the role controller service, which is an internal
* implementation detail inside role.
*
* @hide
*/
@Deprecated
@RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
@UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
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");
UserHandle user = getContextUserIfAppropriate();
try {
return mService.addRoleHolderFromControllerAsUser(roleName, packageName,
user.getIdentifier());
} 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.app.role.RoleControllerService}.
*
* Note: Using this API requires holding
* {@link #PERMISSION_MANAGE_ROLES_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.
*
* @see #getRoleHolders(String)
* @see #addRoleHolderFromController(String, String)
*
* @deprecated This is only usable by the role controller service, which is an internal
* implementation detail inside role.
*
* @hide
*/
@Deprecated
@RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
@UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
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");
UserHandle user = getContextUserIfAppropriate();
try {
return mService.removeRoleHolderFromControllerAsUser(roleName, packageName,
user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns the list of all roles that the given package is currently holding
*
* @param packageName the package name
* @return the list of role names
*
* @deprecated This is only usable by the role controller service, which is an internal
* implementation detail inside role.
*
* @hide
*/
@Deprecated
@NonNull
@RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@SystemApi
@UserHandleAware(enabledSinceTargetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
public List