Android权限机制

1. 权限定义

Android权限框架在默认情况下,任何应用都没有权限执行对其他应用、操作系统或用户有不利影响的任何操作。
Android系统本身定义了哪些权限了,我们可以参考frameworks\base\core\res\AndroidManifest.xml,相关权限都定义在该文件中

    ...
    <permission android:name="android.permission.BLUETOOTH" android:description="@string/permdesc_bluetooth" android:label="@string/permlab_bluetooth" android:protectionLevel="normal" />
    ...

    <permission android:name="android.permission.MANAGE_USB" android:protectionLevel="signature|privileged" />
    ...

文件中声明了很多权限,是不是所有的应用都能使用这些权限呢?
答案肯定是否定的,我们可以看到permission下都有android:protectionLevel标签,该标签有以下几个类型

(1)normal:权限被声明为Normal级别,任何应用都可以申请,在安装应用时,不会直接提示给用户,点击全部才会展示。
(2)dangerous:权限被声明为Dangerous级别,任何应用都可以申请,在安装应用时,会直接提示给用户。
(3)signature:权限被声明为Signature级别,只有和定义该权限者具有相同签名的应用才可以申请该权限。(系统权限则需要和系统相同签名)
(4)signature|privileged:表示为相同签名或者为特权应用可以申请该权限
该标签的含义可以参考https://developer.android.com/guide/topics/manifest/permission-element

1.1 权限级别

自定义过权限的同学可能会对下面两个概念有些模糊
1.android:protectionLevel中的privilegedsystem
2.applicationInfo.flags中的 FLAG_SYSTEMPRIVATE_FLAG_PRIVILEGED

1.1.1 privileged 和 system

细心的朋友可能发现上面android:protectionLevel在不同API中同一个权限时可能写的方式不一样,比如android.permission.TV_INPUT_HARDWARE权限

在Android Marshmallow中定义如下:

    <permission android:name="android.permission.TV_INPUT_HARDWARE" android:protectionLevel="signature|privileged" />

而在Android Lollipop中定义如下:

    <permission android:name="android.permission.TV_INPUT_HARDWARE" android:protectionLevel="signatureOrSystem" />

其实在API23之前定义为signatureOrSystemsignature|system,在其之后定义为signature|privileged,通过PermissionInfo.java中的fixProtectionLevel()可以看出来
Android Marshmallow 该方法如下:

    public static int fixProtectionLevel(int level) {
        if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
            level = PROTECTION_SIGNATURE | PROTECTION_FLAG_PRIVILEGED;
        }
        return level;
    }

Android Lollipop 该方法如下:

    public static int fixProtectionLevel(int level) {
        if (level == PROTECTION_SIGNATURE_OR_SYSTEM) {
            level = PROTECTION_SIGNATURE | PROTECTION_FLAG_SYSTEM;
        }
        return level;
    }

所以,在android:protectionLevel中的privilegedsystem是等价的,为了让大家避免概念混淆的原因,在高版本上做了区分。

1.1.2 FLAG_SYSTEM 和 PRIVATE_FLAG_PRIVILEGED

在这我们把带有ApplicationInfo.FLAG_SYSTEM标记叫做系统App,把ApplicationInfo.PRIVATE_FLAG_PRIVILEGED叫做特权App。
那么问题来了,系统App和特权App是什么关系呢?
在PMS(PackageManagerService)中判断是否为系统App的方法如下:

    private static boolean isSystemApp(PackageParser.Package pkg) {
        return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
    }

判断是否为特权App的方法如下:

    private static boolean isPrivilegedApp(PackageParser.Package pkg) {
        return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
    }

我们是否可以从从这两个方法可以得出,这两个标志位貌似是同级关系,并不存在包含关系呢?
事实上,从直观的角度上来说系统App > 特权App,也就是说系统App包含特权App。
可以applicationInfo.flags 初始化可以参考PMS的构造函数,系统App可以分为几类:

