Android 应用权限导致系统无法开机

一、问题发生的现象及日志打印

1.1 问题发生现象

  开机向导中增加蓝牙、WIFI及设置时区的权限后,adb push到手机里面,重启手机无法开机,卡到开机动画界面,无法进入开机向导。

1.2 Log打印分析

​  从日志看应该是应用本身是priv级别的app,并且申请了BLUETOOTH_PRIVILEGE、WRITE_SECURE_SETTINGS、INTERACT_ACROSS_USERS和SET_TIME_ZONE,但是没有在权限白名单中添加,所以导致类似日志打印,最终会导致system server重启,系统无法开机。

11-10 07:03:49.084  2392  2392 W PackageManager: Privileged permission android.permission.BLUETOOTH_PRIVILEGED for package com.dten.setupwiard - not in privapp-permissions whitelist
11-10 07:03:49.085  2392  2392 W PackageManager: Privileged permission android.permission.WRITE_SECURE_SETTINGS for package com.dten.setupwiard - not in privapp-permissions whitelist
11-10 07:03:49.085  2392  2392 W PackageManager: Privileged permission android.permission.INTERACT_ACROSS_USERS for package com.dten.setupwiard - not in privapp-permissions whitelist
11-10 07:03:49.085  2392  2392 W PackageManager: Privileged permission android.permission.SET_TIME_ZONE for package com.dten.setupwiard - not in privapp-permissions whitelist


//  进而会有如下日志打印,导致system server进程重启
10 06:13:08.174  2645  2645 E System  : ******************************************
11-10 06:13:08.177  2645  2645 E System  : ************ Failure starting system services
11-10 06:13:08.177  2645  2645 E System  : java.lang.IllegalStateException: Signature|privileged permissions not in privapp-permissions whitelist: {com.dten.setupwiard: android.permission.WRITE_SECURE_SETTINGS, com.dten.setupwiard: android.permission.INTERACT_ACROSS_USER
S, com.dten.setupwiard: android.permission.BLUETOOTH_PRIVILEGED, com.dten.setupwiard: android.permission.SET_TIME_ZONE}
11-10 06:13:08.177  2645  2645 E System  :      at com.android.server.pm.permission.PermissionManagerService.systemReady(PermissionManagerService.java:2956)
11-10 06:13:08.177  2645  2645 E System  :      at com.android.server.pm.permission.PermissionManagerService.access$100(PermissionManagerService.java:122)
11-10 06:13:08.177  2645  2645 E System  :      at com.android.server.pm.permission.PermissionManagerService$PermissionManagerServiceInternalImpl.systemReady(PermissionManagerService.java:3017)
11-10 06:13:08.177  2645  2645 E System  :      at com.android.server.pm.PackageManagerService.systemReady(PackageManagerService.java:21902)
11-10 06:13:08.177  2645  2645 E System  :      at com.android.server.SystemServer.startOtherServices(SystemServer.java:2096)
11-10 06:13:08.177  2645  2645 E System  :      at com.android.server.SystemServer.run(SystemServer.java:539)
11-10 06:13:08.177  2645  2645 E System  :      at com.android.server.SystemServer.main(SystemServer.java:367)
11-10 06:13:08.177  2645  2645 E System  :      at java.lang.reflect.Method.invoke(Native Method)
11-10 06:13:08.177  2645  2645 E System  :      at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
11-10 06:13:08.177  2645  2645 E System  :      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1050)

二、代码逻辑分析

​  根据报错日志定位到代码逻辑是PermissionManagerService.java(frameworks\base\services\core\java\com\android\server\pm\permission)中的grantSignaturePermission()方法

private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,         
        BasePermission bp, PermissionsState origPermissions) {
        .....
       //可以看到,如果该pkg对应的perm不在白名单中,则会下面加入过denied的列表中,导致下面的not in priv的日志打印
        if (!hasPrivappWhitelistEntry(perm, pkg)) {                                       
            // Only report violations for apps on system image                           
            if (!mSystemReady && !pkg.isUpdatedSystemApp()) {                             
                // it's only a reportable violation if the permission isn't explicitly denied          
                ArraySet<String> deniedPermissions = null;                               
                if (pkg.isVendor()) {                                                     
                    deniedPermissions = SystemConfig.getInstance()                       
                            .getVendorPrivAppDenyPermissions(pkg.packageName);           
                } else if (pkg.isProduct()) {                                             
                    deniedPermissions = SystemConfig.getInstance()                       
                            .getProductPrivAppDenyPermissions(pkg.packageName);           
                } else if (pkg.isProductServices()) {                                     
                    deniedPermissions = SystemConfig.getInstance()                       
                            .getProductServicesPrivAppDenyPermissions(pkg.packageName);   
                } else {                                                                 
                    deniedPermissions = SystemConfig.getInstance()                       
                            .getPrivAppDenyPermissions(pkg.packageName);                 
                }                                                                         
                final boolean permissionViolation =                                       
                        deniedPermissions == null || !deniedPermissions.contains(perm);                
                if (permissionViolation) {                                               
                    Slog.w(TAG, "Privileged permission " + perm + " for package "         
                            + pkg.packageName + " - not in privapp-permissions whitelist");                                                                                 
                    if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {         
                        if (mPrivappPermissionsViolations == null) {                     
                            mPrivappPermissionsViolations = new ArraySet<>();             
                        }                                                                 
                        mPrivappPermissionsViolations.add(pkg.packageName + ": " + perm); 
                    }                                                                     
                } else {                                                                 
                    return false;                                                         
                }                                                                         
            }                                                                             
            if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {                
                return false;                                                             
            }                                                                             
        }                                                                                 
    }                                                                                     
     ......       
 }

