RuntimePermisson介绍

本文介绍了Android运行时权限的控制界面,包括权限的撤消和授予流程。详细解析了`AppPermissionsFragment`如何加载权限组,并展示了如何筛选出适用于Runtime Permissions的权限。同时,概述了权限的撤销和授予涉及的方法及权限状态的管理核心——`PermissionsState`。

1. 控制界面

在设置–》应用–》Email–》权限,然后可以看到最直观的界面显示 AppPermissionsFragment.java

这个界面是在packages/apps/PackageInstaller 里面来进行控制。
1. com.android.packageinstaller.permission.ui.AppPermissionsFragment#onCreate
在初始化过程中,会创建AppPermissions

    @Override
    public void onCreate(Bundle savedInstanceState) {
...

        mAppPermissions = new (activity, packageInfo, null, true, new Runnable() {
            @Override
            public void run() {
                getActivity().finish();
            }
        });
        loadPreferences();
    }
  • com.android.packageinstaller.permission.model.AppPermissions#AppPermissions 接下来直接进入到
  • com.android.packageinstaller.permission.model.AppPermissions#loadPermissionGroups
    private void loadPermissionGroups() {
...

        if (mFilterPermissions != null) {
...
        } else {
            for (String requestedPerm : mPackageInfo.requestedPermissions) {
                if (hasGroupForPermission(requestedPerm)) {
                    continue;
                }
                // create AppPermissionGroup according to the package info
                `AppPermissionGroup group = AppPermissionGroup.create(mContext,
                        mPackageInfo, requestedPerm);`
                if (group == null) {
                    continue;
                }

                mGroups.add(group);
            }
        }

...
    }
  • com.android.packageinstaller.permission.model.#create(android.content.Context, android.content.pm.PackageInfo, java.lang.String)
    这个方法中,首先会根据permissionName收集permissionInfo
        try {
            permissionInfo = context.getPackageManager().getPermissionInfo(permissionName, 0);
        } catch (PackageManager.NameNotFoundException e) {
            return null;
        }

static PermissionInfo generatePermissionInfo(
            BasePermission bp, int flags) {
        if (bp.perm != null) {
            return PackageParser.generatePermissionInfo(bp.perm, flags);
        }
        PermissionInfo pi = new PermissionInfo();
        pi.name = bp.name;
        pi.packageName = bp.sourcePackage;
        pi.nonLocalizedLabel = bp.name;
        pi.protectionLevel = bp.protectionLevel;
        return pi;
    }

从以上信息可以看出,PermissionInfo大致上会由name、packageName、nonLocalizedLabel、protectionLevel组成。
接下来就会根据PermissionInfo收集PermissionGroupInfo。由于PermissionGroupInfo里面包含了所有的Permission,Android仅仅处理runtime permissions.所以最终会将PermissionGroupInfo里面的非runtime permisson和当前应用没有请求的权限过滤出去,这样就返回了当前应用请求的所有的android runtime permission.以下信息都会过滤:

  • 非Android 定义的权限
  • 非Runtime Permission(requestedPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS)
  • targetapi 低于Android 6.0以下的应用不适用于Android Runtime Permission
  • -
 public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
            PackageItemInfo groupInfo, List<PermissionInfo> permissionInfos,
            UserHandle userHandle) {


                groupInfo.packageName, groupInfo.loadLabel(context.getPackageManager()),
                loadGroupDescription(context, groupInfo), groupInfo.packageName, groupInfo.icon,
                userHandle);

        if (groupInfo instanceof PermissionInfo) {
            permissionInfos = new ArrayList<>();
            permissionInfos.add((PermissionInfo) groupInfo);
        }

        if (permissionInfos == null || permissionInfos.isEmpty()) {
            return null;
        }

        final int permissionCount = packageInfo.requestedPermissions.length;
        for (int i = 0; i < permissionCount; i++) {
            String requestedPermission = packageInfo.requestedPermissions[i];

            PermissionInfo requestedPermissionInfo = null;

            for (PermissionInfo permissionInfo : permissionInfos) {
                if (requestedPermission.equals(permissionInfo.name)) {
                    requestedPermissionInfo = permissionInfo;
                    break;
                }
            }

            if (requestedPermissionInfo == null) {
                continue;
            }

            // Collect only runtime permissions.
            if (requestedPermissionInfo.protectionLevel != PermissionInfo.PROTECTION_DANGEROUS) {
                continue;
            }

            // Don't allow toggle of non platform defined permissions for legacy apps via app ops.
            if (packageInfo.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1
                    && !PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)) {
                continue;
            }

            final boolean granted = (packageInfo.requestedPermissionsFlags[i]
                    & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;

            final int appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)
                    ? AppOpsManager.permissionToOpCode(requestedPermissionInfo.name)
                    : AppOpsManager.OP_NONE;

            final boolean appOpAllowed = appOp != AppOpsManager.OP_NONE
                    && context.getSystemService(AppOpsManager.class).checkOp(appOp,
                    packageInfo.applicationInfo.uid, packageInfo.packageName)
                    == AppOpsManager.MODE_ALLOWED;

            final int flags = context.getPackageManager().getPermissionFlags(
                    requestedPermission, packageInfo.packageName, userHandle);

            Permission permission = new Permission(requestedPermission, granted,
                    appOp, appOpAllowed, flags);
            group.addPermission(permission);
        }

        return group;
    }

