1. 前言

在项目开发中,需求:app中有恢复出厂设置的功能,分解这个需求的时候,第一反应肯定不是第三方app,恢复出厂设置肯定需要有系统权限,属于系统级的app。然后在看手机系统中的功能,恢复出厂设置功能属于设置模块,找到源码阅读,当然是能借用的代码就拿过来直接用了。一段操作猛如虎,然后烧写版本无法开机,瞬间石化~, 怎么办,还是看log分析原因吧。

2. log分析

06-23 14:49:09.414  5472  5472 E AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: main
06-23 14:49:09.414  5472  5472 E AndroidRuntime: java.lang.IllegalStateException: Signature|privileged permissions not in privapp-permissions whitelist: {com.test.mtk: android.permission.MASTER_CLEAR}
06-23 14:49:09.414  5472  5472 E AndroidRuntime:        at com.android.server.pm.permission.PermissionManagerService.systemReady(PermissionManagerService.java:2967)
06-23 14:49:09.414  5472  5472 E AndroidRuntime:        at com.android.server.pm.permission.PermissionManagerService.access$100(PermissionManagerService.java:122)
06-23 14:49:09.414  5472  5472 E AndroidRuntime:        at com.android.server.pm.permission.PermissionManagerService$PermissionManagerServiceInternalImpl.systemReady(PermissionManagerService.java:3028)
06-23 14:49:09.414  5472  5472 E AndroidRuntime:        at com.android.server.pm.PackageManagerService.systemReady(PackageManagerService.java:21995)
06-23 14:49:09.414  5472  5472 E AndroidRuntime:        at com.android.server.SystemServer.startOtherServices(SystemServer.java:2104)
06-23 14:49:09.414  5472  5472 E AndroidRuntime:        at com.android.server.SystemServer.run(SystemServer.java:529)
06-23 14:49:09.414  5472  5472 E AndroidRuntime:        at com.android.server.SystemServer.main(SystemServer.java:366)
06-23 14:49:09.414  5472  5472 E AndroidRuntime:        at java.lang.reflect.Method.invoke(Native Method)
06-23 14:49:09.414  5472  5472 E AndroidRuntime:        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:503)
06-23 14:49:09.414  5472  5472 E AndroidRuntime:        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:914)

Signature|privileged permissions not in privapp-permissions whitelist: {com.test.mtk: android.permission.MASTER_CLEAR}

如果要恢复出厂设置,则需要在AndroidManifest.xml文件中配置

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

而且我也配置了 android:sharedUserId="android.uid.system"  表示属于系统级别的app, 但还是报这个错误, 意思是android.permission.MASTER_CLEAR此权限没有配置到 私有权限白名单中。

3. 需求分解

2.1 首先我们来看设置模块恢复出厂设置的代码:

# packages/apps/Settings/src/com/android/settings/MasterClearConfirm.javaprivate void doMasterClear() {Intent intent = new Intent(Intent.ACTION_FACTORY_RESET);intent.setPackage("android");intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);intent.putExtra(Intent.EXTRA_REASON, "MasterClearConfirm");intent.putExtra(Intent.EXTRA_WIPE_EXTERNAL_STORAGE, mEraseSdCard);intent.putExtra(Intent.EXTRA_WIPE_ESIMS, mEraseEsims);getActivity().sendBroadcast(intent);}

就是发送一个带有Intent.ACTION_FACTORY_RESET 属性的广播给系统, 系统服务端frameworks/base/services/core/java/com/android/server/MasterClearReceiver.java 接收到广播后做恢复出厂的任务,大致是这么一个流程。

2.2 接下来我们在看看Intent.ACTION_FACTORY_RESET 属性的定义

 /*** A broadcast action to trigger a factory reset.** <p>The sender must hold the {@link android.Manifest.permission#MASTER_CLEAR} permission. The* reason for the factory reset should be specified as {@link #EXTRA_REASON}.** <p>Not for use by third-party applications.** @see #EXTRA_FORCE_FACTORY_RESET** {@hide}*/@SystemApi@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)public static final String ACTION_FACTORY_RESET = "android.intent.action.FACTORY_RESET";

