深度解析 Android 系统属性

news2025/2/25 18:03:45

目录

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 path
        if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
            // Try legacy path
            load_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/bash

partition="$1"

if [ "$#" -ne 1 ]; then
  echo "Usage: $0 <partition>" 1>&2
  exit 1
fi

echo "# 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-set
	BUILD_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/Makefile

ifdef 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 shell

rice14:/ # 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)
endif

FINAL_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.coloradmin.cn/o/1528853.html

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

相关文章

【C++】string 类---字符判断与大小写转换(超详细解析!)

目录 一、string 类的介绍 二、字符大小写转换与判断常用函数 &#x1f4a6; 字符大小写判断 ① isalpha() ② isalnum() ③ isdigit() ④ islower() ⑤ isupper() &#x1f4a6; 字符大小写转换 ① tolower() ✨方法一&#xff1a; ✨方法二&#xff1a; ② toupper() ✨方…

为什么说金融/财务人需要学Python??

Python 是财务人和金融人的多功能工具箱&#xff01;它有非常多的细分应用&#xff0c;接下来我将给你详细介绍为什么财务、金融方向需要学 Python 以及有哪些方向的细分应用&#xff01; 一、为什么金融/财务人需要 Python&#xff1f; 想在行业中崭露头角&#xff1f;那你可…

106 基于消息队列来做 mysql 大数据表数据的遍历处理

前言 最近有这样的一个需求, 我们存在一张 很大的 mysql 数据表, 数据量大概是在 六百万左右 然后 需要获取所有的记录, 将数据传输到 es 中 然后 当时 我就写了一个脚本来读取 这张大表, 然后 分页获取数据, 然后 按页进行数据处理 转换到 es 但是存在的问题是, 前面 还…

CTF题型 SSTI(2) Flask-SSTI典型题巩固

CTF题型 SSTI(2) Flask-SSTI典型题巩固 文章目录 CTF题型 SSTI(2) Flask-SSTI典型题巩固前记1.klf__sstiSSTI_Fuzz字典&#xff08;网上收集自己补充&#xff09; 2.klf_2数字问题如何解决了&#xff1f;|count |length都被禁&#xff1f; 3.klf_3 前记 从基础到自己构造paylo…

java String的深入了解

1、String 概述 &#xff08;1&#xff09;String 类在 java.lang 包下&#xff0c;所以使用的时候不需要导包。 &#xff08;2&#xff09;String 类代表字符串&#xff0c;Java程序中的所有字符串文字&#xff08;例如“abc”&#xff09;都被实现为此类的实例。也就是说&a…

【漏洞复现】用友U8Cloud base64 SQL注入漏洞

0x01 产品简介 用友U8 Cloud是用友推出的新一代云ERP&#xff0c;主要聚焦成长型、创新型企业&#xff0c;提供企业级云ERP整体解决方案。 0x02 漏洞概述 用友U8 Cloud 存在SQL注入漏洞&#xff0c;未授权的攻击者可通过此漏洞获取数据库权限&#xff0c;从而盗取用户数据&a…

Leetcode 48. 旋转图像

心路历程&#xff1a; 第一次需要这种类型的题。 一开始从双指针、递归、栈队的角度去思考问题&#xff0c;没有发现明显的特征。 后来想到这个算是二维数组问题&#xff0c;应该也是双指针的一种。 总感觉有什么妙招可以一下子解决&#xff0c;但是没想出来就去找的网上的答案…

Android 之 GMS 认证知多少?

GMS认证 1.什么是GMS GMS全称Google Mobile Service&#xff0c;谷歌移动服务。 为什么要通过GMS认证 Android 系统是开源的&#xff0c;但是 Google 针对GMS所提供的服务却是收费的&#xff0c;比如Google Map&#xff0c;Google Play&#xff0c;Youtube&#xff0c;Gmai…

Stable Diffusion + Segment Anything试用

安装 从continue-revolution/sd-webui-segment-anything安装插件分割模型下载后放到这个位置&#xff1a;${sd-webui}/extension/sd-webui-segment-anything/models/sam下&#xff0c;可以下载3个不同大小的模型&#xff0c;从大到小如下&#xff1a;vit_h is 2.56GB, vit_l i…

嵌入式汇编语言简介

