目录
  1. 1. 一、属性服务概述
    1. 1.1. 1.1 什么是 Android 属性系统
    2. 1.2. 1.2 属性系统在系统启动中的角色
  2. 2. 二、旧版属性系统架构(Android 9 及之前)
    1. 2.1. 2.1 架构概览
    2. 2.2. 2.2 核心数据结构
    3. 2.3. 2.3 属性读取流程 (getprop)
    4. 2.4. 2.4 属性写入流程 (setprop)
  3. 3. 三、新一代属性系统(Android 10+ Property ABI)
    1. 3.1. 3.1 为什么要重新设计
    2. 3.2. 3.2 新版架构
    3. 3.3. 3.3 property_contexts 文件
    4. 3.4. 3.4 按命名空间隔离——Treble 兼容性
    5. 3.5. 3.5 新版本中的数据结构变化
  4. 4. 四、属性的生命周期
    1. 4.1. 4.1 属性来源
    2. 4.2. 4.2 属性文件格式
    3. 4.3. 4.3 持久化属性 (persist.*)
  5. 5. 五、属性变化通知与触发器机制
    1. 5.1. 5.1 init 端的 epoll 通知
    2. 5.2. 5.2 属性触发器在 init.rc 中的使用
  6. 6. 六、ctl.* 控制属性:驱动服务启动/停止
    1. 6.1. 6.1 ctl.start / ctl.stop 机制
    2. 6.2. 6.2 init 端的处理
    3. 6.3. 6.3 控制属性与 SELinux 权限
  7. 7. 七、Java 层 SystemProperties API
    1. 7.1. 7.1 SystemProperties 类
    2. 7.2. 7.2 JNI 层实现
  8. 8. 八、重要系统属性详解
    1. 8.1. 8.1 ro.* (Read-Only)属性
    2. 8.2. 8.2 persist.* 属性
    3. 8.3. 8.3 sys.* 属性
    4. 8.4. 8.4 debug.* 属性
  9. 9. 九、SELinux 与属性安全
    1. 9.1. 9.1 属性权限控制模型
    2. 9.2. 9.2 属性写入的 SELinux 检查流程
  10. 10. 十、内核参数传递与 First Stage 属性
    1. 10.1. 10.1 内核命令行参数的传递
    2. 10.2. 10.2 DT (Device Tree) 属性
  11. 11. 十一、核心面试题
【吃透源码系列】之Android系统启动(四)属性服务

Android 属性服务(Property Service)是 Android 系统中一个极其重要但又容易被人忽视的底层基础设施。从系统启动到应用运行,从 getprop / setprop 命令行工具到 SystemProperties Java API,属性系统承载了全局配置、进程通信和系统状态管理的职责。本文将基于 Android 11 (API 30) 的 AOSP 源码,深入剖析属性服务的架构演进、实现原理和使用场景。

一、属性服务概述

1.1 什么是 Android 属性系统

Android 属性系统是一个全局的、基于键值对的系统配置存储和通信机制。它类似于 Linux 的 /proc 文件系统或 Windows 的注册表,但设计上更轻量,专门针对嵌入式设备优化。属性值在整个系统范围内可见,可以被任何进程读取(受 SELinux 策略限制),但只能由 init 进程写入。

核心特点:

  • 全局共享:所有进程可读取同一份属性数据(通过共享内存 mmap)
  • 写入集中化:只有 init 进程的 property_service 能实际修改属性值,其他进程通过 socket 向 init 发送写入请求
  • 不可持久化(默认):普通属性在重启后丢失,只有 persist.* 前缀的属性会写入闪存
  • 变更通知:属性值变化时,可通过 epoll 机制通知监听者

1.2 属性系统在系统启动中的角色

在 init 进程的第二阶段(SecondStageMain)中,属性服务是第一批被初始化的核心组件:

// system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {
// ... SELinux 初始化 ...

// 第一步:初始化属性服务的基础设施(创建共享内存、加载默认属性)
property_init();

// 第二步:处理内核命令行和 DT 中的属性
process_kernel_dt();
process_kernel_cmdline();

// 第三步:导出内核启动属性
export_kernel_boot_props();

// 第四步:设置启动时间戳等属性
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));

