Le proprietà di sistema offrono un modo pratico per condividere informazioni, in genere configurazioni, a livello di sistema. Ogni partizione può utilizzare internamente le proprie proprietà di sistema. Un problema può verificarsi quando si accede alle proprietà tra le partizioni,
ad esempio /vendor
che accede alle proprietà definite da /system
. A partire da Android 8.0,
alcune partizioni, come /system
, possono essere aggiornate, mentre /vendor
rimane
invariata. Poiché le proprietà di sistema sono solo un dizionario globale di coppie chiave-valore stringa senza schema, è difficile stabilizzarle. La partizione
/system
potrebbe modificare o rimuovere le proprietà da cui dipende la partizione /vendor
senza preavviso.
A partire dalla release di Android 10, le proprietà di sistema
a cui si accede tra le partizioni vengono schematizzate in file di descrizione Sysprop e
le API per accedere alle proprietà vengono generate come funzioni concrete per C++ e Rust
e come classi per Java. Queste API sono più comode da usare perché non sono necessarie stringhe magiche (come ro.build.date
) per l'accesso e perché possono essere tipizzate staticamente. La stabilità dell'ABI viene verificata anche in fase di build e la build
viene interrotta se si verificano modifiche incompatibili. Questo controllo funge da interfacce
definite in modo esplicito tra le partizioni. Queste API possono anche garantire la coerenza tra
Rust, Java e C++.
Definisci le proprietà di sistema come API
Definisci le proprietà di sistema come API con file di descrizione Sysprop (.sysprop
),
che utilizzano un TextFormat di protobuf, con lo schema seguente:
// 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 di proprietà che descrive un insieme di proprietà. Il significato dei suoi 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 statica finale (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 delle proprietà. |
Di seguito sono riportati i significati dei campi del messaggio Property
.
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 le API getter e setter
Nota: le proprietà con il prefisso ro. potrebbero non 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à del sistema sottostante, ad esempio
ro.build.date .
|
enum_values
|
(Enum , EnumList only) Una stringa separata da una barra verticale(|)
che consiste in possibili valori enum. Ad esempio, value1|value2 .
|
integer_as_bool
|
(Boolean , BooleanList solo) Imposta i setter in modo che utilizzino
0 e 1 anziché false e
true .
|
legacy_prop_name
|
(facoltativo, solo proprietà Readonly ) Il nome legacy della
proprietà di sistema sottostante. Quando viene chiamato il getter, l'API getter tenta di leggere
prop_name e utilizza legacy_prop_name se
prop_name non esiste. Utilizza legacy_prop_name quando
ritiri una proprietà esistente e passi a una nuova.
|
Ogni tipo di proprietà viene 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
|
ULong | 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
|
T List | 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 i moduli sysprop_library
con i file di descrizione Sysprop.
sysprop_library
funge da API per C++, Java e Rust. Il sistema di build
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,
}
Devi includere i file degli elenchi di API nell'origine per i controlli delle API. Per farlo,
crea file API e una directory api
. Inserisci la directory api
nella stessa directory di Android.bp
. I nomi dei file 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 firme API
congelate più recenti. Il sistema di build controlla se le API vengono modificate
confrontando questi file API con i file API generati in fase di build ed emette un
messaggio di errore e istruzioni per aggiornare il file current.txt
se current.txt
non corrisponde ai codici sorgente. Ecco un esempio di organizzazione di directory e file:
├── api
│ ├── PlatformProperties-current.txt
│ └── PlatformProperties-latest.txt
└── Android.bp
I moduli client Rust, Java e C++ possono essere collegati a sysprop_library
per utilizzare
le API generate. Il sistema di compilazione crea link 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: ["libPlatformProperties"],
}
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, puoi accedere alle proprietà definite nel seguente modo.
Esempio di 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);
}
…
}
Esempio 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 in 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);
}
…
}
…