• Android权限机制(二) 权限控制的设计


    本文将会深入Framework层了解Android权限机制是如何起作用的。

    由于Android尚未引入权限控制功能,我们将会讨论如何修改Android代码来达到权限控制的目的,以及一些解决方案。

    一、调用需要权限的API

    我们将通过一个API调用的例子来了解Android系统是如何判断权限的:

    ContentResolver resolver = getContentResolver();
    Cursor cur = resolver.query(  
            ContactsContract.Contacts.CONTENT_URI,  
            null,  
            null,  
            null,  
            ContactsContract.Contacts.DISPLAY_NAME  
                    + " COLLATE LOCALIZED ASC");

    以上代码用于读取联系人,所以需要在AndroidManifest中加上:

    <uses-permission android:name="android.permission.READ_CONTACTS" />
    

      

    OK,开始通过时序图一探究竟。

    /frameworks/base/services/java/com/android/server/pm/PackageManagerService.java

    PackageManagerService.checkUidPermission(String, int):

    public int checkUidPermission(String permName, int uid) {
        final boolean enforcedDefault = isPermissionEnforcedDefault(permName);
        synchronized (mPackages) {
            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
            if (obj != null) {
                GrantedPermissions gp = (GrantedPermissions)obj;
                // 判断是否具有该权限
                if (gp.grantedPermissions.contains(permName)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            } else {
                HashSet<String> perms = mSystemPermissions.get(uid);
                if (perms != null && perms.contains(permName)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            }
            if (!isPermissionEnforcedLocked(permName, enforcedDefault)) {
                return PackageManager.PERMISSION_GRANTED;
            }
        }
        return PackageManager.PERMISSION_DENIED;
    }
    

      

    我们可以从“Android权限机制(一) 权限申请” -> “四、两个数据结构:PackageParser.Package和Settings.mUserIds” -> “2. Settings.mUserIds”得知权限信息是如何被存放进去的。

    二、检查是否具有权限

    因此,调用需要权限的API时候,部分会通过Context.checkPermission(String permission, int pid, int uid)来查询该app进程是否声明了对应的permission。
    最后调用到PackageManagerService的checkUidPermission()(检查大部分permissions)或checkPermission()(如RECEIVE_BOOT_COMPLETED,ACCESS_ALL_EXTERNAL_STORAGE)。
    如果缺乏权限的话,最终会返回PackageManager.PERMISSION_DENIED,于是调用API处根据判断会抛出异常,停止执行。

    有人可能会问:为什么从Context.checkPermission()到最终的PackageManagerService.checkUidPermission(),中间要经过那么多个类?
    这是为了先通过pid来判断,如果不符合条件就会直接返回PackageManager.PERMISSION_DENIED,节省时间。

    三、如何在安装后控制app的权限(修改Framework代码)

    根据以上的代码分析,我们可以得知大部分permission的查询都会经过

    ActivityManagerService.checkComponentPermission() -> ActivityManager.checkComponentPermission() ->
    PackageManagerService.checkUidPermission()
    的流程,那么我们便可以在这些地方插入我们想要的权限控制代码(以下称为BlockedPermission)。

    1. BlockedPermission的保存

    根据“Android权限机制(一)”的分析我们得知,保存uid/package的permission的数据结构是在PKMS当中,那么为了方便,可以把BlockedPermission的数据结构放在这里面。
    为了简单,可以创建一个HashMap<string, string="">,两个string分别对应permission和package。(当然这样就不如前面以uid为下标的ArrayList高效,但实际实践中发现没什么影响。)

    由于HashMap只能在PKMS运行时存在,所以需要保存到文件系统中。可以采用XML的方式(如Framework内部提供的FastXmlSerializer),方便备份和恢复。

    备份时间:每一次更新BlockedPermission的时候备份;
    恢复时间:PKMS构造的时候。
    XML存放位置:具有系统权限的目录下,如/data/system/

    2. API的设计

    写:

    PKMS需要向App层开放写BlockedPermission的API,如writeBlockedPermission()方法,那么除了在PackageManagerService中实现这个方法之外,还要在IPckageManager.aidl中声明。

    读:

    同样在PKMS中实现方法readBlockedPermission(),通过对permission和package的查询返回是否被block。

    安全检查:

    对于写API而言,只能允许被特定的system app调用,所以需要对调用进程的pid和uid做检查。

    3. API的调用

    写API的调用:

    在完成的Framework的改进之后,需要在App层编写具有UI的system app,方便用户直观地控制每个app的权限。注意system app需要platform的signature。

    读API的调用:

    通过前面的permisssion查询流程,我们可以选择在AMS,AM和PKMS中调用API检查是否需要拦截。为了在API拦截之后做好错误处理,最好在AMS中调用。
    在返回PackageManager.PERMISSION_DENIED之后,Framework往往会抛出SecurityException。但是大部分第三方app并不会去捕获这个异常,所以会导致app crash。

    当app crash时,AMS的crashApplication()将会被调用,为了将BlockedPermission与普通的缺乏permission情况区分出来,所以需要在调用读BlockedPermission的API之后留下足够的信息,这也就是为什么把读API放在AMS的原因。

    四、其他方法

    to be continued...

  • 相关阅读:
    git删除远程tag
    date的getTime问题
    EasyExcel读取excel文件反射成实体后全为NULL
    springboot回滚部分异常
    Java8 LocalDate、Date、LocalDateTime、时间戳的转换
    mysql未查到行,返回一条默认结果
    Maven No archetype found in remote catalog. Defaulting to internal catalog.
    HashMap相关资料
    HibernateHql
    用户名登录
  • 原文地址:https://www.cnblogs.com/jacobchen/p/3828588.html
Copyright © 2020-2023  润新知