看注释我们知道,使用此action 必须要申请MASTER_CLEAR权限,不能用于第三方app。 好接下来我们去看看MASTER_CLEAR权限定义的地方

2.3 所有权限定义的地方在 frameworks/base/core/res/AndroidManifest.xml 文件中

    <!-- Not for use by third-party applications. --><permission android:name="android.permission.MASTER_CLEAR"android:protectionLevel="signature|privileged" />

从定义的地方,此权限的保护级别为  signature|privileged ,第一点需要有系统签名, 第二点需要放置到手机 */priv-app/ 分区下,才可以正常使用该权限。通过这两点,我们就知道自己的app要放置到手机的哪个分区了。我工程的Android.mk 文件如下:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES := $(call all-java-files-under, src)LOCAL_MANIFEST_FILE := AndroidManifest.xmlLOCAL_RESOURCE_DIR := $(LOCAL_PATH)/resLOCAL_ASSET_DIR := $(LOCAL_PATH)/assetsLOCAL_PACKAGE_NAME := TestApp#app编译生成路径:product/priv-app
LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT)/priv-appLOCAL_PRIVILEGED_MODULE := trueLOCAL_PRIVATE_PLATFORM_APIS := true#系统签名
LOCAL_CERTIFICATE := platformLOCAL_STATIC_ANDROID_LIBRARIES := \androidx.core_core \androidx.appcompat_appcompatinclude $(BUILD_PACKAGE)include $(call all-makefiles-under,$(LOCAL_PATH))

把工程代码放到系统源码中编译,最后app生成路径为:  product/priv-app/TestApp/

4. 源码看根因

3.1 好了接下来,我们通过log找到源码报错的地方

#/frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.javaprivate void systemReady() {mSystemReady = true;// mPrivappPermissionsViolations 不为空,就抛出异常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);}

通过log查看堆栈  或者 Android studio 断点调试   流程图如下:

SystemServer,  PMS , PermissionManagerService 都是系统核心的服务,当走到PermissionManagerService.SystemReady()方法这里的时候,直接抛出一个异常,导致系统没有正常启动,就无法顺利开机。

3.2 我们在看看判断条件  mPrivappPermissionsViolations != null 时才会抛出异常,接下来继续分析mPrivappPermissionsViolations是在哪里赋值的

#/frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.javaprivate boolean grantSignaturePermission(String perm, PackageParser.Package pkg,BasePermission bp, PermissionsState origPermissions) {boolean oemPermission = bp.isOEM();boolean vendorPrivilegedPermission = bp.isVendorPrivileged();boolean privilegedPermission = bp.isPrivileged() || bp.isVendorPrivileged();boolean privappPermissionsDisable =RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE;boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName());boolean platformPackage = PLATFORM_PACKAGE_NAME.equals(pkg.packageName);if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivileged()&& !platformPackage && platformPermission) {if (!hasPrivappWhitelistEntry(perm, pkg)) {// Only report violations for apps on system imageif (!mSystemReady && !pkg.isUpdatedSystemApp()) {// it's only a reportable violation if the permission isn't explicitly deniedArraySet<String> deniedPermissions = null;if (pkg.isVendor()) {deniedPermissions = SystemConfig.getInstance().getVendorPrivAppDenyPermissions(pkg.packageName);} else if (pkg.isProduct()) {#走这里的分支  deniedPermissions 值为nulldeniedPermissions = SystemConfig.getInstance().getProductPrivAppDenyPermissions(pkg.packageName);} else if (pkg.isProductServices()) {deniedPermissions = SystemConfig.getInstance().getProductServicesPrivAppDenyPermissions(pkg.packageName);} else {deniedPermissions = SystemConfig.getInstance().getPrivAppDenyPermissions(pkg.packageName);}#通过打印log: deniedPermissions == null 为true, 说明权限白名单中没有配置MASTER_CLEAR权限,这里是问题的原因。final boolean permissionViolation =deniedPermissions == null || !deniedPermissions.contains(perm);#这里的判断条件为true  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有值,不为空mPrivappPermissionsViolations.add(pkg.packageName + ": " + perm);}} else {return false;}}

