aosp开发中,遇到从上层做开关,hal层做判断的需要,比如控制摄像头使用。这时候就需要找一个属性可以在java和c c++ 层同时操作的属性,系统学习了下属性存储,在此记录!
1. 什么是 SystemProperties
SystemProperties 是 Android 系统提供的一种键值对存储机制,用于管理全局配置属性。这些属性通常用于控制系统行为、硬件功能或调试选项。它们在系统启动时由 init 进程加载,并在运行时可被修改(视权限而定)。
- 存储位置: 属性通常存储在内存中,部分持久化属性(如以 persist. 开头的)会保存到文件中(例如 /data/property/)。
- 访问方式: 提供 Java 和 C/C++ 两种 API,分别用于应用层和原生层。
- 权限: 系统属性分为只读和读写属性,修改通常需要系统权限(root 或 system 用户)。
2. Java 层:SystemProperties 类
2.1 原理
在 Java 层,SystemProperties 是 Android SDK 提供的一个工具类,位于 android.os.SystemProperties 包中。它通过 JNI(Java Native Interface)调用底层的 C/C++ 属性服务(property_service),间接与 Android 的属性系统交互。
- 底层实现: 调用 libandroid_runtime.so 中的 native 方法,最终映射到 libcutils 的 property_get 和 property_set 函数。
- 权限限制: 在普通应用中,SystemProperties.set 可能因权限不足而失败,通常需要系统签名或运行在系统进程中。
2.2 API
- 读取: SystemProperties.get(String key, String def)
- key: 属性名。
- def: 默认值(若属性不存在则返回)。
- 返回: 属性值的字符串。
- 写入: SystemProperties.set(String key, String value)
- key: 属性名。
- value: 要设置的值。
2.3 示例代码
import android.os.SystemProperties;
public class PropertyDemo {
private static final String KEY = "persist.sys.flyscale.cam";
// 读取属性
public static boolean isCameraEnabled() {
String value = SystemProperties.get(KEY, "1"); // 默认值为 "1"
return "1".equals(value);
}
// 写入属性
public static void setCameraEnabled(boolean enabled) {
try {
SystemProperties.set(KEY, enabled ? "1" : "0");
System.out.println("Set " + KEY + " to " + (enabled ? "1" : "0"));
} catch (Exception e) {
System.err.println("Failed to set property: " + e.getMessage());
}
}
public static void main(String[] args) {
// 测试读取
boolean enabled = isCameraEnabled();
System.out.println("Camera enabled: " + enabled);
// 测试写入
setCameraEnabled(false);
System.out.println("Camera enabled after set: " + isCameraEnabled());
}
}
3. C++ 层:property_get 和 property_set
3.1 原理
在 C++ 层,属性操作通过 <cutils/properties.h> 提供的函数实现。这些函数直接与 Android 的属性服务(property_service)通信,后者运行在 init 进程中,负责管理属性内存和持久化存储。
- 实现: 基于 socket 通信,property_get/set 通过 UNIX 域 socket 与 property_service 交互。
- 内存管理: 属性存储在共享内存中,property_service 维护一个键值对表。
- 持久化: 以 persist. 开头的属性会写入 /data/property/ 目录下的文件。
3.2 API
- 读取: int property_get(const char* key, char* value, const char* default_value)
- key: 属性名。
- value: 存储属性值的缓冲区。
- default_value: 默认值。
- 返回: 属性值的长度(成功时),或默认值的长度(失败时)。
- 写入: int property_set(const char* key, const char* value)
- key: 属性名。
- value: 要设置的值。
- 返回: 0 表示成功,非 0 表示失败。
3.3 示例代码
#include <cutils/properties.h>
#include <string.h>
#include <stdio.h>
// 检查 Flyscale 摄像头是否启用
static bool isFlyscaleCameraEnabled() {
char propValue[PROPERTY_VALUE_MAX];
property_get("persist.sys.flyscale.cam", propValue, "1"); // 默认启用
bool enabled = (strcmp(propValue, "1") == 0);
return enabled;
}
// 设置 Flyscale 摄像头启用状态
static int setFlyscaleCameraEnabled(bool enabled) {
const char* value = enabled ? "1" : "0";
int result = property_set("persist.sys.flyscale.cam", value);
if (result == 0) {
printf("Set persist.sys.flyscale.cam to %s\n", value);
} else {
printf("Failed to set property: %d\n", result);
}
return result;
}
int main() {
// 测试读取
bool enabled = isFlyscaleCameraEnabled();
printf("Camera enabled: %d\n", enabled);
// 测试写入
setFlyscaleCameraEnabled(false);
printf("Camera enabled after set: %d\n", isFlyscaleCameraEnabled());
return 0;
}
4. 理论对比
特性 | Java 层 (SystemProperties) | C++ 层 (property_get/set) |
---|---|---|
调用方式 | 通过 JNI 间接调用 | 直接调用原生函数 |
适用场景 | 应用层(Activity、服务等) | 原生层(HAL、系统服务等) |
返回值 | 字符串(String) | 长度(int)或状态码 |
缓冲区管理 | 内部处理,无需手动分配 | 需手动分配字符数组(如 PROPERTY_VALUE_MAX) |
异常处理 | 抛出异常(需 try-catch) | 返回错误码(需检查) |
性能 | 因 JNI 有额外开销,略慢 | 直接调用,性能更高 |
权限要求 | 系统签名或 root 权限 | 取决于调用者的进程权限 |
5. 实际应用场景
5.1 Java 层
- 设置应用: 如前文 CustomFunctionSettingActivity,通过 UI 修改属性。
- 调试工具: 在应用中动态开关功能。
5.2 C++ 层
- 硬件抽象层 (HAL): 检查硬件启用状态。
- 系统服务: 如 AudioFlinger 或 CameraService,根据属性调整行为。
5.3 属性同步
- Java 层设置的属性会立即反映到 C++ 层,反之亦然,因为它们共享同一个底层存储。
6. 示例:Java 和 C++ 协同工作
假设我们要实现一个摄像头开关功能:
- Java UI 层(设置界面):
java
Switch cameraSwitch = findViewById(R.id.swbtn_camera); cameraSwitch.setChecked(SystemProperties.get("persist.sys.flyscale.cam", "1").equals("1")); cameraSwitch.setOnCheckedChangeListener((button, isChecked) -> { SystemProperties.set("persist.sys.flyscale.cam", isChecked ? "1" : "0"); });
- C++ 驱动层(检查状态):
bool isCameraActive() { char value[PROPERTY_VALUE_MAX]; property_get("persist.sys.flyscale.cam", value, "1"); return strcmp(value, "1") == 0; }
- 运行流程:
- 用户在 UI 中打开开关,Java 层将属性设为 "1"。
- C++ 层检测到属性为 "1",启用摄像头。
7. 注意事项
7.1 权限
- Java: 非系统应用调用 set 会抛出 SecurityException,需在 AndroidManifest.xml 中声明 <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />(但仍需系统签名)。
- C++: 在用户态运行可能失败,需确保调用者有 system 或 root 权限。
7.2 属性长度
- PROPERTY_VALUE_MAX(通常 92 字节)限制了属性值的长度,超出部分会被截断。
7.3 持久化
- 以 persist. 开头的属性会保存到 /data/property/,重启后保留。
- 普通属性(如 sys.)重启后丢失。
7.4 测试方法
- 命令行:
adb shell getprop persist.sys.flyscale.cam # 查看属性 adb shell setprop persist.sys.flyscale.cam 0 # 设置属性
- 重启验证: 检查持久化属性是否生效。
8. 总结
- Java 层适合快速开发和 UI 交互,依赖 JNI。
- C++ 层更高效,适合底层模块直接操作。
- 二者通过同一属性系统通信,实现跨层协作。