深度解析 Android 系统属性

目录

Android系统属性

1.属性在哪里?

2.属性长什么样?

3.如何读写属性:

4.属性的作用

属性文件生成过程

如何添加系统属性

1.添加系统属性到 /system/build.prop

2.添加系统属性到 /vendor/build.prop

3.添加系统属性到 /product/build.prop


Android系统属性

  • 属性文件生成过程分析
  • 如何添加系统属性
  • 属性与 Selinux
  • 属性系统整体框架与启动过程分析
  • 属性读写过程源码分析

在 Android 系统中,为统一管理系统的属性,设计了一个统一的属性系统,每个属性都是一个 key-value 对。 我们可以通过 shell 命令,Native 函数接口,Java 函数接口的方式来读写这些 key-vaule 对。

1.属性在哪里?

init 进程在启动会去加载后缀为 .prop 的属性文件, 将属性文件中的属性加载到共享内存中, 这样系统就有了默认的一些属性。

属性文件都在哪里呢?

属性文件的后缀绝大部分都是 prop,我们可以在 Android 模拟器的 shell 环境下搜索:

find . -name "*.prop"/default.prop
/data/local.prop
/system/build.prop
/system/product/build.prop
/vendor/build.prop
/vendor/odm/etc/build.prop
/vendor/default.prop
/data/property/

 我们看看 /default.prop 属性文件的内容:

cat /default.prop#
# ADDITIONAL_DEFAULT_PROPERTIES
#
ro.actionable_compatible_property.enabled=true
ro.postinstall.fstab.prefix=/system
ro.secure=0
ro.allow.mock.location=1
ro.debuggable=1
debug.atrace.tags.enableflags=0
dalvik.vm.image-dex2oat-Xms=64m
dalvik.vm.image-dex2oat-Xmx=64m
dalvik.vm.dex2oat-Xms=64m
dalvik.vm.dex2oat-Xmx=512m
dalvik.vm.usejit=true
dalvik.vm.usejitprofiles=true
dalvik.vm.dexopt.secondary=true
dalvik.vm.appimageformat=lz4
ro.dalvik.vm.native.bridge=0
pm.dexopt.first-boot=extract
pm.dexopt.boot=extract
pm.dexopt.install=speed-profile
pm.dexopt.bg-dexopt=speed-profile
pm.dexopt.ab-ota=speed-profile
pm.dexopt.inactive=verify
pm.dexopt.shared=speed
dalvik.vm.dex2oat-resolve-startup-strings=true
dalvik.vm.dex2oat-max-image-block-size=524288
dalvik.vm.minidebuginfo=true
dalvik.vm.dex2oat-minidebuginfo=true
ro.iorapd.enable=false
tombstoned.max_tombstone_count=50
persist.traced.enable=1
ro.com.google.locationfeatures=1
ro.setupwizard.mode=DISABLED
persist.sys.usb.config=adb

可以看出属性确实是一些 key-value 对。

init 进程会调用 property_load_boot_defaults 函数来加载属性文件:

void property_load_boot_defaults(bool load_debug_prop) {// TODO(b/117892318): merge prop.default and build.prop files into one// We read the properties and their values into a map, in order to always allow properties// loaded in the later property files to override the properties in loaded in the earlier// property files, regardless of if they are "ro." properties or not.std::map<std::string, std::string> properties;if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {// Try recovery pathif (!load_properties_from_file("/prop.default", nullptr, &properties)) {// Try legacy pathload_properties_from_file("/default.prop", nullptr, &properties);}}load_properties_from_file("/system/build.prop", nullptr, &properties);load_properties_from_file("/vendor/default.prop", nullptr, &properties);load_properties_from_file("/vendor/build.prop", nullptr, &properties);if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);} else {load_properties_from_file("/odm/default.prop", nullptr, &properties);load_properties_from_file("/odm/build.prop", nullptr, &properties);}load_properties_from_file("/product/build.prop", nullptr, &properties);load_properties_from_file("/product_services/build.prop", nullptr, &properties);load_properties_from_file("/factory/factory.prop", "ro.*", &properties);if (load_debug_prop) {LOG(INFO) << "Loading " << kDebugRamdiskProp;load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);}for (const auto& [name, value] : properties) {std::string error;if (PropertySet(name, value, &error) != PROP_SUCCESS) {LOG(ERROR) << "Could not set '" << name << "' to '" << value<< "' while loading .prop files" << error;}}property_initialize_ro_product_props();property_derive_build_fingerprint();update_sys_usb_config();
}

从源码中我们也可以看到 init 进程加载了哪些属性文件以及加载的顺序。

2.属性长什么样?

每一个属性是一个 key-value 对:

ro.actionable_compatible_property.enabled=true
ro.postinstall.fstab.prefix=/system
ro.secure=0
ro.allow.mock.location=1
ro.debuggable=1
debug.atrace.tags.enableflags=0
dalvik.vm.image-dex2oat-Xms=64m
dalvik.vm.image-dex2oat-Xmx=64m

