Implementare le proprietà di sistema come API

Le proprietà di sistema offrono un modo conveniente per condividere le informazioni, a livello di sistema. Ogni partizione può utilizzare le proprie proprietà di sistema internamente. Può verificarsi un problema quando si accede alle proprietà tra le partizioni come /vendor che accede alle proprietà definite da /system. A partire da Android 8.0, è possibile eseguire l'upgrade di alcune partizioni, come /system, mentre /vendor rimane senza modifiche. Poiché le proprietà di sistema sono solo un dizionario globale di stringhe coppie chiave-valore senza schema, è difficile stabilizzare le proprietà. La La partizione /system potrebbe modificare o rimuovere le proprietà /vendor dipende senza preavviso.

A partire dalla release Android 10, le proprietà di sistema alle partizioni è possibile creare uno schematizzato in file di descrizione Sysprop, Le API per accedere alle proprietà sono generate come funzioni concrete per C++ e Rust, e classi per Java. Queste API sono più facili da usare perché non servono (ad esempio ro.build.date) sono necessarie per l'accesso e perché possono essere digitate in modo statico. La stabilità dell'ABI viene verificata anche al momento della build si interrompono se si verificano modifiche incompatibili. Questo controllo agisce come definito in modo esplicito le interfacce tra le partizioni. Queste API possono inoltre fornire coerenza 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 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 di 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 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 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 Accesso a 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(|) che è composto da possibili valori enum. Ad esempio, value1|value2.
integer_as_bool (Solo Boolean, BooleanList) Consenti ai setter di utilizzare 0 e 1 anziché false e true,
legacy_prop_name (facoltativo, solo proprietà Readonly) Il nome precedente della proprietà 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. Usa legacy_prop_name quando il ritiro di una proprietà esistente e il suo trasferimento a una nuova.

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 uno rust_library, uno 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, creare file API e una directory api. Inserisci la directory api nella stessa come Android.bp. I nomi file dell'API sono <module_name>-current.txt, <module_name>-latest.txt. <module_name>-current.txt contiene le firme dell'API di codici sorgente correnti e <module_name>-latest.txt blocca l'ultimo Firme delle API. 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 codice messaggio di errore e istruzioni per aggiornare il file current.txt se current.txt non corrisponde ai codici sorgente. Ecco un esempio di directory e file organizzazione:

├── api
│   ├── PlatformProperties-current.txt
│   └── PlatformProperties-latest.txt
└── Android.bp

I moduli client Rust, Java e C++ possono collegarsi a sysprop_library per l'utilizzo le API generate. Il sistema di compilazione crea collegamenti dai client al C++ generato, librerie Java e Rust, che consentono 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 sysprop_library nome 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);
    }
    …
}
…