我把app放置到product/priv-app 路径下,所以走的逻辑为:

else if (pkg.isProduct()) {deniedPermissions = SystemConfig.getInstance().getProductPrivAppDenyPermissions(pkg.packageName);
}

继续跟踪代码,找到SystemConfig.java 中的 getProductPrivAppDenyPermissions 方法

public ArraySet<String> getProductPrivAppDenyPermissions(String packageName) {return mProductPrivAppDenyPermissions.get(packageName);}

接下来,我们继续看mProductPrivAppDenyPermissions变量是怎么赋值的, 我们回到SystemConfig.java  的构造方法:

#frameworks/base/core/java/com/android/server/SystemConfig.javaSystemConfig() {// Read configuration from systemreadPermissions(Environment.buildPath(Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);// Read configuration from the old permissions dirreadPermissions(Environment.buildPath(Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);// Vendors are only allowed to customize theseint vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS| ALLOW_ASSOCIATIONS;if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {// For backward compatibilityvendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);}readPermissions(Environment.buildPath(Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);readPermissions(Environment.buildPath(Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);// Allow ODM to customize system configs as much as Vendor, because /odm is another// vendor partition other than /vendor.int odmPermissionFlag = vendorPermissionFlag;readPermissions(Environment.buildPath(Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);readPermissions(Environment.buildPath(Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);String skuProperty = SystemProperties.get(SKU_PROPERTY, "");if (!skuProperty.isEmpty()) {String skuDir = "sku_" + skuProperty;readPermissions(Environment.buildPath(Environment.getOdmDirectory(), "etc", "sysconfig", skuDir), odmPermissionFlag);readPermissions(Environment.buildPath(Environment.getOdmDirectory(), "etc", "permissions", skuDir),odmPermissionFlag);}// Allow OEM to customize theseint oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS;readPermissions(Environment.buildPath(Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);readPermissions(Environment.buildPath(Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag);// Allow Product to customize all system configsreadPermissions(Environment.buildPath(Environment.getProductDirectory(), "etc", "sysconfig"), ALLOW_ALL);readPermissions(Environment.buildPath(Environment.getProductDirectory(), "etc", "permissions"), ALLOW_ALL);// Allow /product_services to customize all system configsreadPermissions(Environment.buildPath(Environment.getProductServicesDirectory(), "etc", "sysconfig"), ALLOW_ALL);readPermissions(Environment.buildPath(Environment.getProductServicesDirectory(), "etc", "permissions"), ALLOW_ALL);}

这段代码的大致工作:就是读取手机中 system/etc/permissions/    product/etc/permissions/  odm/etc/permissions/   vendor/ete/permissons/ 路径下的权限配置.xml 文件,如下:

如果该路径下没有log中提及的   com.test.mtk.xml  文件,就满足上面源码分析的条件,就抛异常导致系统无法正常开机启动。

5. 解决方案

