前言
google在 Android 6.0 开始引入了权限申请机制,将所有权限分成了正常权限和危险权限。App每次在使用危险权限时需要动态的申请并得到用户的授权才能使用,否则会抛出异常。
那么权限申请、校验又是如何实现的呢?这也是我们这篇笔记的要分析的重点。
权限的分类:
系统权限分为两类:正常权限 和 危险权限。
正常权限不会直接给用户隐私权带来风险。如果您的应用在其清单中列出了正常权限,系统将自动授予 该权限。
危险权限会授予应用访问用户机密数据的权限。如果您的应用在其清单中列出了正常权限,系统将自动 授予该权限。如果您列出了危险权限,则用户必须明确批准您的应用使用这些权限。
核心方法简介
ContextCompat.checkSelfPermission
检查应用是否具有某个危险权限。- 如果应用具有此权限,方法将返回 PackageManager.PERMISSION_GRANTED,并且应用可以继续操作。
- 如果应用不具有此权限,方法将返回 PackageManager.PERMISSION_DENIED,且应用必须明确向用户要求权限。
ActivityCompat.shouldShowRequestPermissionRationale
判断是否之前拒绝过某个权限的申请- 如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true。
- 如果用户在过去拒绝了权限请求,并在 权限请求系统对话框中选择了 Don't ask again 选项,此方法将返回 false。
- 如果设备禁止应用具有该权限,此方法也会返回 false。
ActivityCompat.requestPermissions
应用可以通过这个方法动态申请权限,调用后会弹出一个对话框提示用户授权所申请的权限。Activity.onRequestPermissionsResult
当应用请求权限时,系统将向用户显示一个对话框。当用户响应时,系统将调用应用的 onRequestPermissionsResult() 方法,向其传递用户响应,处理对应的场景。
Android 权限申请流程开放给开发人员核心的方法主要有4个,通过这4个核心方法开发者就能实现我们应用的权限申请。
权限申请流程
从业务层面上拆解权限申请步骤如下图所示
通过上面的步骤,我们来进一步深入源码层来了解PKMS 是如何控制我们应用的权限的
权限检查一 ContextCompat.checkSelfPermission
ContextCompat.checkSelfPermission
=> ContextImpl.checkPermission
=> AMS.checkPermission
=>AMS.checkComponentPermission
=> AM.checkComponentPermission
权限检查会跨进程Binder 调用AMS.checkPermission
接着一步步调用到AM.checkComponentPermission
AM.checkComponentPermission
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
// Root, system server get to do everything.
final int appId = UserHandle.getAppId(uid);
//如果uid 是ROOT_UID 或者 系统Uid 则直接拥有权限
if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
return PackageManager.PERMISSION_GRANTED;
}
// Isolated processes don't get any permissions.
//孤立进程没有任何权限,普通应用进程都不是孤立进程
if (UserHandle.isIsolated(uid)) {
return PackageManager.PERMISSION_DENIED;
}
// If there is a uid that owns whatever is being accessed, it has
// blanket access to it regardless of the permissions it requires.
//拥有权限的id 和申请的id 是相同的 则直接拥有该权限
if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {//owningUid == -1 不执行下面代码
return PackageManager.PERMISSION_GRANTED;
}
// If the target is not exported, then nobody else can get to it.
if (!exported) { //exported == true 不执行下面代码
/*
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
here);
*/
return PackageManager.PERMISSION_DENIED;
}
if (permission == null) {
return PackageManager.PERMISSION_GRANTED;
}
try {
//获取PAKMS Binder
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
AppGlobals.getPackageManager
=> ActivityThread.getPackageManager
ActivityThread.getPackageManager
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
return sPackageManager;
}
final IBinder b = ServiceManager.getService("package");
//sPackageManager == PackageManagerService 实例
sPackageManager = IPackageManager.Stub.asInterface(b);
return sPackageManager;
}
因为PKMS 和 AMS 在同一个进程,所以这边的 sPackageManager 就是继承了IPackageManager.Stub 的 PackageManagerService 实例。接着就进入PackageManagerService.checkUidPermission
PackageManagerService.checkUidPermission
=> PermissionManagerService.checkUidPermission
PermissionManagerService.checkUidPermission
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);
}
//这个用于外面拦截,可以做一些额外操作,不过最终还是执行 checkUidPermissionImpl
return checkPermissionDelegate.checkUidPermission(permName, uid,
this::checkUidPermissionImpl);
}
PermissionManagerService.checkUidPermissionImpl
private int checkUidPermissionImpl(String permName, int uid) {
//1.PackageManagerInternalImpl 获取应用的package信息
final AndroidPackage pkg = mPackageManagerInt.getPackage(uid);
//2.校验该应用是否已经拥有该权限
return checkUidPermissionInternal(pkg, uid, permName);
}
//PackageManagerService.java PackageManagerInternalImpl内部类
public AndroidPackage getPackage(int uid) {
synchronized (mLock) {
final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID);
AndroidPackage pkg = null;
final int numPackages = packageNames == null ? 0 : packageNames.length;
for (int i = 0; pkg == null && i < numPackages; i++) {
pkg = mPackages.get(packageNames[i]);
}
return pkg;
}
}
从PackageManagerService 的 mPackages集合中获取对应app的package
PermissionManagerService.checkUidPermissionInternal
=> PermissionManagerService.checkPermissionInternal
=> PermissionManagerService.checkSinglePermissionInternal
PermissionManagerService.checkSinglePermissionInternal
private boolean checkSinglePermissionInternal(int uid,
@NonNull PermissionsState permissionsState, @NonNull String permissionName) {
//当前应用 permissionsState存有应用权限形象,调用该方法确认是否有权限
if (!permissionsState.hasPermission(permissionName, UserHandle.getUserId(uid))) {
return false;
}
//InstantApp 相关逻辑
if (mPackageManagerInt.getInstantAppPackageName(uid) != null) {
return mSettings.isPermissionInstant(permissionName);
}
return true;
}
PermissionsState.hasPermission
public boolean hasPermission(String name, int userId) {
synchronized (mLock) {
if (mPermissions == null) {
return false;
}
//permissionData 保存应用权限申请情况
PermissionData permissionData = mPermissions.get(name);
return permissionData != null && permissionData.isGranted(userId);
}
}
权限检查二 ActivityCompat.shouldShowRequestPermissionRationale
检查权限拒绝情况
ActivityCompat.shouldShowRequestPermissionRationale
=> Activity.shouldShowRequestPermissionRationale
=> PKMS.shouldShowRequestPermissionRationale
跨进程 Binder 调用
PKMS.shouldShowRequestPermissionRationale
public boolean shouldShowRequestPermissionRationale(String permName,
String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
//...
//如果权限已申请也会返回false,所以先要进行权限检查一在进入权限检查二
if (checkPermission(permName, packageName, userId)
== PackageManager.PERMISSION_GRANTED) {
return false;
}
final int flags;
final long identity = Binder.clearCallingIdentity();
try {
//返回权限申请操作的标记
flags = getPermissionFlagsInternal(permName, packageName, callingUid, userId);
} finally {
Binder.restoreCallingIdentity(identity);
}
final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
| PackageManager.FLAG_PERMISSION_POLICY_FIXED
| PackageManager.FLAG_PERMISSION_USER_FIXED;
//判断是否是用户强制拒绝(不在提醒)或系统拒绝或政策拒绝
if ((flags & fixedFlags) != 0) {
return false;
}
//...
// 判断是否是用户点击取消
return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
}
PKMS.getPermissionFlagsInternal
获取权限申请标记
private int getPermissionFlagsInternal(
String permName, String packageName, int callingUid, int userId) {
//...
synchronized (mLock) {
//如果没有权限申请记录,直接返回 0
if (mSettings.getPermissionLocked(permName) == null) {
return 0;
}
}
//...
PermissionsState permissionsState = ps.getPermissionsState();
//获取权限标记
return permissionsState.getPermissionFlags(permName, userId);
}
权限申请 ActivityCompat.requestPermissions
应用可以通过这个方法动态申请权限,调用后会弹出一个对话框提示用户授权所申请的权限。
ActivityCompat.requestPermissions
=> Activity.requestPermissions
Activity.requestPermissions
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
//通过PKMS来封装隐式跳转 permission.ui.GrantPermissionsActivity Intent
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
Activity.getPackageManager
=> ContextImpl.getPackageManager
=> ApplicationPackageManager.getPackageManager
ApplicationPackageManager 派生自PackageManager 所以 getPackageManager().buildRequestPermissionsIntent
== PackageManager.buildRequestPermissionsIntent
PackageManager.buildRequestPermissionsIntent
public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS";
public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
if (ArrayUtils.isEmpty(permissions)) {
throw new IllegalArgumentException("permission cannot be null or empty");
}
Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
intent.setPackage(getPermissionControllerPackageName());
return intent;
}
返回一个隐式的Intent,用来打开系统的应用。那么打开什么应用呢?我们可以从ACTION_REQUEST_PERMISSIONS 的值入手
<!-- PackageInstaller AndroidManifest.xml -->
<activity android:name="com.android.permissioncontroller.permission.ui.GrantPermissionsActivity"
android:configChanges="keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:theme="@style/GrantPermissions.FilterTouches"
android:visibleToInstantApps="true"
android:inheritShowWhenLocked="true">
<intent-filter android:priority="1">
<action android:name="android.content.pm.action.REQUEST_PERMISSIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
我们通过 android.content.pm.action.REQUEST_PERMISSIONS 这个action 一个个系统应用的找,最终找到对应Activity
GrantPermissionsActivity.showNextPermissionGroupGrantRequest
在GrantPermissionsActivity.onCreate
中调用GrantPermissionsActivity.showNextPermissionGroupGrantRequest
来进行权限组的申请,这也就表明当你申请权限组某个权限时,也就是申请整个权限组的权限,申请成功你将拥有整个权限组的权限
private boolean showNextPermissionGroupGrantRequest() {
int numGroupStates = mRequestGrantPermissionGroups.size();
//确定权限申请个数
int numGrantRequests = 0;
for (int i = 0; i < numGroupStates; i++) {
//做过滤,排除不需要申请和重复的权限申请
if (shouldShowRequestForGroupState(mRequestGrantPermissionGroups.valueAt(i))) {
numGrantRequests++;
}
}
int currentIndex = 0;
//...
for (GroupState groupState : groupStates) {
if (!shouldShowRequestForGroupState(groupState)) {
continue;
}
if (groupState.mState == GroupState.STATE_UNKNOWN) {
//...权限申请UI内容初始化
// Set the permission message as the title so it can be announced.
setTitle(message);
//显示权限申请UI numGrantRequests 总的权限组申请个数; currentIndex 当前申请到第几个
mViewHandler.updateUi(groupState.mGroup.getName(), numGrantRequests, currentIndex,
icon, message, detailMessage, mButtonVisibilities);
return true;
}
if (groupState.mState != GroupState.STATE_SKIPPED) {
currentIndex++;
}
}
//没有可申请的权限了
return false;
}
mViewHandler 在 onCreate 时候创建的 com.android.permissioncontroller.permission.ui.handheld.GrantPermissionsViewHandlerImpl 实例。接下来我们看mViewHandler相关的点击事件
mViewHandler.onClick
public void onClick(View view) {
int id = view.getId();
if (id == R.id.grant_singleton) {
if (mResultListener != null) {
//取消权限申请
mResultListener.onPermissionGrantResult(mGroupName, CANCELED);
} else {
mActivity.finish();
}
return;
}
int button = -1;
try {
button = BUTTON_RES_ID_TO_NUM.get(id);
} catch (NullPointerException e) {
// Clicked a view which is not one of the defined buttons
return;
}
switch (button) {
case ALLOW_BUTTON:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
//始终同意权限申请
mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ALWAYS);
}
break;
case ALLOW_FOREGROUND_BUTTON:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
//只允许前台使用权限
mResultListener.onPermissionGrantResult(mGroupName,
GRANTED_FOREGROUND_ONLY);
}
break;
case ALLOW_ALWAYS_BUTTON:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
//始终同意权限申请
mResultListener.onPermissionGrantResult(mGroupName,
GRANTED_ALWAYS);
}
break;
case ALLOW_ONE_TIME_BUTTON:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
//仅此一次
mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ONE_TIME);
}
break;
case DENY_BUTTON:
case NO_UPGRADE_BUTTON:
case NO_UPGRADE_OT_BUTTON:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
//拒绝权限申请
mResultListener.onPermissionGrantResult(mGroupName, DENIED);
}
break;
case DENY_AND_DONT_ASK_AGAIN_BUTTON:
case NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON:
case NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
//拒绝权限申请 并不再提示
mResultListener.onPermissionGrantResult(mGroupName,
DENIED_DO_NOT_ASK_AGAIN);
}
break;
}
}
mResultListener负责点击交互回调,在GrantPermissionsActivity.onCreate
中设置。接着看下回调的处理。
GrantPermissionsActivity.onPermissionGrantResult
public void onPermissionGrantResult(String name,
@GrantPermissionsViewHandler.Result int result) {
GroupState foregroundGroupState = getForegroundGroupState(name);
GroupState backgroundGroupState = getBackgroundGroupState(name);
//...
switch (result) {
case CANCELED://取消
if (foregroundGroupState != null) {
reportRequestResult(foregroundGroupState.affectedPermissions,
PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED);
}
if (backgroundGroupState != null) {
reportRequestResult(backgroundGroupState.affectedPermissions,
PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_IGNORED);
}
setResultAndFinish();
return;
case GRANTED_ALWAYS :
if (foregroundGroupState != null) {
onPermissionGrantResultSingleState(foregroundGroupState, true, false, false);
}
if (backgroundGroupState != null) {
onPermissionGrantResultSingleState(backgroundGroupState, true, false, false);
}
break;
case GRANTED_FOREGROUND_ONLY :
if (foregroundGroupState != null) {
onPermissionGrantResultSingleState(foregroundGroupState, true, false, false);
}
if (backgroundGroupState != null) {
onPermissionGrantResultSingleState(backgroundGroupState, false, false, false);
}
break;
case GRANTED_ONE_TIME:
if (foregroundGroupState != null) {
onPermissionGrantResultSingleState(foregroundGroupState, true, true, false);
}
if (backgroundGroupState != null) {
onPermissionGrantResultSingleState(backgroundGroupState, false, true, false);
}
break;
case DENIED :
if (foregroundGroupState != null) {
onPermissionGrantResultSingleState(foregroundGroupState, false, false, false);
}
if (backgroundGroupState != null) {
onPermissionGrantResultSingleState(backgroundGroupState, false, false, false);
}
break;
case DENIED_DO_NOT_ASK_AGAIN :
if (foregroundGroupState != null) {
onPermissionGrantResultSingleState(foregroundGroupState, false, false, true);
}
if (backgroundGroupState != null) {
onPermissionGrantResultSingleState(backgroundGroupState, false, false, true);
}
break;
}
//进入下一个权限申请,直到所有权限申请完毕
if (!showNextPermissionGroupGrantRequest()) {
setResultAndFinish();
}
}
onPermissionGrantResult
方法里主要是以下两个方法
onPermissionGrantResultSingleState
更新权限状态showNextPermissionGroupGrantRequest
申请下一个权限,重复权限申请第一步
GrantPermissionsActivity.onPermissionGrantResultSingleState
private void onPermissionGrantResultSingleState(GroupState groupState, boolean granted,
boolean isOneTime, boolean doNotAskAgain) {
if (groupState != null && groupState.mGroup != null
&& groupState.mState == GroupState.STATE_UNKNOWN) {
if (granted) {
int permissionGrantRequestResult =
PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED;
if (isOneTime) {
groupState.mGroup.setOneTime(true);
permissionGrantRequestResult =
PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED_ONE_TIME;
} else {
groupState.mGroup.setOneTime(false);
}
groupState.mGroup.grantRuntimePermissions(true, doNotAskAgain,
groupState.affectedPermissions);
groupState.mState = GroupState.STATE_ALLOWED;
} else {
groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
groupState.affectedPermissions);
groupState.mGroup.setOneTime(false);
groupState.mState = GroupState.STATE_DENIED;
}
}
}
这个方法的核心目的就是将groupState权限状态更新,更新完之后就是将结果返回setResultAndFinish
。
权限回调
在GrantPermissionsActivity权限申请完毕之后并调用setResultAndFinish
。通过AMS跨进程调用,将结果返回ActivityThread.handleResumeActivity
=> ActivityThread.performResumeActivity
=> ActivityThread.deliverResults
=> Activity.dispatchActivityResult
Activity.dispatchActivityResult
void dispatchActivityResult(String who, int requestCode, int resultCode, Intent data,
String reason) {
mFragments.noteStateNotSaved();
if (who == null) {
onActivityResult(requestCode, resultCode, data);
} else if (who.startsWith(REQUEST_PERMISSIONS_WHO_PREFIX)) {
//activity的权限申请回调
who = who.substring(REQUEST_PERMISSIONS_WHO_PREFIX.length());
if (TextUtils.isEmpty(who)) {
//分发权限申请结果
dispatchRequestPermissionsResult(requestCode, data);
} else {
//fragment的权限申请回调
Fragment frag = mFragments.findFragmentByWho(who);
if (frag != null) {
dispatchRequestPermissionsResultToFragment(requestCode, data, frag);
}
}
} //...
}
Activity.dispatchRequestPermissionsResult
private void dispatchRequestPermissionsResult(int requestCode, Intent data) {
mHasCurrentPermissionsRequest = false;
// If the package installer crashed we may have not data - best effort.
String[] permissions = (data != null) ? data.getStringArrayExtra(
PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES) : new String[0];
final int[] grantResults = (data != null) ? data.getIntArrayExtra(
PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS) : new int[0];
//也就是最后一步 权限回调处理
onRequestPermissionsResult(requestCode, permissions, grantResults);
}
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
/* callback - no nothing */
}
最后应用重载onRequestPermissionsResult
处理权限结果回调
完结
整个权限申请逻辑还是比较清晰,期间的跨进程调用需要对Binder,AMS,PKMS 有一定的了解。
PKMS 分析 juejin.cn/post/688292…
Activity启动流程 juejin.cn/post/687819…
AMS 启动流程 juejin.cn/post/687705…
Binder 原理 gityuan.com/2015/10/31/…