Menerapkan Properti Sistem sebagai API

Properti sistem menyediakan cara mudah untuk berbagi informasi, biasanya konfigurasi, 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 oleh /system . Sejak Android 8.0, beberapa partisi, seperti /system , dapat ditingkatkan, sementara /vendor tidak diubah. Karena properti sistem hanyalah kamus global pasangan kunci/nilai string tanpa skema, sulit untuk menstabilkan properti. Partisi /system dapat mengubah atau menghapus properti yang bergantung pada partisi /vendor tanpa pemberitahuan apa pun.

Dimulai dengan rilis Android 10, properti sistem yang diakses di seluruh partisi dibuat skemanya menjadi file Deskripsi Sysprop, dan API untuk mengakses properti dibuat sebagai fungsi konkret untuk C++ dan Rust, serta class untuk Java. API ini lebih mudah digunakan karena tidak diperlukan string ajaib (seperti ro.build.date ) untuk mengaksesnya, dan karena dapat diketik secara statis. Stabilitas ABI juga diperiksa pada waktu build, dan build akan rusak jika terjadi perubahan yang tidak kompatibel. Pemeriksaan ini bertindak sebagai antarmuka yang ditentukan secara eksplisit antar partisi. API ini juga dapat memberikan konsistensi antara Rust, Java, dan C++.

Mendefinisikan properti sistem sebagai API

Definisikan 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 dari bidangnya adalah sebagai berikut.

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

Arti dari field pesan Property adalah sebagai berikut.

Bidang Arti
api_name Nama API yang dihasilkan.
type Jenis properti ini.
access Readonly : Menghasilkan API pengambil saja

Writeonce , ReadWrite : Menghasilkan API pengambil dan penyetel

Catatan: Properti dengan awalan ro. tidak boleh menggunakan 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 ( Enum , hanya EnumList ) String yang dipisahkan batang(|) yang berisi kemungkinan nilai enum. Misalnya, value1|value2 .
integer_as_bool ( Boolean , BooleanList saja) Jadikan penyetel menggunakan 0 dan 1 , bukan false dan true .
legacy_prop_name (opsional, hanya properti Readonly ) Nama warisan dari 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 sudah ada dan pindah ke properti baru.

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

Jenis C++ Jawa Karat
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
Panjang std::optional<std::uint64_t> Optional<Long> u64
Dobel std::optional<double> Optional<Double> f64
Rangkaian std::optional<std::string> Optional<String> String
enum std::optional<{api_name}_values> Optional<{api_name}_values> {ApiName}Values
Daftar T std::vector<std::optional<T>> List<T> Vec<T>

Berikut ini 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
}

Mendefinisikan perpustakaan properti sistem

Anda sekarang dapat mendefinisikan modul sysprop_library dengan file Deskripsi Sysprop. sysprop_library berfungsi sebagai API untuk C++, Java dan Rust. Sistem pembangunan 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 melakukan ini, buat file API dan direktori api . Letakkan direktori api di direktori yang sama dengan 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 tanda tangan API terbaru yang dibekukan. Sistem pembangunan memeriksa apakah API diubah dengan membandingkan file API ini dengan file API yang dihasilkan pada waktu pembangunan dan mengeluarkan pesan kesalahan dan instruksi untuk memperbarui file current.txt jika current.txt tidak cocok dengan kode sumber. Berikut ini contoh direktori dan organisasi file:

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

Modul klien Rust, Java, dan C++ dapat ditautkan ke sysprop_library untuk menggunakan API yang dihasilkan. Sistem pembangunan membuat tautan dari klien ke pustaka C++, Java, dan Rust yang dihasilkan, 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"],
}

Perhatikan bahwa nama perpustakaan Rust dihasilkan dengan mengonversi nama sysprop_library menjadi huruf kecil, menggantikan . dan - dengan _ , lalu menambahkan lib dan menambahkan _rust .

Dalam contoh di atas, 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 Jawa:

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