4.1  我的app是放置在手机系统product/priv-app 路径下的, 对应的权限白名单文件也应该放置到product/etc/permissions/ 路径下  怎么修改呢?我是直接通过Andorid.mk  文件实现的

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES := $(call all-java-files-under, src)LOCAL_MANIFEST_FILE := AndroidManifest.xmlLOCAL_RESOURCE_DIR := $(LOCAL_PATH)/resLOCAL_ASSET_DIR := $(LOCAL_PATH)/assetsLOCAL_PACKAGE_NAME := TestAppLOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT)/priv-app######编译priv-app 权限到apk中###########
LOCAL_REQUIRED_MODULES := com.test.mtk.xmlLOCAL_PRIVILEGED_MODULE := true
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platformLOCAL_STATIC_ANDROID_LIBRARIES := \androidx.core_core \androidx.appcompat_appcompatinclude $(BUILD_PACKAGE)######预编译priv-app 权限,输出路径为product/etc/permissions###########
# Permissions pre-grant
include $(CLEAR_VARS)
LOCAL_MODULE := com.test.mtk.xml
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT)/etc/permissions
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)include $(call all-makefiles-under,$(LOCAL_PATH))

com.test.mtk.xml 内容:

<?xml version="1.0" encoding="utf-8"?>
<permissions><privapp-permissions package="com.test.mtk"><permission name="android.permission.MASTER_CLEAR" /></privapp-permissions>
</permissions>

4.2  假如你的app打算编译到手机的system/priv-app 路径下,则配置的私有权限白名单要对应放置到system/etc/permissions/ 路径下,则有两种修改方法:

第一种方案 : 修改 framework/base/data/etc/privapp-permissions-platform.xml 把自己的权限加入其中,它最后编译后输出的路径为:/system/etc/permissions/,  是由Android.bp实现

# framework/base/data/etc/Android.bpprebuilt_etc {name: "privapp-permissions-platform.xml",sub_dir: "permissions",src: "privapp-permissions-platform.xml",
}

第二种方案: 还是通过Android.mk 文件实现,稍微修改一下路径就可以了:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optionalLOCAL_SRC_FILES := $(call all-java-files-under, src)LOCAL_MANIFEST_FILE := AndroidManifest.xmlLOCAL_RESOURCE_DIR := $(LOCAL_PATH)/resLOCAL_ASSET_DIR := $(LOCAL_PATH)/assetsLOCAL_PACKAGE_NAME := TestApp#app编译到system/priv-app/路径下
LOCAL_MODULE_PATH := $(TARGET_OUT)/priv-app######编译priv-app 权限到apk中###########
LOCAL_REQUIRED_MODULES := com.test.mtk.xmlLOCAL_PRIVILEGED_MODULE := true
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platformLOCAL_STATIC_ANDROID_LIBRARIES := \androidx.core_core \androidx.appcompat_appcompatinclude $(BUILD_PACKAGE)######预编译priv-app 权限,输出路径为system/etc/permissions###########
# Permissions pre-grant
include $(CLEAR_VARS)
LOCAL_MODULE := com.test.mtk.xml
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)include $(call all-makefiles-under,$(LOCAL_PATH))

推荐这种方案,原因:私有权限白名单(com.test.mtk.xml)是与app的源码在一起,便于代码移植,也算是间接的解耦了。 如果是用第一种方案,你做的功能,如果换一个人来维护,这个privapp-permissions-platform.xml 修改很容易漏掉就又无法开机了。

