Systemeigenschaften als APIs implementieren

Systemeigenschaften bieten eine bequeme Möglichkeit, Informationen systemweit implementiert. Jede Partition kann ihre eigenen Systemeigenschaften verwenden intern. Ein Problem kann auftreten, wenn Partitionsübergreifend auf Attribute zugegriffen wird. wie z. B. /vendor, der auf /system-definierte Attribute zugreift. Seit Android 8.0 Einige Partitionen, z. B. /system, können aktualisiert werden, während /vendor übrig bleibt unverändert lassen. Da Systemattribute nur ein globales Wörterbuch von Strings sind, Schlüssel/Wert-Paare ohne Schema sind, lassen sich Eigenschaften stabilisieren. Die Die Partition /system könnte Eigenschaften ändern oder entfernen, die von der /vendor von der Partition abhängig ist.

Ab Android 10 werden Systemeigenschaften Der partitionsübergreifende Zugriff wird in Sysprop-Beschreibungsdateien schematisiert. APIs für den Zugriff auf Attribute werden als konkrete Funktionen für C++ und Rust generiert. und Klassen für Java. Diese APIs sind praktischer, da es keine magischen wie ro.build.date, sind für den Zugriff erforderlich und können statisch eingegeben wurde. Die ABI-Stabilität wird auch zum Zeitpunkt der Erstellung überprüft und der Build bei inkompatiblen Änderungen ab. Diese Prüfung funktioniert wie explizit definiert Schnittstellen zwischen den Partitionen. Diese APIs können auch für Konsistenz zwischen Rust, Java und C++.

Systemeigenschaften als APIs definieren

Definieren Sie Systemeigenschaften als APIs mit Sysprop-Beschreibungsdateien (.sysprop). die ein TextFormat von protobuf mit dem folgenden Schema verwenden:

// 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;
}

Eine Sysprop-Beschreibungsdatei enthält eine Eigenschaftsnachricht, die ein Gruppe von Eigenschaften. Die Felder haben folgende Bedeutung.

Feld Bedeutung
owner Legen Sie als Wert die Partition fest, zu der die Attribute gehören: Platform, Vendor oder Odm.
module Wird zum Erstellen eines Namespace (C++) oder einer statischen finalen Klasse (Java) verwendet, in dem generierten APIs platziert werden. Beispiel: com.android.sysprop.BuildProperties wird der Namespace com::android::sysprop::BuildProperties in C++ sein, und die Klasse BuildProperties im Paket in com.android.sysprop in Java.
prop Liste der Properties.

Die Property-Nachrichtenfelder haben folgende Bedeutung:

Feld Bedeutung
api_name Der Name der generierten API.
type Der Typ dieser Eigenschaft.
access Readonly: Generiert nur die Getter API
Writeonce, ReadWrite: Generiert Getter- und Setter-APIs
Hinweis: In Properties mit dem Präfix ro. dürfen keine ReadWrite-Zugriff.
scope Internal: Nur der Eigentümer hat Zugriff.
Public: Jeder hat Zugriff, mit Ausnahme von NDK-Modulen.
prop_name Der Name der zugrunde liegenden Systemeigenschaft, z. B. ro.build.date
enum_values (nur Enum, EnumList) Ein durch Striche(|) getrennter String die aus möglichen enum-Werten besteht. Beispiel: value1|value2.
integer_as_bool (Nur Boolean, BooleanList) Setter verwenden 0 und 1 anstelle von false und true
legacy_prop_name (optional, nur Readonly-Properties) Der alte Name des zugrunde liegende Systemeigenschaft. Beim Aufrufen des Getters versucht die Getter API, Daten zu lesen, prop_name und verwendet legacy_prop_name, wenn prop_name ist nicht vorhanden. legacy_prop_name verwenden, wenn Sie verwerfen eine bestehende Property und verschieben sie in eine neue.

Jeder Eigenschaftstyp ist den folgenden Typen in C++, Java und Rust zugeordnet.

Typ C++ Java Rust
Boolesch std::optional<bool> Optional<Boolean> bool
Ganzzahl std::optional<std::int32_t> Optional<Integer> i32
UInt std::optional<std::uint32_t> Optional<Integer> u32
Lang std::optional<std::int64_t> Optional<Long> i64
Lang std::optional<std::uint64_t> Optional<Long> u64
Double std::optional<double> Optional<Double> f64
String std::optional<std::string> Optional<String> String
Aufzählung std::optional<{api_name}_values> Optional<{api_name}_values> {ApiName}Values
T-Liste std::vector<std::optional<T>> List<T> Vec<T>

Hier ist ein Beispiel für eine Sysprop-Beschreibungsdatei, die drei Eigenschaften definiert:

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

Bibliotheken für Systemeigenschaften definieren

Sie können jetzt sysprop_library-Module mit Sysprop-Beschreibungsdateien definieren. sysprop_library dient als API für C++, Java und Rust. Das Build-System Generiert intern einen rust_library, einen java_library und einen cc_library für jede Instanz von sysprop_library.

// File: Android.bp
sysprop_library {
    name: "PlatformProperties",
    srcs: ["android/sysprop/PlatformProperties.sysprop"],
    property_owner: "Platform",
    vendor_available: true,
}

Sie müssen Dateien mit API-Listen in die Quelle für API-Prüfungen aufnehmen. Gehen Sie dazu wie folgt vor: API-Dateien und ein api-Verzeichnis erstellen Legen Sie das Verzeichnis api als Android.bp. Die API-Dateinamen sind <module_name>-current.txt, <module_name>-latest.txt. <module_name>-current.txt enthält die API-Signaturen. der aktuellen Quellcodes und <module_name>-latest.txt enthält die letzten eingefrorenen Codes API-Signaturen. Das Build-System prüft, ob die APIs durch beim Vergleichen dieser API-Dateien mit generierten API-Dateien zur Build-Zeit und gibt einen Fehlermeldung und Anleitung zum Aktualisieren der Datei current.txt, wenn current.txt nicht mit den Quellcodes übereinstimmt. Hier ist ein Beispiel für ein Verzeichnis und eine Datei Organisation:

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

Rust-, Java- und C++-Clientmodule können zur Verwendung mit sysprop_library verknüpft werden generierten APIs. Das Build-System erstellt Links von Clients zu generierter C++-, Java- und Rust-Bibliotheken, wodurch Clients Zugriff auf generierte APIs erhalten.

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"],
}

Der Name der Rust-Bibliothek wird durch Umwandeln des sysprop_library-Objekts generiert. Namen in Kleinbuchstaben, wobei . und - durch _ ersetzt und dann lib und _rust wird angehängt.

Im vorherigen Beispiel könnten Sie folgendermaßen auf definierte Properties zugreifen:

Rust-Beispiel:

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-Beispiel:

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

C++-Beispiel:

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