/* * 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.permission; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.os.Build.VERSION_CODES.S; import static android.permission.flags.Flags.FLAG_SHOULD_REGISTER_ATTRIBUTION_SOURCE; import static android.permission.flags.Flags.serverSideAttributionRegistration; import android.Manifest; import android.annotation.CheckResult; import android.annotation.DurationMillisLong; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.PropertyInvalidatedCache; import android.companion.virtual.VirtualDevice; import android.companion.virtual.VirtualDeviceManager; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.content.AttributionSource; import android.content.Context; import android.content.PermissionChecker; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.pm.permission.SplitPermissionInfoParcelable; import android.media.AudioManager; import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.permission.flags.Flags; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; import android.util.Log; import android.util.Slog; import com.android.internal.R; import com.android.internal.annotations.Immutable; import com.android.internal.util.CollectionUtils; import com.android.modules.utils.build.SdkLevel; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; /** * System level service for accessing the permission capabilities of the platform. * * @hide */ @SystemApi @SystemService(Context.PERMISSION_SERVICE) public final class PermissionManager { private static final String LOG_TAG = PermissionManager.class.getName(); /** * The permission is granted. */ public static final int PERMISSION_GRANTED = 0; /** * The permission is denied. Applicable only to runtime permissions. *
* The app isn't expecting the permission to be denied so that a "no-op" action should be taken, * such as returning an empty result. */ public static final int PERMISSION_SOFT_DENIED = 1; /** * The permission is denied. *
* The app should receive a {@code SecurityException}, or an error through a relevant callback. */ public static final int PERMISSION_HARD_DENIED = 2; /** @hide */ @IntDef(prefix = { "PERMISSION_" }, value = { PERMISSION_GRANTED, PERMISSION_SOFT_DENIED, PERMISSION_HARD_DENIED }) @Retention(RetentionPolicy.SOURCE) public @interface PermissionResult {} /** * The set of flags that indicate that a permission state has been explicitly set * * @hide */ public static final int EXPLICIT_SET_FLAGS = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_GRANTED_BY_DEFAULT | FLAG_PERMISSION_GRANTED_BY_ROLE; /** * Activity action: Launch UI to review permission decisions. *
* Important:You must protect the activity that handles this action with the * {@link android.Manifest.permission#START_REVIEW_PERMISSION_DECISIONS} permission to ensure * that only the system can launch this activity. The system will not launch activities that are * not properly protected. *
* Input: Nothing. *
** Output: Nothing. *
*/ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @RequiresPermission(android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS) public static final String ACTION_REVIEW_PERMISSION_DECISIONS = "android.permission.action.REVIEW_PERMISSION_DECISIONS"; /** @hide */ public static final String LOG_TAG_TRACE_GRANTS = "PermissionGrantTrace"; /** @hide */ public static final String KILL_APP_REASON_PERMISSIONS_REVOKED = "permissions revoked"; /** @hide */ public static final String KILL_APP_REASON_GIDS_CHANGED = "permission grant or revoke changed gids"; private static final String SYSTEM_PKG = "android"; /** * Refuse to install package if groups of permissions are bad * - Permission groups should only be shared between apps sharing a certificate * - If a permission belongs to a group that group should be defined * * @hide */ @ChangeId @EnabledAfter(targetSdkVersion = S) public static final long CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS = 146211400; /** * Whether to use the new {@link com.android.server.permission.access.AccessCheckingService}. * * @hide */ public static final boolean USE_ACCESS_CHECKING_SERVICE = SdkLevel.isAtLeastV(); /** * The time to wait in between refreshing the exempted indicator role packages */ private static final long EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS = 15000; private static long sLastIndicatorUpdateTime = -1; private static final int[] EXEMPTED_ROLES = {R.string.config_systemAmbientAudioIntelligence, R.string.config_systemUiIntelligence, R.string.config_systemAudioIntelligence, R.string.config_systemNotificationIntelligence, R.string.config_systemTextIntelligence, R.string.config_systemVisualIntelligence}; private static final String[] INDICATOR_EXEMPTED_PACKAGES = new String[EXEMPTED_ROLES.length]; /** * Note: Changing this won't do anything on its own - you should also change the filtering in * {@link #shouldTraceGrant}. * * See log output for tag {@link #LOG_TAG_TRACE_GRANTS} * * @hide */ public static final boolean DEBUG_TRACE_GRANTS = false; /** * @hide */ public static final boolean DEBUG_TRACE_PERMISSION_UPDATES = false; /** * Additional debug log for virtual device permissions. * @hide */ public static final boolean DEBUG_DEVICE_PERMISSIONS = false; /** * Intent extra: List of PermissionGroupUsages *
* Type: {@code List
For example, if an app registers a location listener it should have the location * permission but no data is actually sent to the app at the moment of registration * and you should use {@link #checkPermissionForPreflight(String, AttributionSource)} * to determine if the app has or may have location permission (if app has only foreground * location the grant state depends on the app's fg/gb state) and this check will not * leave a trace that permission protected data was delivered. When you are about to * deliver the location data to a registered listener you should use this method which * will evaluate the permission access based on the current fg/bg state of the app and * leave a record that the data was accessed. * *
Requires the start of the AttributionSource chain to have the UPDATE_APP_OPS_STATS * permission for the app op accesses to be given the TRUSTED_PROXY/PROXIED flags, otherwise the * accesses will have the UNTRUSTED flags. * * @param permission The permission to check. * @param attributionSource the permission identity * @param message A message describing the reason the permission was checked * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}. * * @see #checkPermissionForPreflight(String, AttributionSource) */ @PermissionResult @RequiresPermission(value = Manifest.permission.UPDATE_APP_OPS_STATS, conditional = true) public int checkPermissionForDataDelivery(@NonNull String permission, @NonNull AttributionSource attributionSource, @Nullable String message) { return PermissionChecker.checkPermissionForDataDelivery(mContext, permission, // FIXME(b/199526514): PID should be passed inside AttributionSource. PermissionChecker.PID_UNKNOWN, attributionSource, message); } /** * * Similar to checkPermissionForDataDelivery, except it results in an app op start, rather than * a note. If this method is used, then {@link #finishDataDelivery(String, AttributionSource)} * must be used when access is finished. * * @param permission The permission to check. * @param attributionSource the permission identity * @param message A message describing the reason the permission was checked * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}. * *
Requires the start of the AttributionSource chain to have the UPDATE_APP_OPS_STATS * permission for the app op accesses to be given the TRUSTED_PROXY/PROXIED flags, otherwise the * accesses will have the UNTRUSTED flags. * * @see #checkPermissionForDataDelivery(String, AttributionSource, String) */ @PermissionResult @RequiresPermission(value = Manifest.permission.UPDATE_APP_OPS_STATS, conditional = true) public int checkPermissionForStartDataDelivery(@NonNull String permission, @NonNull AttributionSource attributionSource, @Nullable String message) { return PermissionChecker.checkPermissionForDataDelivery(mContext, permission, // FIXME(b/199526514): PID should be passed inside AttributionSource. PermissionChecker.PID_UNKNOWN, attributionSource, message, true); } /** * Indicate that usage has finished for an {@link AttributionSource} started with * {@link #checkPermissionForStartDataDelivery(String, AttributionSource, String)} * * @param permission The permission to check. * @param attributionSource the permission identity to finish */ public void finishDataDelivery(@NonNull String permission, @NonNull AttributionSource attributionSource) { PermissionChecker.finishDataDelivery(mContext, AppOpsManager.permissionToOp(permission), attributionSource); } /** * Checks whether a given data access chain described by the given {@link AttributionSource} * has a given permission. Call this method if you are the datasource which would not blame you * for access to the data since you are the data. Use this API if you are the datasource of the * protected state. * * NOTE: Use this method only for permission checks at the * point where you will deliver the permission protected data to clients. * *
For example, if an app registers a location listener it should have the location * permission but no data is actually sent to the app at the moment of registration * and you should use {@link #checkPermissionForPreflight(String, AttributionSource)} * to determine if the app has or may have location permission (if app has only foreground * location the grant state depends on the app's fg/gb state) and this check will not * leave a trace that permission protected data was delivered. When you are about to * deliver the location data to a registered listener you should use this method which * will evaluate the permission access based on the current fg/bg state of the app and * leave a record that the data was accessed. * *
Requires the start of the AttributionSource chain to have the UPDATE_APP_OPS_STATS * permission for the app op accesses to be given the TRUSTED_PROXY/PROXIED flags, otherwise the * accesses will have the UNTRUSTED flags. * * @param permission The permission to check. * @param attributionSource the permission identity * @param message A message describing the reason the permission was checked * @return The permission check result which is either {@link #PERMISSION_GRANTED} * or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}. * * @see #checkPermissionForPreflight(String, AttributionSource) */ @PermissionResult @RequiresPermission(value = Manifest.permission.UPDATE_APP_OPS_STATS, conditional = true) public int checkPermissionForDataDeliveryFromDataSource(@NonNull String permission, @NonNull AttributionSource attributionSource, @Nullable String message) { return PermissionChecker.checkPermissionForDataDeliveryFromDataSource(mContext, permission, PermissionChecker.PID_UNKNOWN, attributionSource, message); } /** * Checks whether a given data access chain described by the given {@link AttributionSource} * has a given permission. * * NOTE: Use this method only for permission checks at the * preflight point where you will not deliver the permission protected data * to clients but schedule permission data delivery, apps register listeners, * etc. * *
For example, if an app registers a data listener it should have the required
* permission but no data is actually sent to the app at the moment of registration
* and you should use this method to determine if the app has or may have the
* permission and this check will not leave a trace that permission protected data
* was delivered. When you are about to deliver the protected data to a registered
* listener you should use {@link #checkPermissionForDataDelivery(String,
* AttributionSource, String)} which will evaluate the permission access based
* on the current fg/bg state of the app and leave a record that the data was accessed.
*
* @param permission The permission to check.
* @param attributionSource The identity for which to check the permission.
* @return The permission check result which is either {@link #PERMISSION_GRANTED}
* or {@link #PERMISSION_SOFT_DENIED} or {@link #PERMISSION_HARD_DENIED}.
*/
@PermissionResult
public int checkPermissionForPreflight(@NonNull String permission,
@NonNull AttributionSource attributionSource) {
return PermissionChecker.checkPermissionForPreflight(mContext, permission,
attributionSource);
}
/**
* Retrieve all of the information we know about a particular permission.
*
* @param permissionName the fully qualified name (e.g. com.android.permission.LOGIN) of the
* permission you are interested in
* @param flags additional option flags to modify the data returned
* @return a {@link PermissionInfo} containing information about the permission, or {@code null}
* if not found
*
* @hide Pending API
*/
@Nullable
public PermissionInfo getPermissionInfo(@NonNull String permissionName,
@PackageManager.PermissionInfoFlags int flags) {
try {
final String packageName = mContext.getOpPackageName();
return mPermissionManager.getPermissionInfo(permissionName, packageName, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Query for all of the permissions associated with a particular group.
*
* @param groupName the fully qualified name (e.g. com.android.permission.LOGIN) of the
* permission group you are interested in. Use {@code null} to find all of the
* permissions not associated with a group
* @param flags additional option flags to modify the data returned
* @return a list of {@link PermissionInfo} containing information about all of the permissions
* in the given group, or {@code null} if the group is not found
*
* @hide Pending API
*/
@Nullable
public List
* It is good to make your permission tree name descriptive, because you are taking possession
* of that entire set of permission names. Thus, it must be under a domain you control, with a
* suffix that will not match any normal permissions that may be declared in any applications
* that are part of that domain.
*
* New permissions must be added before any .apks are installed that use those permissions.
* Permissions you add through this method are remembered across reboots of the device. If the
* given permission already exists, the info you supply here will be used to update it.
*
* @param permissionInfo description of the permission to be added
* @param async whether the persistence of the permission should be asynchronous, allowing it to
* return quicker and batch a series of adds, at the expense of no guarantee the
* added permission will be retained if the device is rebooted before it is
* written.
* @return {@code true} if a new permission was created, {@code false} if an existing one was
* updated
* @throws SecurityException if you are not allowed to add the given permission name
*
* @see #removePermission(String)
*
* @hide Pending API
*/
public boolean addPermission(@NonNull PermissionInfo permissionInfo, boolean async) {
try {
return mPermissionManager.addPermission(permissionInfo, async);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes a permission that was previously added with
* {@link #addPermission(PermissionInfo, boolean)}. The same ownership rules apply -- you are
* only allowed to remove permissions that you are allowed to add.
*
* @param permissionName the name of the permission to remove
* @throws SecurityException if you are not allowed to remove the given permission name
*
* @see #addPermission(PermissionInfo, boolean)
*
* @hide Pending API
*/
public void removePermission(@NonNull String permissionName) {
try {
mPermissionManager.removePermission(permissionName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Retrieve all of the information we know about a particular group of permissions.
*
* @param groupName the fully qualified name (e.g. com.android.permission_group.APPS) of the
* permission you are interested in
* @param flags additional option flags to modify the data returned
* @return a {@link PermissionGroupInfo} containing information about the permission, or
* {@code null} if not found
*
* @hide Pending API
*/
@Nullable
public PermissionGroupInfo getPermissionGroupInfo(@NonNull String groupName,
@PackageManager.PermissionGroupInfoFlags int flags) {
try {
return mPermissionManager.getPermissionGroupInfo(groupName, flags);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Retrieve all of the known permission groups in the system.
*
* @param flags additional option flags to modify the data returned
* @return a list of {@link PermissionGroupInfo} containing information about all of the known
* permission groups
*
* @hide Pending API
*/
@NonNull
public List
* Note: Using this API requires holding
* {@code android.permission.GRANT_RUNTIME_PERMISSIONS} and if the user ID is not the current
* user {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
*
* @param packageName the package to which to grant the permission
* @param permissionName the permission name to grant
* @param user the user for which to grant the permission
*
* @see #revokeRuntimePermission(String, String, android.os.UserHandle, String)
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
//@SystemApi
public void grantRuntimePermission(@NonNull String packageName,
@NonNull String permissionName, @NonNull UserHandle user) {
String persistentDeviceId = getPersistentDeviceId(mContext.getDeviceId());
if (persistentDeviceId == null) {
return;
}
grantRuntimePermissionInternal(packageName, permissionName, persistentDeviceId, user);
}
/**
* Grant a runtime permission to an application which the application does not already have. The
* permission must have been requested by the application. If the application is not allowed to
* hold the permission, a {@link java.lang.SecurityException} is thrown. If the package or
* permission is invalid, a {@link java.lang.IllegalArgumentException} is thrown.
*
* @param packageName the package to which to grant the permission
* @param permissionName the permission name to grant
* @param persistentDeviceId the device Id to which to grant the permission
*
* @see #revokeRuntimePermission(String, String, String, String)
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
@SystemApi
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public void grantRuntimePermission(@NonNull String packageName,
@NonNull String permissionName, @NonNull String persistentDeviceId) {
grantRuntimePermissionInternal(packageName, permissionName, persistentDeviceId,
mContext.getUser());
}
private void grantRuntimePermissionInternal(@NonNull String packageName,
@NonNull String permissionName, @NonNull String persistentDeviceId,
@NonNull UserHandle user) {
if (DEBUG_TRACE_GRANTS
&& shouldTraceGrant(packageName, permissionName, user.getIdentifier())) {
Log.i(LOG_TAG_TRACE_GRANTS, "App " + mContext.getPackageName() + " is granting "
+ packageName + " "
+ permissionName + " for user " + user.getIdentifier()
+ " for persistent device " + persistentDeviceId, new RuntimeException());
}
try {
mPermissionManager.grantRuntimePermission(packageName, permissionName,
persistentDeviceId, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Revoke a runtime permission that was previously granted by
* {@link #grantRuntimePermission(String, String, android.os.UserHandle)}. The permission must
* have been requested by and granted to the application. If the application is not allowed to
* hold the permission, a {@link java.lang.SecurityException} is thrown. If the package or
* permission is invalid, a {@link java.lang.IllegalArgumentException} is thrown.
*
* Note: Using this API requires holding
* {@code android.permission.REVOKE_RUNTIME_PERMISSIONS} and if the user ID is not the current
* user {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
*
* @param packageName the package from which to revoke the permission
* @param permissionName the permission name to revoke
* @param user the user for which to revoke the permission
* @param reason the reason for the revoke, or {@code null} for unspecified
*
* @see #grantRuntimePermission(String, String, android.os.UserHandle)
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
//@SystemApi
public void revokeRuntimePermission(@NonNull String packageName,
@NonNull String permissionName, @NonNull UserHandle user, @Nullable String reason) {
String persistentDeviceId = getPersistentDeviceId(mContext.getDeviceId());
if (persistentDeviceId == null) {
return;
}
revokeRuntimePermissionInternal(packageName, permissionName, persistentDeviceId, user,
reason);
}
/**
* Revoke a runtime permission that was previously granted by
* {@link #grantRuntimePermission(String, String, String)}. The permission must
* have been requested by and granted to the application. If the application is not allowed to
* hold the permission, a {@link java.lang.SecurityException} is thrown. If the package or
* permission is invalid, a {@link java.lang.IllegalArgumentException} is thrown.
*
* @param packageName the package from which to revoke the permission
* @param permissionName the permission name to revoke
* @param persistentDeviceId the persistent device id for which to revoke the permission
* @param reason the reason for the revoke, or {@code null} for unspecified
*
* @see #grantRuntimePermission(String, String, String)
*
* @hide
*/
@RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
@SystemApi
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public void revokeRuntimePermission(@NonNull String packageName,
@NonNull String permissionName, @NonNull String persistentDeviceId,
@Nullable String reason) {
revokeRuntimePermissionInternal(packageName, permissionName, persistentDeviceId,
mContext.getUser(), reason);
}
private void revokeRuntimePermissionInternal(@NonNull String packageName,
@NonNull String permissionName, @NonNull String persistentDeviceId,
@NonNull UserHandle user, @Nullable String reason) {
if (DEBUG_TRACE_PERMISSION_UPDATES
&& shouldTraceGrant(packageName, permissionName, user.getIdentifier())) {
Log.i(LOG_TAG, "App " + mContext.getPackageName() + " is revoking " + packageName + " "
+ permissionName + " for user " + user.getIdentifier()
+ " for persistent device "
+ persistentDeviceId + " with reason "
+ reason, new RuntimeException());
}
try {
mPermissionManager.revokeRuntimePermission(packageName, permissionName,
persistentDeviceId, user.getIdentifier(), reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Gets the state flags associated with a permission.
*
* @param packageName the package name for which to get the flags
* @param permissionName the permission for which to get the flags
* @param user the user for which to get permission flags
* @return the permission flags
*
* @hide
*/
@PackageManager.PermissionFlags
@RequiresPermission(anyOf = {
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
android.Manifest.permission.GET_RUNTIME_PERMISSIONS
})
//@SystemApi
public int getPermissionFlags(@NonNull String packageName, @NonNull String permissionName,
@NonNull UserHandle user) {
String persistentDeviceId = getPersistentDeviceId(mContext.getDeviceId());
if (persistentDeviceId == null) {
return 0;
}
return getPermissionFlagsInternal(packageName, permissionName, persistentDeviceId, user);
}
/**
* Gets the state flags associated with a permission.
*
* @param packageName the package name for which to get the flags
* @param permissionName the permission for which to get the flags
* @param persistentDeviceId the persistent device Id for which to get permission flags
* @return the permission flags
*
* @hide
*/
@PackageManager.PermissionFlags
@RequiresPermission(anyOf = {
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
android.Manifest.permission.GET_RUNTIME_PERMISSIONS
})
@SystemApi
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public int getPermissionFlags(@NonNull String packageName, @NonNull String permissionName,
@NonNull String persistentDeviceId) {
return getPermissionFlagsInternal(packageName, permissionName, persistentDeviceId,
mContext.getUser());
}
private int getPermissionFlagsInternal(@NonNull String packageName,
@NonNull String permissionName, @NonNull String persistentDeviceId,
@NonNull UserHandle user) {
try {
return mPermissionManager.getPermissionFlags(packageName, permissionName,
persistentDeviceId, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Updates the flags associated with a permission by replacing the flags in the specified mask
* with the provided flag values.
*
* @param packageName The package name for which to update the flags
* @param permissionName The permission for which to update the flags
* @param flagMask The flags which to replace
* @param flagValues The flags with which to replace
* @param user The user for which to update the permission flags
*
* @hide
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
})
//@SystemApi
public void updatePermissionFlags(@NonNull String packageName, @NonNull String permissionName,
@PackageManager.PermissionFlags int flagMask,
@PackageManager.PermissionFlags int flagValues, @NonNull UserHandle user) {
String persistentDeviceId = getPersistentDeviceId(mContext.getDeviceId());
if (persistentDeviceId == null) {
return;
}
updatePermissionFlagsInternal(packageName, permissionName, flagMask, flagValues,
persistentDeviceId, user);
}
/**
* Updates the flags associated with a permission by replacing the flags in the specified mask
* with the provided flag values.
*
* @param packageName The package name for which to update the flags
* @param permissionName The permission for which to update the flags
* @param persistentDeviceId The persistent device for which to update the permission flags
* @param flagMask The flags which to replace
* @param flagValues The flags with which to replace
*
* @hide
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS
})
@SystemApi
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public void updatePermissionFlags(@NonNull String packageName, @NonNull String permissionName,
@NonNull String persistentDeviceId,
@PackageManager.PermissionFlags int flagMask,
@PackageManager.PermissionFlags int flagValues
) {
updatePermissionFlagsInternal(packageName, permissionName, flagMask, flagValues,
persistentDeviceId, mContext.getUser());
}
private void updatePermissionFlagsInternal(@NonNull String packageName,
@NonNull String permissionName,
@PackageManager.PermissionFlags int flagMask,
@PackageManager.PermissionFlags int flagValues, @NonNull String persistentDeviceId,
@NonNull UserHandle user
) {
if (DEBUG_TRACE_PERMISSION_UPDATES && shouldTraceGrant(packageName, permissionName,
user.getIdentifier())) {
Log.i(LOG_TAG, "App " + mContext.getPackageName() + " is updating flags for "
+ packageName + " " + permissionName + " for user "
+ user.getIdentifier() + " for persistentDeviceId " + persistentDeviceId + ": "
+ DebugUtils.flagsToString(PackageManager.class, "FLAG_PERMISSION_", flagMask)
+ " := " + DebugUtils.flagsToString(PackageManager.class, "FLAG_PERMISSION_",
flagValues), new RuntimeException());
}
try {
final boolean checkAdjustPolicyFlagPermission =
mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.Q;
mPermissionManager.updatePermissionFlags(packageName, permissionName, flagMask,
flagValues, checkAdjustPolicyFlagPermission,
persistentDeviceId, user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Gets the restricted permissions that have been allowlisted and the app is allowed to have
* them granted in their full form.
*
* Permissions can be hard restricted which means that the app cannot hold them or soft
* restricted where the app can hold the permission but in a weaker form. Whether a permission
* is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard restricted} or
* {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted} depends on the permission
* declaration. Allowlisting a hard restricted permission allows for the to hold that permission
* and allowlisting a soft restricted permission allows the app to hold the permission in its
* full, unrestricted form.
*
* There are four allowlists:
*
* Permissions can be hard restricted which means that the app cannot hold them or soft
* restricted where the app can hold the permission but in a weaker form. Whether a permission
* is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard restricted} or
* {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted} depends on the permission
* declaration. Allowlisting a hard restricted permission allows for the to hold that permission
* and allowlisting a soft restricted permission allows the app to hold the permission in its
* full, unrestricted form.
* There are four allowlists:
*
* You need to specify the allowlists for which to set the allowlisted permissions which will
* clear the previous allowlisted permissions and replace them with the provided ones.
*
* @param packageName the app for which to get allowlisted permissions
* @param permissionName the allowlisted permission to add
* @param allowlistFlags the allowlists to which to add. Passing multiple flags updates all
* specified allowlists.
* @return whether the permission was added to the allowlist
* @throws SecurityException if you try to modify a allowlist that you have no access to.
*
* @see #getAllowlistedRestrictedPermissions(String, int)
* @see #removeAllowlistedRestrictedPermission(String, String, int)
* @see PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM
* @see PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE
* @see PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER
*
* @hide Pending API
*/
@RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS,
conditional = true)
public boolean addAllowlistedRestrictedPermission(@NonNull String packageName,
@NonNull String permissionName,
@PackageManager.PermissionWhitelistFlags int allowlistFlags) {
try {
return mPermissionManager.addAllowlistedRestrictedPermission(packageName,
permissionName, allowlistFlags, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes a allowlisted restricted permission for an app.
*
* Permissions can be hard restricted which means that the app cannot hold them or soft
* restricted where the app can hold the permission but in a weaker form. Whether a permission
* is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard restricted} or
* {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted} depends on the permission
* declaration. Allowlisting a hard restricted permission allows for the to hold that permission
* and allowlisting a soft restricted permission allows the app to hold the permission in its
* full, unrestricted form.
* There are four allowlists:
*
* You need to specify the allowlists for which to set the allowlisted permissions which will
* clear the previous allowlisted permissions and replace them with the provided ones.
*
* @param packageName the app for which to get allowlisted permissions
* @param permissionName the allowlisted permission to remove
* @param allowlistFlags the allowlists from which to remove. Passing multiple flags updates all
* specified allowlists.
* @return whether the permission was removed from the allowlist
* @throws SecurityException if you try to modify a allowlist that you have no access to.
*
* @see #getAllowlistedRestrictedPermissions(String, int)
* @see #addAllowlistedRestrictedPermission(String, String, int)
* @see PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM
* @see PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE
* @see PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER
*
* @hide Pending API
*/
@RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS,
conditional = true)
public boolean removeAllowlistedRestrictedPermission(@NonNull String packageName,
@NonNull String permissionName,
@PackageManager.PermissionWhitelistFlags int allowlistFlags) {
try {
return mPermissionManager.removeAllowlistedRestrictedPermission(packageName,
permissionName, allowlistFlags, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Checks whether an application is exempted from having its permissions be automatically
* revoked when the app is unused for an extended period of time.
*
* Only the installer on record that installed the given package, or a holder of
* {@code WHITELIST_AUTO_REVOKE_PERMISSIONS} is allowed to call this.
*
* @param packageName the app for which to set exemption
* @return whether the app is exempted
* @throws SecurityException if you you have no access to this
*
* @see #setAutoRevokeExempted
*
* @hide Pending API
*/
@RequiresPermission(value = Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS,
conditional = true)
public boolean isAutoRevokeExempted(@NonNull String packageName) {
try {
return mPermissionManager.isAutoRevokeExempted(packageName, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Marks an application exempted from having its permissions be automatically revoked when the
* app is unused for an extended period of time.
*
* Only the installer on record that installed the given package is allowed to call this.
*
* Packages start in exempted state, and it is the installer's responsibility to un-exempt the
* packages it installs, unless auto-revoking permissions from that package would cause
* breakages beyond having to re-request the permission(s).
*
* @param packageName the app for which to set exemption
* @param exempted whether the app should be exempted
* @return whether any change took effect
* @throws SecurityException if you you have no access to modify this
*
* @see #isAutoRevokeExempted
*
* @hide Pending API
*/
@RequiresPermission(value = Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS,
conditional = true)
public boolean setAutoRevokeExempted(@NonNull String packageName, boolean exempted) {
try {
return mPermissionManager.setAutoRevokeExempted(packageName, exempted,
mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Get whether you should show UI with rationale for requesting a permission. You should do this
* only if you do not have the permission and the context in which the permission is requested
* does not clearly communicate to the user what would be the benefit from grating this
* permission.
*
* @param permissionName a permission your app wants to request
* @return whether you can show permission rationale UI
*
* @hide
*/
//@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public boolean shouldShowRequestPermissionRationale(@NonNull String permissionName) {
try {
final String packageName = mContext.getPackageName();
return mPermissionManager.shouldShowRequestPermissionRationale(packageName,
permissionName, mContext.getDeviceId(), mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Add a listener for permission changes for installed packages.
*
* @param listener the listener to add
*
* @hide
*/
//@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
public void addOnPermissionsChangeListener(
@NonNull PackageManager.OnPermissionsChangedListener listener) {
synchronized (mPermissionListeners) {
if (mPermissionListeners.get(listener) != null) {
return;
}
final OnPermissionsChangeListenerDelegate delegate =
new OnPermissionsChangeListenerDelegate(listener, Looper.getMainLooper());
try {
mPermissionManager.addOnPermissionsChangeListener(delegate);
mPermissionListeners.put(listener, delegate);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/**
* Remove a listener for permission changes for installed packages.
*
* @param listener the listener to remove
*
* @hide
*/
//@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
public void removeOnPermissionsChangeListener(
@NonNull PackageManager.OnPermissionsChangedListener listener) {
synchronized (mPermissionListeners) {
final IOnPermissionsChangeListener delegate = mPermissionListeners.get(listener);
if (delegate != null) {
try {
mPermissionManager.removeOnPermissionsChangeListener(delegate);
mPermissionListeners.remove(listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
}
/**
* Gets the version of the runtime permission database.
*
* @return The database version, -1 when this is an upgrade from pre-Q, 0 when this is a fresh
* install.
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS
})
public @IntRange(from = 0) int getRuntimePermissionsVersion() {
try {
return mPackageManager.getRuntimePermissionsVersion(mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets the version of the runtime permission database.
*
* @param version The new version.
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS
})
public void setRuntimePermissionsVersion(@IntRange(from = 0) int version) {
try {
mPackageManager.setRuntimePermissionsVersion(version, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Get set of permissions that have been split into more granular or dependent permissions.
*
* E.g. before {@link android.os.Build.VERSION_CODES#Q} an app that was granted
* {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access the location while it was in
* foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#Q}
* the location permission only grants location access while the app is in foreground. This
* would break apps that target before {@link android.os.Build.VERSION_CODES#Q}. Hence whenever
* such an old app asks for a location permission (i.e. the
* {@link SplitPermissionInfo#getSplitPermission()}), then the
* {@link Manifest.permission#ACCESS_BACKGROUND_LOCATION} permission (inside
* {@link SplitPermissionInfo#getNewPermissions}) is added.
*
* Note: Regular apps do not have to worry about this. The platform and permission controller
* automatically add the new permissions where needed.
*
* @return All permissions that are split.
*/
public @NonNull List
* When this timeoutMillis is reached if the importance level is <= importanceToKeepSessionAlive
* then the session is extended until either the importance goes above
* importanceToKeepSessionAlive which will end the session or <= importanceToResetTimer which
* will continue the session and reset the timer.
*
* Importance levels are defined in {@link android.app.ActivityManager.RunningAppProcessInfo}.
*
* Once the session ends
* {@link PermissionControllerService#onOneTimePermissionSessionTimeout(String)} is invoked.
*
* Note that if there is currently an active session for a package a new one isn't created but
* each parameter of the existing one will be updated to the more aggressive of both sessions.
* This means that durations will be set to the shortest parameter and importances will be set
* to the lowest one.
*
* Note: Default device permissions are not inherited in this API. Returns the
* exact permission states for the requested device.
*
* @param packageName name of the package you are checking against
* @param persistentDeviceId id of the persistent device you are checking against
* @return mapping of all permission states keyed by their permission names
*
* @hide
*/
@SystemApi
@NonNull
@RequiresPermission(anyOf = {
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
android.Manifest.permission.GET_RUNTIME_PERMISSIONS
})
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public Map
* Note: This API returns the underlying permission state
* as-is and is mostly intended for permission managing system apps. To
* perform an access check for a certain app, please use the
* {@link Context#checkPermission} APIs instead.
*
* @param permissionName The name of the permission you are checking for.
* @param packageName The name of the package you are checking against.
* @param persistentDeviceId The id of the physical device that you are checking permission
* against.
*
* @return If the package has the permission on the device, PERMISSION_GRANTED is
* returned. If it does not have the permission on the device, PERMISSION_DENIED
* is returned.
*
* @see VirtualDevice#getPersistentDeviceId()
* @see PackageManager#PERMISSION_GRANTED
* @see PackageManager#PERMISSION_DENIED
*
* @hide
*/
@SystemApi
@PermissionResult
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public int checkPermission(@NonNull String permissionName, @NonNull String packageName,
@NonNull String persistentDeviceId) {
return sPackageNamePermissionCache.query(
new PackageNamePermissionQuery(permissionName, packageName, persistentDeviceId,
mContext.getUserId()));
}
/**
* Make checkPackageNamePermission() bypass the cache in this process.
*
* @hide
*/
public static void disablePackageNamePermissionCache() {
sPackageNamePermissionCache.disableLocal();
}
private final class OnPermissionsChangeListenerDelegate
extends IOnPermissionsChangeListener.Stub implements Handler.Callback {
private static final int MSG_PERMISSIONS_CHANGED = 1;
private final PackageManager.OnPermissionsChangedListener mListener;
private final Handler mHandler;
public OnPermissionsChangeListenerDelegate(
PackageManager.OnPermissionsChangedListener listener, Looper looper) {
mListener = listener;
mHandler = new Handler(looper, this);
}
@Override
public void onPermissionsChanged(int uid, String persistentDeviceId) {
mHandler.obtainMessage(MSG_PERMISSIONS_CHANGED, uid, 0, persistentDeviceId)
.sendToTarget();
}
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_PERMISSIONS_CHANGED: {
final int uid = msg.arg1;
final String persistentDeviceId = msg.obj.toString();
mListener.onPermissionsChanged(uid, persistentDeviceId);
return true;
}
default:
return false;
}
}
}
/**
* Data class for the state of a permission requested by a package
*
* @hide
*/
@SystemApi
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public static final class PermissionState implements Parcelable {
private final boolean mGranted;
private final int mFlags;
/** @hide */
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public PermissionState(boolean granted, int flags) {
mGranted = granted;
mFlags = flags;
}
/**
* Returns whether this permission is granted
*/
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public boolean isGranted() {
return mGranted;
}
/**
* Returns the flags associated with this permission state
* @see PackageManager#getPermissionFlags
*/
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public int getFlags() {
return mFlags;
}
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
@Override
public int describeContents() {
return 0;
}
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
parcel.writeBoolean(mGranted);
parcel.writeInt(mFlags);
}
private PermissionState(Parcel parcel) {
this(parcel.readBoolean(), parcel.readInt());
}
@FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
public static final @NonNull Creator
*
*
* @param packageName the app for which to get allowlisted permissions
* @param allowlistFlag the flag to determine which allowlist to query. Only one flag can be
* passed.
* @return the allowlisted permissions that are on any of the allowlists you query for
* @throws SecurityException if you try to access a allowlist that you have no access to
*
* @see #addAllowlistedRestrictedPermission(String, String, int)
* @see #removeAllowlistedRestrictedPermission(String, String, int)
* @see PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM
* @see PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE
* @see PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER
*
* @hide Pending API
*/
@NonNull
@RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS,
conditional = true)
public Set
*
*
*
*