Runtime Permission源码跟踪
Android 8.1.0
请求权限时弹窗代码
应用使用requestPermissions申请权限时,系统会弹出一个选择窗口,可进行允许。
源码在packages/apps/PackageInstaller/文件下
GrantPermissionsActivity.java是进行权限分配的弹出窗口,通过GrantPermissionsDefaultViewHandler来控制GrantPermissionsActivity的ui视图,
按钮的点击事件是通过GrantPermissionsViewHandler.ResultListener接口来处理的,GrantPermissionsActivity实现了该接口
public class GrantPermissionsActivity extends OverlayTouchActivity
implements GrantPermissionsViewHandler.ResultListener {
mViewHandler = new com.android.packageinstaller.permission.ui.handheld
.GrantPermissionsViewHandlerImpl(this, getCallingPackage())
.setResultListener(this);
}
GrantPermissionsActivity.java
调用groupState.mGroup.grantRuntimePermissions获取权限,mGroup是AppPermissionGroup.java对象,grantRuntimePermissions实现过程后面会说。
先说下请求权限窗口中的UI,比如显示”要允许xx拔打电话和管理通话嘛“这条文本是
调用GrantPermissionsViewHandlerImpl的updateUi进行界面显示
private boolean showNextPermissionGroupGrantRequest() {
final int groupCount = mRequestGrantPermissionGroups.size();
int currentIndex = 0;
for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
if (groupState.mState == GroupState.STATE_UNKNOWN) {
// 应用名称
CharSequence appLabel = mAppPermissions.getAppLabel();
// groupState.mGroup.getDescription()是权限对应的中文描述
// 由AppPermissionGroup.java获取,比如“拨打电话和管理通话”
// 注意就算请求的是一个子权限,但是获取到的将是子项对应的整个组
// 比如申请android.permission.CALL_PHONE,允许后,会把它对应的整个电话组
// 权限都获取到(包含拔打电话、读取通话记录、读取手机状态和身份、修改通话记录)
Spanned message = Html.fromHtml(getString(R.string.permission_warning_template,
appLabel, groupState.mGroup.getDescription()), 0);
// Set the permission message as the title so it can be announced.
setTitle(message);
// 此updateUi会进行UI更新,比如请求权限的应用名,请求的权限对应名称,请求应用的应用图标等
mViewHandler.updateUi(groupState.mGroup.getName(), groupCount, currentIndex,Icon.createWithResource(resources, icon), message,
groupState.mGroup.isUserSet());
//......
}
}
}
mViewHandler对应类是GrantPermissionsViewHandlerImpl.java
mMessageView = (TextView) mCurrentDesc.findViewById(R.id.permission_message);
private void updateDescription() { // 由updateUi调用
mIconView.setImageDrawable(mGroupIcon.loadDrawable(mActivity));
mMessageView.setText(mGroupMessage); // 比如显示“要允许xx拔打电话吗?”
}
到此请求权限时弹窗代码已结束。
接下来说说设置中的应用权限窗口。
设置中应用权限窗口分析
设置–应用和通知–应用信息—xx应用—权限
此窗口对应源码为
packages/apps/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/AppPermissionsFragment.java
// 设置的权限窗口中改变某应用的权限时,会触发此函数
public boolean onPreferenceChange(final Preference preference, Object newValue) {
// 只展试部分关键代码
// key是权限,比如android.permission.CALL_PHONE
PermissionInfo permInfo = pm.getPermissionInfo(key, 0);
// AppPermissions mAppPermissions
final AppPermissionGroup title_group
= mAppPermissions.getPermissionGroup(permInfo.group);
if (newValue == Boolean.TRUE) {
title_group.grantRuntimePermissions(false, filterPermissions); // 取得权限
} else {
title_group.revokeRuntimePermissions(false, filterPermissions); // 取消权限
}
}
上面的mAppPermissions是AppPermissions对象
title_group是AppPermissions.java代码中的mAppPermissions提供
请求权限弹窗与设置中权限UI请求权限都要用到AppPermissionGroup类,调用类中的grantRuntimePermissions函数。
AppPermissionGroup
packages/apps/PackageInstaller/src/com/android/packageinstaller/permission/model/AppPermissions.java
// 在构造时在loadPermissionGroups初始化AppPermissionGroup数组
public AppPermissions(Context context, PackageInfo packageInfo, String[] filterPermissions,
boolean sortGroups, Runnable onErrorCallback) {
mContext = context;
mPackageInfo = packageInfo;
mFilterPermissions = filterPermissions;
mAppLabel = BidiFormatter.getInstance().unicodeWrap(
packageInfo.applicationInfo.loadSafeLabel(
context.getPackageManager()).toString());
mSortGroups = sortGroups;
mOnErrorCallback = onErrorCallback;
loadPermissionGroups(); // 调用addPermissionGroupIfNeeded
}
private void addPermissionGroupIfNeeded(String permission) {
if (getGroupForPermission(permission) != null) {
return;
}
// AppPermissionGroup对应AppPermissionGroup.java
AppPermissionGroup group = AppPermissionGroup.create(mContext,
mPackageInfo, permission);
if (group == null) {
return;
}
mGroups.add(group);
}
title_group对应AppPermissionGroup.java,AppPermissionGroup.grantRuntimePermissions此函数里面调用的是
private final PackageManager mPackageManager;
mPackageManager.grantRuntimePermission(mPackageInfo.packageName,permission.getName(), mUserHandle);
// 会跳到PackageManger中, 最终调用到PMS中
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
```java
@Override
public void grantRuntimePermission(String packageName, String name, final int userId) {
grantRuntimePermission(packageName, name, userId, false /* Only if not fixed by policy */);
}
运行时权限获取的权限会通过private void writePermissionsSync(int userId)
更新到此文件中/data/system/users/0/runtime-permissions.xml
某应用,电话组权限未授权时如下
<pkg name="com.android.sdk23ApiTest.wangyong">
<item name="android.permission.READ_CALL_LOG" granted="false" flags="1" />
<item name="android.permission.READ_PHONE_STATE" granted="false" flags="1" />
<item name="android.permission.CALL_PHONE" granted="false" flags="1" />
<item name="android.permission.WRITE_CALL_LOG" granted="false" flags="1" />
</pkg>
允许电话组权限后变成这样
<pkg name="com.android.sdk23ApiTest.wangyong">
<item name="android.permission.READ_CALL_LOG" granted="true" flags="0" />
<item name="android.permission.READ_PHONE_STATE" granted="true" flags="0" />
<item name="android.permission.CALL_PHONE" granted="true" flags="0" />
<item name="android.permission.WRITE_CALL_LOG" granted="true" flags="0" />
</pkg>
Android 6以前的版本是保存在在data/system/packages.xml配置文件中,老版本代码就不分析了。
作者:帅得不敢出门 谢绝转载