// 第五步:加载持久化属性,启动属性服务 socket
property_load_boot_defaults();
start_property_service();

// 之后才能处理 rc 文件(其中大量使用属性触发器)
LoadBootScripts(am, sm);
// ...
}

二、旧版属性系统架构(Android 9 及之前)

2.1 架构概览

在 Android 9 及更早版本中,属性系统采用经典的”init 集中写”模式:

┌──────────────┐    setprop    ┌──────────────┐
│ Any Process │──────────────►│ init (PID1) │
│ │ socket │ │
│ (client) │ │ property_ │
│ │◄──────────────│ service │
└──────────────┘ getprop └──────┬───────┘
│ (mmap) │ write
▼ ▼
┌──────────────────────────────────────────┐
│ /dev/__properties__ │
│ (Android Shared Memory - ASHMEM) │
│ ┌────────────────────────────────────┐ │
│ │ prop_info_area: │ │
│ │ prop_bt[0..n] // binary tree │ │
│ │ prop_info[0..n] // per-property │ │
│ │ name+value strings │ │
│ └────────────────────────────────────┘ │
└──────────────────────────────────────────┘

关键源码路径:

  • system/core/init/property_service.cpp — init 端属性服务
  • system/core/libcutils/properties.cpp — libcutils 的属性读写 API
  • bionic/libc/bionic/system_property_api.cpp — bionic C API(__system_property_get/set)
  • frameworks/base/core/java/android/os/SystemProperties.java — Java 层 API

2.2 核心数据结构

旧版属性系统使用 trie(前缀树)来组织属性键名,但是一个简化的二叉查找树实现:

// bionic/libc/include/sys/_system_properties.h (旧版)
struct prop_area {
unsigned volatile count;
unsigned volatile serial;
unsigned magic;
unsigned version;
unsigned reserved[4];
prop_bt to_prop_bt_node[0]; // 二叉查找树根节点数组
};

struct prop_bt {
uint32_t namelen;
volatile uint32_t prop; // 指向 prop_info 的偏移量
volatile uint32_t left; // 左子节点偏移
volatile uint32_t right; // 右子节点偏移
volatile uint32_t children; // 子节点偏移
char name[0];
};

struct prop_info {
volatile uint32_t serial; // 序列号(每次更新递增,用于检测变化)
volatile uint64_t value_off; // value 字符串在 prop_area 中的偏移量
char name[0];
};

整个属性区域的内存布局:

[prop_area header]
[prop_bt root nodes]
[... more prop_bt nodes ...]
[... prop_info entries ...]
[... name strings ...]
[... value strings ...]

2.3 属性读取流程 (getprop)

由于属性区域是只读共享内存,读取过程不需要与 init 进程通信:

// bionic/libc/bionic/system_property_api.cpp
int __system_property_get(const char* name, char* value) {
// 1. 查找 prop_info
const prop_info* pi = __system_property_find(name);
if (pi == nullptr) return 0;

// 2. 从共享内存中读取值
return __system_property_read(pi, nullptr, value);
}

// bionic/libc/bionic/system_property_read.cpp
int __system_property_read(const prop_info* pi, char* name, char* value) {
// 使用序列号机制保证读一致性:如果读取过程中值被修改了,重试
while (true) {
uint32_t serial = pi->serial;
// 从 prop_area 中读取 value 字符串
memcpy(value, ...);
// 检查序列号是否变化(变化说明在读取过程中被 init 修改了)
if (serial == pi->serial) break;
}
}

2.4 属性写入流程 (setprop)

写入必须通过 init 进程中转:

// system/core/libcutils/properties.cpp
int property_set(const char* key, const char* value) {
// 向 init 的属性服务 socket 发送 PROP_MSG_SETPROP2 消息
return __system_property_set_filename(keyfd, key, value);
}

init 进程端接收并处理写入请求:

// system/core/init/property_service.cpp (旧版)
static void handle_property_set_fd() {
// 1. 从 socket 接收消息
prop_msg msg;
recv(property_set_fd, &msg, sizeof(msg), 0);

// 2. SELinux 权限检查
if (check_mac_perms(msg.name, ...)) {
// 3. 更新共享内存中的属性
__system_property_update(pi, msg.value, msg.value_len);

// 4. 如果是 persist.* 属性,写入持久化文件
if (strncmp(msg.name, "persist.", 8) == 0) {
write_persistent_property(msg.name, msg.value);
}

// 5. 通知 epoll 监听者(属性变化触发器)
property_changed(msg.name, msg.value);
}
}

