Реализуйте системные свойства как API.

Свойства системы предоставляют удобный способ обмена информацией (обычно конфигурациями) в масштабах всей системы. Каждый раздел может использовать свои собственные системные свойства внутри себя. Проблема может возникнуть, когда доступ к свойствам осуществляется из разных разделов, например, /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 ( .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-интерфейсы получения и установки.
Примечание. Свойства с префиксом ro. не может использовать доступ ReadWrite .
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 ) Устаревшее имя базового системного свойства. При вызове метода получения API-интерфейс получения пытается прочитать prop_name и использует legacy_prop_name , если prop_name не существует. Используйте legacy_prop_name при объявлении устаревшего свойства и переходе на новое свойство.

Каждый тип свойства соответствует следующим типам в 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
Длинный std::optional<std::int64_t> Optional<Long> i64
Улонг std::optional<std::uint64_t> Optional<Long> u64
Двойной std::optional<double> Optional<Double> f64
Нить std::optional<std::string> Optional<String> String
Перечисление std::optional<{api_name}_values> Optional<{api_name}_values> {ApiName}Values
Т-список 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_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
: ["PlatformProperties"],
}

rust_binary
{
    name
: "rust_client",
    srcs
: ["src/main.rs"],
    rustlibs
: ["libplatformproperties_rust"],
}

Обратите внимание, что имя библиотеки Rust генерируется путем преобразования имени sysprop_library в нижний регистр с заменой . и - с _ , а затем добавить lib и добавить _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
       
);
   
}
   

}

Пример С++:

#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);
   
}
   

}