第一类sharedUserId为 android.uid.systemandroid.uid.phone,android.uid.log,android.uid.nfc,android.uid.bluetooth,android.uid.shell,android.uid.se(Android O)这类应用在PMS初始化是被赋予了ApplicationInfo.FLAG_SYSTEMApplicationInfo.PRIVATE_FLAG_PRIVILEGED标志

    public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
    ...
    mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
    ...

第二类为预装的App,我们可以继续走读PMS构造函数,

    //VENDOR_OVERLAY_DIR = "/vendor/overlay";
    File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
    scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

    //frameworkDir = /system/framework
    scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR
            | PackageParser.PARSE_IS_PRIVILEGED,
            scanFlags | SCAN_NO_DEX, 0);
    // system/pri-app
    final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
    scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR
            | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

    // /system/app
    final File systemAppDir = new File(Environment.getRootDirectory(), "app");
    scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

    File vendorAppDir = new File("/vendor/app");
    try {
        vendorAppDir = vendorAppDir.getCanonicalFile();
    } catch (IOException e) {
        // failed to look up canonical path, continue with original one
    }
    scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
    // /oem/app
    final File oemAppDir = new File(Environment.getOemDirectory(), "app");
    scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

可以看出,总共有/vendor/overlay/system/framework(无代码的资源包)system/pri-app/system/app/vendor/app,/oem/app这些路径,
通过scanDirLI方法进行扫描相关路径,分析调用逻辑

Created with Raphaël 2.1.2 scanDirLI scanPackageLI scanPackageDirtyLI

    private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
        ...
        if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
        } else {
            // Only allow system apps to be flagged as core apps.
            pkg.coreApp = false;
        }
        if ((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
        }
        ...
    }

可以得知PackageParser.PARSE_IS_SYSTEM被解析为ApplicationInfo.FLAG_SYSTEM
parseFlags&PackageParser.PARSE_IS_PRIVILEGED被解析为ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
所以这几个路径也可以分为两类:
/vendor/overlay/system/app/vendor/app,/oem/app 只加上了FLAG_SYSTEM标志
/system/frameworksystem/pri-app添加了FLAG_SYSTEMPRIVATE_FLAG_PRIVILEGED标志。
由于/system/framework只是写资源包,所以可以这么认为,上述这两类系统App中,特定的sharedUserId和system/pri-app为特权App。

结合1.1.11.1.2 中的内容,privilegedsystem是等价的,所以我们通常说有system或者privileged权限时,通常指的是特权App。
所以我们在集成第三方应用时,三方应用没有系统签名,却又需要使用system级别权限时,我们可以把第三方应用集成到system/pri-app目录即可;反过来,如果预装第三方应用,却不想让其拥有system级别权限时,可以将其集成到system/app目录下即可。

2 权限申请

在安装应用时,会对应用所申请的权限进行相关校验和解析,相关安装逻辑如下

graph TD;
  processPendingInstall --> installPackageLI;
  installPackageLI-->replacePackageLI;
  installPackageLI-->installNewPackageLI;
  installNewPackageLI-->scanPackageLI;
  scanPackageLI--> scanPackageDirtyLI  ;
  scanPackageDirtyLI-- 设置flag和privateFlags -->updateSettingsLI;
  updateSettingsLI-->updatePermissionsLPw;
  updatePermissionsLPw-->grantPermissionsLPw;

