Android权限检测申请源码流程分析
Android系统的权限开发用起来很方便,知识点不多,详见Android Permission 权限总结。但是它的系统校验检测机制比较复杂,从framework层PackageManager、PermissionManager、PermissionManagerInternal到系统服务PackageManagerService、PermissionManagerService、PermissionManagerServiceInternal。
只是浅显的看一下大致的源码逻辑,有兴趣和时间的可以从下面两大块源码入手完整的分析一些Android权限机制:
/frameworks/base/core/java/android/permission
/frameworks/base/services/core/java/com/android/server/pm/permission
1、权限检测源码流程
检查程序是否获得某一个权限方法详见Android Permission 权限总结。
1.1、从Context开始
不管通过什么方法最后都会调用到Context的checkPermission(String permission, int pid, int uid)方法
@CheckResult(suggest="#enforcePermission(String,int,int,String)")
@PackageManager.PermissionResult
public abstract int checkPermission(@NonNull String permission, int pid, int uid);
在各种Context及其初始化流程一文中,我们知道Context的实现类是ContextImpl。实现了检测权限的抽象方法:
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return PermissionManager.checkPermission(permission, pid, uid);
}
1.2、借助PermissionManager
理所应当的由专门负责权限管理的PermissionManager来校验权限
/** @hide */
public static int checkPermission(@Nullable String permission, int pid, int uid) {
return sPermissionCache.query(new PermissionQuery(permission, pid, uid));
}
看到sPermissionCache这个变量就应该立马联想到权限查询先查cache,而且会缓存到这里
private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache =
new PropertyInvalidatedCache<PermissionQuery, Integer>(
16, CACHE_KEY_PACKAGE_INFO) {
@Override
protected Integer recompute(PermissionQuery query) {
return checkPermissionUncached(query.permission, query.pid, query.uid);
}
};
PropertyInvalidatedCache类在android.app包中,是Android 30新增的用来实现LRU缓存,以前的源码中并没有查到这个类。当缓存中没有数据就会调用下面的方法并将其结果缓存起来:
/**
* Fetch a result from scratch in case it's not in the cache at all. Called unlocked: may
* block. If this function returns null, the result of the cache query is null. There is no
* "negative cache" in the query: we don't cache null results at all.
*/
protected abstract Result recompute(Query query);
sPermissionCache中没有对应的权限时就会调用PermissionManager中的checkPermissionUncached(String permission, int pid, int uid)方法
private static int checkPermissionUncached(@Nullable String permission, int pid, int uid) {
final IActivityManager am = ActivityManager.getService();
...
try {
return am.checkPermission(permission, pid, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
1.3、AMS服务
熟悉Activity启动流程的对ActivityManagerService一定不陌生,它实现了IActivityManager.Stub远程接口,检查权限的方法具体实现如下:
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
return checkComponentPermission(permission, pid, uid, -1, true);
}
调用下面的checkComponentPermission()方法,注释太皮了:如果有一个显式的权限正在被检查,并且这个权限来自一个被拒绝访问该权限的进程,那么就拒绝它。最终,这可能不是很正确——这意味着即使调用者因为其他原因(例如作为它试图访问的组件的所有者)拥有访问权,它仍然会失败。这也意味着系统和根uid能够拒绝自己的权限访问,这…嗯好的。¯\ _(ツ)_ /¯
public static int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
// If there is an explicit permission being checked, and this is coming from a process
// that has been denied access to that permission, then just deny. Ultimately this may
// not be quite right -- it means that even if the caller would have access for another
// reason (such as being the owner of the component it is trying to access), it would still
// fail. This also means the system and root uids would be able to deny themselves
// access to permissions, which... well okay. ¯\_(ツ)_/¯
if (permission != null) {
synchronized (sActiveProcessInfoSelfLocked) {
ProcessInfo procInfo = sActiveProcessInfoSelfLocked.get(pid);
if (procInfo != null && procInfo.deniedPermissions != null
&& procInfo.deniedPermissions.contains(permission)) {
return PackageManager.PERMISSION_DENIED;
}
}
}
return ActivityManager.checkComponentPermission(permission, uid,
owningUid, exported);
}
1.4、绕道ActivityManager
接着又绕到了ActivityManager,调用ActivityManager的checkComponentPermission()方法:
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
... ...
if (permission == null) {
return PackageManager.PERMISSION_GRANTED;
}
try {
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
最后调用AppGlobals类中的getPackageManager()方法,获取IPackageManager远程服务。这个类提供了与流程相关的某些全局变量的特殊私有访问方法,其实是多此一举封装了一下,这很Android。
/**
* Return the raw interface to the package manager.
* @return The package manager.
*/
@UnsupportedAppUsage
public static IPackageManager getPackageManager() {
return ActivityThread.getPackageManager();
}
1.5、PackageManagerService
进入PackageManagerService包管理系统服务,它的检查权限方法啥也不干,封装了PermissionManagerService权限管理服务:
// NOTE: Can't remove without a major refactor. Keep around for now.
@Override
public int checkUidPermission(String permName, int uid) {
try {
// Because this is accessed via the package manager service AIDL,
// go through the permission manager service AIDL
return mPermissionManagerService.checkUidPermission(permName, uid);
} catch (RemoteException ignore) { }
return PackageManager.PERMISSION_DENIED;
}
1.6、PermissionManagerService
通过mPermissionManagerService也就是PermissionManagerService权限管理服务检查权限:
public int checkUidPermission(String permName, int uid) {
// Not using Objects.requireNonNull() here for compatibility reasons.
if (permName == null) {
return PackageManager.PERMISSION_DENIED;
}
final int userId = UserHandle.getUserId(uid);
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
final CheckPermissionDelegate checkPermissionDelegate;
synchronized (mLock) {
checkPermissionDelegate = mCheckPermissionDelegate;
}
if (checkPermissionDelegate == null) {
return checkUidPermissionImpl(permName, uid);
}
return checkPermissionDelegate.checkUidPermission(permName, uid,
this::checkUidPermissionImpl);
}
1.7、虚晃一下PermissionManagerInternal
前面方法中的CheckPermissionDelegate是定义在PermissionManagerInternal中的interface,
/** Interface to override permission checks via composition */
public interface CheckPermissionDelegate {
/**
* Checks whether the given package has been granted the specified permission.
*/
int checkPermission(String permName, String pkgName, int userId,
TriFunction<String, String, Integer, Integer> superImpl);
/**
/**
* Checks whether the given uid has been granted the specified permission.
*/
int checkUidPermission(String permName, int uid,
BiFunction<String, Integer, Integer> superImpl);
}
我翻遍了Service、internal、framework层的代码,没有知道它的实现类,而且网上关于这个类的信息极少。难道线索就此断了?不!
1.8、回到PMS服务
在1.6节的方法中,仔细看发现不会调用CheckPermissionDelegate类的方法,而是直接走PMS内部的权限检测方法checkUidPermissionImpl(String permName, int uid):
private int checkUidPermissionImpl(String permName, int uid) {
final AndroidPackage pkg = mPackageManagerInt.getPackage(uid);
return checkUidPermissionInternal(pkg, uid, permName);
}
经过中间的回调,最终通过下面的checkUidPermissionInternal(AndroidPackage pkg, int uid,String permissionName)方法校验权限:
private int checkUidPermissionInternal(@Nullable AndroidPackage pkg, int uid,
@NonNull String permissionName) {
if (pkg != null) {
final int userId = UserHandle.getUserId(uid);
return checkPermissionInternal(pkg, false, permissionName, userId);
}
if (checkSingleUidPermissionInternal(uid, permissionName)) {
return PackageManager.PERMISSION_GRANTED;
}
final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName);
if (fullerPermissionName != null
&& checkSingleUidPermissionInternal(uid, fullerPermissionName)) {
return PackageManager.PERMISSION_GRANTED;
}
return PackageManager.PERMISSION_DENIED;
}
这其中涉及到几个全局变量保存系统全部权限的数组、保存权限的映射,说到底权限检测也就是String匹配校验...
/**
* Built-in permissions. Read from system configuration files. Mapping is from
* UID to permission name.
*/
private final SparseArray<ArraySet<String>> mSystemPermissions;
/** If the permission of the value is granted, so is the key */
private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>();
... ...
2、申请权限源码流程
如果检测出来没有某项权限,需要用requestPermissions(String[] permissions, int requestCode)动态申请权限。在Activity、Fragment中都有请求权限的方法,在Activity和Fragment中都可以调用requestPermissions,请求权限的方法名字相同,但是实现不同。
2.1、在Activity中请求权限
在Activity中,有调用了一个隐藏的方法startActivityForResult(String who, Intent intent, int requestCode,Bundle options)方法
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
if (requestCode < 0) {
throw new IllegalArgumentException("requestCode should be >= 0");
}
if (mHasCurrentPermissionsRequest) {
Log.w(TAG, "Can request only one set of permissions at a time");
// Dispatch the callback with empty arrays which means a cancellation.
onRequestPermissionsResult(requestCode, new String[0], new int[0]);
return;
}
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
启动系统Activity请求权限,也就是出现授权的弹框:
private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:";
2.2、在Fragment中请求权限
在Fragment中较为繁琐,先借助一个FragmentHostCallback回调对象mHost中的onRequestPermissionsFromFragment(this, permissions, requestCode)方法。
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mHost.onRequestPermissionsFromFragment(this, permissions, requestCode);
}
而FragmentActivity实现了FragmentHostCallback接口
@Override
public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
@NonNull String[] permissions, int requestCode) {
FragmentActivity.this.requestPermissionsFromFragment(fragment, permissions,
requestCode);
}
进而又调用了FragmentActivity中的requestPermissionsFromFragment()方法
/**
* Called by Fragment.requestPermissions() to implement its behavior.
*/
void requestPermissionsFromFragment(@NonNull Fragment fragment, @NonNull String[] permissions,
int requestCode) {
if (requestCode == -1) {
ActivityCompat.requestPermissions(this, permissions, requestCode);
return;
}
checkForValidRequestCode(requestCode);
try {
mRequestedPermissionsFromFragment = true;
int requestIndex = allocateRequestIndex(fragment);
ActivityCompat.requestPermissions(this, permissions,
((requestIndex + 1) << 16) + (requestCode & 0xffff));
} finally {
mRequestedPermissionsFromFragment = false;
}
}
在该方法中终于快看到头了ActivityCompat.requestPermissions(this, permissions, requestCode);
public static void requestPermissions(final @NonNull Activity activity,
final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode) {
... ...
activity.requestPermissions(permissions, requestCode);
... ...
}
这其中的activity.requestPermissions(permissions, requestCode)调用的正式第一种Activity中的requestPermissions()申请权限的方法。
2.3、权限申请回调
ActivityCompat类中定义的接口OnRequestPermissionsResultCallback:
public interface OnRequestPermissionsResultCallback {
/**
* 请求权限的结果的回调。 每次调用时都会调用此方法
*{@link #requestPermissions(android.app.Activity,String[], int)}
*权限请求与用户的交互可能会中断。 在这种情况下,您将收到空的权限和结果数组,应将其视为取消
*
* @param requestCode 传入的请求代码 {@link #requestPermissions(android.app.Activity, String[], int)}
* @param permissions 请求的权限,永远不会为空。
* @param grantResults 授予相应权限的结果:要么拒绝,要么授予。
* android.content.pm.PackageManager#PERMISSION_GRANTED
* or
* android.content.pm.PackageManager#PERMISSION_DENIED 永远不会为空
* @see #requestPermissions(android.app.Activity, String[], int)
*/
void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults);
}
AppCompatActivity继承自FragmentActivity,在FragmentActivity中可以看到实现了ActivityCompat.OnRequestPermissionsResultCallback接口。我们就可以在Activity去重写这个请求权限的回调方法,系统会回调该方法。
public class FragmentActivity extends ComponentActivity implements ActivityCompat.OnRequestPermissionsResultCallback...{
...
}
推荐资料:
浅析Android权限机制
Android权限校验过程
Android 9.0 activity启动源码分析
Android9.0动态运行时权限源码分析及封装改造
Android权限系统:运行时权限检查和申请