Mengimplementasikan properti sistem sebagai API

Properti sistem menyediakan cara mudah untuk berbagi informasi, biasanya konfigurasi standar, di seluruh sistem. Setiap partisi dapat menggunakan properti sistemnya sendiri secara internal. Masalah dapat terjadi ketika properti diakses di seluruh partisi, seperti /vendor mengakses properti yang ditentukan /system. Sejak Android 8.0, beberapa partisi, seperti /system, dapat diupgrade, sedangkan /vendor dibiarkan tidak berubah. Karena properti sistem hanyalah kamus global dari {i>string<i} pasangan nilai kunci tanpa skema, properti akan sulit distabilkan. Tujuan Partisi /system dapat mengubah atau menghapus properti yang /vendor partisi bergantung pada tanpa pemberitahuan apa pun.

Dimulai dengan rilis Android 10, properti sistem yang diakses di seluruh partisi diskemakan ke dalam file {i>Sysprop Description<i}, dan API untuk mengakses properti dibuat sebagai fungsi konkret untuk C++ dan Rust, dan class untuk Java. API ini lebih nyaman digunakan karena tidak perlu string (seperti ro.build.date) diperlukan untuk akses, dan karena string tersebut dapat diketik secara statis. Stabilitas ABI juga diperiksa pada waktu build, dan rusak jika terjadi perubahan yang tidak kompatibel. Pemeriksaan ini berfungsi sebagaimana yang didefinisikan secara eksplisit antarmuka antar partisi. API ini juga bisa memberikan konsistensi antara Rust, Java dan C++.

Menentukan properti sistem sebagai API

Menentukan properti sistem sebagai API dengan file Deskripsi Sysprop (.sysprop), yang menggunakan TextFormat protobuf, dengan skema berikut:

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

Satu file Deskripsi Sysprop berisi satu pesan properti, yang menjelaskan sekumpulan properti. Arti kolomnya adalah sebagai berikut.

Kolom Arti
owner Tetapkan ke partisi yang memiliki properti: Platform, Vendor, atau Odm.
module Digunakan untuk membuat namespace (C++) atau class final statis (Java) tempat API yang dihasilkan akan ditempatkan. Misalnya, com.android.sysprop.BuildProperties akan menjadi namespace com::android::sysprop::BuildProperties di C++, dan class BuildProperties dalam paket di com.android.sysprop di Java.
prop Daftar properti.

Arti kolom pesan Property adalah sebagai berikut.

Kolom Arti
api_name Nama API yang dihasilkan.
type Jenis properti ini.
access Readonly: Hanya menghasilkan API pengambil
Writeonce, ReadWrite: Menghasilkan API pengambil dan penyetel
Catatan: Properti dengan awalan ro. mungkin tidak digunakan Akses ReadWrite.
scope Internal: Hanya pemilik yang dapat mengakses.
Public: Semua orang dapat mengakses, kecuali modul NDK.
prop_name Nama properti sistem yang mendasarinya, misalnya ro.build.date.
enum_values (hanya Enum, EnumList) String yang dipisahkan batang(|) yang terdiri dari kemungkinan nilai enum. Misalnya, value1|value2.
integer_as_bool (Boolean, BooleanList saja) Menggunakan penyetel 0 dan 1, bukan false dan true.
legacy_prop_name (opsional, hanya properti Readonly) Nama lama properti properti sistem yang mendasarinya. Saat memanggil pengambil, API pengambil mencoba membaca prop_name dan menggunakan legacy_prop_name jika prop_name tidak ada. Gunakan legacy_prop_name saat menghentikan penggunaan properti yang ada dan beralih ke properti baru.

Setiap jenis properti dipetakan ke jenis berikut di C++, Java, dan Rust.

Jenis C++ Java Rust
Boolean std::optional<bool> Optional<Boolean> bool
Bilangan Bulat std::optional<std::int32_t> Optional<Integer> i32
UInt std::optional<std::uint32_t> Optional<Integer> u32
Panjang std::optional<std::int64_t> Optional<Long> i64
ULong std::optional<std::uint64_t> Optional<Long> u64
Ganda std::optional<double> Optional<Double> f64
String std::optional<std::string> Optional<String> String
{i>Enumer<i} std::optional<{api_name}_values> Optional<{api_name}_values> {ApiName}Values
Daftar T std::vector<std::optional<T>> List<T> Vec<T>

Berikut adalah contoh file Deskripsi Sysprop yang mendefinisikan tiga properti:

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

Menentukan library properti sistem

Sekarang Anda dapat menentukan modul sysprop_library dengan file Deskripsi Sysprop. sysprop_library berfungsi sebagai API untuk C++, Java, dan Rust. Sistem build secara internal menghasilkan satu rust_library, satu java_library, dan satu cc_library untuk setiap instance sysprop_library.

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

Anda harus menyertakan file daftar API di sumber untuk pemeriksaan API. Untuk melakukannya: membuat file API dan direktori api. Tempatkan direktori api di sebagai Android.bp. Nama file API adalah <module_name>-current.txt, <module_name>-latest.txt. <module_name>-current.txt menyimpan tanda tangan API dari kode sumber saat ini, dan <module_name>-latest.txt menyimpan kode beku terakhir Tanda tangan API. Sistem build akan memeriksa apakah API diubah oleh membandingkan file API ini dengan file API yang dihasilkan pada waktu build dan pesan error dan petunjuk untuk memperbarui file current.txt jika current.txt tidak cocok dengan kode sumber. Berikut adalah contoh direktori dan file organisasi:

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

Modul klien Rust, Java, dan C++ dapat ditautkan ke sysprop_library untuk digunakan API yang dihasilkan. Sistem build membuat link dari klien ke C++ yang dihasilkan, Library Java dan Rust, sehingga memberi klien akses ke API yang dihasilkan.

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

Perlu diperhatikan bahwa nama library Rust dibuat dengan mengonversi sysprop_library nama menjadi huruf kecil, mengganti . dan - dengan _, lalu menambahkan lib dan menambahkan _rust.

Pada contoh sebelumnya, Anda dapat mengakses properti yang ditentukan sebagai berikut.

Contoh karat:

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

  …
}

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

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