[TOC]
0 综述
基于 Android 7.1.1 源码,分析系统的权限管理机制!
我们来分析下 dangerous 权限的申请,这类权限只能在 Activity 中申请,Activity 提供了如下接口,来帮助程序申请权限:
1 Activity.requestPermissions - 请求入口
1 | public final void requestPermissions(@NonNull String[] permissions, int requestCode) { |
1.1 PackageManager.buildRequestPermissionsIntent
1 | public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) { |
这里创建了一个 intent,action 为 android.content.pm.action.REQUEST_PERMISSIONS,同时用 intent 封装了权限信息!1
2
3
4
5
6
public static final String ACTION_REQUEST_PERMISSIONS =
"android.content.pm.action.REQUEST_PERMISSIONS"; // hide
public static final String EXTRA_REQUEST_PERMISSIONS_NAMES =
"android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
接下来,我们要问,谁会接受这个 intent 呢?
下面还调用了 getPermissionControllerPackageName 方法,设置了 intent 的接收者1
2
3
4
5
6
public String getPermissionControllerPackageName() {
synchronized (mPackages) {
return mRequiredInstallerPackage; // 就是 PackageInstaller!
}
}
就是 PackageInstaller,我们需要进入 PackageInstaller,分析接下来的逻辑!
2 PackageInstaller.GrantPermissionsActivity
通过分析 PackageInstaller 的代码,我们知道了 GrantPermissionsActivity 会接受该 intent!
1 | <activity android:name=".permission.ui.GrantPermissionsActivity" |
GrantPermissionsActivity 虽然是一个 Activity,但实际的现实效果是一个 Dialog,下面我们会一个一个分析!
2.1 GrantPermissionsActivity.onCreate
我们去 onCreate 方法中去看看:
1 | public class GrantPermissionsActivity extends OverlayTouchActivity |
2.1.1 new GrantPermissionsViewHandlerImpl(this)
有一个成员变量 private GrantPermissionsViewHandler mViewHandler,用于处理权限弹窗的显示和相关逻辑显示,在 onCreate 方法中,首先会创建一个 GrantPermissionsViewHandlerImpl 对象!1
2
3
4
5
6
7
8public final class GrantPermissionsViewHandlerImpl
implements GrantPermissionsViewHandler, OnClickListener {
public GrantPermissionsViewHandlerImpl(Context context) {
mContext = context;
}
}
同时,又调用了 setResultListener,设置监听器!
1 |
|
这里的 ResultListener 就是 GrantPermissionsActivity,因为其实现了 ResultListener 接口!
2.1.2 GrantPermissionsActivity.setResultAndFinish
当遇到一些特殊情况,不能继续申请权限的时候,我们会调用 setResultAndFinish 结束申请!
1 | private void setResultAndFinish() { |
继续调用了 setResultIfNeeded 方法!1
2
3
4
5
6
7
8
9
10
11private void setResultIfNeeded(int resultCode) {
if (!mResultSet) {
mResultSet = true;
logRequestedPermissionGroups();
Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS);
result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions);
result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, mGrantResults);
// 调用 Activity.setResult 方法!
setResult(resultCode, result);
}
}
2.1.3 GrantPermissionsActivity.getCallingPackageInfo
该方法用于获得申请运行时权限的应用的 PackageInfo 对象!1
2
3
4
5
6
7
8
9
10private PackageInfo getCallingPackageInfo() {
try {
//【2.1.3.1】最终调用 PMS 的 getPackageInfo 方法!
return getPackageManager().getPackageInfo(getCallingPackage(),
PackageManager.GET_PERMISSIONS);
} catch (NameNotFoundException e) {
Log.i(LOG_TAG, "No package: " + getCallingPackage(), e);
return null;
}
}
PackageManager.GET_PERMISSIONS 标志是为了限制查询到的数据的量!
2.1.3.1 PackageManagerS.getPackageInfo
flags 传入 PackageManager.GET_PERMISSIONS!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
flags = updateFlagsForPackage(flags, userId, packageName);
enforceCrossUserPermission(Binder.getCallingUid(), userId,
false /* requireFullPermission */, false /* checkShell */, "get package info");
synchronized (mPackages) {
// 如果 package 重命名过,返回旧名字!
packageName = normalizePackageNameLPr(packageName);
final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
PackageParser.Package p = null;
if (matchFactoryOnly) {
final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName);
if (ps != null) {
return generatePackageInfo(ps, flags, userId);
}
}
// 从 PMS.mPackages 中获取扫描解析后的 PackageParser.Package 对象!
if (p == null) {
p = mPackages.get(packageName);
if (matchFactoryOnly && p != null && !isSystemApp(p)) {
return null;
}
}
if (DEBUG_PACKAGE_INFO)
Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
if (p != null) {
//【2.1.3.2】如果有扫描结果,那就调用 generatePackageInfo 返回包信息!
return generatePackageInfo((PackageSetting)p.mExtras, flags, userId);
}
// 如果没有扫描结果,那么我们从上一次的安装信息中获取包信息!
if (!matchFactoryOnly && (flags & MATCH_UNINSTALLED_PACKAGES) != 0) {
final PackageSetting ps = mSettings.mPackages.get(packageName);
return generatePackageInfo(ps, flags, userId);
}
}
return null;
}
流程很简单,不多说了!
2.1.3.2 PackageManagerS.generatePackageInfo
1 | private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) { |
继续看:
2.1.3.3 PackageManagerS.generatePackageInfo
1 | public static PackageInfo generatePackageInfo(PackageParser.Package p, |
最后,返回的是一个 PackageInfo 对象!
2.1.4 GrantPermissionsActivity.updateDefaultResults
更新下权限的默认授予情况!1
2
3
4
5
6
7
8
9
10
11private void updateDefaultResults(PackageInfo callingPackageInfo, int permissionPolicy) {
final int requestedPermCount = mRequestedPermissions.length;
for (int i = 0; i < requestedPermCount; i++) {
String permission = mRequestedPermissions[i];
//【2.1.3.1 】如果申请应用的信息存在,那么会调用 computePermissionGrantState 方法,获得权限的默认授予状态!
// 否则,所有运行时权限默认为 deny!
mGrantResults[i] = callingPackageInfo != null
? computePermissionGrantState(callingPackageInfo, permission, permissionPolicy)
: PERMISSION_DENIED;
}
}
关键的实现是在 computePermissionGrantState 中!
2.1.4.1 GrantPermissionsActivity.computePermissionGrantState
该方法用于根据参数,计算该运行时权限的授予情况,这里的 permission 是应用本次申请的运行时权限!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41private int computePermissionGrantState(PackageInfo callingPackageInfo,
String permission, int permissionPolicy) {
boolean permissionRequested = false;
//【1】如果运行时权限 permission 在 package 请求权限列表中
// 并且已经授予,那么就返回 PERMISSION_GRANTED!
for (int i = 0; i < callingPackageInfo.requestedPermissions.length; i++) {
if (permission.equals(callingPackageInfo.requestedPermissions[i])) {
permissionRequested = true;
if ((callingPackageInfo.requestedPermissionsFlags[i]
& PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
return PERMISSION_GRANTED;
}
break;
}
}
//【2】申请的运行时权限,应用没有申明,返回 PERMISSION_DENIED!
if (!permissionRequested) {
return PERMISSION_DENIED;
}
//【3】只有基本权限类型为 dangerous 的权限才需要动态申请,其他类型的权限,默认返回 PERMISSION_DENIED!
try {
PermissionInfo pInfo = getPackageManager().getPermissionInfo(permission, 0);
if ((pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
!= PermissionInfo.PROTECTION_DANGEROUS) {
return PERMISSION_DENIED;
}
} catch (NameNotFoundException e) {
return PERMISSION_DENIED;
}
//【4】根据 permissionPolicy 的值来处理权限的授予情况,如果是 PERMISSION_POLICY_AUTO_GRANT,表示自动授予;
// 那么返回 PERMISSION_GRANTED,否则返回 PERMISSION_DENIED!
switch (permissionPolicy) {
case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: {
return PERMISSION_GRANTED;
}
default: {
return PERMISSION_DENIED;
}
}
}
最后 computePermissionGrantState 的结果会保存 mGrantResults 数组中!
不多说了!
2.1.5 new AppPermissions - 封装 App 的权限请求信息
这里的 onErrorCallback 是一个 Runnable,执行后会触发 setResultAndFinish 方法!
1 | public AppPermissions(Context context, PackageInfo packageInfo, String[] permissions, |
mFilterPermissions 属性表示要过滤的权限,这里我们传入的是 null;
同时,AppPermissions 还有如下的属性:
1 | // 保存所有运行时权限所属的组信息 AppPermissionGroup |
2.1.5.1 AppPermissions.loadPermissionGroups
加载权限组信息
1 | private void loadPermissionGroups() { |
对于 mPackageInfo.requestedPermissions,不仅包含运行时权限,还包含安装时权限!
2.1.5.1.1 AppPermissions.hasGroupForPermission
加载权限组信息!1
2
3
4
5
6
7
8
9private boolean hasGroupForPermission(String permission) {
//【1】遍历所有的 AppPermissionGroup,如果 permission 在 group 的权限集合中,返回 true!
for (AppPermissionGroup group : mGroups) {
if (group.hasPermission(permission)) {
return true;
}
}
return false;
}
AppPermissions 内部有一个 mGroups,保存所有的权限组信息 AppPermissionGroup 实例!
1 | public final class AppPermissionGroup implements Comparable<AppPermissionGroup> { |
AppPermissionGroup 内部有一个 mPermissions 哈希表,用于保存该 group 中所有权限的权限名和其 Permission 的映射关系!
2.1.5.1.2 AppPermissionGroup.create[3] - 创建运行时权限组信息
接下来我们看下,AppPermissionGroup 的创建,String permissionName 表示的是该 package 的申请的权限:
1 | public static AppPermissionGroup create(Context context, PackageInfo packageInfo, |
这里可以看到,流程【2】中,会排除掉所有的非 dangerous 类型的权限!
在创建一个 group 的时候,会将属于该 group 的所有权限都查出来!!
这里继续调用了五参数的 create 方法!
2.1.5.1.3 AppPermissionGroup.create[5]
继续调用 create 方法来创建 AppPermissionGroup 对象!
参数分析:
- PackageInfo packageInfo:本次请求权限的 package 的 PackageInfo 对象;
- PackageItemInfo groupInfo:该 package 的请求的权限所属的组信息;
- List
permissionInfos :属于该组的所有权限信息;
1 | public static AppPermissionGroup create(Context context, PackageInfo packageInfo, |
到这里,AppPermissionGroup 就正式创建完了!
2.1.5.1.3.1 new AppPermissionGroup
创建了一个 AppPermissionGroup,封装运行时权限所在权限组信息!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27public final class AppPermissionGroup implements Comparable<AppPermissionGroup> {
... ... ...
private AppPermissionGroup(Context context, PackageInfo packageInfo, String name,
String declaringPackage, CharSequence label, CharSequence description,
String iconPkg, int iconResId, UserHandle userHandle) {
mContext = context;
mUserHandle = userHandle;
mPackageManager = mContext.getPackageManager();
mPackageInfo = packageInfo;
mAppSupportsRuntimePermissions = packageInfo.applicationInfo
.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1; // 该应用是否支持运行时权限!
mAppOps = context.getSystemService(AppOpsManager.class);
mActivityManager = context.getSystemService(ActivityManager.class);
mDeclaringPackage = declaringPackage;
mName = name;
mLabel = label;
mDescription = description;
if (iconResId != 0) {
mIconPkg = iconPkg;
mIconResId = iconResId;
} else {
mIconPkg = context.getPackageName();
mIconResId = R.drawable.ic_perm_device_info;
}
}
... ... ...
}
AppPermissionGroup 内部有一个 mPermissions 集合,负责管理该 group 中该 package 申请的权限!
2.1.5.1.3.2 new Permission
该 Permission 是 PackageInstaller 定义的一个类,和系统中的不一样!
Permission 用来封装一个运行时权限信息!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public final class Permission {
private final String mName;
private final String mAppOp;
private boolean mGranted; // 是否已经授予该权限!
private boolean mAppOpAllowed;
private int mFlags;
public Permission(String name, boolean granted,
String appOp, boolean appOpAllowed, int flags) {
mName = name; //
mGranted = granted;
mAppOp = appOp;
mAppOpAllowed = appOpAllowed;
mFlags = flags;
}
2.1.5.1.3.3 AppPermissionGroup.addPermission
将该运行时权限添加到 AppPermissionGroup.mPermissions 中去!1
2
3private void addPermission(Permission permission) {
mPermissions.put(permission.getName(), permission);
}
2.1.6 处理运行时权限组
1 | //【3】接着遍历本次请求的所有运行时权限! |
当然,一般情况下,我们会进入 default 分支!
这里的 mRequestGrantPermissionGroups 用于保存所有的需要申请的 group 信息:
1 | private static final class GroupState { |
AppPermissionGroup 会被封装为一个 GroupState 对象,GroupState.mState 保存了该组的授予情况!
2.1.6.1 AppPermissionGroup.isUserFixed/isPolicyFixed
判断该权限组中是否包含没被 user fix 的权限,如果包含,返回 false!
1 | public boolean isUserFixed() { |
调用了 Permission.isUserFixed 方法!1
2
3public boolean isUserFixed() {
return (mFlags & PackageManager.FLAG_PERMISSION_USER_FIXED) != 0;
}
同样的,isPolicyFixed 也是类似的作用,判断该权限组中是否包含没有被 policy fix 的权限!1
2
3
4
5
6
7
8
9
10
11public boolean isPolicyFixed() {
final int permissionCount = mPermissions.size();
for (int i = 0; i < permissionCount; i++) {
Permission permission = mPermissions.valueAt(i);
//【1】该权限组中没有任何一个 policy fix 的权限,那个这个组才是 no policy fix 的!
if (permission.isPolicyFixed()) {
return true;
}
}
return false;
}
这是因为 policy fix 属于系统的默认授予机制,针对整个组的权限!
同样的调用了 Permission.isPolicyFixed 方法!1
2
3public boolean isPolicyFixed() {
return (mFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
}
2.1.6.2 AppPermissionGroup.areRuntimePermissionsGranted
该方法用户判断该 group 中是否有运行时权限被授予!1
2
3public boolean areRuntimePermissionsGranted() {
return areRuntimePermissionsGranted(null);
}
接着会调用一参数的 areRuntimePermissionsGranted 方法,参数的意思不多说了!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public boolean areRuntimePermissionsGranted(String[] filterPermissions) {
if (LocationUtils.isLocationGroupAndProvider(mName, mPackageInfo.packageName)) {
return LocationUtils.isLocationEnabled(mContext);
}
//【1】遍历该组中的所有运行时权限,根据前面知道,该组中的权限都是该 package 申请的!
final int permissionCount = mPermissions.size();
for (int i = 0; i < permissionCount; i++) {
Permission permission = mPermissions.valueAt(i);
if (filterPermissions != null // 如果 filterPermissions 不为 null,那就只处理其内部的权限!
&& !ArrayUtils.contains(filterPermissions, permission.getName())) {
continue;
}
//【1.1】如果应用支持运行时权限机制,并且该权限已经被授予,那就返回 true!
if (mAppSupportsRuntimePermissions) {
if (permission.isGranted()) {
return true;
}
} else if (permission.isGranted() && (permission.getAppOp() == null
|| permission.isAppOpAllowed())) {
return true;
}
}
return false; // 如果该 group 中没有运行时权限被授予,返回 false!
}
如果有一个运行时权限被授予,那么就会返回 true!
如果没有一个运行时权限被授予,那么就会返回 false!
2.1.7 GPViewHandlerImpl.createView
接着,通过 setContentView,创建布局界面!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public View createView() {
mRootView = (ManualLayoutFrame) LayoutInflater.from(mContext)
.inflate(R.layout.grant_permissions, null);
mButtonBar = (ButtonBarLayout) mRootView.findViewById(R.id.button_group);
mButtonBar.setAllowStacking(true);
mMessageView = (TextView) mRootView.findViewById(R.id.permission_message);
mIconView = (ImageView) mRootView.findViewById(R.id.permission_icon);
mCurrentGroupView = (TextView) mRootView.findViewById(R.id.current_page_text);
mDoNotAskCheckbox = (CheckBox) mRootView.findViewById(R.id.do_not_ask_checkbox);
mAllowButton = (Button) mRootView.findViewById(R.id.permission_allow_button);
mDialogContainer = (ViewGroup) mRootView.findViewById(R.id.dialog_container);
mDescContainer = (ViewGroup) mRootView.findViewById(R.id.desc_container);
mCurrentDesc = (ViewGroup) mRootView.findViewById(R.id.perm_desc_root);
mAllowButton.setOnClickListener(this); // 授予按钮!
mRootView.findViewById(R.id.permission_deny_button).setOnClickListener(this); // 拒绝按钮!
mDoNotAskCheckbox.setOnClickListener(this); // 不在提醒按钮!
if (mGroupName != null) { // mGroupName 用于恢复界面显示!
updateDescription();
updateGroup();
updateDoNotAskCheckBox();
}
return mRootView;
}
我们看到,这里给三个按钮绑定的点击事件!
1 |
|
当我们点击 allow 或者 deny 后,会回调 mResultListener.onPermissionGrantResult 方法!
当我们点击授予后,执行:1
mResultListener.onPermissionGrantResult(mGroupName, true, false);
当我们点击拒绝后,执行:1
mResultListener.onPermissionGrantResult(mGroupName, false, mShowDonNotAsk && mDoNotAskCheckbox.isChecked());
- onPermissionGrantResult 参数解析
onPermissionGrantResult 的第二个参数 granted 表示授予或者拒绝,为 true 表示授予,为 false 表示拒绝;
onPermissionGrantResult 的第三个参数 doNotAskAgain 表示是否不再询问,为 true 表示不再询问,为 false 需再次询问;
onPermissionGrantResult 参数处理
- 如果用户本次授予了权限,那么 doNotAskAgain 为 false,意味着下次会继续询问;
- 如果用户本次拒绝了权限,但是没有选择不再提醒,那么 doNotAskAgain 为 false,意味着下次会继续询问;
- 如果用户本次拒绝了权限,但是同时选择不再提醒,那么 doNotAskAgain 为 true,那么下次就不会再提醒询问了;
当我们点击不再提醒 CheckBox 后,可以看到,授予按钮是无法点击的!
2.1.7.1 GrantPermissionsActivity.onPermissionGrantResult
mResultListener 实际上就是 GrantPermissionsActivity,我们去看看 onPermissionGrantResult 方法!
1 |
|
可以看到,对于每个组的运行时权限,最后调用的是 setResultAndFinish 会将权限处理的结果返回给请求的 activity!
2.1.8 GPViewHandlerImpl.showNextPermissionGroupGrantRequest
接下来,就是请求运行时权限了!mRequestGrantPermissionGroups 中保存了所有需要请求的运行时权限组!
1 | private boolean showNextPermissionGroupGrantRequest() { |
最后会调用了 updateUi 方法,更新 ui 显示!
updateUi 的最后一个参数:boolean showDonNotAsk 表示是否显示 “不再提醒” 的 CheckBox,为 true,表示不显示,其值取决于:1
groupState.mGroup.isUserSet()
我们去看看 group 的 isUserSet 方法!1
2
3
4
5
6
7
8
9
10public boolean isUserSet() {
final int permissionCount = mPermissions.size();
for (int i = 0; i < permissionCount; i++) {
Permission permission = mPermissions.valueAt(i);
if (!permission.isUserSet()) {
return false;
}
}
return true;
}
也就是说,取决于该 group 中该应用请求的运行时权限是否都是 user set 的,即:之前用户是否拒绝授予过权限,只有之前拒绝过并且没有选择不在提醒,下一次提醒用户时,才会显示 checkbox!
2.1.8.1 GPViewHandlerImpl.updateUi
showDonNotAsk 的取值为 groupState.mGroup.isUserSet(),就似乎说,如果用户之前拒绝了该权限并且没有选择不再提醒,那么再次提示时 showDonNotAsk 为 true!
1 |
|
接下来,就会显示出权限弹窗了!
用户会通过点击弹窗中的按钮,授予或者拒绝权限!
2.1.8.1.1 GPViewHandlerImpl.updateDescription
1 | private void updateDescription() { |
2.1.8.1.2 GPViewHandlerImpl.updateGroup
1 | private void updateGroup() { |
2.1.8.1.3 GPViewHandlerImpl.updateDoNotAskCheckBox
是否显示 CheckBox 取决于 mShowDonNotAsk,如果 mShowDonNotAsk 为 true,那就显示 checkBox!
CheckBox 的默认状态则是由 mDoNotAskChecked 来决定,默认 mDoNotAskChecked 为 false,此时 CheckBox 没有被选中!
同时 CheckBox 的点击也会修改 mDoNotAskChecked 的值!1
2
3
4
5
6
7
8
9
10
11
12private void updateDoNotAskCheckBox() {
if (mShowDonNotAsk) {
// 显示 CheckBox
mDoNotAskCheckbox.setVisibility(View.VISIBLE);
mDoNotAskCheckbox.setOnClickListener(this);
mDoNotAskCheckbox.setChecked(mDoNotAskChecked); // 根据 mDoNotAskChecked 的值设置 CheckBox 的状态!
} else {
// 不显示 CheckBox
mDoNotAskCheckbox.setVisibility(View.GONE);
mDoNotAskCheckbox.setOnClickListener(null);
}
}
2.1.9 GrantPermissionsActivity.setResultAndFinish
处理结果:1
2
3
4private void setResultAndFinish() {
setResultIfNeeded(RESULT_OK);
finish(); // 调用 finish,结束当前 activity!
}
setResultAndFinish 会调用 setResultIfNeeded 方法!1
2
3
4
5
6
7
8
9
10
11
12
13private void setResultIfNeeded(int resultCode) {
if (!mResultSet) {
mResultSet = true;
logRequestedPermissionGroups();
// 同样的,创建一个 intent,action 为 ACTION_REQUEST_PERMISSIONS!
Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS);
// 保存了请求的权限和请求结果!
result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions);
result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, mGrantResults);
// 将结果返回给请求权限的 activity
setResult(resultCode, result);
}
}
在调用了 finish 后,请求结果会返回到请求的 acitivty!
2.1.9.1 RequestActivity.onRequestPermissionsResult
1 |
|
最后,我们会在请求的 activity 中收到权限请求的结果!
3 AppPermissionGroup: Grant or Revoke - 授予/拒绝
下面我们来看看 AppPermissionGroup 是如何授予和拒绝权限的!
3.1 AppPermissionGroup.grantRuntimePermissions
fixedByTheUser 表示是否是被用户 fix 的:
对于 permissionPolicy 如果为 DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT 的情况,其值为 false,我们会自动授予权限!
正常情况下,是需要根据用户的选择来设置这个值的,如果用户本次授予了权限,那么 fixedByTheUser 依然为 false;
1 | public boolean grantRuntimePermissions(boolean fixedByTheUser) { |
调用的二参数的同名方法!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89public boolean grantRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
final int uid = mPackageInfo.applicationInfo.uid;
//【1】我们仅将权限切换到支持运行时权限的应用程序,否则,如果权限授予应用程序,则会切换与权限对应的应用程序op。
for (Permission permission : mPermissions.values()) {
if (filterPermissions != null // 处理权限过滤,这里不关注!
&& !ArrayUtils.contains(filterPermissions, permission.getName())) {
continue;
}
if (mAppSupportsRuntimePermissions) { //【1.1】对于运行时权限进入这里!
if (permission.isSystemFixed()) { // 如果运行时权限被 system fix 了,结束处理!
return false;
}
//【1.1.1】确保运行时权限在授予之前,AppOp 为 allow 状态!
// 判断该权限是否有对应的 AppOps,如果有且不处于 Allow 状态,设置其为 Allow!
if (permission.hasAppOp() && !permission.isAppOpAllowed()) {
permission.setAppOpAllowed(true);
mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_ALLOWED);
}
if (!permission.isGranted()) {
permission.setGranted(true);
//【4.1】调用了 pms 的接口授予用户权限
mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
permission.getName(), mUserHandle);
}
//【1.1.2】如果 fixedByTheUser 为 false,更新权限的 flags。
if (!fixedByTheUser) {
// 如果该运行时权限处于 user fix 或者 user set 状态,那么我们会强制取消 user fix 状态!
// 这样用户就可以再次请求请求权限,因为其不处于 user fix 状态!
if (permission.isUserFixed() || permission.isUserSet()) {
permission.setUserFixed(false);
permission.setUserSet(true);
// 更新权限的 flags,
// 去掉 flags 中 FLAG_PERMISSION_USER_FIXED 和 FLAG_PERMISSION_USER_SET 位!
mPackageManager.updatePermissionFlags(permission.getName(),
mPackageInfo.packageName,
PackageManager.FLAG_PERMISSION_USER_FIXED
| PackageManager.FLAG_PERMISSION_USER_SET,
0, mUserHandle);
}
}
} else {
//【1.2】如果应用不支持运行时权限,这里不处理未授予情况,因为在 API 23 之前运行时权限是自动授予的!
if (!permission.isGranted()) {
continue;
}
int killUid = -1;
int mask = 0;
// 如果权限没有对应的 app op,那么说明这是一个第三方应用,那么我们不对其做调整;
// 否则,我们会调整其 app op!
if (permission.hasAppOp()) {
// 如果权限不允许 app op,那就开启 app ops!
if (!permission.isAppOpAllowed()) {
permission.setAppOpAllowed(true);
// 开启 app op
mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_ALLOWED);
// 对于旧版本的不支持运行时权限的应用,当其运行时权限改变后,其不会尝试重试资源的访问,
// 所以我们需要杀掉其进程,使其重新加载!
killUid = uid;
}
// 如果应用从不支持运行时权限升级到了支持运行时权限的版本,升级后不能自动授予权限!
// 权限设置了 FLAG_PERMISSION_REVOKE_ON_UPGRADE 标志位!
if (permission.shouldRevokeOnUpgrade()) {
permission.setRevokeOnUpgrade(false);
mask |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
}
}
if (mask != 0) {
// 更新该权限的 flags,取消 FLAG_PERMISSION_REVOKE_ON_UPGRADE 标志位!
mPackageManager.updatePermissionFlags(permission.getName(),
mPackageInfo.packageName, mask, 0, mUserHandle);
}
if (killUid != -1) { // 杀掉进程!
mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE);
}
}
}
return true;
}
对于允许权限的情况:
- 那么 fixedByTheUser 只能是 false,我们会取消 user set 和 user fix 标志位!
3.2 AppPermissionGroup.revokeRuntimePermissions
对于 permissionPolicy 为 DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY 的情况,fixedByTheUser 为 false,同时会自动撤销权限!
正常情况下,是需要根据用户的选择来设置这个值的,如果用户本次拒绝了权限,但是 fixedByTheUser 可能有两种情况:
- 如果用户只是拒绝了权限,但是没有点击不再提醒,那么 fixedByTheUser 为 false;
- 如果用户不仅拒绝了权限,同时选择了不再提醒,那么 fixedByTheUser 为 true;
1 | public boolean revokeRuntimePermissions(boolean fixedByTheUser) { |
调用了 revokeRuntimePermissions 的另一个方法!1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93public boolean revokeRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
final int uid = mPackageInfo.applicationInfo.uid;
//【1】我们仅将权限切换到支持运行时权限的应用程序,否则,如果权限授予应用程序,则会切换与权限对应的应用程序 op。
for (Permission permission : mPermissions.values()) {
if (filterPermissions != null
&& !ArrayUtils.contains(filterPermissions, permission.getName())) {
continue;
}
if (mAppSupportsRuntimePermissions) { //【1.1】对于运行时权限进入这里!
if (permission.isSystemFixed()) { // 如果运行时权限被 system fix 了,结束处理!
return false;
}
if (permission.isGranted()) {
permission.setGranted(false);
//【4.2】调用了 PMS 的接口撤销权限
mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
permission.getName(), mUserHandle);
}
//【1.1.1】更新权限的标志位!
if (fixedByTheUser) {
//【1.1.1.1】如果 fixedByTheUser 是 true,且权限是 user set 或者不是 user fix,
// 我们取消 set 设置其为 user fix!
if (permission.isUserSet() || !permission.isUserFixed()) {
permission.setUserSet(false);
permission.setUserFixed(true);
// 取消 user set 标志,设置 user fix 标志!
mPackageManager.updatePermissionFlags(permission.getName(),
mPackageInfo.packageName,
PackageManager.FLAG_PERMISSION_USER_SET
| PackageManager.FLAG_PERMISSION_USER_FIXED,
PackageManager.FLAG_PERMISSION_USER_FIXED,
mUserHandle);
}
} else {
//【1.1.1.2】如果 fixedByTheUser 是 false,且权限不是 user set,我们设置其为 user set!
if (!permission.isUserSet()) {
permission.setUserSet(true);
// 设置 user set 标志,表示用户已经做过选择!!
mPackageManager.updatePermissionFlags(permission.getName(),
mPackageInfo.packageName,
PackageManager.FLAG_PERMISSION_USER_SET,
PackageManager.FLAG_PERMISSION_USER_SET,
mUserHandle);
}
}
} else {
//【1.2】如果应用不支持运行时权限,这里不处理未授予情况,因为在 API 23 之前运行时权限是自动授予的!
if (!permission.isGranted()) {
continue;
}
int mask = 0;
int flags = 0;
int killUid = -1;
// 如果权限没有对应的 app op,那么说明这是一个第三方应用,那么我们不对其做调整;
// 否则,我们会调整其 app op!
if (permission.hasAppOp()) {
if (permission.isAppOpAllowed()) { // 如果权限允许 app op,那就关闭 app ops!
permission.setAppOpAllowed(false);
mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_IGNORED);
// 对于旧版本的不支持运行时权限的引用,当其运行时权限改变后,其不会尝试重试资源的访问,
// 所以我们需要杀掉其进程,使其重新加载!
killUid = uid;
}
// 如果应用从不支持运行时权限升级到了支持运行时权限的版本,升级后不能自动授予权限!
// 权限未设置 FLAG_PERMISSION_REVOKE_ON_UPGRADE 标志位!
if (!permission.shouldRevokeOnUpgrade()) {
permission.setRevokeOnUpgrade(true);
mask |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
flags |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
}
}
if (mask != 0) {
// 更新该权限的 flags,增加 FLAG_PERMISSION_REVOKE_ON_UPGRADE 标志位!
mPackageManager.updatePermissionFlags(permission.getName(),
mPackageInfo.packageName, mask, flags, mUserHandle);
}
if (killUid != -1) { // 杀掉进程!
mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE);
}
}
}
return true;
}
对于拒绝权限的情况:
如果用户没有选择不再提醒,那么 fixedByTheUser 为 false,我们会设置 user set 标志位!
如果用户同时还选择了不再提醒,那么 fixedByTheUser 为 true,那么会取消 user set 标志位,同时设置 user fix 标志位!
4 PackageManagerService: Grant or Revoke - 核心接口
当然,核心方法是调用 pms 的接口完成的,我们去看看这些方法!
4.1 PackageManagerS.grantRuntimePermission
授予运行时权限:
1 |
|
4.2 PackageManagerS.revokeRuntimePermission
拒绝运行时权限!
1 |
|
最后的又进入了 PermissionsState 对象,这个我在 PMS 中有分析过,这里就不在多说了!!
5 Activity.shouldShowRequestPermissionRationale - 解释权限含义
为了防止用户因为不理解而拒绝了权限,我们可以在恰当的时间给用户解释为什么申请权限,这里要用到 shouldShowRequestPermissionRationale 方法!
1 | public boolean shouldShowRequestPermissionRationale(@NonNull String permission) { |
最终会调用 PackageManagerService 中的方法!
5.1 PackageManagerS.shouldShowRequestPermissionRationale
该方法的返回值决定了是否应该给用户解释权限!
1 |
|
policy fix 和 system fix 这些状态都是系统机制的处理!
我们可以看到:
- 如果用户授予了权限,那么该方法返回的是 false;
- 如果该权限被 system/policy/user fix 了,即用户拒绝了授权,又点击了不再提醒,那返回的是 false;
- 如果该权限被 user set 了,即用户拒绝了授权,但是没有点击不再提醒,那么会返回 true;