三、新一代属性系统(Android 10+ Property ABI)

3.1 为什么要重新设计

旧版属性系统存在几个固有问题:

  1. 安全粒度不足:所有进程可以读取同一块共享内存中的所有属性,只能通过 SELinux 控制 set,无法控制 get
  2. 写时竞争:init 写入时,读进程可能读到不完整的数据(虽然有序列号重试,但本质是 workaround)
  3. 可扩展性:只有一块共享内存,无法按安全域隔离
  4. 性能问题:序列号自旋在极端并发场景下效率低

Google 在 Android 10 中重写了属性系统,引入了 property_contexts 文件来控制每个属性的读/写权限,并将属性按命名空间进行隔离。

3.2 新版架构

┌──────────────────────────────────────────────────────┐
│ init (PID=1) │
│ ┌─────────────────────────────────────────────┐ │
│ │ property_service │ │
│ │ (唯一写入者,接收所有 setprop 请求) │ │
│ └──────────────┬──────────────────────────────┘ │
│ │ 管理 │
└─────────────────┼─────────────────────────────────────┘

┌─────────────┼─────────────┬──────────────────┐
▼ ▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌──────────┐ ┌──────────────┐
│ system │ │ vendor │ │ product │ │ system_ext │
│ props │ │ props │ │ props │ │ props │
│ (mmap) │ │ (mmap) │ │ (mmap) │ │ (mmap) │
└─────────┘ └─────────┘ └──────────┘ └──────────────┘
▲ ▲ ▲ ▲
│ │ │ │
/dev/ /dev/ /dev/ /dev/
__properties__/system __properties__/vendor ...

Android 10+ 将属性区域拆分为多个独立的文件:

  • /dev/__properties__/property_info — 属性元信息(名称到区域的映射)
  • /dev/__properties__/system/build.prop 映射的属性 → system 区域
  • /dev/__properties__/vendor/build.prop 映射的属性 → vendor 区域
  • 等等

3.3 property_contexts 文件

property_contexts 是 SELinux 策略的一部分,定义了哪些属性属于哪个安全标签,从而控制不同进程对属性的访问权限。

# system/sepolicy/private/property_contexts
ro.boot. u:object_r:bootloader_prop:s0 exact string
sys.usb. u:object_r:system_radio_prop:s0 exact string
persist.audio. u:object_r:audio_prop:s0 exact string
log.tag. u:object_r:debug_prop:s0 exact string
ctl. u:object_r:ctl_prop:s0 exact string

格式说明:

  • ro.boot. — 前缀匹配(以 ro.boot. 开头的所有属性)
  • exact string — 精确字符串匹配类型
  • u:object_r:bootloader_prop:s0 — SELinux 安全上下文标签

进程要读取一个属性时,需要具有对应 SELinux type 的访问权限。例如,只有具有 bootloader_prop 读权限的进程才能读 ro.boot.* 属性。

3.4 按命名空间隔离——Treble 兼容性

VTS (Vendor Test Suite) 要求 vendor 和 system 的接口独立演进。属性隔离是这一要求的具体体现:

// system/core/property_service/libpropertyinfoserializer/PropertyInfo.cpp
// property_info 文件定义了每条属性的上下文(读哪个 mmap 文件、SELinux 标签等)
struct PropertyInfoEntry {
std::string name; // 属性名
std::string filename; // 所在的属性文件路径
std::string prefix; // 前缀
uint32_t context; // SELinux 上下文索引
};

当一个进程尝试读取某个属性时,属性系统首先查找 property_info 找到该属性所在的 mmap 文件,然后仅从该文件中读取数据。系统进程通常映射 system 和 vendor 两份区域,而 vendor 进程可能只映射 vendor 区域。

3.5 新版本中的数据结构变化

Android 10+ 重新设计了属性内存布局,使用更高效的哈希表而不是二叉查找树:

// system/core/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
struct PropertyInfoArea {
uint32_t current_version;
uint32_t magic;
uint32_t serial;
// ... 哈希表入口 ...
};

属性读取 API 的位置也发生了迁移:

  • 旧版:bionic/libc/bionic/system_property_api.cpp(在 bionic 中)
  • 新版:system/core/libsystem/libsystem_properties.cpp(在 libsystem 中,向下兼容封装)

四、属性的生命周期

4.1 属性来源

系统中的属性有多种来源,按加载顺序排列:

第一阶段(init 第一阶段,挂载文件系统阶段):

// system/core/init/init_first_stage.cpp
// 在内核命令行和 DT 中传递的属性
property_set("ro.boot.hardware", hardware.c_str());
property_set("ro.boot.serialno", serialno.c_str());

第二阶段(init 第二阶段,属性服务初始化后):

// system/core/init/init.cpp
// 内核属性传播
export_kernel_boot_props();
// 从以下文件加载默认属性
property_load_boot_defaults();

property_load_boot_defaults() 会加载多个属性文件:

// system/core/init/property_service.cpp
void property_load_boot_defaults() {
// 按顺序加载,后加载的文件可覆盖前面的值
load_properties_from_file("/system/etc/prop.default", nullptr);
load_properties_from_file("/system/build.prop", nullptr);
load_properties_from_file("/vendor/build.prop", nullptr);
load_properties_from_file("/product/build.prop", nullptr);
load_properties_from_file("/system_ext/build.prop", nullptr);
load_properties_from_file("/vendor/default.prop", nullptr);
load_properties_from_file("/odm/etc/build.prop", nullptr);
// ...
// 加载持久化属性
load_persistent_properties();
}

运行阶段(应用可以通过 API 设置属性):

// Java 层
SystemProperties.set("my.debug.flag", "true");
// Native 层
property_set("my.debug.flag", "true");

4.2 属性文件格式

build.prop 文件格式很简单,每行一个键值对:

# /system/build.prop (示例)
ro.build.version.sdk=30
ro.build.version.release=11
ro.product.model=Pixel 4
ro.build.fingerprint=google/flame/flame:11/.../

解析实现:

// system/core/init/property_service.cpp
static void load_properties_from_file(const char* filename, const char* filter) {
std::string content;
if (!android::base::ReadFileToString(filename, &content)) {
return;
}
// 逐行解析
for (const auto& line : android::base::Split(content, "\n")) {
// 跳过注释和空行
if (line.empty() || line[0] == '#') continue;
// 解析 key=value
std::string::size_type eq = line.find('=');
if (eq != std::string::npos) {
std::string key = android::base::Trim(line.substr(0, eq));
std::string value = android::base::Trim(line.substr(eq + 1));
property_set(key.c_str(), value.c_str());
}
}
}

4.3 持久化属性 (persist.*)

persist. 开头的属性会在写入时被立即保存到 flash 存储中,重启后仍然有效:

// system/core/init/persistent_properties.cpp
void WritePersistentProperty(const std::string& name, const std::string& value) {
// 持久化属性存储路径
std::string temp_path = PERSISTENT_PROPERTY_DIR "/" + name + ".tmp";
std::string final_path = PERSISTENT_PROPERTY_DIR "/" + name;

// 1. 原子写入:先写临时文件
android::base::WriteStringToFile(value, temp_path);

// 2. rename 原子替换
rename(temp_path.c_str(), final_path.c_str());
}

持久化属性文件存储在 /data/property/ 目录下(Android 9+)。每个属性一个独立文件,文件名为属性名,内容为属性值。

五、属性变化通知与触发器机制

5.1 init 端的 epoll 通知

当属性值被修改时,init 进程会向 epoll fd 写入通知,唤醒正在等待属性变化的代码:

// system/core/init/property_service.cpp
void property_changed(const std::string& name, const std::string& value) {
// 如果 init 正在等待某个属性(如 waiting_for_prop)
if (name == "sys.something" && value == "1") {
// 通知 InitFatalReboot 等
}

// 向 property_fd 写入通知
if (property_fd >= 0) {
// 唤醒 epoll_wait 上的监听者
}
}

5.2 属性触发器在 init.rc 中的使用

init.rc 中大量使用 on property:<name>=<value> 触发器:

# system/core/rootdir/init.rc
on property:sys.boot_completed=1
# 启动 boot 动画结束后的服务
start bootanim_end

on property:sys.sysctl.extra_free_kbytes=*
write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}

on property:persist.sys.usb.config=*
start adbd

init 的主事件循环会在 epoll_wait 返回后检查是否有属性触发器被满足:

// system/core/init/action_manager.cpp
void ActionManager::QueuePropertyTrigger(const std::string& name,
const std::string& value) {
// 遍历所有 Action,检查是否有 property 触发器匹配
for (auto& action : actions_) {
if (action->CheckPropertyTrigger(name, value)) {
action->QueueAllCommands(current_executing_);
}
}
}

六、ctl.* 控制属性:驱动服务启动/停止

6.1 ctl.start / ctl.stop 机制

Android 属性系统中有一类特殊的属性,前缀为 ctl.(control),用于通过属性机制间接控制服务的启动和停止:

# 通过 setprop 启动 zygote_secondary 服务
setprop ctl.start zygote_secondary

# 通过 setprop 停止 surfaceflinger 服务
setprop ctl.stop surfaceflinger

注意:ctl.startctl.stop 不与普通的 ro.*sys.* 属性一样存储在共享内存中。它们是非持久化的控制消息。

6.2 init 端的处理

// system/core/init/property_service.cpp
static uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, ...) {
if (android::base::StartsWith(name, "ctl.")) {
// 这是控制属性,不在共享内存中存储值
HandleControlMessage(name.c_str() + 4, value, source_context, cr);
return PROP_SUCCESS;
}
// 普通属性:更新共享内存...
}

void HandleControlMessage(const std::string& msg, const std::string& name,
const std::string& source_context, ...) {
Service* svc = ServiceList::GetInstance().FindService(name);
if (svc == nullptr) {
LOG(ERROR) << "Control message: Could not find '" << name << "' for ctl." << msg;
return;
}

if (msg == "start") {
svc->Start();
} else if (msg == "stop") {
svc->Stop();
} else if (msg == "restart") {
svc->Restart();
}
}

6.3 控制属性与 SELinux 权限

控制属性是系统的薄弱点,因为它可以直接启动/停止关键服务。Android 通过 SELinux 严格控制哪些进程可以设置 ctl.* 属性:

# system/sepolicy/public/init.te
# 只有 init 自身和某些特权系统进程才能设置 ctl.* 属性
set_prop(init, ctl_prop)
set_prop(system_server, ctl_prop)

七、Java 层 SystemProperties API

7.1 SystemProperties 类

Android 框架层通过 SystemProperties 类提供 Java 语言的属性访问接口:

// frameworks/base/core/java/android/os/SystemProperties.java
public class SystemProperties {
// 通过 JNI 调用 native property_get/property_set
public static String get(String key, String def) {
// ...
}

public static int getInt(String key, int def) {
// ...
}

public static boolean getBoolean(String key, boolean def) {
// ...
}

public static long getLong(String key, long def) {
// ...
}

public static void set(String key, String val) {
// ...
}
}

7.2 JNI 层实现

// frameworks/base/core/jni/android_os_SystemProperties.cpp
static jstring SystemProperties_getSS(
JNIEnv* env, jclass clazz, jstring keyJ, jstring defJ) {
const char* key = env->GetStringUTFChars(keyJ, NULL);
const char* def = env->GetStringUTFChars(defJ, NULL);

// 使用 libcutils 的 property_get
char buf[PROP_VALUE_MAX];
int len = property_get(key, buf, def);
// PROP_VALUE_MAX = 92,限制属性值最大 91 个字符 + '\0'

env->ReleaseStringUTFChars(keyJ, key);
env->ReleaseStringUTFChars(defJ, def);

return env->NewStringUTF(buf);
}

注意 PROP_VALUE_MAX = 92 这个限制:Android 属性值的最大长度为 91 个字符(加 null 终止符共 92 字节)。这意味着属性不适合存储大数据,只适合存储短配置值。

八、重要系统属性详解

8.1 ro.* (Read-Only)属性

ro. 开头的属性一旦设置就不能再修改。这些属性通常在系统启动早期(init 第一阶段)由 bootloader 或内核命令行设置,或者从 build.prop 文件加载:

ro.boot.hardware       # 硬件平台名
ro.build.version.sdk # SDK 版本号 (API level)
ro.product.model # 产品型号
ro.build.fingerprint # 构建指纹(用于 OTA 更新验证)
ro.build.type # 构建类型 (user/userdebug/eng)
ro.debuggable # 是否可调试
ro.secure # 安全模式标志

read-only 属性的实现:

// system/core/init/property_service.cpp
static bool IsReadOnlyProperty(const std::string& name) {
return android::base::StartsWith(name, "ro.");
}

static uint32_t HandlePropertySet(const std::string& name, ...) {
// ...
if (IsReadOnlyProperty(name)) {
LOG(ERROR) << "Read-only property was already set";
return PROP_ERROR_READ_ONLY_PROPERTY;
}
// ...
}

8.2 persist.* 属性

如前面所述,持久化属性保存在 /data/property/ 目录中。常用场景:

persist.sys.locale          # 系统语言设置
persist.sys.timezone # 时区
persist.audio.fluence.speaker # 音频降噪配置
persist.sys.usb.config # USB 配置模式

8.3 sys.* 属性

sys. 前缀的属性表示系统运行时状态,不是只读也不是持久化,可以被系统服务修改。常见的有:

sys.boot_completed      # 启动完成标志 (值为 "1")
sys.usb.state # USB 状态 (如 "mtp,adb")
sys.usb.config # USB 配置
sys.boot.reason # 启动原因
sys.powerctl # 电源控制 (shutdown/reboot)

8.4 debug.* 属性

用于调试和开发,通常在 eng/userdebug 构建中可用:

debug.atrace.tags.enableflags   # atrace 启用标志
debug.force_rtl # 强制 RTL 布局
debug.hwui.renderer # HWUI 渲染器选项

九、SELinux 与属性安全

9.1 属性权限控制模型

SELinux 通过以下机制控制属性访问:

  1. prop_contexts:定义每个属性的 SELinux type
  2. property.te:定义属性的 SELinux type
  3. 各 .te 文件:定义各域对属性的 set/get 权限
# system/sepolicy/public/property.te
type system_prop, property_type;
type ctl_prop, property_type;
type bootloader_prop, property_type;
type audio_prop, property_type;
# ...

# system/sepolicy/private/system_server.te
get_prop(system_server, audio_prop) # system_server 可以读取 audio 属性
set_prop(system_server, system_prop) # system_server 可以设置 system 属性

9.2 属性写入的 SELinux 检查流程

// system/core/init/property_service.cpp
static bool CheckMacPerms(const std::string& name, const char* target_context,
const char* source_context, ...) {
if (!selinux_enabled) return true;

// 1. 获取属性的安全上下文(type)
// 通过 property_contexts 查找匹配项
char* prop_type = nullptr;
property_info_area->GetPropertyInfo(name.c_str(), &prop_type, ...);

// 2. 调用 SELinux 检查
bool has_access = check_mac_perms(target_context, source_context,
prop_type, name.c_str());
return has_access;
}

十、内核参数传递与 First Stage 属性

10.1 内核命令行参数的传递

Android 系统通过内核命令行向 init 传递初始属性:

# 来自 bootloader 的典型内核命令行
androidboot.hardware=flame androidboot.boot_devices=soc/1d84000.ufshc
androidboot.serialno=XXXXXXXX androidboot.slot_suffix=_a
skip_initramfs rootwait ro init=/init

在 init 第一阶段,这些参数被解析为早期的 ro.boot.* 属性:

// system/core/init/init_first_stage.cpp
static void SetInitAvbVersionInRecovery() {
// 将 androidboot.xxx 转换为 ro.boot.xxx 属性
// 如 androidboot.hardware=flame → ro.boot.hardware=flame
}

10.2 DT (Device Tree) 属性

除了内核命令行,Android 也支持从 Device Tree 中读取属性:

// system/core/init/init.cpp
static void process_kernel_dt() {
// 读取 /proc/device-tree/firmware/android/compatible
// 将 DT 中的属性导入系统属性
// 注意:DT 属性的优先级高于命令行属性
}

十一、核心面试题

