Sistem özelliklerini API olarak uygulayın

Sistem özellikleri, bilgileri (genellikle yapılandırmaları) sistem genelinde paylaşmak için kullanışlı bir yol sunar. Her bölüm, kendi sistem özelliklerini dahili olarak kullanabilir. Özelliklere bölümlerden erişildiğinde (ör. /vendor, /system tarafından tanımlanan özelliklere eriştiğinde) bir sorun oluşabilir. Android 8.0'dan itibaren /system gibi bazı bölümler yükseltilebilir ve /vendor değiştirilmeden bırakılır. Sistem özellikleri, şema içermeyen dize anahtar/değer çiftlerinin yalnızca genel bir sözlüğü olduğundan, özellikleri sabitlemek zordur. /system bölümü, /vendor bölümünün bağımlı olduğu özellikleri herhangi bir bildirimde bulunmadan değiştirebilir veya kaldırabilir.

Android 10 sürümünden itibaren, bölümlerden erişilen sistem özellikleri Sysprop Description dosyalarına şematikleştirilerek, özelliklere erişmek için kullanılan API'ler C++ ve Rust için somut işlevler, Java için sınıflar şeklinde oluşturulur. Erişim için sihirli dizelere (ro.build.date gibi) gerek olmadığından ve bunlar statik olarak yazılabileceğinden bu API'lerin kullanımı daha kolaydır. Derleme sırasında ABI kararlılığı ve uyumsuz değişiklikler olursa derleme araları da kontrol edilir. Bu kontrol, bölümler arasında açıkça tanımlanmış arayüzler olarak işlev görür. Bu API'ler ayrıca Rust, Java ve C++ arasında tutarlılık sağlayabilir.

Sistem özelliklerini API olarak tanımlama

Sistem özelliklerini, aşağıdaki şemaya göre Protobuf TextFormat kullanan Sysprop Açıklama dosyalarına (.sysprop) sahip API'ler olarak tanımlayın:

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

Bir Sysprop Açıklama dosyası, bir özellik grubunu açıklayan bir özellik mesajı içerir. Alanlarının anlamı aşağıdaki gibidir.

Alan Anlamı
owner Şu mülklerin sahibi olan bölüm olarak ayarlayın: Platform, Vendor veya Odm.
module Oluşturulan API'lerin yerleştirildiği bir ad alanı (C++) veya statik son sınıf (Java) oluşturmak için kullanılır. Örneğin com.android.sysprop.BuildProperties, C++'ta com::android::sysprop::BuildProperties ad alanı ve Java'da com.android.sysprop paketinde bulunan BuildProperties sınıfı olur.
prop Tesis listesi.

Property mesaj alanlarının anlamları aşağıdaki gibidir.

Alan Anlamı
api_name Oluşturulan API'nin adı.
type Bu mülkün türü.
access Readonly: Yalnızca getter API oluşturur
Writeonce, ReadWrite: Alıcı ve setter API'leri oluşturur
Not: ro. ön ekine sahip mülkler ReadWrite erişimini kullanamaz.
scope Internal: Yalnızca sahibi erişebilir.
Public: NDK modülleri dışında herkes erişebilir.
prop_name Temel sistem özelliğinin adı (ör. ro.build.date).
enum_values (Yalnızca Enum, EnumList) Olası enum değerlerinden oluşan, çubuk(|) ile ayrılmış bir dize. Örneğin, value1|value2.
integer_as_bool (Yalnızca Boolean, BooleanList) Setterların false ve true yerine 0 ve 1 kullanmasını sağlayın.
legacy_prop_name (isteğe bağlı, yalnızca Readonly mülkleri) Temel sistem özelliğinin eski adı. Alıcı çağrırken, alıcı API prop_name okumaya çalışır ve prop_name yoksa legacy_prop_name kullanır. Mevcut bir mülkü kullanımdan kaldırıp yeni bir mülke taşırken legacy_prop_name kullanın.

Her özellik türü, C++, Java ve Rust'ta aşağıdaki türlerle eşlenir.

Tür C++ Java Rust
Boole std::optional<bool> Optional<Boolean> bool
Tam sayı std::optional<std::int32_t> Optional<Integer> i32
Kullanıcı arayüzü std::optional<std::uint32_t> Optional<Integer> u32
Uzun std::optional<std::int64_t> Optional<Long> i64
ULong std::optional<std::uint64_t> Optional<Long> u64
Çift std::optional<double> Optional<Double> f64
Dize std::optional<std::string> Optional<String> String
Sıralama std::optional<{api_name}_values> Optional<{api_name}_values> {ApiName}Values
T Listesi std::vector<std::optional<T>> List<T> Vec<T>

Burada, üç özelliği tanımlayan bir Sysprop Açıklama dosyası örneğini bulabilirsiniz:

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

Sistem özelliği kitaplıklarını tanımlama

Artık Sysprop Açıklama dosyalarıyla sysprop_library modül tanımlayabilirsiniz. sysprop_library; C++, Java ve Rust için bir API işlevi görür. Derleme sistemi, her sysprop_library örneği için dahili olarak bir rust_library, bir java_library ve bir cc_library oluşturur.

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

API kontrolleri için kaynağa API listesi dosyaları eklemeniz gerekir. Bunu yapmak için API dosyaları ve bir api dizini oluşturun. api dizinini Android.bp ile aynı dizine yerleştirin. API dosya adları <module_name>-current.txt, <module_name>-latest.txt şeklindedir. <module_name>-current.txt, mevcut kaynak kodlarının API imzalarını barındırır. <module_name>-latest.txt ise en son dondurulmuş API imzalarını barındırır. Derleme sistemi, API dosyalarını derleme sırasında oluşturulan API dosyalarıyla karşılaştırarak API'lerin değiştirilip değiştirilmediğini kontrol eder. current.txt, kaynak kodlarıyla eşleşmiyorsa hata mesajı ve current.txt dosyasını güncelleme talimatları gönderir. Aşağıda örnek bir dizin ve dosya düzeni verilmiştir:

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

Rust, Java ve C++ istemci modülleri, oluşturulan API'leri kullanmak için sysprop_library ile bağlantı kurabilir. Derleme sistemi, istemcilerden oluşturulan C++, Java ve Rust kitaplıklarına bağlantı oluşturarak istemcilerin, oluşturulan API'lere erişmesini sağlar.

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

Rust kitaplığı adının, sysprop_library adı küçük harfe dönüştürüldükten, . ve - yerine _ dönüştürülüp lib eklenerek ve _rust eklenerek oluşturulduğunu unutmayın.

Yukarıdaki örnekte, tanımlanan özelliklere aşağıdaki gibi erişebilirsiniz.

Pas örneği:

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 örneği:

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++ örneği:

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