权限的目的是保护用户的隐私。应用访问敏感数据,例如通讯录和SMS,还有系统特性,如摄像头,都需要申请权限;根据权限的类型,系统会自动赋予,或者让用户决定是否给予权限;
权限等级可以分为四个等级:
protectionLevel
(1)Normal
权限被声明为Normal级别,任何应用都可以申请,在安装应用时,不会直接提示给用户,点击全部才会展示。
(2)Dangerous
权限被声明为Dangerous级别,任何应用都可以申请,在安装应用时,会直接提示给用户。
(3)Signature
权限被声明为Signature级别,只有和该apk(定义了这个权限的apk)用相同的私钥签名的应用才可以申请该权限。
frameworks/base/core/res/AndroidManifest.xml声明的权限为Signature级别,那么只有Android官方使用相同私钥签名的应用才可以申请该权限。
(4)SignatureOrSystem
权限被声明为SignatureOrSystem级别,有两种应用可以申请该权限。
1)和该apk(定义了这个权限的apk)用相同的私钥签名的应用
2)在/system/app目录下的应用
对于APP targetSdkVersion >= 23, 在应用安装的时候不会显示应用需要的权限; 应需要在使用时动态申请,并且用户可以选择拒绝授权访问这些权限,已授予过的权限,用户也可以去APP设置页面去关闭授权;
杜宇targetSdkVersion <23 , 会在安装时候显示应用需要的所有权限;如果用户允许,就会授予应用所有权限,如果不允许,就会停止安装;
AppOpsManager Target < 23; 由AppOpsService处理,持久化到appops.xml;
Runtime-permission Target >= 23;由PackageManagerService 处理;持久化到runtime-permission.xml
另外权限不仅是系统特性, 还可以用于限制调用者;如Activtiy 中如果有android:permission,那么只有这个caller拥有这个权限,才可以启动这个Activity;
这个权限会在,startActivity(),startActivityForResult的时候检测,如果没有会抛出SecurityException;
普通权限
权限组
系统根据权限用途又定义了权限组,每个权限都可属于一个权限组,每个权限组可以包含多个权限。例如联系人权限组,包含读取联系人、修改联系人和获取账户三个权限。
- 如果应用申请访问一个危险权限,而此应用目前没有对应的权限组内的任何权限,系统会弹窗提示用户要访问的权限组(注意不是权限)。例如无论你申请READ_CONTACTS还是WRITE_CONTACTS,都是提示应用需要访问联系人信息。
- 如果用户申请访问一个危险权限,而应用已经授权同权限组的其他权限,则系统会直接授权,不会再与用户有交互。例如应用已经请求并授予了READ_CONTACTS权限,那么当应用申请WRITE_CONTACTS时,系统会立即授予该权限。下面为危险权限和权限组:
ADB工具
配合runtime permission, 也新增了一些相关的adb 命令:
查看所有的dangerous permissions: adb shell pm list permissions –g –d
安装app并且对所有列在app manifest文件下的所有permission给予授权: adb install -g
授权给某个app某个permission: adb pm grant
撤销授权: adb pm revoke
Setting APP中对应用权限管理:
菜单:
应用和通知:AppAndNotificationDashboardFragment.java;
应用信息:ManageApplications.java,列出所有APP;
具体应用详细界面:InstalledAppDetails.java;
点击"权限",进入
Packageinstaller APP 权限修改界面
详细列出应用需要的权限
ManagePermissionsActivity.java
AppPermissionsFragment.java:
管理应用权限类:
AppPermissions.java:
构造函数:
通过packageInof 获得应用所有权限,在封装成permissiongroup;
在AppPremissionsFragment中显示Group
AppPermissionGroup.java
针对targetSdkVersion是否高于23做了不同处理,targetSdkVersion<23的所有的权限都在packages.xml中,grante一直是true,无法被跟新;如果targetSdkVersion>=23支持动态权限管理,那就更新动态权限,并将其持久化到runtime-permission.xml中,并更新其granted值,如果targetSdkVersion<23 ,也不是动态管理,那就只更新AppOps,这是4.3引入的老的动态权限管理模型,不过这里主要是将权限持久化到appops.xml中,更新权限后30分钟才会持久化到appops.xml中,不过对于其granted的值是没有做任何更新的,仅仅是更新了packages.xml中的flag,这个flag可以配合appops.xml标识是否被授权(对于targetSdkVersion<23的适用。
禁止权限
revokeRuntimePermissionsrevoke(false)
----------mPackageManager.revokeRuntimePermission更新权限
----------mPackageManager.updatePermissionFlag更新permissionflag
对于targetSdkVersion < 23的应用
允许权限
grantRuntimePermissions(false)
----------mPackageManager.grantRuntimePermission
1 判断是否可以动态权限管理,targetSdkVersion> = 23
2 更新AppOps服务
3 调用packageManager更新其runtime-permission.xml 中granted值
4 更新permissionflag
如果不是动态权限,
1 更新AppOps 服务
2 更新permissionflag
PackageManagerService-- grantRuntimePermission
1 PackageManagerService中通过mPackage得到目标pkg ;
2 通过mSettings.mPermissions获取到目标权限bp ;
3 通过pkg得到一个PermissionsState对象permissionsState;
4 使用permissionsState对象来对pkg的bp进行赋予权限的操作;
1 通过ensurePermissionData方法获取一个PermissionData对象pd;
2 使用pd来赋予权限
5 持久化到runtime-permission.xml
private void grantRuntimePermission(String packageName, String name, final int userId,
boolean overridePolicy) {
if (!sUserManager.exists(userId)) {
Log.e(TAG, "No such user:" + userId);
return;
}
final int callingUid = Binder.getCallingUid();
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
"grantRuntimePermission");
enforceCrossUserPermission(callingUid, userId,
true /* requireFullPermission */, true /* checkShell */,
"grantRuntimePermission");
final int uid;
final PackageSetting ps;
synchronized (mPackages) {
//1 通过mPackage得到目标pkg
final PackageParser.Package pkg = mPackages.get(packageName);
if (pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
// 2通过mSettings.mPermissions获取到目标权限bp ;
final BasePermission bp = mSettings.mPermissions.get(name);
if (bp == null) {
throw new IllegalArgumentException("Unknown permission: " + name);
}
ps = (PackageSetting) pkg.mExtras;
if (ps == null
|| filterAppAccessLPr(ps, callingUid, userId)) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
//app将该permission 注册到AndroidManifest中
//并且该permission 是Runtime 或者是development permission
enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);
// If a permission review is required for legacy apps we represent
// their permissions as always granted runtime ones since we need
// to keep the review required permission flag per user while an
// install permission's state is shared across all users.
if (mPermissionReviewRequired
&& pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
&& bp.isRuntime()) {
return;
}
uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
//3 通过pkg得到一个PermissionsState对象permissionsState;
final PermissionsState permissionsState = ps.getPermissionsState();
final int flags = permissionsState.getPermissionFlags(name, userId);
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
throw new SecurityException("Cannot grant system fixed permission "
+ name + " for package " + packageName);
}
if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
throw new SecurityException("Cannot grant policy fixed permission "
+ name + " for package " + packageName);
}
if (bp.isDevelopment()) {
// Development permissions must be handled specially, since they are not
// normal runtime permissions. For now they apply to all users.
if (permissionsState.grantInstallPermission(bp) !=
PermissionsState.PERMISSION_OPERATION_FAILURE) {
scheduleWriteSettingsLocked();
}
return;
}
if (ps.getInstantApp(userId) && !bp.isInstant()) {
throw new SecurityException("Cannot grant non-ephemeral permission"
+ name + " for package " + packageName);
}
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
return;
}
//4 使用permissionsState对象来对pkg的bp进行赋予权限的操作;
final int result = permissionsState.grantRuntimePermission(bp, userId);
switch (result) {
case PermissionsState.PERMISSION_OPERATION_FAILURE: {
return;
}
case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
mHandler.post(new Runnable() {
@Override
public void run() {
killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
}
});
}
break;
}
if (bp.isRuntime()) {
logPermissionGranted(mContext, name, packageName);
}
mOnPermissionChangeListeners.onPermissionsChanged(uid);
//5 持久化到runtime-permission.xml
// Not critical if that is lost - app has to request again.
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
// Only need to do this if user is initialized. Otherwise it's a new user
// and there are no processes running as the user yet and there's no need
// to make an expensive call to remount processes for the changed permissions.
if (READ_EXTERNAL_STORAGE.equals(name)
|| WRITE_EXTERNAL_STORAGE.equals(name)) {
final long token = Binder.clearCallingIdentity();
try {
if (sUserManager.isInitialized(userId)) {
StorageManagerInternal storageManagerInternal = LocalServices.getService(
StorageManagerInternal.class);
storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
权限检测 checkUidPermission
1 通过UID 获得应用的SettingBase;
2 通过SettingBase 获得PermissionsState
3 更具permissionsState 判断是否具有权限
public int checkUidPermission(String permName, int uid) {
final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(caSllingUid);
final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
final boolean isUidInstantApp = getInstantAppPackageName(uid) != null;
final int userId = UserHandle.getUserId(uid);
if (!sUserManager.exists(userId)) {
return PackageManager.PERMISSION_DENIED;
}
//permName.equals();
synchronized (mPackages) {
Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
if (obj != null) {
if (obj instanceof SharedUserSetting) {
if (isCallerInstantApp) {
return PackageManager.PERMISSION_DENIED;
}
} else if (obj instanceof PackageSetting) {
final PackageSetting ps = (PackageSetting) obj;
if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
return PackageManager.PERMISSION_DENIED;
}
}
final SettingBase settingBase = (SettingBase) obj;
final PermissionsState permissionsState = settingBase.getPermissionsState();
if (permissionsState.hasPermission(permName, userId)) {
if (isUidInstantApp) {
BasePermission bp = mSettings.mPermissions.get(permName);
if (bp != null && bp.isInstant()) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
return PackageManager.PERMISSION_GRANTED;
}
}
// Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
ArraySet perms = mSystemPermissions.get(uid);
if (perms != null) {
if (perms.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
.contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
}
return PackageManager.PERMISSION_DENIED;
}
录音权限判断
ServiceUtilities.cpp
1 packagemanager--- checkPermission
2 AppOpsManager--- noteOp
bool recordingAllowed(const String16& opPackageName, pid_t pid, uid_t uid) {
// we're always OK.
if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
static const String16 sRecordAudio("android.permission.RECORD_AUDIO");
// We specify a pid and uid here as mediaserver (aka MediaRecorder or StageFrightRecorder)
// may open a record track on behalf of a client. Note that pid may be a tid.
// IMPORTANT: Don't use PermissionCache - a runtime permission and may change.
const bool ok = checkPermission(sRecordAudio, pid, uid);
if (!ok) {
ALOGE("Request requires android.permission.RECORD_AUDIO");
return false;
}
// To permit command-line native tests
if (uid == AID_ROOT) return true;
String16 checkedOpPackageName = opPackageName;
// In some cases the calling code has no access to the package it runs under.
// For example, code using the wilhelm framework's OpenSL-ES APIs. In this
// case we will get the packages for the calling UID and pick the first one
// for attributing the app op. This will work correctly for runtime permissions
// as for legacy apps we will toggle the app op for all packages in the UID.
// The caveat is that the operation may be attributed to the wrong package and
// stats based on app ops may be slightly off.
if (checkedOpPackageName.size() <= 0) {
sp sm = defaultServiceManager();
sp binder = sm->getService(String16("permission"));
if (binder == 0) {
ALOGE("Cannot get permission service");
return false;
}
sp permCtrl = interface_cast(binder);
Vector packages;
permCtrl->getPackagesForUid(uid, packages);
if (packages.isEmpty()) {
ALOGE("No packages for calling UID");
return false;
}
checkedOpPackageName = packages[0];
}
AppOpsManager appOps;
if (appOps.noteOp(AppOpsManager::OP_RECORD_AUDIO, uid, checkedOpPackageName)
!= AppOpsManager::MODE_ALLOWED) {
ALOGE("Request denied by app op OP_RECORD_AUDIO");
return false;
}
return true;
}