等号左边是属性的名字,等号右边是属性的值

属性的分类:

  • 一般属性:普通的 key-value 对,没有其他功能,系统启动后,如果修改了某个属性值(仅修改了内存中的值,未写入到文件),再重启系统,修改的值不会被保存下来,读取到的仍是修改前的值
  • 特殊属性
    • 属性名称以 ro 开头,那么这个属性被视为只读属性。一旦设置,属性值不能改变。
    • net 开头的属性,顾名思义,就是与网络相关的属性,net 属性中有一个特殊的属性:net.change,它记录了每一次最新设置和更新的 net 属性,也就是每次设置和更新 net,属性时则会自动的更新 net.change 属性,net.change 属性的 value 就是这个被设置或者更新的 net 属性的 name。例如我们更新了属性 net.bt.name 的值,由于 net 有属性发生了变化,那么属性服务就会自动更新 net.change,将其值设置为 net.bt.name
    • 以 persist 为开头的属性值,当在系统中通过 setprop 命令设置这个属性时,就会在 /data/property/ 目录下会保存一个副本。这样在系统重启后,按照加载流程这些 persist 属性的值就不会消失了。
    • 属性 ctrl.start 和 ctrl.stop 是用来启动和停止服务。这里的服务是指定义在 rc 后缀文件中的服务。当我们向 ctrl.start 属性写入一个值时,属性服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入 init.svc.<服务名> 属性中,可以通过查询这个属性值,以确定服务是否已经启动。

3.如何读写属性:

命令行:

getprop "wlan.driver.status"
setprop "wlan.driver.status"  "timeout"

Native 代码:

char buf[20]="qqqqqq";
char tempbuf[PROPERTY_VALUE_MAX];
property_set("type_value",buf);
property_get("type_value",tempbuf,"0");

Java 代码:

String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
SystemProperties.set("service.bootanim.exit", "0");

4.属性的作用

常见的属性文件的作用如下:

属性文件生成过程

运行的系统中有很多属性文件:

/default.prop
/data/local.prop
/system/build.prop
/system/product/build.prop
/vendor/build.prop
/vendor/odm/etc/build.prop
/vendor/default.prop

那么这些属性文件是从哪里来的呢?

我们先看一下 /system/build.prop 文件的开头:

# begin common build properties
# autogenerated by build/make/tools/buildinfo_common.sh
ro.system.build.date=Fri Oct 13 17:23:40 CST 2023
# ......

注释里面说,当前文件由 build/make/tools/buildinfo_common.sh 自动生成,我们去看一下这个 shell 脚本的具体内容:

cat build/make/tools/buildinfo_common.sh#!/bin/bashpartition="$1"if [ "$#" -ne 1 ]; thenecho "Usage: $0 <partition>" 1>&2exit 1
fiecho "# begin common build properties"
echo "# autogenerated by $0"echo "ro.${partition}.build.date=`$DATE`"
echo "ro.${partition}.build.date.utc=`$DATE +%s`"
echo "ro.${partition}.build.fingerprint=$BUILD_FINGERPRINT"
echo "ro.${partition}.build.id=$BUILD_ID"
echo "ro.${partition}.build.tags=$BUILD_VERSION_TAGS"
echo "ro.${partition}.build.type=$TARGET_BUILD_TYPE"
echo "ro.${partition}.build.version.incremental=$BUILD_NUMBER"
echo "ro.${partition}.build.version.release=$PLATFORM_VERSION"
echo "ro.${partition}.build.version.sdk=$PLATFORM_SDK_VERSION"echo "ro.product.${partition}.brand=$PRODUCT_BRAND"
echo "ro.product.${partition}.device=$PRODUCT_DEVICE"
echo "ro.product.${partition}.manufacturer=$PRODUCT_MANUFACTURER"
echo "ro.product.${partition}.model=$PRODUCT_MODEL"
echo "ro.product.${partition}.name=$PRODUCT_NAME"echo "# end common build properties"

上述脚本中,通过 echo 打印了许多信息。这些信息都进入了文件中,那么调用过程中一定做了重定向操作。

我们在 build/make 中搜索 buildinfo_common.sh,看看哪里使用了这个脚本:

cd build/make
grep -R "buildinfo_common.sh" ../core/Makefile:BUILDINFO_COMMON_SH := build/make/tools/buildinfo_common.sh

可以看到在 build/make/core/Makefile 文件中,脚本的路径赋值给了 BUILDINFO_COMMON_SH 变量。

我们接着搜这个变量:

# 还是在 build/make 目录下
grep -R "BUILDINFO_COMMON_SH" ../core/Makefile:BUILDINFO_COMMON_SH := build/make/tools/buildinfo_common.sh
./core/Makefile:        bash $(BUILDINFO_COMMON_SH) "$(1)" >> $(2)
./core/Makefile:$(INSTALLED_DEFAULT_PROP_TARGET): $(BUILDINFO_COMMON_SH) $(intermediate_system_build_prop)
./core/Makefile:$(intermediate_system_build_prop): $(BUILDINFO_SH) $(BUILDINFO_COMMON_SH) $(INTERNAL_BUILD_ID_MAKEFILE) $(BUILD_SYSTEM)/version_defaults.mk $(system_prop_file) $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(API_FINGERPRINT)
./core/Makefile:$(INSTALLED_VENDOR_BUILD_PROP_TARGET): $(BUILDINFO_COMMON_SH) $(intermediate_system_build_prop)
./core/Makefile:$(INSTALLED_PRODUCT_BUILD_PROP_TARGET): $(BUILDINFO_COMMON_SH) $(product_prop_files)
./core/Makefile:$(INSTALLED_ODM_BUILD_PROP_TARGET): $(BUILDINFO_COMMON_SH)
./core/Makefile:$(INSTALLED_PRODUCT_SERVICES_BUILD_PROP_TARGET): $(BUILDINFO_COMMON_SH)

注意到第二行的输出 ./core/Makefile: bash $(BUILDINFO_COMMON_SH) "$(1)" >> $(2),这里调用了上面的 shell 脚本

打开文件,看到它是函数 generate-common-build-props-with-product-vars-set 的一部分:

define generate-common-build-props-with-product-vars-setBUILD_FINGERPRINT="$(BUILD_FINGERPRINT_FROM_FILE)" \BUILD_ID="$(BUILD_ID)" \BUILD_NUMBER="$(BUILD_NUMBER_FROM_FILE)" \BUILD_VERSION_TAGS="$(BUILD_VERSION_TAGS)" \DATE="$(DATE_FROM_FILE)" \PLATFORM_SDK_VERSION="$(PLATFORM_SDK_VERSION)" \PLATFORM_VERSION="$(PLATFORM_VERSION)" \TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)" \bash $(BUILDINFO_COMMON_SH) "$(1)" >> $(2)
endef

这里用函数的参数做了从定向,我们接着搜,哪里使用这个函数:

grep -R "generate-common-build-props-with-product-vars-set" ../core/Makefile:        $(call generate-common-build-props-with-product-vars-set,$(1),$(2))
./core/Makefile:define generate-common-build-props-with-product-vars-set
./core/Makefile:                $(call generate-common-build-props-with-product-vars-set,system,$@)

有两个地方调用了 generate-common-build-props-with-product-vars-set 函数:

我们主要看第二个地方:

# 定义 system_prop_file 变量的值
# system_prop_file 代表了一个 prop 属性文件
ifdef TARGET_SYSTEM_PROP
system_prop_file := $(TARGET_SYSTEM_PROP)
else
system_prop_file := $(wildcard $(TARGET_DEVICE_DIR)/system.prop)
endif# 这里是一个 Makefile 规则
# intermediate_system_build_prop 的值类似于 out/target/product/xxx/obj/ETC/system_build_prop_intermediates/build.prop 是一个中间文件,用于生成最终的 /system/build.prop
$(intermediate_system_build_prop): $(BUILDINFO_SH) $(BUILDINFO_COMMON_SH) $(INTERNAL_BUILD_ID_MAKEFILE) $(BUILD_SYSTEM)/version_defaults.mk $(system_prop_file) $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(API_FINGERPRINT)@echo Target buildinfo: $@@mkdir -p $(dir $@)$(hide) echo > $@
ifneq ($(PRODUCT_OEM_PROPERTIES),)$(hide) echo "#" >> $@; \echo "# PRODUCT_OEM_PROPERTIES" >> $@; \echo "#" >> $@;$(hide) $(foreach prop,$(PRODUCT_OEM_PROPERTIES), \echo "import /oem/oem.prop $(prop)" >> $@;)
endif$(hide) PRODUCT_BRAND="$(PRODUCT_SYSTEM_BRAND)" \PRODUCT_MANUFACTURER="$(PRODUCT_SYSTEM_MANUFACTURER)" \PRODUCT_MODEL="$(PRODUCT_SYSTEM_MODEL)" \PRODUCT_NAME="$(PRODUCT_SYSTEM_NAME)" \PRODUCT_DEVICE="$(PRODUCT_SYSTEM_DEVICE)" \# 在这里调用 generate-common-build-props-with-product-vars-set 生成中间文件# out/target/product/xxx/obj/ETC/system_build_prop_intermediates/build.prop$(call generate-common-build-props-with-product-vars-set,system,$@)$(hide) TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)" \TARGET_BUILD_FLAVOR="$(TARGET_BUILD_FLAVOR)" \TARGET_DEVICE="$(TARGET_DEVICE)" \PRODUCT_DEFAULT_LOCALE="$(call get-default-product-locale,$(PRODUCT_LOCALES))" \PRODUCT_DEFAULT_WIFI_CHANNELS="$(PRODUCT_DEFAULT_WIFI_CHANNELS)" \PRIVATE_BUILD_DESC="$(PRIVATE_BUILD_DESC)" \BUILD_ID="$(BUILD_ID)" \BUILD_DISPLAY_ID="$(BUILD_DISPLAY_ID)" \DATE="$(DATE_FROM_FILE)" \BUILD_USERNAME="$(BUILD_USERNAME)" \BUILD_HOSTNAME="$(BUILD_HOSTNAME)" \BUILD_NUMBER="$(BUILD_NUMBER_FROM_FILE)" \BOARD_BUILD_SYSTEM_ROOT_IMAGE="$(BOARD_BUILD_SYSTEM_ROOT_IMAGE)" \AB_OTA_UPDATER="$(AB_OTA_UPDATER)" \PLATFORM_VERSION="$(PLATFORM_VERSION)" \PLATFORM_SECURITY_PATCH="$(PLATFORM_SECURITY_PATCH)" \PLATFORM_BASE_OS="$(PLATFORM_BASE_OS)" \PLATFORM_SDK_VERSION="$(PLATFORM_SDK_VERSION)" \PLATFORM_PREVIEW_SDK_VERSION="$(PLATFORM_PREVIEW_SDK_VERSION)" \PLATFORM_PREVIEW_SDK_FINGERPRINT="$$(cat $(API_FINGERPRINT))" \PLATFORM_VERSION_CODENAME="$(PLATFORM_VERSION_CODENAME)" \PLATFORM_VERSION_ALL_CODENAMES="$(PLATFORM_VERSION_ALL_CODENAMES)" \PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION="$(PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION)" \BUILD_VERSION_TAGS="$(BUILD_VERSION_TAGS)" \$(if $(OEM_THUMBPRINT_PROPERTIES),BUILD_THUMBPRINT="$(BUILD_THUMBPRINT_FROM_FILE)") \TARGET_CPU_ABI_LIST="$(TARGET_CPU_ABI_LIST)" \TARGET_CPU_ABI_LIST_32_BIT="$(TARGET_CPU_ABI_LIST_32_BIT)" \TARGET_CPU_ABI_LIST_64_BIT="$(TARGET_CPU_ABI_LIST_64_BIT)" \TARGET_CPU_ABI="$(TARGET_CPU_ABI)" \TARGET_CPU_ABI2="$(TARGET_CPU_ABI2)" \bash $(BUILDINFO_SH) >> $@# 把 system_prop_file 中的内容写入中间文件$(hide) $(foreach file,$(system_prop_file), \if [ -f "$(file)" ]; then \echo Target buildinfo from: "$(file)"; \echo "" >> $@; \echo "#" >> $@; \echo "# from $(file)" >> $@; \echo "#" >> $@; \cat $(file) >> $@; \echo "# end of $(file)" >> $@; \fi;)# FINAL_BUILD_PROPERTIES 的值来自 ADDITIONAL_BUILD_PROPERTIES# ADDITIONAL_BUILD_PROPERTIES 主要是 PRODUCT_PROPERTY_OVERRIDES 这个变量赋值, 而这个变量就是我们开发时经常用于自定义属性的, 一般在产品配置目录下定义# 这里把 FINAL_BUILD_PROPERTIES 中的属性写入中间文件$(if $(FINAL_BUILD_PROPERTIES), \$(hide) echo >> $@; \echo "#" >> $@; \echo "# ADDITIONAL_BUILD_PROPERTIES" >> $@; \echo "#" >> $@; )$(hide) $(foreach line,$(FINAL_BUILD_PROPERTIES), \echo "$(line)" >> $@;)$(hide) build/make/tools/post_process_props.py $@ $(PRODUCT_SYSTEM_PROPERTY_BLACKLIST)build_desc :=ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))
INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
else
INSTALLED_RECOVERYIMAGE_TARGET :=
endif# INSTALLED_BUILD_PROP_TARGET 的值为 out/target/product/xxx/system/build.prop
# 也就是我们最终要生成的文件
$(INSTALLED_BUILD_PROP_TARGET): $(intermediate_system_build_prop) $(INSTALLED_RECOVERYIMAGE_TARGET)@echo "Target build info: $@"# 这里把中间文件写入到最终文件中,同时剔除 ro.product.first_api_level 相关的行$(hide) grep -v 'ro.product.first_api_level' $(intermediate_system_build_prop) > $@

以上代码总结一下:

  • 准备好一个中间文件 out/target/product/xxx/obj/ETC/system_build_prop_intermediates/build.prop
  • 调用 buildinfo_common.sh,向中间文件写入数据
  • 把 system_prop_file 中的内容写入中间文件
  • 这里把用户自定义的的属性(PRODUCT_PROPERTY_OVERRIDES)写入中间文件
  • 这里把中间文件写入到最终文件(out/target/product/xxx/system/build.pro)中,同时剔除 ro.product.first_api_level 相关的行