由于上面条件,如果对应应用的perm不再权限白名单中,则会增加到deniedPermission中,后面在systemready中会抛出异常,导致system server进程重启。

 private void systemReady() {      
     mSystemReady = true;                                                                 
     if (mPrivappPermissionsViolations != null) {                                        
         throw new IllegalStateException("Signature|privileged permissions not in "               
                 + "privapp-permissions whitelist: " + mPrivappPermissionsViolations);            
     }                                                                                                                                                             
     mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); 
     mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class);        
 }                                                                                                

​ 所以问题的关键是hasPrivappWhitelistEntry方法,我们重点看下这个方法,该方法中如果是product下则权限白名单是从通过SystemConfig类中getProductPrivAppPermissions方法。

 private boolean hasPrivappWhitelistEntry(String perm, PackageParser.Package pkg) {       
     ArraySet<String> wlPermissions = null;
     ....
     if (pkg.isProduct()) {                                                          
         wlPermissions = SystemConfig.getInstance().getProductPrivAppPermissions(pkg.packageName);  
     } 
     ....
     // Let's check if this package is whitelisted...                                     
     boolean whitelisted = wlPermissions != null && wlPermissions.contains(perm);      
     // If it's not, we'll also tail-recurse to the parent.                               
     return whitelisted ||                                                               
     pkg.parentPackage != null && hasPrivappWhitelistEntry(perm, pkg.parentPackage);
 }   

SystemConfig的getProductPrivAppPermissions方法很简单,只是返回mProductPrivAppPermissions 数据类型,是一个ArrayMap的数据结构,以应用包名为key,存储应用的priv-app 的权限白名单。

final ArrayMap<String, ArraySet<String>> mProductPrivAppPermissions = new ArrayMap<>(); 

​ 到目前为止,逻辑就很简单了,重点看下mProductPrivAppPermissions是如何添加元素即可,根据代码逻辑,mProductPrivAppPermissions是通过系统在开机过程中读取/product/etc/permissions/ 、/vendor/etc/permissions/ 、/system/etc/permissions/ 下的xml文件所得。因此我们需要给开机向导应用增加对应的权限白名单。

三、解决方案思路

  根据第二块的代码分析,解决思路就比较清楚了,关键是给开机向导app增加权限白名单。

3.1 第一种尝试

  在privapp-permissions-platform.xml(frameworks/base/data/etc/)中增加如下代码,进行验证,adb push 到手机中进行验证,发现问题并没没有解决。

<permissions>
    <privapp-permissions package="com.dten.setupwiard">
        <permission name="android.permission.SET_TIME_ZONE"/>
        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
    </privapp-permissions>
</permissions>

进一步分析代码,可以看到,不同目录下的应用对应的权限文件路径是不一样的,privapp-permissions-platform.xml文件被编译到/system/etc/permissions/下,所以只对/system/priv-app/下的应用有效果。而决定xml文件编译到那个路径下,是在frameworks/base/data/etc/Andoid.bp来决定的。

prebuilt_etc {                               
    name: "privapp-permissions-platform.xml",
    sub_dir: "permissions",                  
    src: "privapp-permissions-platform.xml", 
}                                            

prebuilt_etc {                                      
    name: "privapp_whitelist_com.android.settings", 
    product_specific: true,                         
    sub_dir: "permissions",                         
    src: "com.android.settings.xml",                
    filename_from_src: true,                        
}                                        

3.2 第二种尝试

  根据第一种尝试的分析,可以仿照com.dten.setting,我们创建一个com.dten.setupwiard.xml,并且在Android.bp中增加如下内容。

prebuilt_etc {                                     
    name: "privapp_whitelist_com.dten.setupwiard", 
    product_specific: true,                        
    sub_dir: "permissions",                        
    src: "com.dten.setupwiard.xml",                
    filename_from_src: true,                       
}   

如果觉得现在就ok了,可能会空欢喜一场,因为编译完,/product/etc/permissions/下面并没有生产com.dten.setupwiard.xml文件,根据android的编译规则,以及参考com.android.settings,应该是没有将其拷贝到out目录,所以需要在编译对应模块,比如编译com.dten.setupwiard的Android.mk文件中增加如下编译选项。

LOCAL_REQUIRED_MODULES := privapp_whitelist_com.dten.setupwiard

LOCAL_REQUIRED_MODULES的作用是当编译整个android 源码时,会将Mms模块在编译路径中依赖的module编译到out目录,并且打包,但是单独编译模块不会拷贝打包。

最后,编译完,烧录到设备中,可以成功开机。问题得到解决。

四、验证问题

​ 验证问题主要有两种方式:

  • 编译烧录版本进行验证,该方法比较耗时,但是相对准确,因为最终的方案还是需要编译到版本中验证
  • 快速替换验证,使用adb 命令进行快速验证,前提是userdebug版本,并且Android 10.0以后,必须是非system目录下的文件,因为system目录,没法remount

五、参考文献

  • https://zhuanlan.zhihu.com/p/147668734
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GitFranc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值