嵌入式汇编语言是一种在嵌入式系统开发中广泛使用的编程语言&#xff0c;它直接操作底层硬件资源&#xff0c;具有高效性和灵活性。本文将介绍嵌入式汇编语言的基本概念、特点以及应用场景。 以下是我整理的关于嵌入式开发的一些入门级资料&#xff0c;免费分享给大家&#xf…

零售饮料企业通过精准铺货与动态调整,结合指标平台的智能分析,实现对线下渠道的全面掌控

作为一名消费者&#xff0c;炎热的夏天我们会走进一家便利店&#xff0c;从冰柜中选出一瓶汽水&#xff1b;下午工作有点累了&#xff0c;我们会在公司的自动贩卖机扫码买一瓶快乐水......零售品牌从线上到线下渠道都开展了激烈的竞争&#xff0c;从供应链、物流到销售环节&…

转座子插入序列分析1-GENE-IS分析管道

如果你使用 GENE-IS: Saira Afzal et al。 &#xff0c;2016请引用这篇研究文章。GENE-IS: time-efficient and accurate analysis of viral integration events in large-scale gene therapy data. Molecular Therapy - Nucleic Acids 2016, vol. 6:133-139. DOI:https://doi.…

规划系列的常见术语:龙格现象、控制点、型值点和插值点、规划控制的开环、闭环、前馈、反馈与重规划

参考b站大佬Ally的规划控制系列 1 龙格现象 1.1 初探龙格现象 龙格现象由德国数学家Carl Runge&#xff08;卡尔龙格&#xff09;于1901年发现&#xff0c;龙格函数定义为&#xff1a; f ( x ) 1 25 x 2 1 f(x)\frac{1}{25 x^{2}1} f(x)25x211​ 我们在 [ − 1 , 1 ] [-1…

MNN createRuntime(二)

系列文章目录 MNN createFromBuffer&#xff08;一&#xff09; MNN createRuntime&#xff08;二&#xff09; MNN createSession 之 Schedule&#xff08;三&#xff09; MNN createSession 之创建流水线后端&#xff08;四&#xff09; MNN Session::resize 之流水线编码&am…

后端开发要不要转鸿蒙?

看到一条很有意思的提问&#xff0c;互联网太卷了&#xff0c;熬过了2023才发现&#xff0c;2024更难熬&#xff01;只因行业发展多年&#xff0c;人才过度饱和&#xff01;那后端的出路在哪里&#xff1f; 我推荐大家学【鸿蒙应用开发】新兴行业需求大&#xff0c;各大厂都在…

AI大模型智能大气科学探索之:ChatGPT在大气科学领域建模、数据分析、可视化与资源评估中的高效应用及论文写作

本文深度探讨人工智能在大气科学中的应用&#xff0c;特别是如何结合最新AI模型与Python技术处理和分析气候数据。课程介绍包括GPT-4等先进AI工具&#xff0c;旨在帮助大家掌握这些工具的功能及应用范围。本文内容覆盖使用GPT处理数据、生成论文摘要、文献综述、技术方法分析等…

camunda流程引擎事务管理和乐观锁

本文重点介绍camunda开源流程引擎的事务配置&#xff0c;以及在高并发多线程情况下&#xff0c;可能会发生多个线程尝试对相同流程实例数据进行更改的情况&#xff0c;Camunda如何通过数据库的乐观锁解决这种并发冲突的&#xff0c;并介绍了乐观锁和悲观锁的适用场景、性能影响…

MySQL数据库概念及安装

一、数据库的基本概率 1.1 数据 记录每个人的信息 或者记录数据 1.2 表 存放信息的集合 或者存放行和列的信息 1.3 数据库 表的集合 二、数据库管理系统&#xff08;DBMS&#xff09; 2.1 DBMS定义 &#xff08;DBMS&#xff09;是一种软件&#xff0c;用于创建和管理…

【Python脚本随手笔记】 --- 复制文件并修改权限

&#x1f48c; 所属专栏&#xff1a;【Python脚本随手笔记】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#…

HarmonyOS 通知意图

之前的文章 我们讲了 harmonyos 中的 基础和进度条通知 那么 今天 我们来说说 任何给通知添加意图 通知意图 简单说 就是 当我们点击某个通知 如下图 然后 就会拉起某个 应用 就例如说 我们某个微信好友发消息给我们 我们 点击系统通知 可以直接跳到你们的聊天界面 好 回到…