以上就是 /system/build.prop,其他属性文件我们都可以依葫芦画瓢进行分析了。

如何添加系统属性

常见的自定义系统属性有三类:

  • 添加系统属性到 /system/build.prop
  • 添加系统属性到 /vendor/build.prop
  • 添加系统属性到 /product/build.prop

1.添加系统属性到 /system/build.prop

我们先看看生成 /system/build.prop 文件相关的部分源码:

# build/make/core/Makefileifdef TARGET_SYSTEM_PROP
system_prop_file := $(TARGET_SYSTEM_PROP)
else
system_prop_file := $(wildcard $(TARGET_DEVICE_DIR)/system.prop)
endif

在上文我们分析过 system_prop_file 文件的内容最终会写入到 /system/build.prop 中,/system/build.prop 的值又来自 TARGET_SYSTEM_PROP,所以我们添加一个属性文件,再修改 TARGET_SYSTEM_PROP 的值即可。

在我们的自定义 Product device/jelly/rice14 目录下,添加一个 system.prop 文件,文件的内容如下:

ro.rice14.test=2.0

接着在 device/jelly/rice14/BoardConfig.mk 中添加:

TARGET_SYSTEM_PROP += device/jelly/rice14/system.prop

然后重新编译系统,启动模拟器:

source build/envsetup.sh
lunch rice14-eng
make -j16
emulator

启动虚拟机后,我们可以进入虚拟机终端查看属性值:

adb shellrice14:/ # getprop ro.rice14.test                                     
2.0# end of device/jelly/rice14/system.prop

这里查询到了我们刚添加的属性。

2.添加系统属性到 /vendor/build.prop

同样的,我们先看看生成 /vendor/build.prop 文件相关的部分源码:

ifdef property_overrides_split_enabled
FINAL_VENDOR_BUILD_PROPERTIES += \$(call collapse-pairs, $(PRODUCT_PROPERTY_OVERRIDES))
FINAL_VENDOR_BUILD_PROPERTIES := $(call uniq-pairs-by-first-component, \$(FINAL_VENDOR_BUILD_PROPERTIES),=)
endif  # property_overrides_split_enabled

这里会把 PRODUCT_PROPERTY_OVERRIDES 变量中的值赋值写入到 FINAL_VENDOR_BUILD_PROPERTIES,从变量名字就可以看出,这就是最终的 vendor 属性文件,所以我们通过修改 PRODUCT_PROPERTY_OVERRIDES 变量的值即可添加属性:

# device/jelly/rice14/rice14.mk
PRODUCT_PROPERTY_OVERRIDES += \ro.vendor.xxx=xxx \ro.vendor.yyy=yyy

3.添加系统属性到 /product/build.prop

同样的,我们先看看生成 /product/build.prop 文件相关的部分源码:

ifdef TARGET_PRODUCT_PROP
product_prop_files := $(TARGET_PRODUCT_PROP)
else
product_prop_files := $(wildcard $(TARGET_DEVICE_DIR)/product.prop)
endifFINAL_PRODUCT_PROPERTIES += \$(call collapse-pairs, $(PRODUCT_PRODUCT_PROPERTIES) $(ADDITIONAL_PRODUCT_PROPERTIES))
FINAL_PRODUCT_PROPERTIES := $(call uniq-pairs-by-first-component, \$(FINAL_PRODUCT_PROPERTIES),=)

可以看到这里会把 product_prop_files 文件和 PRODUCT_PRODUCT_PROPERTIES 变量以及 ADDITIONAL_PRODUCT_PROPERTIES 变量中的值都会写入到最终的属性文件中。

所以上两节介绍的两种方式都可以,这里我们演示先通过添加变量值的方式添加属性:

# device/jelly/rice14/rice14.mk
PRODUCT_PRODUCT_PROPERTIES += \ro.product.xxx=xxx \ro.product.yyy=yyy

属性与 Selinux

Android O 以后,属性的 Selinux 的规则变得复杂起来了,今天我们先看一个简单的例子,做基本的了解。在Hal与硬件服务的 treble 章节我们还会继续来学习属性相关的 Selinux 配置。

#读写属性的示例代码

首先我们在 device/jelly/rice14 目录下创建如下的目录:

PropTest/
├── Android.bp
└── prop_test.cpp

其中 prop_test.cpp 具体内容如下:

#include <string>#include <cutils/properties.h>
#define LOG_TAG "prop_test"
#include <log/log.h>
#include <android-base/properties.h>using namespace std;int main(int argc, char *argv[])
{// 打印版本信息char android_version[PROPERTY_VALUE_MAX];property_get("ro.build.version.release", android_version, "");ALOGD("android version : %s", android_version);// 写入自定义属性property_set("vendor.my.prop.test", "xxx");// 读自定义属性char prop_test[PROPERTY_VALUE_MAX];property_get("vendor.my.prop.test", prop_test, "");ALOGD("prop test : %s", prop_test);while(1) {}return 0;
}