Android 私有权限白名单相关推荐

  1. Android系统的特许权限白名单

    目录 一.什么是特权应用 二.Android系统的特许权限 三.特许权限白名单 1.白名单限制宏开关 2.日志打印 3.添加白名单 一.什么是特权应用 特权应用(Privileged Applicat ...

  2. android 添加安装权限白名单

    点击打开链接 有些项目不允许所有APK都拥有安装权限,例如apk只能通过应用商城来安装或者升级,只允许某些特定的apk自升级,不允许pm install等.这就需要添加安装权限白名单来控制. 1.pa ...

  3. android 服务保活之白名单,Android 后台运行白名单,优雅实现保活

    原标题:Android 后台运行白名单,优雅实现保活 保活现状 我们知道,Android 系统会存在杀后台进程的情况,并且随着系统版本的更新,杀进程的力度还有越来越大的趋势.系统这种做法本身出发点是好 ...

  4. Android 特权许可白名单

    一.priv-app 白名单简介   特权应用程序是位于/system/priv-app系统映像目录中的系统应用程序 .从历史上看,设备实施者几乎无法控制哪些特权权限可以授予特权应用程序.从Andro ...

  5. Android增加系统白名单,防止重要应用低内存时被误杀

         Android增加系统白名单,防止重要应用低内存时被误杀 前言    在前面的篇章Lowmemkill问题分析我们详细介绍了系统在低内存的时候杀应用的原因.也许有些读者会说现在手机动辄6G+ ...

  6. Android 7.0修改PMS逻辑添加权限白名单

    今天有任务安排需要实现给任何一个应用在不动态申请权限的情况下,实现权限赋予,大体知道应用权限的赋予逻辑是在PMS中,于是大概研究了下,最终的手段就是在install安装时,就把应用程序AndroidM ...

  7. android service白名单,Android 后台运行白名单实现保活

    保活现状 我们知道,Android 系统会存在杀后台进程的情况,并且随着系统版本的更新,杀进程的力度还有越来越大的趋势.系统这种做法本身出发点是好的,因为可以节省内存,降低功耗,也避免了一些流氓行为. ...

  8. Android 不同机型白名单如何设置,小米、华为、oppo、乐视

    前言 什么是白名单?白名单是对手机中安装运行的应用的允许耗电的一种管理模式.一般手机对应用的管理包括应用后台运行联网.定位.传感器使用或CPU占用的限制.应用加入白名单,就意味着手机不对其采取任何限制 ...

  9. android 5.1 白名单,iqoo5如何将应用加密 iqoo5一键设置应用白名单方法分享

    iqoo5如何将应用加密?iqoo5如何设置应用白名单?对于最新发布上市的iqoo5系列手机,很多的用户都表示非常的喜欢,但是大家对于手机上的一些功能操作都不是比较陌生的,比如想要将应用加密应该如何操 ...

  10. Android系统-应用权限白名单

    Google对于这个需求已经在官网有了说明:跳转 大概总结如下: 1.应用需要申请名为特权应用,特权应用是 priv-app 目录下的系统应用 如何申请特权应用: Android.bp certifi ...

最新文章

  1. Rails测试《一》fixtures简介
  2. VB.Net to C Sharp Converter有用吗?
  3. 计算机基础知识在线作业,福建师范大学《计算机应用基础》在线作业一答案.docx...
  4. vs调试显示16进制如何调节为10进制
  5. 两道递推公式题的解题报告
  6. Powered Addition CodeForces - 1339C(位运算)
  7. 新电脑怎么分盘_电脑如何分盘及删除与合并04
  8. JDBC连接错误:通过端口 1433 连接到主机 localhost 的 TCP/IP 连接失败。。。
  9. 11-Container With Most Water
  10. No package ‘libusb‘ found
  11. Unity渲染管线详解
  12. 对 Unity 动画系统 Mecanim 的调研
  13. [GYCTF2020]Easyphp
  14. kaggle实战:Titanic
  15. 苹果电脑服务器连接显示器,苹果电脑如何外接显示器 苹果电脑外接显示器设置方法...
  16. 使用certbot完成证书的自动发放
  17. Graph Coverage
  18. 高铁盈利地图:东部赚翻 中西部普遍巨亏
  19. 服务器网站权限,在服务器上设置网站权限
  20. android 打开相机拍照功能吗,Android调用相机实现拍照功能

热门文章

  1. [转载]Android学习网站
  2. 如何查看电脑系统版本
  3. java--------------
  4. 域名链接到服务器指定端口
  5. ubuntu18.04键盘背光灯以及Scroll Lock建失效
  6. java获取发件人_Java实现伪造发件人发送邮件
  7. Google浏览器任务栏图标变白
  8. IBM Tivoli Omegamon
  9. 转载:软件工程师的视角看网络(网络那点事)
  10. 如何评价『黑客与画家』