Merge "Move permission methods in PackageManager to PermissionManager."
This commit is contained in:
committed by
Android (Google) Code Review
commit
1923524096
@@ -6681,8 +6681,7 @@ public final class ActivityThread extends ClientTransactionHandler {
|
||||
private InstrumentationInfo prepareInstrumentation(AppBindData data) {
|
||||
final InstrumentationInfo ii;
|
||||
try {
|
||||
ii = new ApplicationPackageManager(
|
||||
null, getPackageManager(), getPermissionManager())
|
||||
ii = new ApplicationPackageManager(null, getPackageManager())
|
||||
.getInstrumentationInfo(data.instrumentationName, 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new RuntimeException(
|
||||
|
||||
@@ -59,8 +59,6 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageItemInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.PackageManager.Property;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.PermissionGroupInfo;
|
||||
import android.content.pm.PermissionInfo;
|
||||
@@ -95,8 +93,6 @@ import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.os.storage.VolumeInfo;
|
||||
import android.permission.IOnPermissionsChangeListener;
|
||||
import android.permission.IPermissionManager;
|
||||
import android.permission.PermissionManager;
|
||||
import android.provider.Settings;
|
||||
import android.system.ErrnoException;
|
||||
@@ -106,7 +102,6 @@ import android.system.StructStat;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.DebugUtils;
|
||||
import android.util.LauncherIcons;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -129,7 +124,6 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -137,14 +131,6 @@ import java.util.Set;
|
||||
public class ApplicationPackageManager extends PackageManager {
|
||||
private static final String TAG = "ApplicationPackageManager";
|
||||
private static final boolean DEBUG_ICONS = false;
|
||||
/**
|
||||
* Note: Changing this won't do anything on it's own - you should also change the filtering in
|
||||
* {@link #shouldTraceGrant}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final boolean DEBUG_TRACE_GRANTS = false;
|
||||
public static final boolean DEBUG_TRACE_PERMISSION_UPDATES = false;
|
||||
|
||||
private static final int DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES = 16384; // 16KB
|
||||
|
||||
@@ -171,6 +157,8 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
@GuardedBy("mLock")
|
||||
private UserManager mUserManager;
|
||||
@GuardedBy("mLock")
|
||||
private PermissionManager mPermissionManager;
|
||||
@GuardedBy("mLock")
|
||||
private PackageInstaller mInstaller;
|
||||
@GuardedBy("mLock")
|
||||
private ArtManager mArtManager;
|
||||
@@ -190,6 +178,15 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
}
|
||||
}
|
||||
|
||||
private PermissionManager getPermissionManager() {
|
||||
synchronized (mLock) {
|
||||
if (mPermissionManager == null) {
|
||||
mPermissionManager = mContext.getSystemService(PermissionManager.class);
|
||||
}
|
||||
return mPermissionManager;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUserId() {
|
||||
return mContext.getUserId();
|
||||
@@ -355,66 +352,41 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<PermissionGroupInfo> getAllPermissionGroups(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();
|
||||
}
|
||||
return getPermissionManager().getAllPermissionGroups(flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags)
|
||||
throws NameNotFoundException {
|
||||
try {
|
||||
final PermissionGroupInfo pgi =
|
||||
mPermissionManager.getPermissionGroupInfo(groupName, flags);
|
||||
if (pgi != null) {
|
||||
return pgi;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
final PermissionGroupInfo permissionGroupInfo = getPermissionManager()
|
||||
.getPermissionGroupInfo(groupName, flags);
|
||||
if (permissionGroupInfo == null) {
|
||||
throw new NameNotFoundException(groupName);
|
||||
}
|
||||
throw new NameNotFoundException(groupName);
|
||||
return permissionGroupInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PermissionInfo getPermissionInfo(String permName, int flags)
|
||||
throws NameNotFoundException {
|
||||
try {
|
||||
final String packageName = mContext.getOpPackageName();
|
||||
final PermissionInfo pi =
|
||||
mPermissionManager.getPermissionInfo(permName, packageName, flags);
|
||||
if (pi != null) {
|
||||
return pi;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
final PermissionInfo permissionInfo = getPermissionManager().getPermissionInfo(permName,
|
||||
flags);
|
||||
if (permissionInfo == null) {
|
||||
throw new NameNotFoundException(permName);
|
||||
}
|
||||
throw new NameNotFoundException(permName);
|
||||
return permissionInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<PermissionInfo> queryPermissionsByGroup(String groupName, int flags)
|
||||
throws NameNotFoundException {
|
||||
try {
|
||||
final ParceledListSlice<PermissionInfo> parceledList =
|
||||
mPermissionManager.queryPermissionsByGroup(groupName, flags);
|
||||
if (parceledList != null) {
|
||||
final List<PermissionInfo> pi = parceledList.getList();
|
||||
if (pi != null) {
|
||||
return pi;
|
||||
}
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
final List<PermissionInfo> permissionInfos = getPermissionManager().queryPermissionsByGroup(
|
||||
groupName, flags);
|
||||
if (permissionInfos == null) {
|
||||
throw new NameNotFoundException(groupName);
|
||||
}
|
||||
throw new NameNotFoundException(groupName);
|
||||
return permissionInfos;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -724,11 +696,7 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
|
||||
@Override
|
||||
public boolean isPermissionRevokedByPolicy(String permName, String pkgName) {
|
||||
try {
|
||||
return mPermissionManager.isPermissionRevokedByPolicy(permName, pkgName, getUserId());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getPermissionManager().isPermissionRevokedByPolicy(pkgName, permName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -750,50 +718,23 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
|
||||
@Override
|
||||
public boolean addPermission(PermissionInfo info) {
|
||||
try {
|
||||
return mPermissionManager.addPermission(info, false);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getPermissionManager().addPermission(info, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addPermissionAsync(PermissionInfo info) {
|
||||
try {
|
||||
return mPermissionManager.addPermission(info, true);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getPermissionManager().addPermission(info, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePermission(String name) {
|
||||
try {
|
||||
mPermissionManager.removePermission(name);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
getPermissionManager().removePermission(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grantRuntimePermission(String packageName, String permissionName,
|
||||
UserHandle user) {
|
||||
if (DEBUG_TRACE_GRANTS
|
||||
&& shouldTraceGrant(packageName, permissionName, user.getIdentifier())) {
|
||||
Log.i(TAG, "App " + mContext.getPackageName() + " is granting " + packageName + " "
|
||||
+ permissionName + " for user " + user.getIdentifier(), new RuntimeException());
|
||||
}
|
||||
try {
|
||||
mPM.grantRuntimePermission(packageName, permissionName, user.getIdentifier());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean shouldTraceGrant(String packageName, String permissionName, int userId) {
|
||||
// To be modified when debugging
|
||||
return false;
|
||||
getPermissionManager().grantRuntimePermission(packageName, permissionName, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -804,124 +745,55 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
@Override
|
||||
public void revokeRuntimePermission(String packageName, String permName, UserHandle user,
|
||||
String reason) {
|
||||
if (DEBUG_TRACE_PERMISSION_UPDATES
|
||||
&& shouldTraceGrant(packageName, permName, user.getIdentifier())) {
|
||||
Log.i(TAG, "App " + mContext.getPackageName() + " is revoking " + packageName + " "
|
||||
+ permName + " for user " + user.getIdentifier() + " with reason " + reason,
|
||||
new RuntimeException());
|
||||
}
|
||||
try {
|
||||
mPermissionManager
|
||||
.revokeRuntimePermission(packageName, permName, user.getIdentifier(), reason);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
getPermissionManager().revokeRuntimePermission(packageName, permName, user, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPermissionFlags(String permName, String packageName, UserHandle user) {
|
||||
try {
|
||||
return mPermissionManager
|
||||
.getPermissionFlags(permName, packageName, user.getIdentifier());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getPermissionManager().getPermissionFlags(packageName, permName, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePermissionFlags(String permName, String packageName,
|
||||
int flagMask, int flagValues, UserHandle user) {
|
||||
if (DEBUG_TRACE_PERMISSION_UPDATES
|
||||
&& shouldTraceGrant(packageName, permName, user.getIdentifier())) {
|
||||
Log.i(TAG, "App " + mContext.getPackageName() + " is updating flags for "
|
||||
+ packageName + " "
|
||||
+ permName + " for user " + user.getIdentifier() + ": "
|
||||
+ 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(permName, packageName, flagMask,
|
||||
flagValues, checkAdjustPolicyFlagPermission, user.getIdentifier());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
getPermissionManager().updatePermissionFlags(packageName, permName, flagMask, flagValues,
|
||||
user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Set<String> getWhitelistedRestrictedPermissions(
|
||||
@NonNull String packageName, @PermissionWhitelistFlags int flags) {
|
||||
try {
|
||||
final int userId = getUserId();
|
||||
final List<String> whitelist = mPermissionManager
|
||||
.getWhitelistedRestrictedPermissions(packageName, flags, userId);
|
||||
if (whitelist != null) {
|
||||
return new ArraySet<>(whitelist);
|
||||
}
|
||||
return Collections.emptySet();
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getPermissionManager().getAllowlistedRestrictedPermissions(packageName, flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addWhitelistedRestrictedPermission(@NonNull String packageName,
|
||||
@NonNull String permName, @PermissionWhitelistFlags int flags) {
|
||||
try {
|
||||
final int userId = getUserId();
|
||||
return mPermissionManager
|
||||
.addWhitelistedRestrictedPermission(packageName, permName, flags, userId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getPermissionManager().addAllowlistedRestrictedPermission(packageName, permName,
|
||||
flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setAutoRevokeWhitelisted(
|
||||
@NonNull String packageName, boolean whitelisted) {
|
||||
try {
|
||||
final int userId = getUserId();
|
||||
return mPermissionManager.setAutoRevokeWhitelisted(packageName, whitelisted, userId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
public boolean setAutoRevokeWhitelisted(@NonNull String packageName, boolean whitelisted) {
|
||||
return getPermissionManager().setAutoRevokeExempted(packageName, whitelisted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoRevokeWhitelisted(@NonNull String packageName) {
|
||||
try {
|
||||
final int userId = getUserId();
|
||||
return mPermissionManager.isAutoRevokeWhitelisted(packageName, userId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getPermissionManager().isAutoRevokeExempted(packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName,
|
||||
@NonNull String permName, @PermissionWhitelistFlags int flags) {
|
||||
try {
|
||||
final int userId = getUserId();
|
||||
return mPermissionManager
|
||||
.removeWhitelistedRestrictedPermission(packageName, permName, flags, userId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getPermissionManager().removeAllowlistedRestrictedPermission(packageName, permName,
|
||||
flags);
|
||||
}
|
||||
|
||||
@Override
|
||||
@UnsupportedAppUsage
|
||||
public boolean shouldShowRequestPermissionRationale(String permName) {
|
||||
try {
|
||||
final String packageName = mContext.getPackageName();
|
||||
return mPermissionManager
|
||||
.shouldShowRequestPermissionRationale(permName, packageName, getUserId());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
return getPermissionManager().shouldShowRequestPermissionRationale(permName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1880,34 +1752,12 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
|
||||
@Override
|
||||
public void addOnPermissionsChangeListener(OnPermissionsChangedListener listener) {
|
||||
synchronized (mPermissionListeners) {
|
||||
if (mPermissionListeners.get(listener) != null) {
|
||||
return;
|
||||
}
|
||||
OnPermissionsChangeListenerDelegate delegate =
|
||||
new OnPermissionsChangeListenerDelegate(listener, Looper.getMainLooper());
|
||||
try {
|
||||
mPermissionManager.addOnPermissionsChangeListener(delegate);
|
||||
mPermissionListeners.put(listener, delegate);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
getPermissionManager().addOnPermissionsChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOnPermissionsChangeListener(OnPermissionsChangedListener listener) {
|
||||
synchronized (mPermissionListeners) {
|
||||
IOnPermissionsChangeListener delegate = mPermissionListeners.get(listener);
|
||||
if (delegate != null) {
|
||||
try {
|
||||
mPermissionManager.removeOnPermissionsChangeListener(delegate);
|
||||
mPermissionListeners.remove(listener);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
getPermissionManager().removeOnPermissionsChangeListener(listener);
|
||||
}
|
||||
|
||||
@UnsupportedAppUsage
|
||||
@@ -1918,11 +1768,9 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
}
|
||||
}
|
||||
|
||||
protected ApplicationPackageManager(ContextImpl context, IPackageManager pm,
|
||||
IPermissionManager permissionManager) {
|
||||
protected ApplicationPackageManager(ContextImpl context, IPackageManager pm) {
|
||||
mContext = context;
|
||||
mPM = pm;
|
||||
mPermissionManager = permissionManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3234,7 +3082,6 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
private final ContextImpl mContext;
|
||||
@UnsupportedAppUsage
|
||||
private final IPackageManager mPM;
|
||||
private final IPermissionManager mPermissionManager;
|
||||
|
||||
/** Assume locked until we hear otherwise */
|
||||
private volatile boolean mUserUnlocked = false;
|
||||
@@ -3245,41 +3092,6 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
private static ArrayMap<ResourceName, WeakReference<CharSequence>> sStringCache
|
||||
= new ArrayMap<ResourceName, WeakReference<CharSequence>>();
|
||||
|
||||
private final Map<OnPermissionsChangedListener, IOnPermissionsChangeListener>
|
||||
mPermissionListeners = new ArrayMap<>();
|
||||
|
||||
public class OnPermissionsChangeListenerDelegate extends IOnPermissionsChangeListener.Stub
|
||||
implements Handler.Callback{
|
||||
private static final int MSG_PERMISSIONS_CHANGED = 1;
|
||||
|
||||
private final OnPermissionsChangedListener mListener;
|
||||
private final Handler mHandler;
|
||||
|
||||
|
||||
public OnPermissionsChangeListenerDelegate(OnPermissionsChangedListener listener,
|
||||
Looper looper) {
|
||||
mListener = listener;
|
||||
mHandler = new Handler(looper, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsChanged(int uid) {
|
||||
mHandler.obtainMessage(MSG_PERMISSIONS_CHANGED, uid, 0).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_PERMISSIONS_CHANGED: {
|
||||
final int uid = msg.arg1;
|
||||
mListener.onPermissionsChanged(uid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRequestPackageInstalls() {
|
||||
try {
|
||||
|
||||
@@ -72,7 +72,6 @@ import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.os.storage.StorageManager;
|
||||
import android.permission.IPermissionManager;
|
||||
import android.permission.PermissionManager;
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
@@ -370,10 +369,9 @@ class ContextImpl extends Context {
|
||||
}
|
||||
|
||||
final IPackageManager pm = ActivityThread.getPackageManager();
|
||||
final IPermissionManager permissionManager = ActivityThread.getPermissionManager();
|
||||
if (pm != null && permissionManager != null) {
|
||||
if (pm != null) {
|
||||
// Doesn't matter if we make more than one instance.
|
||||
return (mPackageManager = new ApplicationPackageManager(this, pm, permissionManager));
|
||||
return (mPackageManager = new ApplicationPackageManager(this, pm));
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -4438,6 +4438,7 @@ public abstract class PackageManager {
|
||||
* @throws NameNotFoundException if a package with the given name cannot be
|
||||
* found on the system.
|
||||
*/
|
||||
//@Deprecated
|
||||
public abstract PermissionInfo getPermissionInfo(@NonNull String permName,
|
||||
@PermissionInfoFlags int flags) throws NameNotFoundException;
|
||||
|
||||
@@ -4450,9 +4451,10 @@ public abstract class PackageManager {
|
||||
* @param flags Additional option flags to modify the data returned.
|
||||
* @return Returns a list of {@link PermissionInfo} containing information
|
||||
* about all of the permissions in the given group.
|
||||
* @throws NameNotFoundException if a package with the given name cannot be
|
||||
* @throws NameNotFoundException if a group with the given name cannot be
|
||||
* found on the system.
|
||||
*/
|
||||
//@Deprecated
|
||||
@NonNull
|
||||
public abstract List<PermissionInfo> queryPermissionsByGroup(@NonNull String permissionGroup,
|
||||
@PermissionInfoFlags int flags) throws NameNotFoundException;
|
||||
@@ -4481,7 +4483,7 @@ public abstract class PackageManager {
|
||||
* Retrieve all of the information we know about a particular group of
|
||||
* permissions.
|
||||
*
|
||||
* @param permName The fully qualified name (i.e.
|
||||
* @param groupName The fully qualified name (i.e.
|
||||
* com.google.permission_group.APPS) of the permission you are
|
||||
* interested in.
|
||||
* @param flags Additional option flags to modify the data returned.
|
||||
@@ -4490,8 +4492,9 @@ public abstract class PackageManager {
|
||||
* @throws NameNotFoundException if a package with the given name cannot be
|
||||
* found on the system.
|
||||
*/
|
||||
//@Deprecated
|
||||
@NonNull
|
||||
public abstract PermissionGroupInfo getPermissionGroupInfo(@NonNull String permName,
|
||||
public abstract PermissionGroupInfo getPermissionGroupInfo(@NonNull String groupName,
|
||||
@PermissionGroupInfoFlags int flags) throws NameNotFoundException;
|
||||
|
||||
/**
|
||||
@@ -4501,6 +4504,7 @@ public abstract class PackageManager {
|
||||
* @return Returns a list of {@link PermissionGroupInfo} containing
|
||||
* information about all of the known permission groups.
|
||||
*/
|
||||
//@Deprecated
|
||||
@NonNull
|
||||
public abstract List<PermissionGroupInfo> getAllPermissionGroups(
|
||||
@PermissionGroupInfoFlags int flags);
|
||||
@@ -4757,6 +4761,7 @@ public abstract class PackageManager {
|
||||
* @return Whether the permission is restricted by policy.
|
||||
*/
|
||||
@CheckResult
|
||||
//@Deprecated
|
||||
public abstract boolean isPermissionRevokedByPolicy(@NonNull String permName,
|
||||
@NonNull String packageName);
|
||||
|
||||
@@ -4805,6 +4810,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @see #removePermission(String)
|
||||
*/
|
||||
//@Deprecated
|
||||
public abstract boolean addPermission(@NonNull PermissionInfo info);
|
||||
|
||||
/**
|
||||
@@ -4814,6 +4820,7 @@ public abstract class PackageManager {
|
||||
* expense of no guarantee the added permission will be retained if
|
||||
* the device is rebooted before it is written.
|
||||
*/
|
||||
//@Deprecated
|
||||
public abstract boolean addPermissionAsync(@NonNull PermissionInfo info);
|
||||
|
||||
/**
|
||||
@@ -4829,6 +4836,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @see #addPermission(PermissionInfo)
|
||||
*/
|
||||
//@Deprecated
|
||||
public abstract void removePermission(@NonNull String permName);
|
||||
|
||||
/**
|
||||
@@ -4881,6 +4889,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
//@Deprecated
|
||||
@SuppressWarnings("HiddenAbstractMethod")
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
|
||||
@@ -4908,6 +4917,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
//@Deprecated
|
||||
@SuppressWarnings("HiddenAbstractMethod")
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
|
||||
@@ -4936,6 +4946,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
//@Deprecated
|
||||
@SystemApi
|
||||
@RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
|
||||
public void revokeRuntimePermission(@NonNull String packageName,
|
||||
@@ -4953,6 +4964,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
//@Deprecated
|
||||
@SuppressWarnings("HiddenAbstractMethod")
|
||||
@SystemApi
|
||||
@RequiresPermission(anyOf = {
|
||||
@@ -4976,6 +4988,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
//@Deprecated
|
||||
@SuppressWarnings("HiddenAbstractMethod")
|
||||
@SystemApi
|
||||
@RequiresPermission(anyOf = {
|
||||
@@ -5040,6 +5053,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @throws SecurityException if you try to access a whitelist that you have no access to.
|
||||
*/
|
||||
//@Deprecated
|
||||
@RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS,
|
||||
conditional = true)
|
||||
public @NonNull Set<String> getWhitelistedRestrictedPermissions(
|
||||
@@ -5106,6 +5120,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @throws SecurityException if you try to modify a whitelist that you have no access to.
|
||||
*/
|
||||
//@Deprecated
|
||||
@RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS,
|
||||
conditional = true)
|
||||
public boolean addWhitelistedRestrictedPermission(@NonNull String packageName,
|
||||
@@ -5175,6 +5190,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @throws SecurityException if you try to modify a whitelist that you have no access to.
|
||||
*/
|
||||
//@Deprecated
|
||||
@RequiresPermission(value = Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS,
|
||||
conditional = true)
|
||||
public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName,
|
||||
@@ -5207,6 +5223,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @throws SecurityException if you you have no access to modify this.
|
||||
*/
|
||||
//@Deprecated
|
||||
@RequiresPermission(value = Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS,
|
||||
conditional = true)
|
||||
public boolean setAutoRevokeWhitelisted(@NonNull String packageName, boolean whitelisted) {
|
||||
@@ -5234,6 +5251,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @throws SecurityException if you you have no access to this.
|
||||
*/
|
||||
//@Deprecated
|
||||
@RequiresPermission(value = Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS,
|
||||
conditional = true)
|
||||
public boolean isAutoRevokeWhitelisted(@NonNull String packageName) {
|
||||
@@ -5252,6 +5270,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
//@Deprecated
|
||||
@SuppressWarnings("HiddenAbstractMethod")
|
||||
@UnsupportedAppUsage
|
||||
public abstract boolean shouldShowRequestPermissionRationale(@NonNull String permName);
|
||||
@@ -7554,6 +7573,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
//@Deprecated
|
||||
@SuppressWarnings("HiddenAbstractMethod")
|
||||
@SystemApi
|
||||
@RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
|
||||
@@ -7567,6 +7587,7 @@ public abstract class PackageManager {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
//@Deprecated
|
||||
@SuppressWarnings("HiddenAbstractMethod")
|
||||
@SystemApi
|
||||
@RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS)
|
||||
|
||||
@@ -19,6 +19,7 @@ package android.permission;
|
||||
import static android.os.Build.VERSION_CODES.S;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.CheckResult;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
@@ -36,11 +37,22 @@ import android.compat.annotation.EnabledAfter;
|
||||
import android.content.Context;
|
||||
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.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.DebugUtils;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.Immutable;
|
||||
@@ -60,7 +72,7 @@ import java.util.Set;
|
||||
@SystemApi
|
||||
@SystemService(Context.PERMISSION_SERVICE)
|
||||
public final class PermissionManager {
|
||||
private static final String TAG = PermissionManager.class.getName();
|
||||
private static final String LOG_TAG = PermissionManager.class.getName();
|
||||
|
||||
/** @hide */
|
||||
public static final String KILL_APP_REASON_PERMISSIONS_REVOKED =
|
||||
@@ -80,6 +92,18 @@ public final class PermissionManager {
|
||||
@EnabledAfter(targetSdkVersion = S)
|
||||
public static final long CANNOT_INSTALL_WITH_BAD_PERMISSION_GROUPS = 146211400;
|
||||
|
||||
/**
|
||||
* Note: Changing this won't do anything on its own - you should also change the filtering in
|
||||
* {@link #shouldTraceGrant}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final boolean DEBUG_TRACE_GRANTS = false;
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static final boolean DEBUG_TRACE_PERMISSION_UPDATES = false;
|
||||
|
||||
private final @NonNull Context mContext;
|
||||
|
||||
private final IPackageManager mPackageManager;
|
||||
@@ -88,6 +112,9 @@ public final class PermissionManager {
|
||||
|
||||
private final LegacyPermissionManager mLegacyPermissionManager;
|
||||
|
||||
private final ArrayMap<PackageManager.OnPermissionsChangedListener,
|
||||
IOnPermissionsChangeListener> mPermissionListeners = new ArrayMap<>();
|
||||
|
||||
private List<SplitPermissionInfo> mSplitPermissionInfos;
|
||||
|
||||
/**
|
||||
@@ -106,6 +133,649 @@ public final class PermissionManager {
|
||||
mLegacyPermissionManager = context.getSystemService(LegacyPermissionManager.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(@NonNull 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(permissionName, packageName,
|
||||
mContext.getUserId());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static boolean shouldTraceGrant(String packageName, String permissionName, int userId) {
|
||||
// To be modified when debugging
|
||||
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)
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
|
||||
//@SystemApi
|
||||
public void grantRuntimePermission(@NonNull String packageName,
|
||||
@NonNull String permissionName, @NonNull UserHandle user) {
|
||||
if (DEBUG_TRACE_GRANTS
|
||||
&& shouldTraceGrant(packageName, permissionName, user.getIdentifier())) {
|
||||
Log.i(LOG_TAG, "App " + mContext.getPackageName() + " is granting " + packageName + " "
|
||||
+ permissionName + " for user " + user.getIdentifier(), new RuntimeException());
|
||||
}
|
||||
try {
|
||||
mPermissionManager.grantRuntimePermission(packageName, permissionName,
|
||||
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 permName 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 permName, @NonNull UserHandle user, @Nullable String reason) {
|
||||
if (DEBUG_TRACE_PERMISSION_UPDATES
|
||||
&& shouldTraceGrant(packageName, permName, user.getIdentifier())) {
|
||||
Log.i(LOG_TAG, "App " + mContext.getPackageName() + " is revoking " + packageName + " "
|
||||
+ permName + " for user " + user.getIdentifier() + " with reason "
|
||||
+ reason, new RuntimeException());
|
||||
}
|
||||
try {
|
||||
mPermissionManager
|
||||
.revokeRuntimePermission(packageName, permName, 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) {
|
||||
try {
|
||||
return mPermissionManager.getPermissionFlags(permissionName, packageName,
|
||||
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) {
|
||||
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() + ": " + 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(permissionName, packageName, flagMask,
|
||||
flagValues, checkAdjustPolicyFlagPermission, 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.
|
||||
* <li>
|
||||
* One for cases where the system exempts the permission when granting a role. This list
|
||||
* corresponds to the {@link PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE} flag. Can be
|
||||
* accessed by pre-installed holders of a dedicated permission.
|
||||
* </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
|
||||
* @see PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE
|
||||
*
|
||||
* @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.getWhitelistedRestrictedPermissions(
|
||||
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.
|
||||
* <li>
|
||||
* One for cases where the system exempts the permission when granting a role. This list
|
||||
* corresponds to the {@link PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE} flag. Can be
|
||||
* accessed by pre-installed holders of a dedicated permission.
|
||||
* </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
|
||||
* @see PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE
|
||||
*
|
||||
* @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.addWhitelistedRestrictedPermission(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.
|
||||
* <li>
|
||||
* One for cases where the system exempts the permission when granting a role. This list
|
||||
* corresponds to the {@link PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE} flag. Can be
|
||||
* accessed by pre-installed holders of a dedicated permission.
|
||||
* </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
|
||||
* @see PackageManager#FLAG_PERMISSION_ALLOWLIST_ROLE
|
||||
*
|
||||
* @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.removeWhitelistedRestrictedPermission(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.isAutoRevokeWhitelisted(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.setAutoRevokeWhitelisted(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(permissionName,
|
||||
packageName, 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.
|
||||
*
|
||||
@@ -174,7 +844,7 @@ public final class PermissionManager {
|
||||
try {
|
||||
parcelableList = ActivityThread.getPermissionManager().getSplitPermissions();
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Error getting split permissions", e);
|
||||
Slog.e(LOG_TAG, "Error getting split permissions", e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -403,10 +1073,11 @@ public final class PermissionManager {
|
||||
// permission this is.
|
||||
final int appId = UserHandle.getAppId(uid);
|
||||
if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
|
||||
Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
|
||||
Slog.w(LOG_TAG, "Missing ActivityManager; assuming " + uid + " holds "
|
||||
+ permission);
|
||||
return PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
|
||||
Slog.w(LOG_TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
|
||||
+ permission);
|
||||
return PackageManager.PERMISSION_DENIED;
|
||||
}
|
||||
@@ -586,4 +1257,35 @@ public final class PermissionManager {
|
||||
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) {
|
||||
mHandler.obtainMessage(MSG_PERMISSIONS_CHANGED, uid, 0).sendToTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_PERMISSIONS_CHANGED: {
|
||||
final int uid = msg.arg1;
|
||||
mListener.onPermissionsChanged(uid);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public class ApplicationPackageManagerTest extends TestCase {
|
||||
private boolean mAllow3rdPartyOnInternal = true;
|
||||
|
||||
public MockedApplicationPackageManager() {
|
||||
super(null, null, null);
|
||||
super(null, null);
|
||||
}
|
||||
|
||||
public void setForceAllowOnExternal(boolean forceAllowOnExternal) {
|
||||
|
||||
@@ -66,7 +66,6 @@ import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.AppOpsManager;
|
||||
import android.app.ApplicationPackageManager;
|
||||
import android.app.IActivityManager;
|
||||
import android.app.admin.DevicePolicyManagerInternal;
|
||||
import android.app.role.RoleManager;
|
||||
@@ -795,8 +794,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
|
||||
private void updatePermissionFlagsInternal(String permName, String packageName, int flagMask,
|
||||
int flagValues, int callingUid, int userId, boolean overridePolicy,
|
||||
PermissionCallback callback) {
|
||||
if (ApplicationPackageManager.DEBUG_TRACE_PERMISSION_UPDATES
|
||||
&& ApplicationPackageManager.shouldTraceGrant(packageName, permName, userId)) {
|
||||
if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
|
||||
&& PermissionManager.shouldTraceGrant(packageName, permName, userId)) {
|
||||
Log.i(TAG, "System is updating flags for " + packageName + " "
|
||||
+ permName + " for user " + userId + " "
|
||||
+ DebugUtils.flagsToString(
|
||||
@@ -1456,8 +1455,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
|
||||
// TODO swap permission name and package name
|
||||
private void grantRuntimePermissionInternal(String permName, String packageName,
|
||||
boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) {
|
||||
if (ApplicationPackageManager.DEBUG_TRACE_GRANTS
|
||||
&& ApplicationPackageManager.shouldTraceGrant(packageName, permName, userId)) {
|
||||
if (PermissionManager.DEBUG_TRACE_GRANTS
|
||||
&& PermissionManager.shouldTraceGrant(packageName, permName, userId)) {
|
||||
Log.i(TAG, "System is granting " + packageName + " "
|
||||
+ permName + " for user " + userId + " on behalf of uid " + callingUid
|
||||
+ " " + mPackageManagerInt.getNameForUid(callingUid),
|
||||
@@ -1633,8 +1632,8 @@ public class PermissionManagerService extends IPermissionManager.Stub {
|
||||
private void revokeRuntimePermissionInternal(String permName, String packageName,
|
||||
boolean overridePolicy, int callingUid, final int userId, String reason,
|
||||
PermissionCallback callback) {
|
||||
if (ApplicationPackageManager.DEBUG_TRACE_PERMISSION_UPDATES
|
||||
&& ApplicationPackageManager.shouldTraceGrant(packageName, permName, userId)) {
|
||||
if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
|
||||
&& PermissionManager.shouldTraceGrant(packageName, permName, userId)) {
|
||||
Log.i(TAG, "System is revoking " + packageName + " "
|
||||
+ permName + " for user " + userId + " on behalf of uid " + callingUid
|
||||
+ " " + mPackageManagerInt.getNameForUid(callingUid),
|
||||
|
||||
Reference in New Issue
Block a user