经过如上操作,会获取权满足所有条件的权限。然后以截图中的形式显示。

权限的撤掉和授予

撤销权限

com.android.packageinstaller.permission.model.AppPermissionGroup#revokeRuntimePermissions

  1. com.android.packageinstaller.permission.model.AppPermissionGroup#revokeRuntimePermissions
  2. com.android.server.pm.PackageManagerService#revokeRuntimePermission
  3. com.android.server.pm.PermissionsState#revokeRuntimePermission
  4. com.android.server.pm.PermissionsState.PermissionData#revoke 此时已经被授予的权限将被取消

  5. 如上所属。

授予权限

  1. com.android.packageinstaller.permission.model.AppPermissionGroup#grantRuntimePermissions
  2. android.app.ApplicationPackageManager#grantRuntimePermission
  3. com.android.server.pm.PackageManagerService#grantRuntimePermission
  4. com.android.server.pm.PermissionsState#grantRuntimePermission
  5. com.android.server.pm.PermissionsState.PermissionData#grant

通过授予和撤销可以看出最终都是通过存入com.android.server.pm.PermissionsState.PermissionData#mUserStates来完成的。
也就是说权限管理里面最终控制的地方实际上是:
frameworks/base/services/core/java/com/android/server/pm/PermissionsState.java

mSettings.mPermissions是如何初始化的

mSettings.mPermissions初始化分为几大部分:

  1. com.android.server.pm.PackageManagerService#PackageManagerService
    根据systemconfig里面的配置来进行初始化
public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
            ...
            SystemConfig systemConfig = SystemConfig.getInstance();
            ...
            // Propagate permission configuration in to package manager.
            ArrayMap<String, SystemConfig.PermissionEntry> permConfig
                    = systemConfig.getPermissions();
            for (int i=0; i<permConfig.size(); i++) {
                SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
                BasePermission bp = mSettings.mPermissions.get(perm.name);
                if (bp == null) {
                    bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
                    mSettings.mPermissions.put(perm.name, bp);
                }
                if (perm.gids != null) {
                    bp.setGids(perm.gids, perm.perUser);
                }
            }
            ...

}

在SystemConfig.getInstance()中,读取system/etc/permissions/里面的所有包含permission标签的xml文件(主要是解析platform.xml),然后将这个值保存到com.android.server.SystemConfig#mPermissions供后面com.android.server.SystemConfig#getPermissions调用。

2、 com.android.server.pm.PackageManagerService#scanPackageDirtyLI
在开机过程中,扫描所有的apk的过程中,会进行如下的动作

  private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
            ...

                ArrayMap<String, BasePermission> permissionMap =
                        p.tree ? mSettings.mPermissionTrees
                                : mSettings.mPermissions;
                BasePermission bp = permissionMap.get(p.info.name);

                // Allow system apps to redefine non-system permissions
                if (bp != null && !Objects.equals(bp.sourcePackage, p.info.packageName)) {
                    final boolean currentOwnerIsSystem = (bp.perm != null
                            && isSystemApp(bp.perm.owner));
                    if (isSystemApp(p.owner)) {
                        if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
                            // It's a built-in permission and no owner, take ownership now
                            bp.packageSetting = pkgSetting;
                            bp.perm = p;
                            bp.uid = pkg.applicationInfo.uid;
                            bp.sourcePackage = p.info.packageName;
                            p.info.flags |= PermissionInfo.FLAG_INSTALLED;
                        } else if (!currentOwnerIsSystem) {
                            String msg = "New decl " + p.owner + " of permission  "
                                    + p.info.name + " is system; overriding " + bp.sourcePackage;
                            reportSettingsProblem(Log.WARN, msg);
                            bp = null;
                        }
                    }
                }

                if (bp == null) {
                    bp = new BasePermission(p.info.name, p.info.packageName,
                            BasePermission.TYPE_NORMAL);
                    permissionMap.put(p.info.name, bp);
                }

}

在permissionMap.put(p.info.name,bp);方法中,对于每一个扫描到的包,都会将他的权限相关的信息,添加到mSettings.mPermissions.

所有的权限的变更都会通过com.android.server.pm.Settings#writeLPr将相关信息写入到/data/system/packages.xml里面去了。

mSettingsFilename = new File(mSystemDir, “packages.xml”);

permission-group 是如何初始化

com.android.server.pm.PackageManagerService#scanPackageDirtyLI
在扫面所有apk和安装某一个apk的过程中,会将所有的含有permission-group的权限组解析出来,存入到
android.content.pm.PackageParser.Package#permissionGroups
最后会将里面会将所有的解析出来的permissionGroups添加到com.android.server.pm.PackageManagerService#mPermissionGroups里面供PackageInstaller调用。

授予应用默认权限

com.android.server.pm.PackageManagerService#systemReady

        // If we upgraded grant all default permissions before kicking off.
        for (int userId : grantPermissionsUserIds) {
            mDefaultPermissionPolicy.grantDefaultPermissions(userId);
        }

在grantDefaultPermissions中,可以授予系统应用默认权限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值