请选择 进入手机版 | 继续访问电脑版

Android App 授权机制简析

[复制链接]
阿峻 发表于 2021-1-1 18:31:05 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
一、授予方式

1.动态授权

从Android M开始,虽然之后的版本会有部门调解,但对危险权限的处置惩罚方式是一致的。无论是亲自编码实现,照旧引用第三方框架,技能都已经很成熟了,在此不做过多的叙述。
2.特定shareUID属性

这里以android.uid.system属性为例。不同于通过install方式安装的App,将该属性添加到AndroidManifest.xml中后,需要先用System Signature进行签名,再像System App一样集成到Rom之中,这样便可以直接使用在AndroidManifest.xml中声明的所有权限。

3.default permission设置

对于那些既不想申请运行时权限,也不想添加android.uid.system属性,纵然放置在system/priv-app目次下,依然不能使用某些在AndroidManifest.xml中声明的权限的System App。在system/etc/permissions目次下,做好default permission设置后,便可直接声明、使用那些设置过的权限。


二、查察授权


  adb shell dumpsys package com.xxx.xxx
未授权:

已授权:

 
三、原理简析

1.动态授权

ActivityCompat.requestPermissions(...):实际是通过PMS的grantRuntimePermission(...)方法,来完成运行时权限的动态分配;最终存放到data/system/users/0/runtime-permissions.xml设置文件中,而安装时权限都是存放在data/system/packages.xml设置文件中的。
不同的Android系统版本,代码结构有一定的差异。下面是Android Q中的部门源码:
  1. //frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java@Overridepublic void grantRuntimePermission(String packageName, String permName, final int userId) {    boolean overridePolicy = (checkUidPermission(            Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid())            == PackageManager.PERMISSION_GRANTED);    mPermissionManager.grantRuntimePermission(permName, packageName, overridePolicy,            getCallingUid(), userId, mPermissionCallback);}
复制代码
  1. //frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.javaprivate void grantRuntimePermission(String permName, String packageName, boolean overridePolicy,        int callingUid, final int userId, PermissionCallback callback) {    if (!mUserManagerInt.exists(userId)) {        Log.e(TAG, "No such user:" + userId);        return;    }    mContext.enforceCallingOrSelfPermission(            android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,            "grantRuntimePermission");    enforceCrossUserPermission(callingUid, userId,            true,  // requireFullPermission            true,  // checkShell            false, // requirePermissionWhenSameUser            "grantRuntimePermission");    final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);    if (pkg == null || pkg.mExtras == null) {        throw new IllegalArgumentException("Unknown package: " + packageName);    }    final BasePermission bp;    synchronized(mLock) {        bp = mSettings.getPermissionLocked(permName);    }    if (bp == null) {        throw new IllegalArgumentException("Unknown permission: " + permName);    }    if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {        throw new IllegalArgumentException("Unknown package: " + packageName);    }    bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);    // 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&#39;s state is shared across all users.    if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M            && bp.isRuntime()) {        return;    }    final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);    final PackageSetting ps = (PackageSetting) pkg.mExtras;    final PermissionsState permissionsState = ps.getPermissionsState();    final int flags = permissionsState.getPermissionFlags(permName, userId);    if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {        Log.e(TAG, "Cannot grant system fixed permission "                + permName + " for package " + packageName);        return;    }    if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {        Log.e(TAG, "Cannot grant policy fixed permission "                + permName + " for package " + packageName);        return;    }    if (bp.isHardRestricted()            && (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {        Log.e(TAG, "Cannot grant hard restricted non-exempt permission "                + permName + " for package " + packageName);        return;    }    if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext,            pkg.applicationInfo, UserHandle.of(userId), permName).canBeGranted()) {        Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package "                + packageName);        return;    }    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) !=                PERMISSION_OPERATION_FAILURE) {            if (callback != null) {                callback.onInstallPermissionGranted();            }        }        return;    }    if (ps.getInstantApp(userId) && !bp.isInstant()) {        throw new SecurityException("Cannot grant non-ephemeral permission"                + permName + " for package " + packageName);    }    if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {        Slog.w(TAG, "Cannot grant runtime permission to a legacy app");        return;    }    final int result = permissionsState.grantRuntimePermission(bp, userId);    switch (result) {        case PERMISSION_OPERATION_FAILURE: {            return;        }        case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {            if (callback != null) {                callback.onGidsChanged(UserHandle.getAppId(pkg.applicationInfo.uid), userId);            }        }        break;    }    if (bp.isRuntime()) {        logPermission(MetricsEvent.ACTION_PERMISSION_GRANTED, permName, packageName);    }    if (callback != null) {        callback.onPermissionGranted(uid, userId);    }    if (bp.isRuntime()) {        notifyRuntimePermissionStateChanged(packageName, userId);    }    // Only need to do this if user is initialized. Otherwise it&#39;s a new user    // and there are no processes running as the user yet and there&#39;s no need    // to make an expensive call to remount processes for the changed permissions.    if (READ_EXTERNAL_STORAGE.equals(permName)            || WRITE_EXTERNAL_STORAGE.equals(permName)) {        final long token = Binder.clearCallingIdentity();        try {            if (mUserManagerInt.isUserInitialized(userId)) {                StorageManagerInternal storageManagerInternal = LocalServices.getService(                        StorageManagerInternal.class);                storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);            }        } finally {            Binder.restoreCallingIdentity(token);        }    }}
