Android 特许权限白名单

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.java

    private 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 := optional

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_MANIFEST_FILE := AndroidManifest.xml

LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res

LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets

LOCAL_PACKAGE_NAME := TestApp

#app编译生成路径:product/priv-app
LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT)/priv-app

LOCAL_PRIVILEGED_MODULE := true

LOCAL_PRIVATE_PLATFORM_APIS := true

#系统签名
LOCAL_CERTIFICATE := platform

LOCAL_STATIC_ANDROID_LIBRARIES := \
    androidx.core_core \
    androidx.appcompat_appcompat

include $(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.java


private 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.java

 private 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 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 值为null
                        deniedPermissions = 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.java

 SystemConfig() {
        // Read configuration from system
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);

        // Read configuration from the old permissions dir
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);

        // Vendors are only allowed to customize these
        int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS
                | ALLOW_ASSOCIATIONS;
        if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {
            // For backward compatibility
            vendorPermissionFlag |= (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 these
        int 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 configs
        readPermissions(Environment.buildPath(
                Environment.getProductDirectory(), "etc", "sysconfig"), ALLOW_ALL);
        readPermissions(Environment.buildPath(
                Environment.getProductDirectory(), "etc", "permissions"), ALLOW_ALL);

        // Allow /product_services to customize all system configs
        readPermissions(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 := optional

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_MANIFEST_FILE := AndroidManifest.xml

LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res

LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets

LOCAL_PACKAGE_NAME := TestApp

LOCAL_MODULE_PATH := $(TARGET_OUT_PRODUCT)/priv-app

######编译priv-app 权限到apk中###########
LOCAL_REQUIRED_MODULES := com.test.mtk.xml

LOCAL_PRIVILEGED_MODULE := true
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform

LOCAL_STATIC_ANDROID_LIBRARIES := \
    androidx.core_core \
    androidx.appcompat_appcompat

include $(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.bp

prebuilt_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 := optional

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_MANIFEST_FILE := AndroidManifest.xml

LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res

LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets

LOCAL_PACKAGE_NAME := TestApp

#app编译到system/priv-app/路径下
LOCAL_MODULE_PATH := $(TARGET_OUT)/priv-app

######编译priv-app 权限到apk中###########
LOCAL_REQUIRED_MODULES := com.test.mtk.xml

LOCAL_PRIVILEGED_MODULE := true
LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform

LOCAL_STATIC_ANDROID_LIBRARIES := \
    androidx.core_core \
    androidx.appcompat_appcompat

include $(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 修改很容易漏掉就又无法开机了。

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值