Created with Raphaël 2.1.2 processPendingInstall installPackageLI installNewPackageLI scanPackageLI scanPackageDirtyLI 设置flag和privateFlags updateSettingsLI updatePermissionsLPw grantPermissionsLPw

    private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace, String packageOfInterest) {
        ...
        final int N = pkg.requestedPermissions.size();
        for (int i=0; i<N; i++) {
            final String name = pkg.requestedPermissions.get(i);
            final BasePermission bp = mSettings.mPermissions.get(name);
            ...
            final String perm = bp.name;
            boolean allowedSig = false;
            int grant = GRANT_DENIED;
            ...
            final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
            switch (level) {
                case PermissionInfo.PROTECTION_NORMAL: {
                    // For all apps normal permissions are install time ones.
                    grant = GRANT_INSTALL;
                } break;
                case PermissionInfo.PROTECTION_DANGEROUS: {
                    if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1) {
                        // For legacy apps dangerous permissions are install time ones.
                        grant = GRANT_INSTALL_LEGACY;
                    } else if (origPermissions.hasInstallPermission(bp.name)) {
                        // For legacy apps that became modern, install becomes runtime.
                        grant = GRANT_UPGRADE;
                    } else if (mPromoteSystemApps
                            && isSystemApp(ps)
                            && mExistingSystemPackages.contains(ps.name)) {
                        // For legacy system apps, install becomes runtime.
                        // We cannot check hasInstallPermission() for system apps since those
                        // permissions were granted implicitly and not persisted pre-M.
                        grant = GRANT_UPGRADE;
                    } else {
                        // For modern apps keep runtime permissions unchanged.
                        grant = GRANT_RUNTIME;
                    }
                } break;
                case PermissionInfo.PROTECTION_SIGNATURE: {
                    // For all apps signature permissions are install time ones.
                    allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
                    if (allowedSig) {
                        grant = GRANT_INSTALL;
                    }
                } break;
            }
            //省略一段更新权限的逻辑
        ...
    }

在grantPermissionsLPw方法中,对权限进行分类,安装时权限* 运行时权限*(target SDK >=23)。
安装时权限 即在安装时进行授予的权限;
* 运行时权限*即为在运行时授予指定用户的权限。
normalsignature级别的权限为安装时权限。
dangerous级别的权限在 Lollipop MR1(API22)及其之前的版本为安装时权限,之后版本为运行时权限。需要注意的是只有在应用的target SDK > 22时,运行时权限才生效。* 如果在开发第三方应用时,不想进行动态检查和适配权限,可以将targetSDK设置小于23即可(为了用户安全和体验,还是建议适配相关权限 \^_^) *

3 权限使用

3.1 signature级别权限使用

如果应用权限定义为android:protectionLevel="signature",那么有下面几种应用可以使用获得该权限:
* 与该应用具有相同签名
* 与系统签名相同的app,即与厂商签名(厂商ROM中的系统app签名)相同的App

如果应用权限定义为android:protectionLevel="signature|privileged"或者signatureOrSystem,那么有下面几种应用可以使用获得该权限:
* 与该应用具有相同签名
* 与系统签名相同的app,即与厂商签名(厂商ROM中的系统app签名)相同的app
* 特权App(/system/priv-app目录下的App)

这两种情况下,对于第二点大家或许会有疑问,第三方应用声明为signature级别的权限,怎么和系统签名相同App也能申请相关权限呢?
我们继续往下分析grantSignaturePermission方法

    private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
        BasePermission bp, PermissionsState origPermissions) {
        boolean allowed;
        allowed = (compareSignatures(
           bp.packageSetting.signatures.mSignatures, pkg.mSignatures)
                   == PackageManager.SIGNATURE_MATCH)
           || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
                   == PackageManager.SIGNATURE_MATCH);
        ...
        return allowed;
    }

在该方法中,比较了平台签名和安装的App签名,如果匹配,则返回允许申请。第三方应用设置权限时,需要考虑到系统签名应用访问相关权限情况!

3.2 权限声明和申请的先后顺序

对于normal或者dangerous权限,需要先声明后使用
signature或者signature|privileged能获取到相关权限的要么是签名一致,要么是系统应用,所以没有先后顺序之分。

    原文作者:燃烧的CO2
    原文地址: https://blog.csdn.net/xhaotianshenjian/article/details/81535259
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