2137 lines
90 KiB
Java
2137 lines
90 KiB
Java
![]() |
/*
|
||
|
* 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.
|
||
|
* <p>
|
||
|
* 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.
|
||
|
* <p>
|
||
|
* 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.
|
||
|
* <p>
|
||
|
* <strong>Important:</strong>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.
|
||
|
* <p>
|
||
|
* Input: Nothing.
|
||
|
* </p>
|
||
|
* <p>
|
||
|
* Output: Nothing.
|
||
|
* </p>
|
||
|
*/
|
||
|
@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
|
||
|
* <p>
|
||
|
* Type: {@code List<PermissionGroupUsage>}
|
||
|
* </p>
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public static final String EXTRA_PERMISSION_USAGES =
|
||
|
"android.permission.extra.PERMISSION_USAGES";
|
||
|
|
||
|
/**
|
||
|
* Specify what permissions are device aware. Only device aware permissions can be granted to
|
||
|
* a remote device.
|
||
|
* @hide
|
||
|
*/
|
||
|
public static final Set<String> DEVICE_AWARE_PERMISSIONS =
|
||
|
Flags.deviceAwarePermissionsEnabled()
|
||
|
? Set.of(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
|
||
|
: Collections.emptySet();
|
||
|
|
||
|
private final @NonNull Context mContext;
|
||
|
|
||
|
private final IPackageManager mPackageManager;
|
||
|
|
||
|
private final IPermissionManager mPermissionManager;
|
||
|
|
||
|
private final LegacyPermissionManager mLegacyPermissionManager;
|
||
|
|
||
|
private final ArrayMap<PackageManager.OnPermissionsChangedListener,
|
||
|
IOnPermissionsChangeListener> mPermissionListeners = new ArrayMap<>();
|
||
|
private PermissionUsageHelper mUsageHelper;
|
||
|
|
||
|
private List<SplitPermissionInfo> mSplitPermissionInfos;
|
||
|
|
||
|
/**
|
||
|
* Creates a new instance.
|
||
|
*
|
||
|
* @param context The current context in which to operate
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
public PermissionManager(@NonNull Context context)
|
||
|
throws ServiceManager.ServiceNotFoundException {
|
||
|
mContext = context;
|
||
|
mPackageManager = AppGlobals.getPackageManager();
|
||
|
mPermissionManager = IPermissionManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
|
||
|
"permissionmgr"));
|
||
|
mLegacyPermissionManager = context.getSystemService(LegacyPermissionManager.class);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks whether a given data access chain described by the given {@link AttributionSource}
|
||
|
* has a given permission.
|
||
|
*
|
||
|
* <strong>NOTE:</strong> Use this method only for permission checks at the
|
||
|
* point where you will deliver the permission protected data to clients.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>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}.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <strong>NOTE:</strong> Use this method only for permission checks at the
|
||
|
* point where you will deliver the permission protected data to clients.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <strong>NOTE:</strong> 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.
|
||
|
*
|
||
|
* <p>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<PermissionInfo> queryPermissionsByGroup(@Nullable String groupName,
|
||
|
@PackageManager.PermissionInfoFlags int flags) {
|
||
|
try {
|
||
|
final ParceledListSlice<PermissionInfo> parceledList =
|
||
|
mPermissionManager.queryPermissionsByGroup(groupName, flags);
|
||
|
if (parceledList == null) {
|
||
|
return null;
|
||
|
}
|
||
|
return parceledList.getList();
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a new dynamic permission to the system. For this to work, your package must have defined
|
||
|
* a permission tree through the
|
||
|
* {@link android.R.styleable#AndroidManifestPermissionTree <permission-tree>} tag in its
|
||
|
* manifest. A package can only add permissions to trees that were defined by either its own
|
||
|
* package or another with the same user id; a permission is in a tree if it matches the name of
|
||
|
* the permission tree + ".": for example, "com.foo.bar" is a member of the permission tree
|
||
|
* "com.foo".
|
||
|
* <p>
|
||
|
* 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.
|
||
|
* <p>
|
||
|
* 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<PermissionGroupInfo> getAllPermissionGroups(
|
||
|
@PackageManager.PermissionGroupInfoFlags int flags) {
|
||
|
try {
|
||
|
final ParceledListSlice<PermissionGroupInfo> parceledList =
|
||
|
mPermissionManager.getAllPermissionGroups(flags);
|
||
|
if (parceledList == null) {
|
||
|
return Collections.emptyList();
|
||
|
}
|
||
|
return parceledList.getList();
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks whether a particular permissions has been revoked for a package by policy. Typically
|
||
|
* the device owner or the profile owner may apply such a policy. The user cannot grant policy
|
||
|
* revoked permissions, hence the only way for an app to get such a permission is by a policy
|
||
|
* change.
|
||
|
*
|
||
|
* @param packageName the name of the package you are checking against
|
||
|
* @param permissionName the name of the permission you are checking for
|
||
|
*
|
||
|
* @return whether the permission is restricted by policy
|
||
|
*
|
||
|
* @hide Pending API
|
||
|
*/
|
||
|
@CheckResult
|
||
|
public boolean isPermissionRevokedByPolicy(@NonNull String packageName,
|
||
|
@NonNull String permissionName) {
|
||
|
try {
|
||
|
return mPermissionManager.isPermissionRevokedByPolicy(packageName, permissionName,
|
||
|
mContext.getDeviceId(), mContext.getUserId());
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public static boolean shouldTraceGrant(
|
||
|
@NonNull String packageName, @NonNull String permissionName, int userId) {
|
||
|
// To be modified when debugging
|
||
|
// template: if ("".equals(packageName) && "".equals(permissionName)) return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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.
|
||
|
* <p>
|
||
|
* <strong>Note: </strong>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.
|
||
|
* <p>
|
||
|
* <strong>Note: </strong>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.
|
||
|
* <p>
|
||
|
* 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.
|
||
|
* <p>
|
||
|
* There are four allowlists:
|
||
|
* <ol>
|
||
|
* <li>
|
||
|
* One for cases where the system permission policy allowlists a permission. This list
|
||
|
* corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM} flag. Can only be
|
||
|
* accessed by pre-installed holders of a dedicated permission.
|
||
|
* <li>
|
||
|
* One for cases where the system allowlists the permission when upgrading from an OS version in
|
||
|
* which the permission was not restricted to an OS version in which the permission is
|
||
|
* restricted. This list corresponds to the
|
||
|
* {@link PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE} flag. Can be accessed by
|
||
|
* pre-installed holders of a dedicated permission or the installer on record.
|
||
|
* <li>
|
||
|
* One for cases where the installer of the package allowlists a permission. This list
|
||
|
* corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER} flag. Can be
|
||
|
* accessed by pre-installed holders of a dedicated permission or the installer on record.
|
||
|
* </ol>
|
||
|
*
|
||
|
* @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<String> getAllowlistedRestrictedPermissions(@NonNull String packageName,
|
||
|
@PackageManager.PermissionWhitelistFlags int allowlistFlag) {
|
||
|
try {
|
||
|
final List<String> allowlist = mPermissionManager.getAllowlistedRestrictedPermissions(
|
||
|
packageName, allowlistFlag, mContext.getUserId());
|
||
|
if (allowlist == null) {
|
||
|
return Collections.emptySet();
|
||
|
}
|
||
|
return new ArraySet<>(allowlist);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Adds a allowlisted restricted permission for an app.
|
||
|
* <p>
|
||
|
* 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.
|
||
|
* <p>There are four allowlists:
|
||
|
* <ol>
|
||
|
* <li>
|
||
|
* One for cases where the system permission policy allowlists a permission. This list
|
||
|
* corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM} flag. Can only be
|
||
|
* accessed by pre-installed holders of a dedicated permission.
|
||
|
* <li>
|
||
|
* One for cases where the system allowlists the permission when upgrading from an OS version in
|
||
|
* which the permission was not restricted to an OS version in which the permission is
|
||
|
* restricted. This list corresponds to the
|
||
|
* {@link PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE} flag. Can be accessed by
|
||
|
* pre-installed holders of a dedicated permission or the installer on record.
|
||
|
* <li>
|
||
|
* One for cases where the installer of the package allowlists a permission. This list
|
||
|
* corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER} flag. Can be
|
||
|
* accessed by pre-installed holders of a dedicated permission or the installer on record.
|
||
|
* </ol>
|
||
|
* <p>
|
||
|
* 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.
|
||
|
* <p>
|
||
|
* 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.
|
||
|
* <p>There are four allowlists:
|
||
|
* <ol>
|
||
|
* <li>
|
||
|
* One for cases where the system permission policy allowlists a permission. This list
|
||
|
* corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM} flag. Can only be
|
||
|
* accessed by pre-installed holders of a dedicated permission.
|
||
|
* <li>
|
||
|
* One for cases where the system allowlists the permission when upgrading from an OS version in
|
||
|
* which the permission was not restricted to an OS version in which the permission is
|
||
|
* restricted. This list corresponds to the
|
||
|
* {@link PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE} flag. Can be accessed by
|
||
|
* pre-installed holders of a dedicated permission or the installer on record.
|
||
|
* <li>
|
||
|
* One for cases where the installer of the package allowlists a permission. This list
|
||
|
* corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER} flag. Can be
|
||
|
* accessed by pre-installed holders of a dedicated permission or the installer on record.
|
||
|
* </ol>
|
||
|
* <p>
|
||
|
* 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.
|
||
|
* <p>
|
||
|
* 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.
|
||
|
* <p>
|
||
|
* Only the installer on record that installed the given package is allowed to call this.
|
||
|
* <p>
|
||
|
* 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.
|
||
|
*
|
||
|
* <p>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.
|
||
|
*
|
||
|
* <p>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<SplitPermissionInfo> getSplitPermissions() {
|
||
|
if (mSplitPermissionInfos != null) {
|
||
|
return mSplitPermissionInfos;
|
||
|
}
|
||
|
|
||
|
List<SplitPermissionInfoParcelable> parcelableList;
|
||
|
try {
|
||
|
parcelableList = ActivityThread.getPermissionManager().getSplitPermissions();
|
||
|
} catch (RemoteException e) {
|
||
|
Slog.e(LOG_TAG, "Error getting split permissions", e);
|
||
|
return Collections.emptyList();
|
||
|
}
|
||
|
|
||
|
mSplitPermissionInfos = splitPermissionInfoListToNonParcelableList(parcelableList);
|
||
|
|
||
|
return mSplitPermissionInfos;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Initialize the PermissionUsageHelper, which will register active app op listeners
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
public void initializeUsageHelper() {
|
||
|
if (mUsageHelper == null) {
|
||
|
mUsageHelper = new PermissionUsageHelper(mContext);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Teardown the PermissionUsageHelper, removing listeners
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
public void tearDownUsageHelper() {
|
||
|
if (mUsageHelper != null) {
|
||
|
mUsageHelper.tearDown();
|
||
|
mUsageHelper = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return A list of permission groups currently or recently used by all apps by all users in
|
||
|
* the current profile group.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@TestApi
|
||
|
@NonNull
|
||
|
@RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
|
||
|
public List<PermissionGroupUsage> getIndicatorAppOpUsageData() {
|
||
|
return getIndicatorAppOpUsageData(new AudioManager().isMicrophoneMute());
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param micMuted whether to consider the microphone muted when retrieving audio ops
|
||
|
* @return A list of permission groups currently or recently used by all apps by all users in
|
||
|
* the current profile group.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@TestApi
|
||
|
@NonNull
|
||
|
@RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
|
||
|
public List<PermissionGroupUsage> getIndicatorAppOpUsageData(boolean micMuted) {
|
||
|
// Lazily initialize the usage helper
|
||
|
initializeUsageHelper();
|
||
|
boolean includeMicrophoneUsage = !micMuted;
|
||
|
return mUsageHelper.getOpUsageDataByDevice(includeMicrophoneUsage,
|
||
|
VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Determine if a package should be shown in indicators. Only a select few roles, and the
|
||
|
* system app itself, are hidden. These values are updated at most every 15 seconds.
|
||
|
* @hide
|
||
|
*/
|
||
|
public static boolean shouldShowPackageForIndicatorCached(@NonNull Context context,
|
||
|
@NonNull String packageName) {
|
||
|
return !getIndicatorExemptedPackages(context).contains(packageName);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the list of packages that are not shown by the indicators. Only a select few roles, and
|
||
|
* the system app itself, are hidden. These values are updated at most every 15 seconds.
|
||
|
* @hide
|
||
|
*/
|
||
|
public static Set<String> getIndicatorExemptedPackages(@NonNull Context context) {
|
||
|
updateIndicatorExemptedPackages(context);
|
||
|
ArraySet<String> pkgNames = new ArraySet<>();
|
||
|
pkgNames.add(SYSTEM_PKG);
|
||
|
for (int i = 0; i < INDICATOR_EXEMPTED_PACKAGES.length; i++) {
|
||
|
String exemptedPackage = INDICATOR_EXEMPTED_PACKAGES[i];
|
||
|
if (exemptedPackage != null) {
|
||
|
pkgNames.add(exemptedPackage);
|
||
|
}
|
||
|
}
|
||
|
return pkgNames;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update the cached indicator exempted packages
|
||
|
* @hide
|
||
|
*/
|
||
|
public static void updateIndicatorExemptedPackages(@NonNull Context context) {
|
||
|
long now = SystemClock.elapsedRealtime();
|
||
|
if (sLastIndicatorUpdateTime == -1
|
||
|
|| (now - sLastIndicatorUpdateTime) > EXEMPTED_INDICATOR_ROLE_UPDATE_FREQUENCY_MS) {
|
||
|
sLastIndicatorUpdateTime = now;
|
||
|
for (int i = 0; i < EXEMPTED_ROLES.length; i++) {
|
||
|
INDICATOR_EXEMPTED_PACKAGES[i] = context.getString(EXEMPTED_ROLES[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/**
|
||
|
* Gets the list of packages that have permissions that specified
|
||
|
* {@code requestDontAutoRevokePermissions=true} in their
|
||
|
* {@code application} manifest declaration.
|
||
|
*
|
||
|
* @return the list of packages for current user
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
@NonNull
|
||
|
@RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
|
||
|
public Set<String> getAutoRevokeExemptionRequestedPackages() {
|
||
|
try {
|
||
|
return CollectionUtils.toSet(mPermissionManager.getAutoRevokeExemptionRequestedPackages(
|
||
|
mContext.getUser().getIdentifier()));
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the list of packages that have permissions that specified
|
||
|
* {@code autoRevokePermissions=disallowed} in their
|
||
|
* {@code application} manifest declaration.
|
||
|
*
|
||
|
* @return the list of packages for current user
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
@NonNull
|
||
|
@RequiresPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
|
||
|
public Set<String> getAutoRevokeExemptionGrantedPackages() {
|
||
|
try {
|
||
|
return CollectionUtils.toSet(mPermissionManager.getAutoRevokeExemptionGrantedPackages(
|
||
|
mContext.getUser().getIdentifier()));
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList(
|
||
|
List<SplitPermissionInfoParcelable> parcelableList) {
|
||
|
final int size = parcelableList.size();
|
||
|
List<SplitPermissionInfo> list = new ArrayList<>(size);
|
||
|
for (int i = 0; i < size; i++) {
|
||
|
list.add(new SplitPermissionInfo(parcelableList.get(i)));
|
||
|
}
|
||
|
return list;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts a {@link List} of {@link SplitPermissionInfo} into a List of
|
||
|
* {@link SplitPermissionInfoParcelable} and returns it.
|
||
|
* @hide
|
||
|
*/
|
||
|
public static List<SplitPermissionInfoParcelable> splitPermissionInfoListToParcelableList(
|
||
|
List<SplitPermissionInfo> splitPermissionsList) {
|
||
|
final int size = splitPermissionsList.size();
|
||
|
List<SplitPermissionInfoParcelable> outList = new ArrayList<>(size);
|
||
|
for (int i = 0; i < size; i++) {
|
||
|
SplitPermissionInfo info = splitPermissionsList.get(i);
|
||
|
outList.add(new SplitPermissionInfoParcelable(
|
||
|
info.getSplitPermission(), info.getNewPermissions(), info.getTargetSdk()));
|
||
|
}
|
||
|
return outList;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* A permission that was added in a previous API level might have split into several
|
||
|
* permissions. This object describes one such split.
|
||
|
*/
|
||
|
@Immutable
|
||
|
public static final class SplitPermissionInfo {
|
||
|
private @NonNull final SplitPermissionInfoParcelable mSplitPermissionInfoParcelable;
|
||
|
|
||
|
@Override
|
||
|
public boolean equals(@Nullable Object o) {
|
||
|
if (this == o) return true;
|
||
|
if (o == null || getClass() != o.getClass()) return false;
|
||
|
SplitPermissionInfo that = (SplitPermissionInfo) o;
|
||
|
return mSplitPermissionInfoParcelable.equals(that.mSplitPermissionInfoParcelable);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int hashCode() {
|
||
|
return mSplitPermissionInfoParcelable.hashCode();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the permission that is split.
|
||
|
*/
|
||
|
public @NonNull String getSplitPermission() {
|
||
|
return mSplitPermissionInfoParcelable.getSplitPermission();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the permissions that are added.
|
||
|
*/
|
||
|
public @NonNull List<String> getNewPermissions() {
|
||
|
return mSplitPermissionInfoParcelable.getNewPermissions();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the target API level when the permission was split.
|
||
|
*/
|
||
|
public int getTargetSdk() {
|
||
|
return mSplitPermissionInfoParcelable.getTargetSdk();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Constructs a split permission.
|
||
|
*
|
||
|
* @param splitPerm old permission that will be split
|
||
|
* @param newPerms list of new permissions that {@code rootPerm} will be split into
|
||
|
* @param targetSdk apps targetting SDK versions below this will have {@code rootPerm}
|
||
|
* split into {@code newPerms}
|
||
|
* @hide
|
||
|
*/
|
||
|
public SplitPermissionInfo(@NonNull String splitPerm, @NonNull List<String> newPerms,
|
||
|
int targetSdk) {
|
||
|
this(new SplitPermissionInfoParcelable(splitPerm, newPerms, targetSdk));
|
||
|
}
|
||
|
|
||
|
private SplitPermissionInfo(@NonNull SplitPermissionInfoParcelable parcelable) {
|
||
|
mSplitPermissionInfoParcelable = parcelable;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Starts a one-time permission session for a given package.
|
||
|
* @see #startOneTimePermissionSession(String, long, long, int, int)
|
||
|
* @hide
|
||
|
* @deprecated Use {@link #startOneTimePermissionSession(String, long, long, int, int)} instead
|
||
|
*/
|
||
|
@Deprecated
|
||
|
@SystemApi
|
||
|
@RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
|
||
|
public void startOneTimePermissionSession(@NonNull String packageName, long timeoutMillis,
|
||
|
@ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer,
|
||
|
@ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
|
||
|
startOneTimePermissionSession(packageName, timeoutMillis, -1,
|
||
|
importanceToResetTimer, importanceToKeepSessionAlive);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Starts a one-time permission session for a given package. A one-time permission session is
|
||
|
* ended if app becomes inactive. Inactivity is defined as the package's uid importance level
|
||
|
* staying > importanceToResetTimer for timeoutMillis milliseconds. If the package's uid
|
||
|
* importance level goes <= importanceToResetTimer then the timer is reset and doesn't start
|
||
|
* until going > importanceToResetTimer.
|
||
|
* <p>
|
||
|
* 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.
|
||
|
* </p>
|
||
|
* <p>
|
||
|
* Importance levels are defined in {@link android.app.ActivityManager.RunningAppProcessInfo}.
|
||
|
* </p>
|
||
|
* <p>
|
||
|
* Once the session ends
|
||
|
* {@link PermissionControllerService#onOneTimePermissionSessionTimeout(String)} is invoked.
|
||
|
* </p>
|
||
|
* <p>
|
||
|
* 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.
|
||
|
* </p>
|
||
|
* @param packageName The package to start a one-time permission session for
|
||
|
* @param timeoutMillis Number of milliseconds for an app to be in an inactive state
|
||
|
* @param revokeAfterKilledDelayMillis Number of milliseconds to wait before revoking on the
|
||
|
* event an app is terminated. Set to -1 to use default
|
||
|
* value for the device.
|
||
|
* @param importanceToResetTimer The least important level to uid must be to reset the timer
|
||
|
* @param importanceToKeepSessionAlive The least important level the uid must be to keep the
|
||
|
* session alive
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
@RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
|
||
|
public void startOneTimePermissionSession(@NonNull String packageName,
|
||
|
@DurationMillisLong long timeoutMillis,
|
||
|
@DurationMillisLong long revokeAfterKilledDelayMillis,
|
||
|
@ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer,
|
||
|
@ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
|
||
|
try {
|
||
|
mPermissionManager.startOneTimePermissionSession(packageName, mContext.getDeviceId(),
|
||
|
mContext.getUserId(), timeoutMillis, revokeAfterKilledDelayMillis);
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Stops the one-time permission session for the package. The callback to the end of session is
|
||
|
* not invoked. If there is no one-time session for the package then nothing happens.
|
||
|
*
|
||
|
* @param packageName Package to stop the one-time permission session for
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
@RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
|
||
|
public void stopOneTimePermissionSession(@NonNull String packageName) {
|
||
|
try {
|
||
|
mPermissionManager.stopOneTimePermissionSession(packageName,
|
||
|
mContext.getUserId());
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks whether the package with the given pid/uid can read device identifiers.
|
||
|
*
|
||
|
* @param packageName the name of the package to be checked for identifier access
|
||
|
* @param message the message to be used for logging during identifier access
|
||
|
* verification
|
||
|
* @param callingFeatureId the feature in the package
|
||
|
* @param pid the process id of the package to be checked
|
||
|
* @param uid the uid of the package to be checked
|
||
|
* @return {@link PackageManager#PERMISSION_GRANTED} if the package is allowed identifier
|
||
|
* access, {@link PackageManager#PERMISSION_DENIED} otherwise
|
||
|
* @hide
|
||
|
*/
|
||
|
@SystemApi
|
||
|
public int checkDeviceIdentifierAccess(@Nullable String packageName, @Nullable String message,
|
||
|
@Nullable String callingFeatureId, int pid, int uid) {
|
||
|
return mLegacyPermissionManager.checkDeviceIdentifierAccess(packageName, message,
|
||
|
callingFeatureId, pid, uid);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Registers an attribution source with the OS. An app can only register an attribution
|
||
|
* source for itself. Once an attribution source has been registered another app can
|
||
|
* check whether this registration exists and thus trust the payload in the source
|
||
|
* object. This is important for permission checking and specifically for app op blaming
|
||
|
* since a malicious app should not be able to force the OS to blame another app
|
||
|
* that doesn't participate in an attribution chain.
|
||
|
*
|
||
|
* @param source The attribution source to register.
|
||
|
* @return The registered new attribution source.
|
||
|
*
|
||
|
* @see #isRegisteredAttributionSource(AttributionSource)
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@TestApi
|
||
|
public @NonNull AttributionSource registerAttributionSource(@NonNull AttributionSource source) {
|
||
|
// We use a shared static token for sources that are not registered since the token's
|
||
|
// only used for process death detection. If we are about to use the source for security
|
||
|
// enforcement we need to replace the binder with a unique one.
|
||
|
try {
|
||
|
if (serverSideAttributionRegistration()) {
|
||
|
IBinder newToken = mPermissionManager.registerAttributionSource(source.asState());
|
||
|
return source.withToken(newToken);
|
||
|
} else {
|
||
|
AttributionSource registeredSource = source.withToken(new Binder());
|
||
|
mPermissionManager.registerAttributionSource(registeredSource.asState());
|
||
|
return registeredSource;
|
||
|
}
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
return source;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks whether an attribution source is registered.
|
||
|
*
|
||
|
* @param source The attribution source to check.
|
||
|
* @return Whether this is a registered source.
|
||
|
*
|
||
|
* @see #registerAttributionSource(AttributionSource)
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@TestApi
|
||
|
@FlaggedApi(FLAG_SHOULD_REGISTER_ATTRIBUTION_SOURCE)
|
||
|
public boolean isRegisteredAttributionSource(@NonNull AttributionSource source) {
|
||
|
try {
|
||
|
return mPermissionManager.isRegisteredAttributionSource(source.asState());
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the number of currently registered attribution sources for a particular UID. This should
|
||
|
* only be used for testing purposes.
|
||
|
* @hide
|
||
|
*/
|
||
|
@RequiresPermission(Manifest.permission.UPDATE_APP_OPS_STATS)
|
||
|
public int getRegisteredAttributionSourceCountForTest(int uid) {
|
||
|
try {
|
||
|
return mPermissionManager.getRegisteredAttributionSourceCount(uid);
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
|
||
|
* USED in CTS or local tests.
|
||
|
*
|
||
|
* @param packageName The package to be revoked
|
||
|
* @param userId The user for which to revoke
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@TestApi
|
||
|
@RequiresPermission(Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL)
|
||
|
public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String packageName,
|
||
|
int userId) {
|
||
|
try {
|
||
|
mPermissionManager.revokePostNotificationPermissionWithoutKillForTest(packageName,
|
||
|
userId);
|
||
|
} catch (RemoteException e) {
|
||
|
e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Only warn once for assuming that root or system UID has a permission
|
||
|
// to reduce duplicate logcat output.
|
||
|
private static volatile boolean sShouldWarnMissingActivityManager = true;
|
||
|
|
||
|
private static int checkPermissionUncached(@Nullable String permission, int pid, int uid,
|
||
|
int deviceId) {
|
||
|
final IActivityManager am = ActivityManager.getService();
|
||
|
if (am == null) {
|
||
|
// Well this is super awkward; we somehow don't have an active ActivityManager
|
||
|
// instance. If we're testing a root or system UID, then they totally have whatever
|
||
|
// permission this is.
|
||
|
final int appId = UserHandle.getAppId(uid);
|
||
|
if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
|
||
|
if (sShouldWarnMissingActivityManager) {
|
||
|
Slog.w(LOG_TAG, "Missing ActivityManager; assuming " + uid + " holds "
|
||
|
+ permission);
|
||
|
sShouldWarnMissingActivityManager = false;
|
||
|
}
|
||
|
return PackageManager.PERMISSION_GRANTED;
|
||
|
}
|
||
|
Slog.w(LOG_TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
|
||
|
+ permission);
|
||
|
return PackageManager.PERMISSION_DENIED;
|
||
|
}
|
||
|
try {
|
||
|
sShouldWarnMissingActivityManager = true;
|
||
|
return am.checkPermissionForDevice(permission, pid, uid, deviceId);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Identifies a permission query.
|
||
|
*
|
||
|
* N.B. we include the checking pid for tracking purposes but don't include it in the equality
|
||
|
* comparison: we use only uid for the actual security check, so comparing pid would result
|
||
|
* in spurious misses.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
@Immutable
|
||
|
private static final class PermissionQuery {
|
||
|
final String permission;
|
||
|
final int pid;
|
||
|
final int uid;
|
||
|
final int deviceId;
|
||
|
|
||
|
PermissionQuery(@Nullable String permission, int pid, int uid, int deviceId) {
|
||
|
this.permission = permission;
|
||
|
this.pid = pid;
|
||
|
this.uid = uid;
|
||
|
this.deviceId = deviceId;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
return TextUtils.formatSimple("PermissionQuery(permission=\"%s\", pid=%d, uid=%d, "
|
||
|
+ "deviceId=%d)", permission, pid, uid, deviceId);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int hashCode() {
|
||
|
// N.B. pid doesn't count toward equality and therefore shouldn't count for
|
||
|
// hashing either.
|
||
|
return Objects.hash(permission, uid, deviceId);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean equals(@Nullable Object rval) {
|
||
|
// N.B. pid doesn't count toward equality!
|
||
|
if (rval == null) {
|
||
|
return false;
|
||
|
}
|
||
|
PermissionQuery other;
|
||
|
try {
|
||
|
other = (PermissionQuery) rval;
|
||
|
} catch (ClassCastException ex) {
|
||
|
return false;
|
||
|
}
|
||
|
return uid == other.uid && deviceId == other.deviceId
|
||
|
&& Objects.equals(permission, other.permission);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
public static final String CACHE_KEY_PACKAGE_INFO = "cache_key.package_info";
|
||
|
|
||
|
/** @hide */
|
||
|
private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache =
|
||
|
new PropertyInvalidatedCache<PermissionQuery, Integer>(
|
||
|
2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") {
|
||
|
@Override
|
||
|
public Integer recompute(PermissionQuery query) {
|
||
|
return checkPermissionUncached(query.permission, query.pid, query.uid,
|
||
|
query.deviceId);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/** @hide */
|
||
|
public static int checkPermission(@Nullable String permission, int pid, int uid, int deviceId) {
|
||
|
return sPermissionCache.query(new PermissionQuery(permission, pid, uid, deviceId));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gets the permission states for requested package and persistent device.
|
||
|
* <p>
|
||
|
* <strong>Note: </strong>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<String, PermissionState> getAllPermissionStates(@NonNull String packageName,
|
||
|
@NonNull String persistentDeviceId) {
|
||
|
try {
|
||
|
return mPermissionManager.getAllPermissionStates(packageName, persistentDeviceId,
|
||
|
mContext.getUserId());
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Make checkPermission() above bypass the permission cache in this process.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
public static void disablePermissionCache() {
|
||
|
sPermissionCache.disableLocal();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Like PermissionQuery, but for permission checks based on a package name instead of
|
||
|
* a UID.
|
||
|
*/
|
||
|
@Immutable
|
||
|
private static final class PackageNamePermissionQuery {
|
||
|
final String permName;
|
||
|
final String pkgName;
|
||
|
final String persistentDeviceId;
|
||
|
@UserIdInt
|
||
|
final int userId;
|
||
|
|
||
|
PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName,
|
||
|
@Nullable String persistentDeviceId, @UserIdInt int userId) {
|
||
|
this.permName = permName;
|
||
|
this.pkgName = pkgName;
|
||
|
this.persistentDeviceId = persistentDeviceId;
|
||
|
this.userId = userId;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
return TextUtils.formatSimple(
|
||
|
"PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s\", "
|
||
|
+ "persistentDeviceId=%s, userId=%s\")",
|
||
|
pkgName, permName, persistentDeviceId, userId);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int hashCode() {
|
||
|
return Objects.hash(permName, pkgName, persistentDeviceId, userId);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public boolean equals(@Nullable Object rval) {
|
||
|
if (rval == null) {
|
||
|
return false;
|
||
|
}
|
||
|
PackageNamePermissionQuery other;
|
||
|
try {
|
||
|
other = (PackageNamePermissionQuery) rval;
|
||
|
} catch (ClassCastException ex) {
|
||
|
return false;
|
||
|
}
|
||
|
return Objects.equals(permName, other.permName)
|
||
|
&& Objects.equals(pkgName, other.pkgName)
|
||
|
&& Objects.equals(persistentDeviceId, other.persistentDeviceId)
|
||
|
&& userId == other.userId;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* @hide */
|
||
|
private static int checkPackageNamePermissionUncached(
|
||
|
String permName, String pkgName, String persistentDeviceId, @UserIdInt int userId) {
|
||
|
try {
|
||
|
return ActivityThread.getPermissionManager().checkPermission(
|
||
|
pkgName, permName, persistentDeviceId, userId);
|
||
|
} catch (RemoteException e) {
|
||
|
throw e.rethrowFromSystemServer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* @hide */
|
||
|
private static PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>
|
||
|
sPackageNamePermissionCache =
|
||
|
new PropertyInvalidatedCache<PackageNamePermissionQuery, Integer>(
|
||
|
16, CACHE_KEY_PACKAGE_INFO, "checkPackageNamePermission") {
|
||
|
@Override
|
||
|
public Integer recompute(PackageNamePermissionQuery query) {
|
||
|
return checkPackageNamePermissionUncached(
|
||
|
query.permName, query.pkgName, query.persistentDeviceId, query.userId);
|
||
|
}
|
||
|
@Override
|
||
|
public boolean bypass(PackageNamePermissionQuery query) {
|
||
|
return query.userId < 0;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Check whether a package has a permission for given device.
|
||
|
*
|
||
|
* @hide
|
||
|
*/
|
||
|
public int checkPackageNamePermission(String permName, String pkgName,
|
||
|
int deviceId, @UserIdInt int userId) {
|
||
|
String persistentDeviceId = getPersistentDeviceId(deviceId);
|
||
|
return sPackageNamePermissionCache.query(
|
||
|
new PackageNamePermissionQuery(permName, pkgName, persistentDeviceId, userId));
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
private String getPersistentDeviceId(int deviceId) {
|
||
|
String persistentDeviceId = null;
|
||
|
|
||
|
if (deviceId == Context.DEVICE_ID_DEFAULT) {
|
||
|
persistentDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
|
||
|
} else if (android.companion.virtual.flags.Flags.vdmPublicApis()) {
|
||
|
VirtualDeviceManager virtualDeviceManager = mContext.getSystemService(
|
||
|
VirtualDeviceManager.class);
|
||
|
if (virtualDeviceManager != null) {
|
||
|
VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
|
||
|
if (virtualDevice == null) {
|
||
|
Slog.e(LOG_TAG, "Virtual device is not found with device Id " + deviceId);
|
||
|
return null;
|
||
|
}
|
||
|
persistentDeviceId = virtualDevice.getPersistentDeviceId();
|
||
|
if (persistentDeviceId == null) {
|
||
|
Slog.e(LOG_TAG, "Cannot find persistent device Id for " + deviceId);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
Slog.e(LOG_TAG, "vdmPublicApis flag is not enabled when device Id " + deviceId
|
||
|
+ "is not default.");
|
||
|
}
|
||
|
return persistentDeviceId;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check whether a package has been granted a permission on a given device.
|
||
|
* <p>
|
||
|
* <strong>Note: </strong>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<PermissionState> CREATOR = new Creator<>() {
|
||
|
public PermissionState createFromParcel(Parcel source) {
|
||
|
return new PermissionState(source);
|
||
|
}
|
||
|
|
||
|
public PermissionState[] newArray(int size) {
|
||
|
return new PermissionState[size];
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
public boolean equals(Object o) {
|
||
|
if (this == o) return true;
|
||
|
if (o == null || getClass() != o.getClass()) return false;
|
||
|
PermissionState that = (PermissionState) o;
|
||
|
return mGranted == that.mGranted && mFlags == that.mFlags;
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
public int hashCode() {
|
||
|
return Objects.hash(mGranted, mFlags);
|
||
|
}
|
||
|
|
||
|
/** @hide */
|
||
|
@Override
|
||
|
public String toString() {
|
||
|
return "PermissionState{"
|
||
|
+ "mGranted=" + mGranted
|
||
|
+ ", mFlags=" + mFlags
|
||
|
+ '}';
|
||
|
}
|
||
|
}
|
||
|
}
|