Android9.0 如何实现动态权限白名单

前言

此次使用Android9.0做一个动态权限白名单的功能,即客户要求他们的app默认就该应用所有的权限,在运行过程中,不需要去动态申请权限,即不弹窗。

在Android M之前, Runtime permissions是直接被当作是install permissons,即在安装的时候就直接grant了。

而在 Android M以后, Android加入了runtime permissions, 也就是dangerous permissons, 这些权限有可能会刺探用户隐私等等危害。

这样系统在安装APP的时候就不会默认grant runtime permissions.

修改思路

申请时,拒绝申请

一开始想着在应用提出动态申请的请求时,就将该请求掐断,这样就不会弹窗出来,即在如下方法中直接返回:

frameworks/base/core/java/android/app/Activity.java
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
    
    return;  //在这里直接return
    if (requestCode < 0) {
        throw new IllegalArgumentException("requestCode should be >= 0");
    }
    if (mHasCurrentPermissionsRequest) {
        Log.w(TAG, "Can request only one set of permissions at a time");
        // Dispatch the callback with empty arrays which means a cancellation.
        onRequestPermissionsResult(requestCode, new String[0], new int[0]);
        return;
    }
    Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
    startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
    mHasCurrentPermissionsRequest = true;
}

这样做,确实不会弹窗,但其实应用并没有获取到想要的动态权限,此路不通。

在check权限时直接返回授权

这里是在检测app权限的时候,直接给app返回授权,但其实这里也没有真正的授权,是一种假授权。

从update permission入手

对Android来说,既然有动态权限的申请,那么就会有一个地方用于处理权限更新的地方,这次决定从这里入手。

通过强大的grep指令,在frameworks目录下进行搜索,最终定位到updatePermission方法中,该方法用于更新应用权限,该方法最终调用了grantPermissions方法,源码如下:

//这里由于该方法太长,只贴出部分的上下文代码
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private void grantPermissions(PackageParser.Package pkg, boolean replace,
    String packageOfInterest, PermissionCallback callback) {
    ......
    synchronized (mLock) {
    final int N = pkg.requestedPermissions.size();
    for (int i = 0; i < N; i++) {
        final String permName = pkg.requestedPermissions.get(i);
        final BasePermission bp = mSettings.getPermissionLocked(permName);
        final boolean appSupportsRuntimePermissions =
                pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;

        if (DEBUG_INSTALL) {
            Log.i(TAG, "Package " + pkg.packageName + " checking " + permName + ": " + bp);
        }

        if (bp == null || bp.getSourcePackageSetting() == null) {
            if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
                if (DEBUG_PERMISSIONS) {
                    Slog.i(TAG, "Unknown permission " + permName
                            + " in package " + pkg.packageName);
                }
            }
            continue;
        }

        // Limit ephemeral apps to ephemeral allowed permissions.
        if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
            if (DEBUG_PERMISSIONS) {
                Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
                        + " for package " + pkg.packageName);
            }
            continue;
        }

        if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
            if (DEBUG_PERMISSIONS) {
                Log.i(TAG, "Denying runtime-only permission " + bp.getName()
                        + " for package " + pkg.packageName);
            }
            continue;
        }

        final String perm = bp.getName();
        boolean allowedSig = false;
        int grant = GRANT_DENIED;

        // Keep track of app op permissions.
        if (bp.isAppOp()) {
            mSettings.addAppOpPackage(perm, pkg.packageName);
        }

        if (bp.isNormal()) {
            // For all apps normal permissions are install time ones.
            grant = GRANT_INSTALL;
        } else if (bp.isRuntime()) {
            // 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 (!appSupportsRuntimePermissions && !mSettings.mPermissionReviewRequired) {
                // For legacy apps dangerous permissions are install time ones.
                grant = GRANT_INSTALL;
            } else if (origPermissions.hasInstallPermission(bp.getName())) {
                // For legacy apps that became modern, install becomes runtime.
                grant = GRANT_UPGRADE;
            } else if (isLegacySystemApp) {
                // 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.
                //permission check whitelist app @{
                if (!isneedCheckPermission(pkg.packageName)) {
                    grant = GRANT_INSTALL;
                } else {
                    grant = GRANT_RUNTIME;
                }
                //permission check whitelist app @}
            }
        } else if (bp.isSignature()) {
            // For all apps signature permissions are install time ones.
            allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
            if (allowedSig) {
                grant = GRANT_INSTALL;
            }
        }

        ......

}

