概述
1. 权限定义
Android权限框架在默认情况下,任何应用都没有权限执行对其他应用、操作系统或用户有不利影响的任何操作。
Android系统本身定义了哪些权限了,我们可以参考frameworksbasecoreresAndroidManifest.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
中的privileged
和system
2.applicationInfo.flags
中的 FLAG_SYSTEM
和PRIVATE_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之前定义为signatureOrSystem
或signature|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
中的privileged
和system
是等价的,为了让大家避免概念混淆的原因,在高版本上做了区分。
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.system
,android.uid.phone
,android.uid.log
,android.uid.nfc
,android.uid.bluetooth
,android.uid.shell
,android.uid.se(Android O)
这类应用在PMS初始化是被赋予了ApplicationInfo.FLAG_SYSTEM
和ApplicationInfo.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方法进行扫描相关路径,分析调用逻辑
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/framework
和system/pri-app
添加了FLAG_SYSTEM
和 PRIVATE_FLAG_PRIVILEGED
标志。
由于/system/framework
只是写资源包,所以可以这么认为,上述这两类系统App中,特定的sharedUserId和system/pri-app为特权App。
结合1.1.1 和 1.1.2 中的内容,privileged
和system
是等价的,所以我们通常说有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;
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)。
安装时权限 即在安装时进行授予的权限;
* 运行时权限*即为在运行时授予指定用户的权限。
normal
和 signature
级别的权限为安装时权限。
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
能获取到相关权限的要么是签名一致,要么是系统应用,所以没有先后顺序之分。
最后
以上就是腼腆枫叶为你收集整理的Android权限机制的全部内容,希望文章能够帮你解决Android权限机制所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复