Системные свойства (sysprops) – это удобный способ обмена информацией, обычно конфигурациями, в масштабе всей системы. Каждый раздел может использовать собственные системные свойства. Проблема может возникнуть, когда доступ к свойствам осуществляется через разделы, например когда /vendor обращается к свойствам, определенным в /system. Начиная с Android 8.0 некоторые разделы, например /system, можно обновлять, а раздел /vendor остается неизменным. Поскольку системные свойства представляют собой глобальный словарь пар "ключ-значение" без схемы, их сложно стабилизировать. Раздел /system может изменить или удалить свойства, от которых зависит раздел /vendor, без предварительного уведомления.
В Android 10 и более поздних версиях системные свойства, доступ к которым осуществляется через разделы, схематизируются в файлы описания sysprop, а API для доступа к свойствам генерируются как конкретные функции для C++ и Rust и классы для Java. Эти API удобнее в использовании, поскольку для доступа к ним не нужны специальные строки (например, ro.build.date) и они могут быть статически типизированы. Стабильность ABI также проверяется во время сборки. Если обнаруживаются несовместимые изменения, сборка прерывается. Эта проверка действует как явно определенный интерфейс между разделами. Эти API также могут обеспечить согласованность между Rust, Java и C++.
Определять системные свойства как API
Определите системные свойства как API с файлами описания системных свойств (.sysprop), в которых используется формат TextFormat протокола Protobuf со следующей схемой:
// 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
|
Используется для создания пространства имен (C++) или статического финального класса (Java), в котором размещаются сгенерированные API. Например,
com.android.sysprop.BuildProperties
это пространство имен com::android::sysprop::BuildProperties в C++
и класс BuildProperties в пакете
com.android.sysprop в Java.
|
prop
|
Список свойств. |
Поля сообщения Property имеют следующие значения:
| Поле | Значение |
|---|---|
api_name
|
Название сгенерированного API. |
type
|
Тип этого свойства. |
access
|
Readonly – генерирует только API-метод получения.
Writeonce, ReadWrite – генерирует API-методы получения и установки.
|
scope
|
Internal: доступ только у владельца.
Public: доступ есть у всех, кроме модулей NDK.
|
prop_name
|
Название свойства базовой системы, например ro.build.date.
|
enum_values
|
(Enum, EnumList) Строка, разделенная вертикальной чертой(|), которая состоит из возможных значений перечисления. Пример: value1|value2.
|
integer_as_bool
|
(Только для Boolean и BooleanList.) Сделайте так, чтобы сеттеры использовали 0 и 1 вместо false и true.
|
legacy_prop_name
|
(необязательное, только для свойств Readonly) Устаревшее название базового системного свойства. При вызове функции getter API пытается прочитать значение prop_name и использует legacy_prop_name, если prop_name не существует. Используйте legacy_prop_name, когда вы отказываетесь от существующего ресурса и переходите на новый.
|
Каждому типу свойства соответствует определенный тип в C++, Java и Rust:
| Тип | C++ (допускает значение NULL) | Java (допускается значение null) | Rust (необязательное или допускающее значение NULL) |
|---|---|---|---|
| Логическое значение | std::optional<bool> |
Optional<Boolean> |
Option<bool> |
| Целое число | std::optional<std::int32_t> |
Optional<Integer> |
Option<i32> |
| UInt | std::optional<std::uint32_t> |
Optional<Integer> |
Option<u32> |
| Длинный | std::optional<std::int64_t> |
Optional<Long> |
Option<i64> |
| ULong | std::optional<std::uint64_t> |
Optional<Long> |
Option<u64> |
| Double | std::optional<double> |
Optional<Double> |
Option<f64> |
| String | std::optional<std::string> |
Optional<String> |
Option<String> |
| Перечисление | std::optional<{api\_name}\_values> |
Optional<{api\_name}\_values> |
Option<{ApiName}Values> |
| T List | std::vector<std::optional<T>> |
List<T> |
Vec<T> |
Вот пример файла Sysprop Description, в котором определены три свойства:
# 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_library с помощью файлов описания Sysprop.
sysprop_library – это API для C++, Java и Rust. Система сборки автоматически создает по одному объекту rust_library, java_library и cc_library для каждого экземпляра sysprop_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: ["libPlatformProperties"],
}
rust_binary {
name: "rust_client",
srcs: ["src/main.rs"],
rustlibs: ["libplatformproperties_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);
}
…
}
…