在Android6.0之前是没有动态权限这个概念的,只有在Android6.0之后才有。在Android6.0之前,对于权限,都将其进行安装,不需要进行GRANT_RUNTIME,统一都是GRANT_INSTALL,而到了Android6.0之后,则多出了一个GRANT_RUNTIME,即运行时权限,如果是normal级别的权限,则会apk安装的过程中,就直接授权,但如果是危险级别的,涉及到用户隐秘的,则需要在调用的时候,弹窗让用户选择是否授权,也就是所谓的GRANT_RUNTIME。

所以我们的方法很简单,就是找到GRANT_RUNTIME ,然后使用GRANT_INSTALL代替即可。

白名单

这里还涉及到一个白名单配置方法,主要是新增一个cfg配置文件,文件内容是每一个应用的包名,这里的包名可以不完整,但一定要有明确的关键词,避免跟其他的应用混淆。该方法如下:

frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
private boolean isneedCheckPermission(String packagename) {
    ArrayList<String> whiteListApp = new ArrayList<String>();

    try {
        BufferedReader br = new BufferedReader(new java.io.InputStreamReader(
            new FileInputStream("/system/etc/WhiteListPermissionFilter.cfg")));

        String line = "";
        while ((line = br.readLine()) != null) {
            whiteListApp.add(line);
        }

        br.close();
    } catch (java.io.FileNotFoundException ex) {
        Log.d(TAG, "WhiteListPermissionFilter.cfg - FileNotFoundException");
        return true;
    } catch(java.io.IOException ex) {
        Log.d(TAG, "WhiteListPermissionFilter.cfg - IOException");
        return true;
    }

    Iterator<String> it = whiteListApp.iterator();

    while (it.hasNext()) {
        String whitelistItem = it.next();
        Log.d(TAG, "whitelistItem:" + whitelistItem);
        if (packagename.contains(whitelistItem)) {
            return false;
        }
    }
    return true;
}

最后

若想关注更多Android系统开发相关文章,可扫码关注如下微信公众号。
在这里插入图片描述

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android 9.0引入了一种新的应用保活机制,即app进程保活名单。这个名单允许应用在后台保持运行状态,以便能够实时接收并处理重要的任务或事件。通过将应用添加到名单中,可以确保应用不会过早地被系统终止,从而提高了应用的用户体验和功能性。 要将应用添加到Android 9.0的app进程保活名单中,需要进行以下步骤: 1. 获取系统唤醒锁:应用需要获取系统唤醒锁以防止系统在特定时间内休眠。可以使用PowerManager类的newWakeLock方法来获取唤醒锁,并在不需要时及时释放锁。 2. 启用前台服务:可以将应用设置为前台服务状态,以使其在后台运行时以高优先级运行,并在通知栏中显示一个持续可见的通知。这样,即使应用在后台运行,用户仍然可以清楚地知道应用正在运行。 3. 注册广播接收器:可以注册一些系统广播的接收器,以确保应用在系统事件发生时得到通知并可以及时做出响应。比如注册ACTION_SCREEN_ON广播接收器,以在设备解锁时触发相应的操作。 4. 利用JobScheduler:JobScheduler是Android系统提供的一个调度框架,可以用于安排长期运行的任务,以便应用可以在后台执行这些任务。可以使用JobScheduler来进行任务的调度和管理,以减少应用在后台被系统终止的风险。 需要注意的是,虽然添加到app进程保活名单可以保持应用在后台的运行状态,但为了避免对系统资源的滥用,应避免长时间运行或频繁使用这些保活方法。合理使用这些保活机制,可以提升应用的性能和用户体验,但也要确保不会对设备的电池寿命和性能造成过多的损失。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值