Le proprietà di sistema forniscono un modo conveniente per condividere informazioni, di solito
configurazioni, a livello di sistema. Ogni partizione può usare internamente
le proprie proprietà di sistema. Un problema può verificarsi quando viene eseguito l'accesso alle proprietà tra le partizioni,
ad esempio /vendor
che accede alle proprietà definite da /system
. A partire da Android 8.0,
è possibile eseguire l'upgrade di alcune partizioni, ad esempio /system
, mentre /vendor
non viene modificato. Poiché le proprietà di sistema sono solo un dizionario globale
di coppie chiave-valore di stringa senza schema, è difficile stabilizzare le proprietà. La partizione /system
potrebbe modificare o rimuovere le proprietà da cui dipende la partizione /vendor
senza alcun preavviso.
A partire dalla release di Android 10, le proprietà di sistema
accessibili tra le partizioni sono schematizzate in file di descrizione Sysprop, mentre
le API per accedere alle proprietà vengono generate come funzioni concrete per C++ e Rust,
e classi per Java. Queste API sono più pratiche da usare perché per l'accesso non sono necessarie stringhe magiche (ad esempio ro.build.date
) e possono essere digitate in modo statico. La stabilità dell'ABI viene controllata anche al momento della build e la build
si interrompe se si verificano modifiche incompatibili. Questo controllo funge da interfacce
definite esplicitamente tra le partizioni. Queste API possono anche fornire coerenza tra
Rust, Java e C++.
Definisci le proprietà di sistema come API
Definisci le proprietà di sistema come API con i file di descrizione Sysprop (.sysprop
),
che utilizzano un valore TextFormat di protobuf, con il seguente schema:
// 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;
}
Un file di descrizione Sysprop contiene un messaggio relativo alle proprietà, che descrive un insieme di proprietà. Il significato dei relativi campi è il seguente.
Campo | Significato |
---|---|
owner
|
Imposta la partizione proprietaria delle proprietà: Platform ,
Vendor o Odm .
|
module
|
Utilizzato per creare uno spazio dei nomi (C++) o una classe finale statica (Java) in cui vengono inserite le API generate. Ad esempio, com.android.sysprop.BuildProperties sarà lo spazio dei nomi com::android::sysprop::BuildProperties in C++ e la classe BuildProperties nel pacchetto in com.android.sysprop in Java.
|
prop
|
Elenco di proprietà. |
I significati dei campi del messaggio Property
sono i seguenti.
Campo | Significato |
---|---|
api_name
|
Il nome dell'API generata. |
type
|
Il tipo di questa proprietà. |
access
|
Readonly : genera solo l'API getter
Writeonce , ReadWrite : genera API getter e setter
Nota: le proprietà con il prefisso ro. non possono utilizzare
l'accesso ReadWrite .
|
scope
|
Internal : solo il proprietario può accedere.
Public : tutti possono accedere, ad eccezione dei moduli NDK.
|
prop_name
|
Il nome della proprietà di sistema sottostante, ad esempio
ro.build.date .
|
enum_values
|
(Solo Enum , EnumList ) Una stringa separata da barre(|) composta da possibili valori enum. Ad esempio, value1|value2 .
|
integer_as_bool
|
(Solo Boolean e BooleanList ) Consenti ai settatori di utilizzare
0 e 1 anziché false e
true .
|
legacy_prop_name
|
(Facoltativo, solo per le proprietà Readonly ) Il nome legacy della
proprietà di sistema sottostante. Quando chiami il getter, l'API getter prova a leggere prop_name e utilizza legacy_prop_name se prop_name non esiste. Utilizza legacy_prop_name quando
ritira una proprietà esistente e passa a una nuova proprietà.
|
Ogni tipo di proprietà è mappato ai seguenti tipi in C++, Java e Rust.
Tipo | C++ | Java | Rust |
---|---|---|---|
Booleano | std::optional<bool>
|
Optional<Boolean>
|
bool
|
Numero intero | std::optional<std::int32_t>
|
Optional<Integer>
|
i32
|
UInt | std::optional<std::uint32_t>
|
Optional<Integer>
|
u32
|
Lungo | std::optional<std::int64_t>
|
Optional<Long>
|
i64
|
ULungo | std::optional<std::uint64_t>
|
Optional<Long>
|
u64
|
Doppio | std::optional<double>
|
Optional<Double>
|
f64
|
Stringa | std::optional<std::string>
|
Optional<String>
|
String
|
Enum | std::optional<{api_name}_values>
|
Optional<{api_name}_values>
|
{ApiName}Values
|
Elenco T | std::vector<std::optional<T>>
|
List<T>
|
Vec<T>
|
Ecco un esempio di file di descrizione Sysprop che definisce tre proprietà:
# 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
}
Definisci le librerie delle proprietà di sistema
Ora puoi definire moduli sysprop_library
con i file di descrizione Sysprop.
sysprop_library
funge da API per C++, Java e Rust. Il sistema di compilazione
genera internamente un rust_library
, un java_library
e un cc_library
per ogni istanza di sysprop_library
.
// File: Android.bp
sysprop_library {
name: "PlatformProperties",
srcs: ["android/sysprop/PlatformProperties.sysprop"],
property_owner: "Platform",
vendor_available: true,
}
Per i controlli delle API, devi includere i file degli elenchi delle API nell'origine. Per farlo, crea dei file API e una directory api
. Inserisci la directory api
nella stessa directory di Android.bp
. I nomi file dell'API sono <module_name>-current.txt
,
<module_name>-latest.txt
. <module_name>-current.txt
contiene le firme API dei codici sorgente attuali, mentre <module_name>-latest.txt
contiene le ultime firme API bloccate. Il sistema di compilazione controlla se le API vengono modificate
confrontando questi file API con i file API generati al momento della build ed emette un
messaggio di errore e le istruzioni per aggiornare il file current.txt
se current.txt
non corrisponde ai codici sorgente. Ecco un esempio di organizzazione
file e directory:
├── api
│ ├── PlatformProperties-current.txt
│ └── PlatformProperties-latest.txt
└── Android.bp
I moduli client Rust, Java e C++ possono collegarsi a sysprop_library
per utilizzare
le API generate. Il sistema di compilazione crea collegamenti dai client alle librerie C++, Java e Rust generate, consentendo così ai client di accedere alle API generate.
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"],
}
Tieni presente che il nome della libreria Rust viene generato convertendo il nome sysprop_library
in minuscolo, sostituendo .
e -
con _
, quindi anteponendo lib
e aggiungendo _rust
.
Nell'esempio precedente, potevi accedere a proprietà definite nel seguente modo.
Esempio di ruggine:
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);
}
…
}
Esempio di 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
);
}
…
}
…
Esempio 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);
}
…
}
…