Q1:Android 属性系统为什么采用”集中写、任意读”的架构,而不是像传统文件系统那样使用文件锁?

答:这种设计有几个考量:(1) 性能:读取路径走 mmap 共享内存,零拷贝、零系统调用、零上下文切换,极其高效;(2) 一致性:所有写入都由唯一的 init 进程串行处理,避免了锁竞争、写写冲突和死锁问题;(3) 安全性:集中写入使得权限检查集中在一点,init 可以在写入前统一执行 SELinux 检查;(4) 变更通知:集中写入使得属性变更时 init 可以统一通知所有监听者(如 init.rc 触发器)。

Q2:property_set()__system_property_set() 有什么关系?为什么有两个 API?

答:property_set()(在 libcutils 中)是高层 API,它向 init 的 property socket 发送消息,init 端收到后执行 SELinux 检查、更新共享内存、处理持久化等。__system_property_set()(在 bionic 中)是底层 API,它直接写入共享内存但不经过 init 的权限检查。__system_property_set() 只能被 init 进程自身调用(init 已经是特权进程),其他进程调用会因为没有 SELinux 写权限而失败。实际代码中,除 init 之外的所有进程都应该使用 property_set()

Q3:Android 10 引入的 Property ABI 改变对 Treble 兼容性有什么帮助?

答:Property ABI 改革将属性按分区隔离(system/vendor/product),确保 vendor 进程只能访问 vendor 分区定义的属性,不能访问 system 分区的内部属性。这解决了 Treble 之前的一个核心痛点——vendor 实现可能依赖未文档化的 system 属性,当 system 升级时这些属性消失或变化导致兼容性断裂。现在通过 property_contexts 和分区隔离,VTS 可以自动验证 vendor 实现只使用了已声明为 public 的属性接口。

Q4:PROP_VALUE_MAX 为什么是 92 字节?如果属性值需要存更长的数据怎么办?

答:92 字节是历史遗留的设计限制,源自 Android 早期共享内存固定页大小(128 字节结构体,部分用于元数据,剩余 92 给值)。这个限制一直延续到 Android 10+ 新版本。如果需要存储更长的数据,有几种替代方案:(1) 使用 persist.* 属性 + 文件存储组合(属性中存文件路径);(2) 使用 SystemConfig / SettingsProvider 数据库;(3) 使用自定义 native 服务通过 Binder 通信。属性系统的设计初衷是存储简短的全局配置项,长数据不应该放在语义简单的键值对系统中。

Q5:当系统同时存在 ro.boot.hardware(命令行设置)和 DT 中相同的属性时,哪个优先级更高?为什么?

答:DT 中设置的属性优先于命令行属性。从安全角度考虑,bootloader 和 DT 是片上系统中最受信任的组件(在 TCB 中),而内核命令行可能被攻击者通过修改 boot image 轻易篡改。因此 Android init 采用了 DT 优先级高于内核命令行的策略。实际上这个检查是通过 SELinux 的 neverallow 规则强制执行的——一旦一个 ro.boot.* 属性被设置,任何后续的写入尝试都会被 IsReadOnlyProperty() 拒绝,而加载的顺序是 DT 优先于命令行解析(process_kernel_dt()process_kernel_cmdline() 之前调用)。

AOSP 核心路径参考:

  • system/core/init/property_service.cpp — init 端属性服务(写入处理、属性文件加载)
  • system/core/init/property_service.h — property_init / start_property_service 声明
  • system/core/init/persistent_properties.cpp — 持久化属性读写
  • system/core/init/init.cpp — init 主函数(属性服务初始化流程)
  • system/core/property_service/libpropertyinfoserializer/ — Android 10+ 属性信息序列化
  • system/core/libcutils/properties.cpp — property_get / property_set API
  • bionic/libc/bionic/system_property_api.cpp — __system_property_get / __system_property_set
  • frameworks/base/core/java/android/os/SystemProperties.java — Java 层 API
  • frameworks/base/core/jni/android_os_SystemProperties.cpp — JNI 实现
  • system/sepolicy/private/property_contexts — 属性的 SELinux 上下文定义
  • system/sepolicy/public/property.te — 属性 type 声明
打赏
  • 微信
  • 支付宝

评论