复制代码
2.特定shareUID属性

在PMS的构造方法中,对sharedUserId和UID进行了映射,最终通过PMS来完成:剖析应用信息、各组件信息、权限信息,分配UID、记录组件信息,更新、授予、生存权限信息。
  1. //frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.javapublic 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);    mSettings.addSharedUserLPw("android.uid.se", SE_UID,            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);    mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,            ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);    // 省略部门代码}
复制代码
3.default permission设置

DefaultPermissionGrantPolicy.grantDefaultPermissions(...):最终也是通过PMS来完成默认权限的管理,在颠末一些列判断后,调用关键方法grantRuntimePermission(...)和updatePermissionFlags(...)。
  1. //frameworks/base/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.javapublic void grantDefaultPermissions(int userId) {    grantPermissionsToSysComponentsAndPrivApps(userId);    grantDefaultSystemHandlerPermissions(userId);    grantDefaultPermissionExceptions(userId);    synchronized (mLock) {        mDefaultPermissionsGrantedUsers.put(userId, userId);    }}private void grantRuntimePermissions(PackageInfo pkg, Set permissionsWithoutSplits,        boolean systemFixed, boolean ignoreSystemPackage,        boolean whitelistRestrictedPermissions, int userId) {    UserHandle user = UserHandle.of(userId);    if (pkg == null) {        return;    }    String[] requestedPermissions = pkg.requestedPermissions;    if (ArrayUtils.isEmpty(requestedPermissions)) {        return;    }    // Intersect the requestedPermissions for a factory image with that of its current update    // in case the latter one removed a     String[] requestedByNonSystemPackage = getPackageInfo(pkg.packageName).requestedPermissions;    int size = requestedPermissions.length;    for (int i = 0; i < size; i++) {        if (!ArrayUtils.contains(requestedByNonSystemPackage, requestedPermissions[i])) {            requestedPermissions[i] = null;        }    }    requestedPermissions = ArrayUtils.filterNotNull(requestedPermissions, String[]::new);    PackageManager pm;    try {        pm = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,                user).getPackageManager();    } catch (NameNotFoundException doesNotHappen) {        throw new IllegalStateException(doesNotHappen);    }    final ArraySet permissions = new ArraySet(permissionsWithoutSplits);    ApplicationInfo applicationInfo = pkg.applicationInfo;    int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;    if (systemFixed) {        newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;    }    // Automatically attempt to grant split permissions to older APKs    final List splitPermissions =            mContext.getSystemService(PermissionManager.class).getSplitPermissions();    final int numSplitPerms = splitPermissions.size();    for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {        final PermissionManager.SplitPermissionInfo splitPerm =                splitPermissions.get(splitPermNum);        if (applicationInfo != null                && applicationInfo.targetSdkVersion < splitPerm.getTargetSdk()                && permissionsWithoutSplits.contains(splitPerm.getSplitPermission())) {            permissions.addAll(splitPerm.getNewPermissions());        }    }    Set grantablePermissions = null;    // In some cases, like for the Phone or SMS app, we grant permissions regardless    // of if the version on the system image declares the permission as used since    // selecting the app as the default for that function the user makes a deliberate    // choice to grant this app the permissions needed to function. For all other    // apps, (default grants on first boot and user creation) we don&#39;t grant default    // permissions if the version on the system image does not declare them.    if (!ignoreSystemPackage            && applicationInfo != null            && applicationInfo.isUpdatedSystemApp()) {        final PackageInfo disabledPkg = getSystemPackageInfo(                mServiceInternal.getDisabledSystemPackageName(pkg.packageName));        if (disabledPkg != null) {            if (ArrayUtils.isEmpty(disabledPkg.requestedPermissions)) {                return;            }            if (!Arrays.equals(requestedPermissions, disabledPkg.requestedPermissions)) {                grantablePermissions = new ArraySet(Arrays.asList(requestedPermissions));                requestedPermissions = disabledPkg.requestedPermissions;            }        }    }    final int numRequestedPermissions = requestedPermissions.length;    // Sort requested permissions so that all permissions that are a foreground permission (i.e.    // permissions that have a background permission) are before their background permissions.    final String[] sortedRequestedPermissions = new String[numRequestedPermissions];    int numForeground = 0;    int numOther = 0;    for (int i = 0; i < numRequestedPermissions; i++) {        String permission = requestedPermissions[i];        if (getBackgroundPermission(permission) != null) {            sortedRequestedPermissions[numForeground] = permission;            numForeground++;        } else {            sortedRequestedPermissions[numRequestedPermissions - 1 - numOther] =                    permission;            numOther++;        }    }    for (int requestedPermissionNum = 0; requestedPermissionNum < numRequestedPermissions;            requestedPermissionNum++) {        String permission = requestedPermissions[requestedPermissionNum];        // If there is a disabled system app it may request a permission the updated        // version ot the data partition doesn&#39;t, In this case skip the permission.        if (grantablePermissions != null && !grantablePermissions.contains(permission)) {            continue;        }        if (permissions.contains(permission)) {            final int flags = mContext.getPackageManager().getPermissionFlags(                    permission, pkg.packageName, user);            // If we are trying to grant as system fixed and already system fixed            // then the system can change the system fixed grant state.            final boolean changingGrantForSystemFixed = systemFixed                    && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0;            // Certain flags imply that the permission&#39;s current state by the system or            // device/profile owner or the user. In these cases we do not want to clobber the            // current state.            //            // Unless the caller wants to override user choices. The override is            // to make sure we can grant the needed permission to the default            // sms and phone apps after the user chooses this in the UI.            if (!isFixedOrUserSet(flags) || ignoreSystemPackage                    || changingGrantForSystemFixed) {                // Never clobber policy fixed permissions.                // We must allow the grant of a system-fixed permission because                // system-fixed is sticky, but the permission itself may be revoked.                if ((flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {                    continue;                }                // Preserve whitelisting flags.                newFlags |= (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT);                // If we are whitelisting the permission, update the exempt flag before grant.                if (whitelistRestrictedPermissions && isPermissionRestricted(permission)) {                    mContext.getPackageManager().updatePermissionFlags(permission,                            pkg.packageName,                            PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT,                            PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, user);                }                // If the system tries to change a system fixed permission from one fixed                // state to another we need to drop the fixed flag to allow the grant.                if (changingGrantForSystemFixed) {                    mContext.getPackageManager().updatePermissionFlags(permission,                            pkg.packageName, flags,                            flags & ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, user);                }                if (pm.checkPermission(permission, pkg.packageName)                        != PackageManager.PERMISSION_GRANTED) {                    mContext.getPackageManager()                            .grantRuntimePermission(pkg.packageName, permission, user);                }                mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,                        newFlags, newFlags, user);                int uid = UserHandle.getUid(userId,                        UserHandle.getAppId(pkg.applicationInfo.uid));                List fgPerms = mPermissionManager.getBackgroundPermissions()                        .get(permission);                if (fgPerms != null) {                    int numFgPerms = fgPerms.size();                    for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) {                        String fgPerm = fgPerms.get(fgPermNum);                        if (pm.checkPermission(fgPerm, pkg.packageName)                                == PackageManager.PERMISSION_GRANTED) {                            // Upgrade the app-op state of the fg permission to allow bg access                            // TODO: Dont&#39; call app ops from package manager code.                            mContext.getSystemService(AppOpsManager.class).setUidMode(                                    AppOpsManager.permissionToOp(fgPerm), uid,                                    AppOpsManager.MODE_ALLOWED);                            break;                        }                    }                }                String bgPerm = getBackgroundPermission(permission);                String op = AppOpsManager.permissionToOp(permission);                if (bgPerm == null) {                    if (op != null) {                        // TODO: Dont&#39; call app ops from package manager code.                        mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid,                                AppOpsManager.MODE_ALLOWED);                    }                } else {                    int mode;                    if (pm.checkPermission(bgPerm, pkg.packageName)                            == PackageManager.PERMISSION_GRANTED) {                        mode = AppOpsManager.MODE_ALLOWED;                    } else {                        mode = AppOpsManager.MODE_FOREGROUND;                    }                    mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid, mode);                }                if (DEBUG) {                    Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")                            + permission + " to default handler " + pkg);                    int appOp = AppOpsManager.permissionToOpCode(permission);                    if (appOp != AppOpsManager.OP_NONE                            && AppOpsManager.opToDefaultMode(appOp)                                    != AppOpsManager.MODE_ALLOWED) {                        // Permission has a corresponding appop which is not allowed by default                        // We must allow it as well, as it&#39;s usually checked alongside the                        // permission                        if (DEBUG) {                            Log.i(TAG, "Granting OP_" + AppOpsManager.opToName(appOp)                                    + " to " + pkg.packageName);                        }                        mContext.getSystemService(AppOpsManager.class).setUidMode(                                appOp, pkg.applicationInfo.uid, AppOpsManager.MODE_ALLOWED);                    }                }            }            // If a component gets a permission for being the default handler A            // and also default handler B, we grant the weaker grant form.            if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0                    && (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0                    && !systemFixed) {                if (DEBUG) {                    Log.i(TAG, "Granted not fixed " + permission + " to default handler "                            + pkg);                }                mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,                        PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, user);            }        }    }}
复制代码
 

参考:
Android 权限的一些细节
深入浅析Android动态权限的机制
Android O 默认授予预制应用运行时权限方法
PackageManagerService处置惩罚应用权限流程

来源:https://blog.csdn.net/ecjtuhq/article/details/111349986
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


专注素材教程免费分享
全国免费热线电话

18768367769

周一至周日9:00-23:00

反馈建议

27428564@qq.com 在线QQ咨询

扫描二维码关注我们

Powered by Discuz! X3.4© 2001-2013 Comsenz Inc.( 蜀ICP备2021001884号-1 )