As propriedades do sistema (sysprops) oferecem uma maneira conveniente de compartilhar informações,
geralmente configurações, em todo o sistema. Cada partição pode usar internamente as próprias propriedades do sistema. Um problema pode ocorrer quando as propriedades são acessadas em
partições, como /vendor acessando propriedades definidas por /system. Desde o
Android 8.0, algumas partições, como /system, podem ser atualizadas,
enquanto /vendor permanece inalterada. Como as propriedades do sistema são apenas um dicionário global de pares de chave-valor de string sem um esquema, é difícil estabilizar as propriedades. A partição /system pode mudar ou remover propriedades de que a partição /vendor depende sem aviso prévio.
No Android 10 e versões mais recentes, as propriedades do sistema
acessadas nas partições são esquematizadas em arquivos de descrição sysprop, e
APIs para acessar propriedades são geradas como funções concretas para C++ e Rust,
e classes para Java. Essas APIs são mais convenientes de usar porque não são necessárias strings mágicas (como ro.build.date) para acesso e porque podem ser digitadas estaticamente. A estabilidade da ABI também é verificada durante o build, e ele
é interrompido se houver mudanças incompatíveis. Essa verificação funciona como interfaces
explicitamente definidas entre partições. Essas APIs também podem oferecer consistência entre
Rust, Java e C++.
Definir propriedades do sistema como APIs
Defina propriedades do sistema como APIs com arquivos de descrição Sysprop (.sysprop),
que usam um TextFormat do protobuf, com o seguinte esquema:
// 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;
}
Um arquivo de descrição de sysprop contém uma mensagem de propriedades, que descreve um conjunto de propriedades. O significado dos campos é o seguinte:
| Campo | Significado |
|---|---|
owner
|
Definido como a partição proprietária das propriedades: platform, vendor ou odm.
|
module
|
Usado para criar um namespace (C++) ou uma classe final estática (Java) em que
as APIs geradas são colocadas. Por exemplo,
com.android.sysprop.BuildProperties
é o namespace com::android::sysprop::BuildProperties em C++,
e a classe BuildProperties no pacote em
com.android.sysprop em Java.
|
prop
|
Lista de propriedades. |
Os significados dos campos de mensagem Property são os seguintes:
| Campo | Significado |
|---|---|
api_name
|
O nome da API gerada. |
type
|
O tipo dessa propriedade. |
access
|
Readonly: gera apenas a API getter.
Writeonce, ReadWrite: gera APIs getter e setter.
|
scope
|
Internal: somente o proprietário pode acessar.
Public: todos podem acessar, exceto módulos NDK.
|
prop_name
|
O nome da propriedade do sistema subjacente, por exemplo,
ro.build.date.
|
enum_values
|
(Enum, somente EnumList) Uma string separada por barra vertical(|)
que consiste em possíveis valores de enumeração. Por exemplo, value1|value2.
|
integer_as_bool
|
(Boolean, somente BooleanList) Faça com que os setters usem
0 e 1 em vez de false e
true.
|
legacy_prop_name
|
(opcional, somente propriedades do Readonly) O nome legado da propriedade do sistema subjacente. Ao chamar o getter, a API tenta ler
prop_name e usa legacy_prop_name se
prop_name não existir. Use legacy_prop_name ao
descontinuar uma propriedade atual e migrar para uma nova.
|
Cada tipo de propriedade é mapeado para os seguintes tipos em C++, Java e Rust:
| Tipo | C++ (aceita nulo) | Java (aceita nulo) | Rust (opcional ou anulável) |
|---|---|---|---|
| Booleano | std::optional<bool> |
Optional<Boolean> |
Option<bool> |
| Número inteiro | std::optional<std::int32_t> |
Optional<Integer> |
Option<i32> |
| UInt | std::optional<std::uint32_t> |
Optional<Integer> |
Option<u32> |
| Long | 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> |
| Enum | std::optional<{api\_name}\_values> |
Optional<{api\_name}\_values> |
Option<{ApiName}Values> |
| Lista T | std::vector<std::optional<T>> |
List<T> |
Vec<T> |
Confira um exemplo de arquivo de descrição de Sysprop que define três propriedades:
# 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
}
Definir bibliotecas de propriedades do sistema
É possível definir módulos sysprop_library com arquivos de descrição de Sysprop.
O sysprop_library serve como uma API para C++, Java e Rust. O sistema de build
gera internamente um rust_library, um java_library e um
cc_library para cada instância de sysprop_library.
// File: Android.bp
sysprop_library {
name: "PlatformProperties",
srcs: ["android/sysprop/PlatformProperties.sysprop"],
property_owner: "Platform",
vendor_available: true,
}
Você precisa incluir arquivos de listas de APIs na origem para verificações de API. Para fazer isso, crie arquivos de API e um diretório api. Coloque o diretório api no mesmo diretório que Android.bp. Os nomes de arquivos da API são <module_name>-current.txt e
<module_name>-latest.txt. <module_name>-current.txt contém as assinaturas de API dos códigos-fonte atuais, e <module_name>-latest.txt contém as assinaturas de API congeladas mais recentes. O sistema de build verifica se as APIs foram alteradas comparando esses arquivos com os arquivos gerados durante a compilação e emite uma mensagem de erro e instruções para atualizar o arquivo current.txt se current.txt não corresponder aos códigos-fonte. Confira um exemplo de organização de diretórios e arquivos:
├── api
│ ├── PlatformProperties-current.txt
│ └── PlatformProperties-latest.txt
└── Android.bp
Os módulos cliente em Rust, Java e C++ podem ser vinculados ao sysprop_library para usar
APIs geradas. O sistema de build cria links de clientes para bibliotecas geradas em C++,
Java e Rust, dando aos clientes acesso às APIs geradas.
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"],
}
No exemplo anterior, você pode acessar as propriedades definidas da seguinte maneira.
Exemplo do 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);
}
…
}
Exemplo em 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
);
}
…
}
…
Exemplo em 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);
}
…
}
…