Android权限分类
Adnroid的权限保护级别由两个部分组成:一部分是基本的权限分类,分为normal, dangerous, signature和internal四个级别:
另一部分是一些额外的标签,如:
一个权限的保护级别由两个部分运算而来,比如18
表示为0x2|0x10
,即PROTECTION_NORMAL | PROTECTION_FLAG_PRIVILIGED
声明安装时权限
使用安装时权限,需要在AndroidManifest.xml
文件中声明相应的权限:
<manifest ...>
<uses-permission android:name="android.permission.***"/>
<application ...>
...
</application>
</manifest>
normal
级别的权限,只需要在声明中指定相应的权限就行signature
级别的权限,需要通过系统签名打包APK- 对于
PROTECTION_FLAG_PRIVILIGED
,在9.0版本之后,Android引入了allow-list机制,对于特权应用priv-app
,需要在/system, /product, /vendor
等目录下的/etc/permissions/priv-app/privapp-permissions-xxx.xml
声明应用可以使用的权限:
<permissions>
<privapp-permissions package="xxx.xxx.xxx">
<permission name="android.permission.xxx"/>
...
</privapp-permissions>
...
</permissions>
请求运行时权限
Android权限工作流
flowchart LR
A{是否需要使用权限}--是-->B[在manifest中声明权限]
A--否-->C[执行相应功能]
B--是-->D{是否使用运行时权限}
D--否-->C
D--是-->F[请求用户授权权限]
F-->C
Android权限机制
framework层
PackageManagerService
管理包的信息,并通过Settings.java
读写packages.xml
文件:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<packages>
<version sdkVersion="34" databaseVersion="3" buildFingerprint="..." fingerprint="..." />
<version volumeUuid="primary_physical" sdkVersion="34" databaseVersion="3" buildFingerprint="..." fingerprint="..." />
<permission-trees />
<!--权限集合-->
<permissions>
...
<item name="android.permission.XXX" package="android" protection="18" />
...
</permissions>
<!--包信息-->
<package name = "..." ... sharedUserId = "..." ... >
<!--签名信息-->
<sigs count="1" schemeVersion="3">
<cert index="0" />
</sigs>
<proper-signing-keyset identifier="1" />
<!--权限信息-->
<perms>
...
</perms>
</package>
...
<!--一些升级过的包的信息-->
<updated-package ... />
...
<!--一些userId信息-->
<shared-user name="android.uid.system" userId="1000">
<sigs count="1" schemeVersion="3">
<cert index="2" />
</sigs>
</shared-user>
...
<domain-verifications>
<active>
<package-state .../>
...
</active>
...
</domain-verifications>
<!--签名信息-->
<keyset-settings version="1">
<keys>
<public-key .../>
...
</keys>
<keysets>
<keyset identifier="1">
<key-id identifier="1" />
</keyset>
...
</keysets>
<lastIssuedKeyId value="23" />
<lastIssuedKeySetId value="23" />
</keyset-settings>
</packages>
,在packages.xml
中,permissions
标签下定义了不同的权限,包含权限名,包名以及保护级别protection
,保护级别由两部分组成,分别是
授权流程
包扫描
通过解析目录下的文件,得到一个PackageImpl
对象:
public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
AndroidPackageHidden, ParsingPackage, ParsingPackageHidden, Parcelable {
...
private String sharedUserId;
private List<ParsedUsesPermission> usesPermissions = emptyList();
}
安装时权限授权
sequenceDiagram
autonumber
participant PackageManagerService
participant Settings
participant InstallPackageHelper
participant PermissionCallback
participant PermissionManagerServiceImpl
participant UidPermissionState
participant PermissionState
Note over PackageManagerService,PermissionState: 更新安装包信息
activate InstallPackageHelper
InstallPackageHelper->>InstallPackageHelper: commitPackagesLocked
activate InstallPackageHelper
InstallPackageHelper->>InstallPackageHelper: updateSettingsLI
activate InstallPackageHelper
InstallPackageHelper->>InstallPackageHelper: updateSetinggsInternalLI
InstallPackageHelper->>PermissionManagerServiceImpl: onPackageInstalled
activate PermissionManagerServiceImpl
deactivate InstallPackageHelper
deactivate InstallPackageHelper
deactivate InstallPackageHelper
activate PermissionManagerServiceImpl
PermissionManagerServiceImpl->>PermissionManagerServiceImpl: onPackageInstalledInternal
activate PermissionManagerServiceImpl
Note over PackageManagerService,PermissionState: 进行授权
PermissionManagerServiceImpl->>PermissionManagerServiceImpl: updatePermissions
activate PermissionManagerServiceImpl
PermissionManagerServiceImpl->>PermissionManagerServiceImpl: restorePermissionState
PermissionManagerServiceImpl->>UidPermissionState: grantPermission
activate UidPermissionState
UidPermissionState->>PermissionState: grant
PermissionState-->>UidPermissionState:
UidPermissionState-->>PermissionManagerServiceImpl:
deactivate UidPermissionState
Note over PackageManagerService,PermissionState: 持久化
PermissionManagerServiceImpl->>+PermissionCallback: onPermissionUpdated
activate PackageManagerService
PermissionCallback->>-PackageManagerService: writeSettings
activate PackageManagerService
PackageManagerService->>PackageManagerService: writeSettingsLPrTEMP
PackageManagerService->>Settings: writeLPr
deactivate PackageManagerService
deactivate PackageManagerService
deactivate PermissionManagerServiceImpl
deactivate PermissionManagerServiceImpl
deactivate PermissionManagerServiceImpl
deactivate PermissionManagerServiceImpl
onPackageInstalled
public void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId,
@NonNull PermissionManagerServiceInternal.PackageInstalledParams params,
@UserIdInt int userId) {
...
onPackageInstalledInternal(pkg, previousAppId, params, userIds);
}
onPackageInstalledInternal
private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, int previousAppId,
@NonNull PermissionManagerServiceInternal.PackageInstalledParams params,
@UserIdInt int[] userIds) {
...
updatePermissions(pkg.getPackageName(), pkg);
for (final int userId : userIds) {
addAllowlistedRestrictedPermissionsInternal(pkg,
params.getAllowlistedRestrictedPermissions(),
FLAG_PERMISSION_WHITELIST_INSTALLER, userId);
grantRequestedPermissionsInternal(pkg, params.getPermissionStates(), userId);
}
}
updatePermissions
private void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) {
/**
*如果传入的pkg为空,需要更新所有包的权限并替换包的权限信息
*否则只替换报的权限信息
**/
final int flags =
(pkg == null ? UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG
: UPDATE_PERMISSIONS_REPLACE_PKG);
//调用另一个updatePermission方法
updatePermissions(
packageName, pkg, getVolumeUuidForPackage(pkg), flags, mDefaultPermissionCallback);
}
private void updatePermissions(final @Nullable String changingPkgName,
final @Nullable AndroidPackage changingPkg,
final @Nullable String replaceVolumeUuid,
@UpdatePermissionFlags int flags,
final @Nullable PermissionCallback callback) {
//callback为PermissionManagerServiceImpl内部定义的mDefaultPermissionCallback
if ((flags & UPDATE_PERMISSIONS_ALL) != 0) {
final boolean replaceAll = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0);
mPackageManagerInt.forEachPackage((AndroidPackage pkg) -> {
...
restorePermissionState(pkg, replace, changingPkgName, callback,
UserHandle.USER_ALL);
});
}
if (changingPkg != null) {
...
restorePermissionState(changingPkg, replace, changingPkgName, callback,
UserHandle.USER_ALL);
}
}
restorePermissionState
private void restorePermissionState(@NonNull AndroidPackage pkg, boolean replace,
@Nullable String changingPackageName, @Nullable PermissionCallback callback,
@UserIdInt int filterUserId) {
final PackageStateInternal ps =
mPackageManagerInt.getPackageStateInternal(pkg.getPackageName());
...
for (final String permissionName : pkg.getRequestedPermissions()) {
final Permission permission;
synchronized (mLock) {
permission = mRegistry.getPermission(permissionName);
}
if (permission == null) {
continue;
}
//如果权限是privileged权限且在allowlist中进行了授权
if (permission.isPrivileged()
&& checkPrivilegedPermissionAllowlist(pkg, ps, permission)) {
if (isPrivilegedPermissionAllowlisted == null) {
isPrivilegedPermissionAllowlisted = new ArraySet<>();
}
isPrivilegedPermissionAllowlisted.add(permissionName);
}
//如果权限是signature权限
if (permission.isSignature() && (shouldGrantPermissionBySignature(pkg, permission)
|| shouldGrantPermissionByProtectionFlags(pkg, ps, permission,
shouldGrantPrivilegedPermissionIfWasGranted))) {
if (shouldGrantSignaturePermission == null) {
shouldGrantSignaturePermission = new ArraySet<>();
}
shouldGrantSignaturePermission.add(permissionName);
}
//内部权限
if (permission.isInternal()
&& shouldGrantPermissionByProtectionFlags(pkg, ps, permission,
shouldGrantPrivilegedPermissionIfWasGranted)) {
if (shouldGrantInternalPermission == null) {
shouldGrantInternalPermission = new ArraySet<>();
}
shouldGrantInternalPermission.add(permissionName);
}
}
//特定uid显式请求的权限
Collection<String> uidRequestedPermissions;
//特定uid隐式具有的权限
Collection<String> uidImplicitPermissions;
...
//进行权限授予的操作
...
//调用callback的onPermissionUpdated方法
if (callback != null) {
callback.onPermissionUpdated(updatedUserIds,
(changingPackageName != null && replace && installPermissionsChanged)
|| runtimePermissionsRevoked, pkg.getUid());
}
}
,方法维护了三个列表: isPrivilegedPermissionAllowlisted
,shouldGrantSignaturePermission
和shouldGrantInternalPermission
,表示需要授予包的不同级别的权限,方法同时维护了两个集合:uidRequestedPermissions
、uidImplicitPermissions
,表示与应用声明的sharedUserId
相同的其他应用声明的权限和该sharedUserId
下的隐式权限
权限授予的逻辑大致如下图:
grantPermission
public boolean grantPermission(@NonNull Permission permission) {
final PermissionState permissionState = getOrCreatePermissionState(permission);
return permissionState.grant();
}
grant
public boolean grant() {
synchronized (mLock) {
if (mGranted) {
return false;
}
mGranted = true;
UidPermissionState.invalidateCache();
return true;
}
}
onPermissionUpdated
private final PermissionCallback mDefaultPermissionCallback = new PermissionCallback() {
...
@Override
public void onInstallPermissionUpdated() {
mPackageManagerInt.writeSettings(true);
}
};
,PermissionManagerServiceImpl
中声明了一个mDefaultPermissionCallback
作为permission
操作的callback
调用对象,其onInstallPermissionUpdated
方法在restorePermissionState
方法中被调用,并调用PackageMangerService::writeSettings
方法
writeSettings & writeSettingsLPrTEMP
void writeSettings(boolean sync) {
synchronized (mLock) {
...
writeSettingsLPrTEMP(sync);
...
}
}
void writeSettingsLPrTEMP(boolean sync) {
snapshotComputer(false);
mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
mSettings.writeLPr(mLiveComputer, sync);
}
运行时权限授权
运行时权限授权主要通过PermissionManagerService
的grantRuntimePermissionInternal
方法,并调用了UidPermissionState::grantPermission
方法,之后,执行了如下代码:
final int uid = UserHandle.getUid(userId, pkg.getUid());
if (callback != null) {
if (isRuntimePermission) {
callback.onPermissionGranted(uid, userId);
} else {
callback.onInstallPermissionGranted();
}
if (permissionHasGids) {
callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId);
}
}
,如果权限是运行时权限,则调用onPermissionGranted
方法,否则调用onInstallPermissionGranted
方法,如果权限对应着权限组,则需要调用onGidsChanged
方法