Android SystemProperties 读写机制详解和案例使用

发布于:2025-04-03 ⋅ 阅读:(17) ⋅ 点赞:(0)

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++ 协同工作

假设我们要实现一个摄像头开关功能:

  1. Java UI 层(设置界面):

    java

  2. 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");
    });

  3. C++ 驱动层(检查状态):
    bool isCameraActive() {
        char value[PROPERTY_VALUE_MAX];
        property_get("persist.sys.flyscale.cam", value, "1");
        return strcmp(value, "1") == 0;
    }

  4. 运行流程:
    • 用户在 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++ 层更高效,适合底层模块直接操作。
  • 二者通过同一属性系统通信,实现跨层协作。