Android运行时权限Runtime Permission源码分析

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配置文件中,老版本代码就不分析了。
作者:帅得不敢出门 谢绝转载

猜你喜欢

转载自blog.csdn.net/zmlovelx/article/details/129218703