配套的编译文件 Android.bp 如下:

至此,整个示例程序就完成了,最后修改我们自定义 Product 的配置文件 device/jelly/rice14/rice14.mk :

PRODUCT_PACKAGES += # ......prop_test \

#Selinux 的配置

接着需要配置 SeLinux:

在 device/jelly/rice14 目录下创建如下的文件与目录:

sepolicy/
├── file_contexts
├── property_contexts
├── property.te
└── prop_te

不要忘了配置自定义 sepolicy 目录,在 device/jelly/rice14/rice14.mk 中添加如下内容:

BOARD_SEPOLICY_DIRS += \device/jelly/rice14/sepolicy

首先在 property.te 中定义属性类型

type vendor_mytest_prop, property_type;

接着在 property_contexts 中配置好我们自定义属性 vendor.my.prop.test 的安全上下文:

vendor.my.prop.test u:object_r:vendor_mytest_prop:s0

接着在 prop_test.te 中配置好可执行文件和对应进程的类型和域转换规则以及属性的读写权限:


# 可执行文件对应进程类型
type  myprop_test_dt, domain;# 可执行文件类型
type myprop_test_dt_exec, exec_type, vendor_file_type, file_type;# 域转换规则
init_daemon_domain(myprop_test_dt)
domain_auto_trans(shell, myprop_test_dt_exec, myprop_test_dt)# 属性读写规则
set_prop(myprop_test_dt, vendor_mytest_prop);
get_prop(myprop_test_dt, vendor_mytest_prop);get_prop(myprop_test_dt, exported2_default_prop);

在属性读写规则中,我们添加了对 exported2_default_prop 类型属性的读取规则,exported2_default_prop 是源码中 ro.build.version.release 属性对应的 type,我是通过如下的搜索命令查找到的:

find . -name "property_contexts" | xargs grep ro.build.version.release
./system/sepolicy/public/property_contexts:ro.build.version.release u:object_r:exported2_default_prop:s0 exact string
./system/sepolicy/prebuilts/api/29.0/public/property_contexts:ro.build.version.release u:object_r:exported2_default_prop:s0 exact string
./system/sepolicy/prebuilts/api/28.0/public/property_contexts:ro.build.version.release u:object_r:exported2_default_prop:s0 exact string

最后我们需要再 file_contexts 中配置可执行文件的安全上下文:

/vendor/bin/prop_test                   u:object_r:myprop_test_dt_exec:s0

接着我们就可以执行在虚拟机的 shell 中执行 prop_test 可执行文件了:

# 重新编译源码
source build/envsetup.sh
lunch rice14-eng
make -j16
# 启动虚拟机
emulator# 重开一个终端,进入虚拟机 shell 环境
adb shell
# root
su 
# selinux 配置为 Permissive 模式
setenforce 0
# 退出 root
exit
prop_test

接着我们查看 log:

logcat | grep prop_test                                                          
10-23 10:25:42.420  2959  2959 W prop_test: type=1400 audit(0.0:27): avc: denied { read } for name="u:object_r:vendor_mytest_prop:s0" dev="tmpfs" ino=6750 scontext=u:r:shell:s0 tcontext=u:object_r:vendor_mytest_prop:s0 tclass=file permissive=0
10-23 10:25:42.424  2959  2959 D prop_test: android version : 10
10-23 10:25:42.428  2959  2959 D prop_test: prop test : 
10-23 10:29:41.930  3704  3704 W prop_test: type=1400 audit(0.0:58): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:29:41.930  3704  3704 W prop_test: type=1400 audit(0.0:60): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:29:41.930  3704  3704 W prop_test: type=1400 audit(0.0:61): avc: denied { use } for path="/dev/goldfish_pipe" dev="tmpfs" ino=7279 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:29:41.930  3704  3704 W prop_test: type=1400 audit(0.0:62): avc: denied { use } for path="/dev/goldfish_pipe" dev="tmpfs" ino=7279 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:31:17.980  3919  3919 I prop_test: type=1400 audit(0.0:87): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=1
10-23 10:31:17.980  3919  3919 I prop_test: type=1400 audit(0.0:88): avc: denied { read write } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:object_r:devpts:s0 tclass=chr_file permissive=1
10-23 10:31:17.980  3919  3919 I prop_test: type=1400 audit(0.0:89): avc: denied { read write } for path="socket:[9765]" dev="sockfs" ino=9765 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=unix_stream_socket permissive=1
10-23 10:31:17.980  3919  3919 I prop_test: type=1400 audit(0.0:90): avc: denied { use } for path="/vendor/bin/prop_test" dev="dm-1" ino=158 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:shell:s0 tclass=fd permissive=1
10-23 10:31:18.000  3919  3919 D prop_test: android version : 10
10-23 10:31:18.002  3919  3919 D prop_test: prop test : xxx

