시스템 속성을 사용하면 정보(보통 구성에 관한 정보)를 시스템 전체에 쉽게 공유할 수 있습니다. 각 파티션은 자체 시스템 속성을 내부적으로 사용할 수 있습니다. 파티션 전체에서 속성에 액세스가 가능하면(예: /system
에 의해 정의된 속성에 액세스하는 /vendor
등) 문제가 발생할 수 있습니다. Android 8.0부터는 /system
등의 일부 파티션을 업그레이드할 수 있지만 /vendor
는 변경되지 않습니다. 시스템 속성은 단순히 스키마가 없는 문자열 키-값 쌍의 전역적 사전에 불과하므로 속성을 안정화하기는 쉽지 않습니다. /system
파티션은 어떠한 알림도 없이 /vendor
파티션이 의존하는 속성을 변경하거나 제거할 수 있습니다.
Android 10 버전부터는 파티션 전체에 걸쳐 액세스된 시스템 속성이 Sysprop 설명 파일로 도식화되며, 속성에 액세스하기 위한 API가 C++ 및 Rust의 확정 함수와 Java의 클래스로 생성됩니다. 이러한 API는 액세스하는 데 특별한 문자열(예: ro.build.date
)이 필요하지 않고 정적으로 입력이 가능하기 때문에 사용하기가 훨씬 편리합니다. ABI 안전성 역시 빌드 시간에 검사되며, 호환되지 않는 변경사항이 발생하면 빌드가 손상됩니다. 이 검사는 파티션 간에 명시적으로 정의된 인터페이스 역할을 합니다. 또한 이러한 API는 Rust, Java, C++ 간의 일관성도 제공합니다.
시스템 속성을 API로 정의
다음 스키마를 사용하여 protobuf의 TextFormat을 사용하는 Sysprop 설명 파일(.sysprop
)을 포함하는 API로 시스템 속성을 정의합니다.
// File: sysprop.proto
syntax = "proto3";
package sysprop;
enum Access {
Readonly = 0;
Writeonce = 1;
ReadWrite = 2;
}
enum Owner {
Platform = 0;
Vendor = 1;
Odm = 2;
}
enum Scope {
Public = 0;
Internal = 2;
}
enum Type {
Boolean = 0;
Integer = 1;
Long = 2;
Double = 3;
String = 4;
Enum = 5;
UInt = 6;
ULong = 7;
BooleanList = 20;
IntegerList = 21;
LongList = 22;
DoubleList = 23;
StringList = 24;
EnumList = 25;
UIntList = 26;
ULongList = 27;
}
message Property {
string api_name = 1;
Type type = 2;
Access access = 3;
Scope scope = 4;
string prop_name = 5;
string enum_values = 6;
bool integer_as_bool = 7;
string legacy_prop_name = 8;
}
message Properties {
Owner owner = 1;
string module = 2;
repeated Property prop = 3;
}
한 개의 Sysprop 설명 파일에는 하나의 속성 집합을 설명하는 한 개의 속성 메시지가 포함됩니다. 필드의 의미는 다음과 같습니다.
필드 | 의미 |
---|---|
owner
|
Platform , Vendor 또는 Odm 속성을 소유하는 파티션으로 설정됩니다. |
module
|
생성된 API가 배치되는 네임스페이스(C++의 경우) 또는 정적 최종 클래스(Java의 경우)를 만드는 데 사용됩니다. 예를 들어 com.android.sysprop.BuildProperties 는 C++의 네임스페이스 com::android::sysprop::BuildProperties , Java의 com.android.sysprop 에 있는 패키지의 BuildProperties 클래스가 됩니다.
|
prop
|
속성 목록입니다. |
Property
메시지 필드의 의미는 다음과 같습니다.
필드 | 의미 |
---|---|
api_name
|
생성된 API의 이름입니다. |
type
|
이 속성의 유형입니다. |
access
|
Readonly : getter API만 생성합니다.
참고: |
scope
|
Internal : 소유자만 액세스할 수 있습니다.
|
prop_name
|
기본 시스템 속성의 이름입니다(예: ro.build.date ).
|
enum_values
|
(Enum , EnumList 만 해당)잠재적인 enum 값으로 구성된, 막대(|)로 구분된 문자열입니다. value1|value2 를 예로 들 수 있습니다.
|
integer_as_bool
|
(Boolean , BooleanList 만 해당) setter에 false 및 true 대신 0 및 1 을 사용하도록 합니다.
|
legacy_prop_name
|
(선택사항, Readonly 속성만 해당) 기본 시스템 속성의 기존 이름입니다. getter를 호출할 때 getter API는 prop_name 읽기를 시도하고 prop_name 이 존재하지 않는 경우 legacy_prop_name 을 사용합니다. 기존 속성에 대한 지원을 중단하고 새 속성으로 이전할 때는 legacy_prop_name 을 사용합니다.
|
각 속성의 유형은 C++, Java, Rust의 다음 유형에 매핑됩니다.
유형 | C++ | Java | Rust |
---|---|---|---|
불리언 | std::optional<bool>
|
Optional<Boolean>
|
bool
|
정수 | std::optional<std::int32_t>
|
Optional<Integer>
|
i32
|
UInt | std::optional<std::uint32_t>
|
Optional<Integer>
|
u32
|
Long | std::optional<std::int64_t>
|
Optional<Long>
|
i64
|
ULong | std::optional<std::uint64_t>
|
Optional<Long>
|
u64
|
Double | std::optional<double>
|
Optional<Double>
|
f64
|
문자열 | std::optional<std::string>
|
Optional<String>
|
String
|
Enum | std::optional<{api_name}_values>
|
Optional<{api_name}_values>
|
{ApiName}Values
|
T 목록 | std::vector<std::optional<T>>
|
List<T>
|
Vec<T>
|
다음은 세 가지 속성을 정의하는 Sysprop 설명 파일의 예입니다.
# File: android/sysprop/PlatformProperties.sysprop
owner: Platform
module: "android.sysprop.PlatformProperties"
prop {
api_name: "build_date"
type: String
prop_name: "ro.build.date"
scope: Public
access: Readonly
}
prop {
api_name: "date_utc"
type: Integer
prop_name: "ro.build.date_utc"
scope: Internal
access: Readonly
}
prop {
api_name: "device_status"
type: Enum
enum_values: "on|off|unknown"
prop_name: "device.status"
scope: Public
access: ReadWrite
}
시스템 속성 라이브러리 정의
이제 Sysprop 설명 파일로 sysprop_library
모듈을 정의할 수 있습니다.
sysprop_library
는 C++, Java, Rust에서 API 역할을 합니다. 빌드 시스템은 sysprop_library
의 각 인스턴스에 대해 한 개의 rust_library
와 한 개의 java_library
, 한 개의 cc_library
를 내부적으로 생성합니다.
// File: Android.bp
sysprop_library {
name: "PlatformProperties",
srcs: ["android/sysprop/PlatformProperties.sysprop"],
property_owner: "Platform",
vendor_available: true,
}
API 검사를 진행하려면 소스에 API 목록 파일을 포함해야 합니다. 이렇게 하려면 API 파일과 api
디렉터리를 생성합니다. api
디렉터리를 Android.bp
와 같은 디렉터리에 배치합니다. API 파일 이름은 <module_name>-current.txt
, <module_name>-latest.txt
입니다. <module_name>-current.txt
는 현재 소스 코드의 API 서명을 보유하고 <module_name>-latest.txt
는 최신 고정 API 서명을 보유합니다. 빌드 시스템은 빌드 시간에 이러한 API 파일을 생성된 API 파일과 비교하여 API 변경 여부를 검사하고 current.txt
가 소스 코드와 일치하지 않는 경우 오류 메시지와 current.txt
파일 업데이트에 관한 안내를 표시합니다. 다음은 디렉터리 및 파일 구성의 예입니다.
├── api
│ ├── PlatformProperties-current.txt
│ └── PlatformProperties-latest.txt
└── Android.bp
Rust, Java, C++ 클라이언트 모듈은 sysprop_library
에 연결하여 생성된 API를 사용할 수 있습니다. 빌드 시스템은 클라이언트에서 생성된 C++, Java, Rust 라이브러리로 연결되는 링크를 생성하여 클라이언트에게 생성된 API에 대한 액세스를 제공합니다.
java_library {
name: "JavaClient",
srcs: ["foo/bar.java"],
libs: ["PlatformProperties"],
}
cc_binary {
name: "cc_client",
srcs: ["baz.cpp"],
shared_libs: ["PlatformProperties"],
}
rust_binary {
name: "rust_client",
srcs: ["src/main.rs"],
rustlibs: ["libplatformproperties_rust"],
}
Rust 라이브러리 이름은 sysprop_library
이름을 소문자로 변환하고 .
및 -
를 _
로 교체한 후 앞에 lib
를 추가하고 _rust
를 추가하여 생성됩니다.
위의 예에서는 다음과 같이 정의된 속성에 액세스할 수 있습니다.
Rust 예시:
use platformproperties::DeviceStatusValues;
fn foo() -> Result<(), Error> {
// Read "ro.build.date_utc". default value is -1.
let date_utc = platformproperties::date_utc()?.unwrap_or_else(-1);
// set "device.status" to "unknown" if "ro.build.date" is not set.
if platformproperties::build_date()?.is_none() {
platformproperties::set_device_status(DeviceStatusValues::UNKNOWN);
}
…
}
Java 예시:
import android.sysprop.PlatformProperties;
…
static void foo() {
…
// read "ro.build.date_utc". default value is -1
Integer dateUtc = PlatformProperties.date_utc().orElse(-1);
// set "device.status" to "unknown" if "ro.build.date" is not set
if (!PlatformProperties.build_date().isPresent()) {
PlatformProperties.device_status(
PlatformProperties.device_status_values.UNKNOWN
);
}
…
}
…
C++ 예시:
#include <android/sysprop/PlatformProperties.sysprop.h>
using namespace android::sysprop;
…
void bar() {
…
// read "ro.build.date". default value is "(unknown)"
std::string build_date = PlatformProperties::build_date().value_or("(unknown)");
// set "device.status" to "on" if it's "unknown" or not set
using PlatformProperties::device_status_values;
auto status = PlatformProperties::device_status();
if (!status.has_value() || status.value() == device_status_values::UNKNOWN) {
PlatformProperties::device_status(device_status_values::ON);
}
…
}
…