Android 8.0权限管理源码分析

权限的目的是保护用户的隐私。应用访问敏感数据,例如通讯录和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;

普通权限

《Android 8.0权限管理源码分析》 image.png

权限组

系统根据权限用途又定义了权限组,每个权限都可属于一个权限组,每个权限组可以包含多个权限。例如联系人权限组,包含读取联系人、修改联系人和获取账户三个权限。

  • 如果应用申请访问一个危险权限,而此应用目前没有对应的权限组内的任何权限,系统会弹窗提示用户要访问的权限组(注意不是权限)。例如无论你申请READ_CONTACTS还是WRITE_CONTACTS,都是提示应用需要访问联系人信息。
  • 如果用户申请访问一个危险权限,而应用已经授权同权限组的其他权限,则系统会直接授权,不会再与用户有交互。例如应用已经请求并授予了READ_CONTACTS权限,那么当应用申请WRITE_CONTACTS时,系统会立即授予该权限。下面为危险权限和权限组:

《Android 8.0权限管理源码分析》 image.png

ADB工具

配合runtime permission, 也新增了一些相关的adb 命令:

  1. 查看所有的dangerous permissions:
              adb shell pm list permissions –g –d
    
  2. 安装app并且对所有列在app manifest文件下的所有permission给予授权:
              adb install -g <path_to_apk>
    
  3. 授权给某个app某个permission:
             adb pm grant <package_name> <permission_name>
    
  4. 撤销授权:
              adb pm revoke <package_name> <permission_name>
    

Setting APP中对应用权限管理:

菜单:
应用和通知:AppAndNotificationDashboardFragment.java;
应用信息:ManageApplications.java,列出所有APP;
具体应用详细界面:InstalledAppDetails.java;
点击”权限”,进入

《Android 8.0权限管理源码分析》 image.png

Packageinstaller APP 权限修改界面

详细列出应用需要的权限
ManagePermissionsActivity.java
AppPermissionsFragment.java:
管理应用权限类:
AppPermissions.java:
构造函数:

《Android 8.0权限管理源码分析》 image.png

通过packageInof 获得应用所有权限,在封装成permissiongroup;

《Android 8.0权限管理源码分析》 image.png

在AppPremissionsFragment中显示Group

《Android 8.0权限管理源码分析》 image.png

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

《Android 8.0权限管理源码分析》 image.png

对于targetSdkVersion < 23的应用

《Android 8.0权限管理源码分析》 image.png

允许权限

grantRuntimePermissions(false)
———-mPackageManager.grantRuntimePermission
1 判断是否可以动态权限管理,targetSdkVersion> = 23
2 更新AppOps服务
3 调用packageManager更新其runtime-permission.xml 中granted值
4 更新permissionflag

《Android 8.0权限管理源码分析》 image.png

《Android 8.0权限管理源码分析》 image.png

如果不是动态权限,
1 更新AppOps 服务
2 更新permissionflag

《Android 8.0权限管理源码分析》 image.png

《Android 8.0权限管理源码分析》 image.png

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<String> 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<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder = sm->getService(String16("permission"));
        if (binder == 0) {
            ALOGE("Cannot get permission service");
            return false;
        }

        sp<IPermissionController> permCtrl = interface_cast<IPermissionController>(binder);
        Vector<String16> 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;
}
    原文作者:NiceDream
    原文地址: https://www.jianshu.com/p/692c3914fea9
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