发现我们的程序任然缺少一些权限,我们把权限相关的 log 拷贝下来,在源码根目录下新建一个 avc_log.txt 文件,把缺少权限相关的 log 拷贝进去:

# avc_log.txt
10-23 10:38:05.670  2910  2910 W prop_test: type=1400 audit(0.0:28): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:38:05.670  2910  2910 W prop_test: type=1400 audit(0.0:30): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:38:05.670  2910  2910 W prop_test: type=1400 audit(0.0:31): avc: denied { use } for path="socket:[10955]" dev="sockfs" ino=10955 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:38:05.670  2910  2910 W prop_test: type=1400 audit(0.0:32): avc: denied { use } for path="/dev/goldfish_pipe" dev="tmpfs" ino=7343 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=0
10-23 10:38:57.400  3034  3034 I prop_test: type=1400 audit(0.0:39): avc: denied { use } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=fd permissive=1
10-23 10:38:57.400  3034  3034 I prop_test: type=1400 audit(0.0:40): avc: denied { read write } for path="/dev/pts/0" dev="devpts" ino=3 scontext=u:r:myprop_test_dt:s0 tcontext=u:object_r:devpts:s0 tclass=chr_file permissive=1
10-23 10:38:57.400  3034  3034 I prop_test: type=1400 audit(0.0:41): avc: denied { read write } for path="socket:[10955]" dev="sockfs" ino=10955 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:adbd:s0 tclass=unix_stream_socket permissive=1
10-23 10:38:57.410  3034  3034 I prop_test: type=1400 audit(0.0:42): avc: denied { use } for path="/vendor/bin/prop_test" dev="dm-1" ino=158 scontext=u:r:myprop_test_dt:s0 tcontext=u:r:shell:s0 tclass=fd permissive=1

接着执行命令 audit2allow -i avc_log.txt 生成缺失的权限配置:

allow myprop_test_dt adbd:fd use;
allow myprop_test_dt adbd:unix_stream_socket { read write };
allow myprop_test_dt devpts:chr_file { read write };
allow myprop_test_dt shell:fd use;

把这些权限配置加入到 prop_test.te 后,重新编译系统,启动虚拟机,程序即可正常运行。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/758254.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

未来教育趋势:AI个性化培训如何推动企业与员工共赢

AI定制学习&#xff1a;重新定义个性化员工培训的未来 随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;我们正目睹并亲历了AI在培训领域所引发的根本性变革。AI技术的整合不仅革新了知识传递的模式&#xff0c;而且重新塑造了个性化学习的内涵。依托于尖端算…

《深入Linux内核架构》第2章 进程管理和调度 (4)

目录 2.6 CFS调度类 2.6.1 数据结构 2.6.2 CFS操作 2.6.3 队列操作 2.6.4 选择下一个进程 2.6.5 处理周期性调度器 2.6.6 唤醒抢占 2.6 CFS调度类 即完全公平调度类&#xff0c;用于调度普通进程。 2.6.1 数据结构 struct sched_class fair_sched_class {.next &am…

Restormer: Efficient Transformer for High-Resolution Image Restoration

Abstract 由于卷积神经网络&#xff08;CNN&#xff09;在从大规模数据中学习可概括的图像先验方面表现良好&#xff0c;因此这些模型已广泛应用于图像恢复和相关任务。最近&#xff0c;另一类神经架构 Transformer 在自然语言和高级视觉任务上表现出了显着的性能提升。虽然 T…

【安全类书籍-2】Web渗透测试:使用Kali Linux

目录 内容简介 作用 下载地址 内容简介 书籍的主要内容是指导读者如何运用Kali Linux这一专业的渗透测试平台对Web应用程序进行全面的安全测试。作者们从攻击者的视角出发,详细阐述了渗透测试的基本概念和技术,以及如何配置Kali Linux以适应渗透测试需求。书中不仅教授读者…

vulnhub-----SickOS靶机

文章目录 1.信息收集2.curl命令反弹shell提权利用POC 1.信息收集 ┌──(root㉿kali)-[~/kali/vulnhub/sockos] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 00:0c:29:10:3c:9b, IPv4: 10.10.10.10 Starting arp-scan 1.9.8 with 256…

邮件客户端 Thunderbird 简单配置

1. 基本情况介绍 原来使用的邮箱客户端是 Office 365 自带的 Outlook 365切换原因&#xff1a;新装电脑&#xff0c;发现原 Outlook 中的账号信息无法迁移&#xff0c;需要耗费大量时间手动配置邮箱使用的邮箱&#xff1a;微软 O365 邮箱、qq 邮箱、163 邮箱、公司私有邮箱 …

北斗卫星引领农机春耕新时代

北斗卫星引领农机春耕新时代 随着现代科技的快速发展&#xff0c;北斗卫星成为了农业领域不可或缺的利器。在农机自动驾驶系统的引领下&#xff0c;农机正逐渐实现自主操作&#xff0c;为农民节省了大量的时间和精力&#xff0c;并最大限度地提高了农作物的产量和质量。 北斗…

