做app开发经常需要申请动态权限,比如WRITE_EXTERNAL_STORAGE
、ACCESS_FINE_LOCATION
,那么今天就从源码分析,动态权限到底是怎么个流程,Android系统是怎么维护各个app的权限状态的,基于Android 10。对源码敏感的同学可以直接拉到底部看总结
检查权限
首先权限申请入口位于frameworks/base/core/java/android/content/Context.java
public abstract class Context {
//省略部分代码
/**
* Determine whether <em>you</em> have been granted a particular permission.
*
* @param permission The name of the permission being checked.
*
* @return {@link PackageManager#PERMISSION_GRANTED} if you have the
* permission, or {@link PackageManager#PERMISSION_DENIED} if not.
*
* @see PackageManager#checkPermission(String, String)
* @see #checkCallingPermission(String)
*/
@PackageManager.PermissionResult
public abstract int checkSelfPermission(@NonNull String permission);
//省略部分代码
}
checkSelfPermission()
是一个抽象方法,方法实现在Context
的子类frameworks/base/core/java/android/app/ContextImpl.java
当中
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context {
//省略部分代码
@Override
public int checkSelfPermission(String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return checkPermission(permission, Process.myPid(), Process.myUid());
}
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
//获取ActivityManagerService代理对象
final IActivityManager am = ActivityManager.getService();
if (am == null) {
// Well this is super awkward; we somehow don't have an active
// ActivityManager instance. If we're testing a root or system
// UID, then they totally have whatever permission this is.
final int appId = UserHandle.getAppId(uid);
//如果是root用户或者是系统应用,直接返回已授权状态
if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
return PackageManager.PERMISSION_GRANTED;
}
Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
+ permission);
return PackageManager.PERMISSION_DENIED;
}
try {
//通过ActivityManagerService来检查权限
return am.checkPermission(permission, pid, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//省略部分代码
}
ContextImpl
通过frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
来检查权限
public class ActivityManagerService extends IActivityManager.Stub
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
//省略部分代码
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
//注意,参数4传的-1,参数5传的true
return checkComponentPermission(permission, pid, uid, -1, true);
}
public static int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
//如果是AMS进程调用此方法,则直接返回已授权状态
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
return ActivityManager.checkComponentPermission(permission, uid,
owningUid, exported);
}
/** @hide */
@UnsupportedAppUsage
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);
//如果是root用户或者是系统应用,直接返回已授权状态
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.
//owningUid为刚刚我们传的参数4,为-1,所以显然这里不满足
if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
return PackageManager.PERMISSION_GRANTED;
}
// If the target is not exported, then nobody else can get to it.
//exported为刚刚我们传的参数5,为true,所以显然这里不满足,这里的意思是如果目标进程不对外开放,则任何对象都无法访问
if (!exported) {
/*
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 {
//通过PackageManagerService检查权限
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
//省略部分代码
}
先是处理了一些特殊情况的逻辑,最终权限判断交由frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
来完成
public class PackageManagerService extends IPackageManager.Stub
implements PackageSender {
//省略部分代码
@Override
public int checkUidPermission(String permName, int uid) {
final CheckPermissionDelegate checkPermissionDelegate;
synchronized (mPackages) {
if (mCheckPermissionDelegate == null) {
return checkUidPermissionImpl(permName, uid);
}
checkPermissionDelegate = mCheckPermissionDelegate;
}
//最终都执行checkUidPermissionImpl()
return checkPermissionDelegate.checkUidPermission(permName, uid,
PackageManagerService.this::checkUidPermissionImpl);
}
private int checkUidPermissionImpl(String permName, int uid) {
synchronized (mPackages) {
//先获取packageName
final String[] packageNames = getPackagesForUid(uid);
PackageParser.Package pkg = null;
final int N = packageNames == null ? 0 : packageNames.length;
for (int i = 0; pkg == null && i < N; i++) {
pkg = mPackages.get(packageNames[i]);
}
//执行PermissionManagerServiceInternal的checkUidPermission()
return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid());
}
}
//省略部分代码
}
可以看到,最终会执行PermissionManagerServiceInternal
的checkUidPermission()
,checkUidPermission()
是一个抽象方法,由PermissionManagerServiceInternal
的子类PermissionManagerService.PermissionManagerServiceInternalImpl
来实现
/**
* Manages all permissions and handles permissions related tasks.
*/
public class PermissionManagerService {
//省略部分代码
/**
* Built-in permissions. Read from system configuration files. Mapping is from
* UID to permission name.
*/
private final SparseArray<ArraySet<String>> mSystemPermissions;
private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal {
//省略部分代码
@Override
public int checkUidPermission(String permName, PackageParser.Package pkg, int uid,
int callingUid) {
//执行PermissionManagerService的checkUidPermission()方法
return PermissionManagerService.this.checkUidPermission(permName, pkg, uid, callingUid);
}
//省略部分代码
}
private int checkUidPermission(String permName, PackageParser.Package pkg, int uid,
int callingUid) {
//获取调用此Service的进程uid,也就是调用者的uid
final int callingUserId = UserHandle.getUserId(callingUid);
//callingUserId是否为即时应用,我称之为快应用,免安装的那种
final boolean isCallerInstantApp =
mPackageManagerInt.getInstantAppPackageName(callingUid) != null;
//uid是否为即时应用,我称之为快应用,免安装的那种
final boolean isUidInstantApp =
mPackageManagerInt.getInstantAppPackageName(uid) != null;
//获取申请权限的进程id,如果是微信申请权限,则uid指微信,通过日志打印得知,userId与callingUserId相等
final int userId = UserHandle.getUserId(uid);
if (!mUserManagerInt.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
if (pkg != null) {
//非常规场景的处理
if (pkg.mSharedUserId != null) {
if (isCallerInstantApp) {
return PackageManager.PERMISSION_DENIED;
}
} else if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) {
return PackageManager.PERMISSION_DENIED;
}
//获取该应用的授权状态,授权状态封装在PermissionState对象当中
final PermissionsState permissionsState =
((PackageSetting) pkg.mExtras).getPermissionsState();
//如果已经授权,则返回PERMISSION_GRANTED
if (permissionsState.hasPermission(permName, userId)) {
if (isUidInstantApp) {
if (mSettings.isPermissionInstant(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
return PackageManager.PERMISSION_GRANTED;
}
}
//如果同权限组中有其他的权限已经授权了,则该组其它权限也直接授权,比如ACCESS_FINE_LOCATION授权了,ACCESS_COURSE_LOCATION也会自动授权
if (isImpliedPermissionGranted(permissionsState, permName, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
//如果pkg为空,说明PMS没有扫描到此安装包,此逻辑应用app不用考虑,通过日志,也没有走到此处
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms != null) {
if (perms.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
if (FULLER_PERMISSION_MAP.containsKey(permName)
&& perms.contains(FULLER_PERMISSION_MAP.get(permName))) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
//都不满足则返回PERMISSION_DENIED
return PackageManager.PERMISSION_DENIED;
}
//省略部分代码
}
先从PackageParser.Package
中读取mExtral
字段,此字段为PackageSetting
对象,再通过PackageSetting
读取当前应用授权状态PermissionState
。PackageParser.Package
表示从磁盘读取的APK文件的完整信息(PMS在初始化时会扫描磁盘,这里不做具体分析),我们知道一个APK文件包含很多信息,包名,版本号,所有注册的组件,需要申请的权限等等,具体可参考manifest文件和gradle文件,而我们的权限信息就在mExtral
字段当中,而mExtral
是一个PackageSetting
对象,涉及到安装包配置变更的数据都保存在此对象中,其中我们通过getPermissionsState()
拿到授权状态PermissionsState
,然后通过hasPermission()
判断当前申请的权限是否已经授权
public final class PermissionsState {
//省略部分代码
@GuardedBy("mLock")
private ArrayMap<String, PermissionData> mPermissions;
/**
* Gets whether the state has a given permission for the specified
* user, regardless if this is an install or a runtime permission.
*
* @param name The permission name.
* @param userId The device user id.
* @return Whether the user has the permission.
*/
public boolean hasPermission(String name, int userId) {
enforceValidUserId(userId);
synchronized (mLock) {
if (mPermissions == null) {
return false;
}
PermissionData permissionData = mPermissions.get(name);
return permissionData != null && permissionData.isGranted(userId);
}
}
//省略部分代码
}
因为一个ap可能涉及到多个权限申请,所以PermissionsState
里定义了一个ArrayMap
来保存所有权限的授权状态,map的key为权限名称,value为PermissionData
,PermissionData
里面定义了一个SparseArray<PermissionsState.PermissionState>
,保存所有用户对当前app指定权限的授权情况
private static final class PermissionData {
private final BasePermission mPerm;
private SparseArray<PermissionState> mUserStates = new SparseArray<>();
public PermissionData(BasePermission perm) {
mPerm = perm;
}
public PermissionData(PermissionData other) {
this(other.mPerm);
final int otherStateCount = other.mUserStates.size();
for (int i = 0; i < otherStateCount; i++) {
final int otherUserId = other.mUserStates.keyAt(i);
PermissionState otherState = other.mUserStates.valueAt(i);
mUserStates.put(otherUserId, new PermissionState(otherState));
}
}
//这里考虑了多用户的情况
public boolean isGranted(int userId) {
if (isInstallPermission()) {
userId = UserHandle.USER_ALL;
}
PermissionState userState = mUserStates.get(userId);
if (userState == null) {
return false;
}
return userState.mGranted;
}
//如果用户点击了允许授权,则调用此方法设置
public boolean grant(int userId) {
if (!isCompatibleUserId(userId)) {
return false;
}
if (isGranted(userId)) {
return false;
}
PermissionState userState = mUserStates.get(userId);
if (userState == null) {
userState = new PermissionState(mPerm.getName());
mUserStates.put(userId, userState);
}
userState.mGranted = true;
return true;
}
//省略部分代码
}
PermissionsState.PermissionState
则是定义了权限的名称和权限的授权情况
public final class PermissionsState {
//省略部分代码
public static final class PermissionState {
//权限名称
private final String mName;
//是否授权
private boolean mGranted;
private int mFlags;
public PermissionState(String name) {
mName = name;
}
public PermissionState(PermissionState other) {
mName = other.mName;
mGranted = other.mGranted;
mFlags = other.mFlags;
}
public boolean isDefault() {
return !mGranted && mFlags == 0;
}
public String getName() {
return mName;
}
public boolean isGranted() {
return mGranted;
}
public int getFlags() {
return mFlags;
}
}
}
以上是Context.checkPermission()
的流程
申请权限
下面再来看看Activity.requestPermissions()
的流程,fragment的权限申请也是走的宿主Activity的权限申请流程
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient {
//省略部分代码
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 intent = getPackageManager().buildRequestPermissionsIntent(permissions);
//启动Activity
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
//省略部分代码
}
通过PackageManager
构建了一个Intent,然后启动了一个Activity
public abstract class PackageManager {
//省略部分代码
@SystemApi
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,注册了ACTION_REQUEST_PERMISSIONS的Activity可以处理此Intent
Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
intent.setPackage(getPermissionControllerPackageName());
return intent;
}
//省略部分代码
}
PackageManager
启动了一个隐式的Intent,ACTION为android.content.pm.action.REQUEST_PERMISSIONS
,那么注册了该ACTION的即可处理此Intent,而GrantPermissionsActivity
就注册了此ACTION
<activity android:name="com.android.packageinstaller.permission.ui.GrantPermissionsActivity"
android:configChanges="keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:theme="@style/GrantPermissions"
android:visibleToInstantApps="true"
android:inheritShowWhenLocked="true">
<intent-filter android:priority="1">
<!--注册了目标action-->
<action android:name="android.content.pm.action.REQUEST_PERMISSIONS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
可以看到此Activity的theme设置为了GrantPermissions,而此theme的定义为
<style name="GrantPermissions"
parent="@android:style/Theme.DeviceDefault.Light.Dialog.Alert">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<!-- The following attributes change the behavior of the dialog, hence they should not be
themed -->
<item name="android:windowIsTranslucent">true</item>
</style>
风格为对话框,没有窗口标题,透明背景,沉浸式状态栏,不就是我们弹出的授权对话框吗
图1
再看看Activity的处理
public class GrantPermissionsActivity extends Activity
implements GrantPermissionsViewHandler.ResultListener {
//省略部分代码
private GrantPermissionsViewHandler mViewHandler;
private String[] mRequestedPermissions;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
//省略部分代码
//获取要申请的权限
mRequestedPermissions = getIntent().getStringArrayExtra(
PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
// Don't allow legacy apps to request runtime permissions.
//6.0以下不需要动态授权
if (callingPackageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
// Returning empty arrays means a cancellation.
mRequestedPermissions = new String[0];
setResultAndFinish();
return;
}
//省略部分代码
//根据不同的设备创建不同的Handler
if (DeviceUtils.isTelevision(this)) {
mViewHandler = new com.android.packageinstaller.permission.ui.television
.GrantPermissionsViewHandlerImpl(this,
mCallingPackage).setResultListener(this);
} else if (DeviceUtils.isWear(this)) {
mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this);
} else if (DeviceUtils.isAuto(this)) {
mViewHandler = new GrantPermissionsAutoViewHandler(this, mCallingPackage, userHandle)
.setResultListener(this);
} else {
mViewHandler = new com.android.packageinstaller.permission.ui.handheld
.GrantPermissionsViewHandlerImpl(this, mCallingPackage, userHandle)
.setResultListener(this);
}
//省略部分代码
//设置页面布局
setContentView(mViewHandler.createView());
//省略部分代码
}
页面布局文件的创建在对应的Handler的createView()
方法当中,因为我是在手机上测试的,所以创建的是GrantPermissionsViewHandlerImpl
public class GrantPermissionsViewHandlerImpl implements GrantPermissionsViewHandler,
OnClickListener {
//省略部分代码
@Override
public View createView() {
mRootView = (ViewGroup) LayoutInflater.from(mActivity)
.inflate(R.layout.grant_permissions, null);
int h = mActivity.getResources().getDisplayMetrics().heightPixels;
mRootView.setMinimumHeight(h);
//省略部分代码
return mRootView;
}
//省略部分代码
}
打开grant_permissions.xml
文件,就是图1中的布局,直接看点击允许和拒绝之后的处理
public class GrantPermissionsViewHandlerImpl implements GrantPermissionsViewHandler,
OnClickListener {
//省略部分代码
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.permission_allow_button:
//点击允许
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ALWAYS);
}
break;
case R.id.permission_allow_always_button:
//点击始终允许
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ALWAYS);
}
break;
case R.id.permission_allow_foreground_only_button:
//点击运行期间允许
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName,
GRANTED_FOREGROUND_ONLY);
}
break;
case R.id.permission_deny_button:
//点击拒绝
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName, DENIED);
}
break;
case R.id.permission_deny_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;
//省略部分代码
}
}
//省略部分代码
}
无论点击什么按钮,都通过ResultListener
进行回调,并携带用户选择的结果,GrantPermissionsActivity
实现了这个接口,看一下它的处理
public class GrantPermissionsActivity extends Activity
implements GrantPermissionsViewHandler.ResultListener {
//省略部分代码
@Override
public void onPermissionGrantResult(String name,
@GrantPermissionsViewHandler.Result int result) {
//记录日志
logGrantPermissionActivityButtons(name, result);
GroupState foregroundGroupState = getForegroundGroupState(name);
GroupState backgroundGroupState = getBackgroundGroupState(name);
//如果设备处于锁屏状态,先解除锁屏
if (result == GRANTED_ALWAYS || result == GRANTED_FOREGROUND_ONLY
|| result == DENIED_DO_NOT_ASK_AGAIN) {
KeyguardManager kgm = getSystemService(KeyguardManager.class);
if (kgm.isDeviceLocked()) {
kgm.requestDismissKeyguard(this, new KeyguardManager.KeyguardDismissCallback() {
@Override
public void onDismissError() {
Log.e(LOG_TAG, "Cannot dismiss keyguard perm=" + name + " result="
+ result);
}
@Override
public void onDismissCancelled() {
// do nothing (i.e. stay at the current permission group)
}
@Override
public void onDismissSucceeded() {
// Now the keyguard is dismissed, hence the device is not locked
// anymore
//解锁后继续执行此方法
onPermissionGrantResult(name, result);
}
});
return;
}
}
//根据用户选择执行对应的动作
switch (result) {
case GRANTED_ALWAYS :
if (foregroundGroupState != null) {
onPermissionGrantResultSingleState(foregroundGroupState, true, false);
}
if (backgroundGroupState != null) {
onPermissionGrantResultSingleState(backgroundGroupState, true, false);
}
break;
case GRANTED_FOREGROUND_ONLY :
if (foregroundGroupState != null) {
onPermissionGrantResultSingleState(foregroundGroupState, true, false);
}
if (backgroundGroupState != null) {
onPermissionGrantResultSingleState(backgroundGroupState, false, false);
}
break;
case DENIED :
if (foregroundGroupState != null) {
onPermissionGrantResultSingleState(foregroundGroupState, false, false);
}
if (backgroundGroupState != null) {
onPermissionGrantResultSingleState(backgroundGroupState, false, false);
}
break;
case DENIED_DO_NOT_ASK_AGAIN :
if (foregroundGroupState != null) {
onPermissionGrantResultSingleState(foregroundGroupState, false, true);
}
if (backgroundGroupState != null) {
onPermissionGrantResultSingleState(backgroundGroupState, false, true);
}
break;
}
if (!showNextPermissionGroupGrantRequest()) {
setResultAndFinish();
}
}
//省略部分代码
}
可以看到最终都会执行onPermissionGrantResultSingleState()
private void onPermissionGrantResultSingleState(GroupState groupState, boolean granted,
boolean doNotAskAgain) {
if (groupState != null && groupState.mGroup != null
&& groupState.mState == GroupState.STATE_UNKNOWN) {
if (granted) {
//用户允许授权
groupState.mGroup.grantRuntimePermissions(doNotAskAgain,
groupState.affectedPermissions);
groupState.mState = GroupState.STATE_ALLOWED;
reportRequestResult(groupState.affectedPermissions,
PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED);
} else {
//用户拒绝授权
groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
groupState.affectedPermissions);
groupState.mState = GroupState.STATE_DENIED;
reportRequestResult(groupState.affectedPermissions, doNotAskAgain
?
PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE
: PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED);
}
}
}
看看允许授权的逻辑
public boolean grantRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
boolean killApp = false;
boolean wasAllGranted = true;
//权限状态配置
// We toggle permissions only to apps that support runtime
// permissions, otherwise we toggle the app op corresponding
// to the permission if the permission is granted to the app.
for (Permission permission : mPermissions.values()) {
if (filterPermissions != null
&& !ArrayUtils.contains(filterPermissions, permission.getName())) {
continue;
}
if (!permission.isGrantingAllowed(mIsEphemeralApp, mAppSupportsRuntimePermissions)) {
// Skip unallowed permissions.
continue;
}
boolean wasGranted = permission.isGrantedIncludingAppOp();
if (mAppSupportsRuntimePermissions) {
// Do not touch permissions fixed by the system.
if (permission.isSystemFixed()) {
wasAllGranted = false;
break;
}
// Ensure the permission app op enabled before the permission grant.
if (permission.affectsAppOp() && !permission.isAppOpAllowed()) {
permission.setAppOpAllowed(true);
}
// Grant the permission if needed.
if (!permission.isGranted()) {
permission.setGranted(true);
}
// Update the permission flags.
if (!fixedByTheUser) {
// Now the apps can ask for the permission as the user
// no longer has it fixed in a denied state.
if (permission.isUserFixed() || permission.isUserSet()) {
permission.setUserFixed(false);
permission.setUserSet(false);
}
}
} else {
// Legacy apps cannot have a not granted permission but just in case.
if (!permission.isGranted()) {
continue;
}
// If the permissions has no corresponding app op, then it is a
// third-party one and we do not offer toggling of such permissions.
if (permission.affectsAppOp()) {
if (!permission.isAppOpAllowed()) {
permission.setAppOpAllowed(true);
// Legacy apps do not know that they have to retry access to a
// resource due to changes in runtime permissions (app ops in this
// case). Therefore, we restart them on app op change, so they
// can pick up the change.
killApp = true;
}
// Mark that the permission should not be be granted on upgrade
// when the app begins supporting runtime permissions.
if (permission.shouldRevokeOnUpgrade()) {
permission.setRevokeOnUpgrade(false);
}
}
// Granting a permission explicitly means the user already
// reviewed it so clear the review flag on every grant.
if (permission.isReviewRequired()) {
permission.unsetReviewRequired();
}
}
// If we newly grant background access to the fine location, double-guess the user some
// time later if this was really the right choice.
if (!wasGranted && permission.isGrantedIncludingAppOp()) {
if (permission.getName().equals(ACCESS_FINE_LOCATION)) {
Permission bgPerm = permission.getBackgroundPermission();
if (bgPerm != null) {
if (bgPerm.isGrantedIncludingAppOp()) {
mTriggerLocationAccessCheckOnPersist = true;
}
}
} else if (permission.getName().equals(ACCESS_BACKGROUND_LOCATION)) {
ArrayList<Permission> fgPerms = permission.getForegroundPermissions();
if (fgPerms != null) {
int numFgPerms = fgPerms.size();
for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) {
Permission fgPerm = fgPerms.get(fgPermNum);
if (fgPerm.getName().equals(ACCESS_FINE_LOCATION)) {
if (fgPerm.isGrantedIncludingAppOp()) {
mTriggerLocationAccessCheckOnPersist = true;
}
break;
}
}
}
}
}
}
if (!mDelayChanges) {
//这里很重要,将授权信息持久化保存到本地
persistChanges(false);
if (killApp) {
killApp(KILL_REASON_APP_OP_CHANGE);
}
}
return wasAllGranted;
}
授权过程就是本地数据配置的过程,最后有一个逻辑很关键,就是将授权信息进行持久化保存,因为当我们关机重启后,需要恢复各个app的授权信息,看看保存到哪儿了
void persistChanges(boolean mayKillBecauseOfAppOpsChange) {
int uid = mPackageInfo.applicationInfo.uid;
int numPermissions = mPermissions.size();
boolean shouldKillApp = false;
for (int i = 0; i < numPermissions; i++) {
Permission permission = mPermissions.valueAt(i);
if (!permission.isSystemFixed()) {
if (permission.isGranted()) {
mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
permission.getName(), mUserHandle);
} else {
boolean isCurrentlyGranted = mContext.checkPermission(permission.getName(), -1,
uid) == PERMISSION_GRANTED;
if (isCurrentlyGranted) {
mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
permission.getName(), mUserHandle);
}
}
}
int flags = (permission.isUserSet() ? PackageManager.FLAG_PERMISSION_USER_SET : 0)
| (permission.isUserFixed() ? PackageManager.FLAG_PERMISSION_USER_FIXED : 0)
| (permission.shouldRevokeOnUpgrade()
? PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE : 0)
| (permission.isPolicyFixed() ? PackageManager.FLAG_PERMISSION_POLICY_FIXED : 0)
| (permission.isReviewRequired()
? PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED : 0);
//更新权限标识位,这里是去更新PMS里面当前app的权限配置
mPackageManager.updatePermissionFlags(permission.getName(),
mPackageInfo.packageName,
PackageManager.FLAG_PERMISSION_USER_SET
| PackageManager.FLAG_PERMISSION_USER_FIXED
| PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE
| PackageManager.FLAG_PERMISSION_POLICY_FIXED
| PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
flags, mUserHandle);
if (permission.affectsAppOp()) {
if (!permission.isSystemFixed()) {
// Enabling/Disabling an app op may put the app in a situation in which it has
// a handle to state it shouldn't have, so we have to kill the app. This matches
// the revoke runtime permission behavior.
if (permission.isAppOpAllowed()) {
shouldKillApp |= allowAppOp(permission, uid);
} else {
shouldKillApp |= disallowAppOp(permission, uid);
}
}
}
}
if (mayKillBecauseOfAppOpsChange && shouldKillApp) {
killApp(KILL_REASON_APP_OP_CHANGE);
}
if (mTriggerLocationAccessCheckOnPersist) {
new LocationAccessCheck(mContext, null).checkLocationAccessSoon();
mTriggerLocationAccessCheckOnPersist = false;
}
}
这里通过AIDL,将当前授权信息发送给PMS,使得PMS中保存的当前app的授权信息得以更新
@Override
public void updatePermissionFlags(String permissionName, String packageName,
int flagMask, int flagValues, UserHandle user) {
try {
//跨进程调用,执行PackageManagerService的updatePermissionFlags()
mPM.updatePermissionFlags(permissionName, packageName, flagMask,
flagValues,
mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.Q,
user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
看看PackageManagerService的处理
@Override
public void updatePermissionFlags(String permName, String packageName, int flagMask,
int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
int callingUid = getCallingUid();
boolean overridePolicy = false;
if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) {
long callingIdentity = Binder.clearCallingIdentity();
try {
if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0) {
if (checkAdjustPolicyFlagPermission) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY,
"Need " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY
+ " to change policy flags");
} else if (!hasTargetSdkInUidLowerThan(callingUid, Build.VERSION_CODES.Q)) {
throw new IllegalArgumentException(
Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " needs "
+ " to be checked for packages targeting "
+ Build.VERSION_CODES.Q + " or later when changing policy "
+ "flags");
}
overridePolicy = true;
}
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
}
//通过PermissionManagerService操作
mPermissionManager.updatePermissionFlags(
permName, packageName, flagMask, flagValues, callingUid, userId,
overridePolicy, mPermissionCallback);
}
最终更新的是PermissionManagerService
当中的数据
private void updatePermissionFlags(String permName, String packageName, int flagMask,
int flagValues, int callingUid, int userId, boolean overridePolicy,
PermissionCallback callback) {
//省略部分代码
//先获取PackageParser.Package
final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
//省略部分代码
//更新Package的mExtras
final PackageSetting ps = (PackageSetting) pkg.mExtras;
final PermissionsState permissionsState = ps.getPermissionsState();
final boolean hadState =
permissionsState.getRuntimePermissionState(permName, userId) != null;
final boolean permissionUpdated =
permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues);
if (permissionUpdated && bp.isRuntime()) {
notifyRuntimePermissionStateChanged(packageName, userId);
}
if (permissionUpdated && callback != null) {
// Install and runtime permissions are stored in different places,
// so figure out what permission changed and persist the change.
if (permissionsState.getInstallPermissionState(permName) != null) {
//通知安装权限更新,就是不需要动态授权的权限
callback.onInstallPermissionUpdated();
} else if (permissionsState.getRuntimePermissionState(permName, userId) != null
|| hadState) {
//通知动态权限状态更新
callback.onPermissionUpdated(new int[] { userId }, false);
}
}
}
可以看到,最终更新的是PackageParser.Package
的mExtras
字段,上面我们分析知道,在检查权限时,也是读取的PackageParser.Package
的mExtras
字段,这里就跟前面的逻辑相呼应了,更新完之后,通过callback进行回调,执行持久化存储
@Override
public void onPermissionUpdated(int[] updatedUserIds, boolean sync) {
synchronized (mPackages) {
for (int userId : updatedUserIds) {
mSettings.writeRuntimePermissionsForUserLPr(userId, sync);
}
}
}
public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
if (sync) {
mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId);
} else {
mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
}
}
private void writePermissionsSync(int userId) {
//定义持久化保存路径
AtomicFile destination = new AtomicFile(getUserRuntimePermissionsFile(userId),
"package-perms-" + userId);
//省略部分代码
FileOutputStream out = null;
try {
//省略部分代码
//保存到本地
destination.finishWrite(out);
if (Build.FINGERPRINT.equals(fingerprint)) {
mDefaultPermissionsGranted.put(userId, true);
}
// Any error while writing is fatal.
} catch (Throwable t) {
Slog.wtf(PackageManagerService.TAG,
"Failed to write settings, restoring backup", t);
destination.failWrite(out);
} finally {
IoUtils.closeQuietly(out);
}
}
//文件名
private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml";
//获取路径
private File getUserRuntimePermissionsFile(int userId) {
// TODO: Implement a cleaner solution when adding tests.
// This instead of Environment.getUserSystemDirectory(userId) to support testing.
File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId));
File file = new File(userDir, RUNTIME_PERMISSIONS_FILE_NAME);
Log.d("jasonwan", "getUserRuntimePermissionsFile: file="+file.getAbsolutePath());
return file;
}
通过log打印得知,权限文件路径位于/data/system/users/0/runtime-permissions.xml
,我们从设备中导出此文件打开看看长啥样
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<runtime-permissions version="8" fingerprint="qti/trinket/trinket:10/QKQ1.200816.002/2211:userdebug/test-keys">
<pkg name="com.android.externalstorage">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3030" />
</pkg>
<pkg name="com.android.companiondevicemanager">
<item name="android.permission.ACCESS_FINE_LOCATION" granted="true" flags="30" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="false" flags="b0" />
<item name="android.permission.ACCESS_BACKGROUND_LOCATION" granted="true" flags="3030" />
</pkg>
<pkg name="com.android.quicksearchbox">
<item name="android.permission.READ_CONTACTS" granted="false" flags="300" />
</pkg>
<pkg name="com.android.soundrecorder">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="false" flags="2380" />
<item name="android.permission.READ_PHONE_STATE" granted="false" flags="300" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="false" flags="2300" />
<item name="android.permission.RECORD_AUDIO" granted="true" flags="320" />
<item name="android.permission.ACCESS_MEDIA_LOCATION" granted="false" flags="380" />
</pkg>
<pkg name="com.android.contacts">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="false" flags="2300" />
<item name="android.permission.READ_PHONE_STATE" granted="true" flags="320" />
<item name="android.permission.CALL_PHONE" granted="true" flags="320" />
<item name="android.permission.WRITE_CONTACTS" granted="true" flags="320" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="320" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="320" />
<item name="android.permission.ACCESS_MEDIA_LOCATION" granted="false" flags="380" />
</pkg>
<pkg name="com.android.mms">
<item name="android.permission.READ_SMS" granted="true" flags="3320" />
<item name="android.permission.RECEIVE_WAP_PUSH" granted="true" flags="3320" />
<item name="android.permission.RECEIVE_MMS" granted="true" flags="3320" />
<item name="android.permission.RECEIVE_SMS" granted="true" flags="3320" />
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="33a0" />
<item name="android.permission.READ_PHONE_STATE" granted="true" flags="320" />
<item name="android.permission.SEND_SMS" granted="true" flags="3320" />
<item name="android.permission.CALL_PHONE" granted="true" flags="320" />
<item name="android.permission.WRITE_CONTACTS" granted="true" flags="320" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3320" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="320" />
<item name="android.permission.ACCESS_MEDIA_LOCATION" granted="true" flags="3a0" />
</pkg>
<pkg name="com.android.launcher3">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="false" flags="2100" />
<item name="android.permission.READ_PHONE_STATE" granted="false" flags="100" />
<item name="android.permission.CALL_PHONE" granted="false" flags="100" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="false" flags="2100" />
</pkg>
<pkg name="com.android.settings.intelligence">
<item name="android.permission.READ_PHONE_STATE" granted="false" flags="100" />
</pkg>
<pkg name="com.android.calendar">
<item name="android.permission.READ_CALENDAR" granted="true" flags="320" />
<item name="android.permission.WRITE_CALENDAR" granted="true" flags="320" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="320" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="320" />
</pkg>
<pkg name="com.android.sharedstoragebackup">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="30b0" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3030" />
</pkg>
<pkg name="com.android.printspooler">
<item name="android.permission.ACCESS_FINE_LOCATION" granted="true" flags="30" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="true" flags="30" />
</pkg>
<!-- 测试Demo-->
<pkg name="com.jason.nodetest">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="b00" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="b00" />
</pkg>
<pkg name="com.android.email">
<item name="android.permission.READ_CALENDAR" granted="true" flags="320" />
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="false" flags="2380" />
<item name="android.permission.READ_PHONE_STATE" granted="false" flags="300" />
<item name="android.permission.WRITE_CONTACTS" granted="true" flags="320" />
<item name="android.permission.WRITE_CALENDAR" granted="true" flags="320" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="320" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="false" flags="2300" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="320" />
<item name="android.permission.ACCESS_MEDIA_LOCATION" granted="false" flags="380" />
</pkg>
<pkg name="com.android.music">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="3320" />
<item name="android.permission.READ_PHONE_STATE" granted="false" flags="300" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3320" />
<item name="android.permission.ACCESS_MEDIA_LOCATION" granted="true" flags="3a0" />
</pkg>
<shared-user name="android.media">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3030" />
</shared-user>
<shared-user name="android.uid.systemui">
<item name="android.permission.READ_CALL_LOG" granted="true" flags="3030" />
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="true" flags="30" />
<item name="android.permission.CAMERA" granted="true" flags="30" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="30" />
<item name="android.permission.RECORD_AUDIO" granted="true" flags="30" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="30" />
</shared-user>
<shared-user name="android.uid.nfc">
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="30b0" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="30" />
</shared-user>
<shared-user name="android.uid.bluetooth">
<item name="android.permission.READ_SMS" granted="true" flags="3030" />
<item name="android.permission.READ_CALL_LOG" granted="true" flags="3030" />
<item name="android.permission.ACCESS_FINE_LOCATION" granted="true" flags="30" />
<item name="com.android.email.permission.READ_ATTACHMENT" granted="true" flags="1030" />
<item name="android.permission.RECEIVE_SMS" granted="true" flags="3030" />
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="true" flags="30" />
<item name="android.permission.SEND_SMS" granted="true" flags="3030" />
<item name="android.permission.WRITE_CONTACTS" granted="true" flags="1030" />
<item name="android.permission.WRITE_CALL_LOG" granted="true" flags="3030" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="1030" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="1030" />
</shared-user>
<shared-user name="android.uid.shared">
<item name="android.permission.READ_PHONE_STATE" granted="true" flags="30" />
<item name="android.permission.WRITE_CONTACTS" granted="true" flags="30" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="30" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="30" />
</shared-user>
<shared-user name="android.uid.system">
<item name="android.permission.READ_CALL_LOG" granted="true" flags="3030" />
<item name="android.permission.ACCESS_FINE_LOCATION" granted="true" flags="1030" />
<item name="android.permission.RECEIVE_WAP_PUSH" granted="true" flags="3030" />
<item name="android.permission.RECEIVE_SMS" granted="true" flags="3030" />
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="false" flags="10b0" />
<item name="android.permission.READ_PHONE_STATE" granted="true" flags="1030" />
<item name="android.permission.SEND_SMS" granted="true" flags="3030" />
<item name="android.permission.CALL_PHONE" granted="true" flags="1330" />
<item name="android.permission.WRITE_CONTACTS" granted="true" flags="1330" />
<item name="android.permission.CAMERA" granted="true" flags="1330" />
<item name="android.permission.WRITE_CALL_LOG" granted="true" flags="3030" />
<item name="android.permission.PROCESS_OUTGOING_CALLS" granted="true" flags="3030" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="30" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.RECORD_AUDIO" granted="true" flags="1330" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="1330" />
<item name="android.permission.ACCESS_BACKGROUND_LOCATION" granted="true" flags="3030" />
<item name="android.permission.ACCESS_MEDIA_LOCATION" granted="true" flags="1030" />
</shared-user>
<shared-user name="android.uid.phone">
<item name="android.permission.READ_SMS" granted="true" flags="3030" />
<item name="android.permission.READ_CALL_LOG" granted="true" flags="3030" />
<item name="android.permission.ACCESS_FINE_LOCATION" granted="true" flags="30" />
<item name="android.permission.RECEIVE_SMS" granted="true" flags="3030" />
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="true" flags="1030" />
<item name="android.permission.READ_PHONE_STATE" granted="true" flags="30" />
<item name="android.permission.SEND_SMS" granted="true" flags="3030" />
<item name="android.permission.CALL_PHONE" granted="true" flags="30" />
<item name="android.permission.WRITE_CONTACTS" granted="true" flags="30" />
<item name="android.permission.CAMERA" granted="true" flags="1030" />
<item name="android.permission.WRITE_CALL_LOG" granted="true" flags="3030" />
<item name="android.permission.USE_SIP" granted="true" flags="1030" />
<item name="android.permission.PROCESS_OUTGOING_CALLS" granted="true" flags="3030" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="1030" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.RECORD_AUDIO" granted="true" flags="1030" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="30" />
<item name="android.permission.ACCESS_BACKGROUND_LOCATION" granted="true" flags="3030" />
<item name="com.android.voicemail.permission.ADD_VOICEMAIL" granted="true" flags="1030" />
</shared-user>
<shared-user name="android.uid.shell">
<item name="android.permission.READ_SMS" granted="true" flags="3030" />
<item name="android.permission.READ_CALENDAR" granted="true" flags="30" />
<item name="android.permission.ACCESS_FINE_LOCATION" granted="true" flags="30" />
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.ACCESS_COARSE_LOCATION" granted="false" flags="30" />
<item name="android.permission.READ_PHONE_STATE" granted="true" flags="30" />
<item name="android.permission.SEND_SMS" granted="true" flags="3030" />
<item name="android.permission.CALL_PHONE" granted="true" flags="30" />
<item name="android.permission.WRITE_CONTACTS" granted="true" flags="30" />
<item name="android.permission.WRITE_CALENDAR" granted="true" flags="30" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="30" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3030" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="30" />
</shared-user>
<shared-user name="android.uid.calendar">
<item name="android.permission.READ_CALENDAR" granted="true" flags="30" />
<item name="android.permission.READ_EXTERNAL_STORAGE" granted="true" flags="30a0" />
<item name="android.permission.WRITE_CALENDAR" granted="true" flags="30" />
<item name="android.permission.GET_ACCOUNTS" granted="true" flags="20" />
<item name="android.permission.WRITE_EXTERNAL_STORAGE" granted="true" flags="3020" />
</shared-user>
<shared-user name="com.android.emergency.uid">
<item name="android.permission.READ_CALL_LOG" granted="true" flags="3030" />
<item name="android.permission.CALL_PHONE" granted="true" flags="30" />
<item name="android.permission.READ_CONTACTS" granted="true" flags="30" />
</shared-user>
</runtime-permissions>
可以看到此文件保存就是各个app的动态权限授权情况,安装权限不在此文件中,我们的测试demo包名为com.jason.nodetest
,可以看到我们只允许读存储卡权限后,读写权限均为已授权。
同样,当系统重启后,PackageManagerService会初始化并启动,初始化时会从UserServiceManager
中获取读取的权限数据
public class PackageManagerService extends IPackageManager.Stub
implements PackageSender {
//省略部分代码
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
//省略部分代码
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings");
//UserManager创建用户时会去读取用户配置文件,其中就包括runtime-permissions.xml文件
mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
//省略部分代码
}
//省略部分代码
}
boolean readLPw(@NonNull List<UserInfo> users) {
//省略部分代码
for (UserInfo user : users) {
//读取所有用户配置信息
mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
}
//省略部分代码
}
@GuardedBy("Settings.this.mLock")
public void readStateForUserSyncLPr(int userId) {
//获取runtime-permissions.xml文件
File permissionsFile = getUserRuntimePermissionsFile(userId);
if (!permissionsFile.exists()) {
return;
}
FileInputStream in;
try {
in = new AtomicFile(permissionsFile).openRead();
} catch (FileNotFoundException fnfe) {
Slog.i(PackageManagerService.TAG, "No permissions state");
return;
}
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
//解析runtime-permissions.xml文件
parseRuntimePermissionsLPr(parser, userId);
} catch (XmlPullParserException | IOException e) {
throw new IllegalStateException("Failed parsing permissions file: "
+ permissionsFile , e);
} finally {
IoUtils.closeQuietly(in);
}
}
@GuardedBy("Settings.this.mLock")
private void parseRuntimePermissionsLPr(XmlPullParser parser, int userId)
throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
switch (parser.getName()) {
case TAG_RUNTIME_PERMISSIONS: {
// If the permisions settings file exists but the version is not set this is
// an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION
int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION,
UPGRADE_VERSION);
mVersions.put(userId, version);
String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT);
mFingerprints.put(userId, fingerprint);
final boolean defaultsGranted = Build.FINGERPRINT.equals(fingerprint);
mDefaultPermissionsGranted.put(userId, defaultsGranted);
} break;
case TAG_PACKAGE: {
String name = parser.getAttributeValue(null, ATTR_NAME);
PackageSetting ps = mPackages.get(name);
if (ps == null) {
Slog.w(PackageManagerService.TAG, "Unknown package:" + name);
XmlUtils.skipCurrentTag(parser);
continue;
}
parsePermissionsLPr(parser, ps.getPermissionsState(), userId);
} break;
case TAG_SHARED_USER: {
//读取shared-user标签,拿到包名
String name = parser.getAttributeValue(null, ATTR_NAME);
SharedUserSetting sus = mSharedUsers.get(name);
if (sus == null) {
Slog.w(PackageManagerService.TAG, "Unknown shared user:" + name);
XmlUtils.skipCurrentTag(parser);
continue;
}
//解析shared-user下的所有item权限
parsePermissionsLPr(parser, sus.getPermissionsState(), userId);
} break;
}
}
}
private void parsePermissionsLPr(XmlPullParser parser, PermissionsState permissionsState,
int userId) throws IOException, XmlPullParserException {
final int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
switch (parser.getName()) {
case TAG_ITEM: {
//读取权限名称
String name = parser.getAttributeValue(null, ATTR_NAME);
BasePermission bp = mPermissions.getPermission(name);
if (bp == null) {
Slog.w(PackageManagerService.TAG, "Unknown permission:" + name);
XmlUtils.skipCurrentTag(parser);
continue;
}
//读取权限授权状态
String grantedStr = parser.getAttributeValue(null, ATTR_GRANTED);
final boolean granted = grantedStr == null
|| Boolean.parseBoolean(grantedStr);
//读取权限的flag
String flagsStr = parser.getAttributeValue(null, ATTR_FLAGS);
final int flags = (flagsStr != null)
? Integer.parseInt(flagsStr, 16) : 0;
//给权限赋值
if (granted) {
permissionsState.grantRuntimePermission(bp, userId);
permissionsState.updatePermissionFlags(bp, userId,
PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
} else {
permissionsState.updatePermissionFlags(bp, userId,
PackageManager.MASK_PERMISSION_FLAGS_ALL, flags);
}
} break;
}
}
可以看到,从runtime-permissions.xml
文件中读取的授权信息最终赋值给了PermissionsState
,而我们在检查权限时也是从PermissionState
中获取的权限授权状态,这里跟前面形成了闭环。
总结
以上就是Android 10动态授权的整个流程,简单总结下就是
1、Android启动,PackageManagerService
启动,扫描APK获取所有权限
2、读取runtime-permissions.xml
文件,获取动态权限授权情况,并将他赋值给PermissionState
3、用户检查权限,从PermissionState
中获取目标权限的授权状态,并返给app
4、用户申请权限,根据用户的选择,设置PermissionState
中对应权限的状态,并持久化存储到runtime-permissions.xml
文件中