MySQL | 内置函数

目录 1. 日期函数 2. 字符串函数 3. 数学函数 4. 其他函数 4.1. USER()查询当前用户 4.2. MD5(STR)对一个字符串进行md5摘要 4.3. DATABASE()显示当前正在使用的数据库 4.4. PASSWORD()函数&#xff0c;MySQL使用该函数对用户进行加密 4.5. IFNULL(VAL1, VAL2)如果VAL1…

window下安装并使用nvm(含卸载node、卸载nvm、全局安装npm)

window下安装并使用nvm&#xff08;含卸载node、卸载nvm、全局安装npm&#xff09; 一、卸载node二、安装nvm三、配置路径和下载源四、使用nvm安装node五、nvm常用命令六、卸载nvm七、全局安装npm、cnpm八、遇到的问题 nvm 全名 node.js version management&#xff0c;顾名思义…

Vivado中的五种仿真模式比较

Vivado中的五种仿真模式 在数字电路设计过程中&#xff0c;通常涵盖三个主要阶段&#xff1a;源代码编写、综合处理以及电路的实现&#xff0c;相应地&#xff0c;电路仿真的应用也与这些阶段紧密相关。根据不同设计阶段的需求&#xff0c;仿真可以被划分为RTL行为级仿真、综合…

练习4-权重衰减(李沐函数简要解析)

环境:练习1的环境 代码详解 0.导入库 import torch from torch import nn from d2l import torch as d2l1.初始化数据 这里初始化出train_iter test_iter 可以查一下之前的获取Fashion数据集后的数据格式与此对应 n_train, n_test, num_inputs, batch_size 20, 100, 200, …

三款.NET代码混淆工具比较分析:ConfuserEx、Obfuscar和Ipa Guard

随着.NET应用程序的广泛应用&#xff0c;保护知识产权和防止逆向工程的需求逐渐增长。本文将详细介绍三款知名的.NET代码混淆工具&#xff1a;ConfuserEx、Obfuscar和Ipa Guard&#xff0c;帮助读者全面了解其功能特点和应用场景。 一、ConfuserEx ConfuserEx是一个.NET代码混…

文件太大无法拷贝到u盘怎么办?可能是这个隐藏原因~

有时候我们新买的U盘&#xff0c;明明有64G的存储空间&#xff0c;怎么一个4.5G的视频想保存到U盘里会提示“文件太大”无法保存呢&#xff1f; 常见的U盘格式有FAT、FAT32、NTFS等&#xff0c;其中FAT32不支持存储单个文件体积大于4G的文件。下面教大家如何对U盘格式进行检测呢…

二开飞机机器人群发,实现自动给多个频道发送消息

频道1 频道2 二开代码部分&#xff1a; const CChatIdListprocess.env.CHANNEL_CHAT_ID_LIST; var channelChatIdArray CChatIdList.split(,);channelChatIdArray.forEach(function(item) {console.log(item); // 这里可以替换为您需要对数组中每个值进行的操作bot.sendM…

个人开发App成功上架手机应用市场的关键步骤

目录 1. 苹果审核和APP备案 2. APP上架操作步骤 3. 审核和发布 4. 上线工作 总结 参考资料 在当前移动应用市场竞争激烈的背景下&#xff0c;个人开发App如何成功上架成为开发者们必须面对的重要任务。本文将重点介绍自建App上架至手机应用市场的流程&#xff0c;包括苹果…

String类型详解

1. Java为何要创造String类 在C语言中,是没有String这个类型的,通常使用字符数组中存放一个个字符,再加上最后一个\0来表示/存放一个字符串.也可以使用一个字符指针指向字符串的首元素,直到遇到\0停止,再加上C语言头文件string.h中封装的函数,对于字符串的操作已经够用了. Java…

学完Python的7大就业方向,哪个赚钱最多?

“ 我想学Python&#xff0c;但是学完Python后都能干啥 &#xff1f;” “ 现在学Python&#xff0c;哪个方向最简单&#xff1f;哪个方向最吃香 &#xff1f;” “ …… ” 相信不少Python的初学者&#xff0c;都会遇到上面的这些问题。大家都知道Python很吃香&#xff0c;薪资…

进程管理与计划任务管理

进程管理 1、查看进程 静态 ps -auxps -elf 动态 top pgrep 查看特定条件的进程 pgrep -l “log” pgrep -l "ssh"pgrep -l -U redhat pstree 查看进程树 pstree -aup 所有用户、进程id\详细信息pstree -ap redhat 查看redhat用户的进程 任务调…

如何实现在固定位置的鼠标连点

鼠大侠的鼠标连点功能是免费的 浏览器搜索下载鼠大侠&#xff0c;指定连点间隔和启动快捷键 点击设置&#xff0c;指定点击位置