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. Mülklere bölümler arasında erişildiğinde (ör. /vendor tarafından tanımlanan mülklere /system tarafından erişilmesi) 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, şeması olmayan dize anahtar/değer çiftlerinin bulunduğu genel bir sözlük olduğundan mülkleri sabitlemek zordur. /system bölümü, /vendor bölümünün bağlı olduğu özellikleri herhangi bir bildirimde bulunmadan değiştirebilir veya kaldırabilir.

Android 10 sürümünden itibaren, bölümler arasında erişilen sistem özellikleri Sysprop Description dosyalarında şematize edilir ve özelliklere erişmek için API'ler C++ ve Rust için somut işlevler, Java için sınıflar olarak oluşturulur. Bu API'lerin kullanımı, erişim için sihirli dizelere (ro.build.date gibi) ihtiyaç duyulmaması ve statik olarak yazılabilmeleri nedeniyle daha kolaydır. ABI kararlılığı derleme sırasında da kontrol edilir ve uyumlu olmayan değişiklikler olursa derleme işlemi kesintiye uğrar. Bu kontrol, bölümler arasında açıkça tanımlanmış arayüzler gibi çalışı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 şemayı kullanan protobuf TextFormat'ı kullanan Sysprop Description dosyalarıyla (.sysprop) API 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 Description dosyası, bir dizi özelliği açıklayan bir properties mesajı içerir. Alanlarının anlamları aşağıdaki gibidir.

Alan Anlamı
owner Mülklerin sahibi olan bölüme 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ı, Java'da ise com.android.sysprop paketindeki BuildProperties sınıfı olur.
prop Tesis listesi.

Property mesaj alanlarının anlamları aşağıda verilmiştir.

Alan Anlamı
api_name Oluşturulan API'nin adı.
type Bu mülkün türü.
access Readonly: Yalnızca alıcı API'si oluşturur
Writeonce, ReadWrite: Alıcı ve ayarlayıcı API'leri oluşturur
Not: ro. ön ekiyle başlayan mülkler ReadWrite erişimini kullanamaz.
scope Internal: Yalnızca sahip erişebilir.
Public: NDK modülleri hariç herkes erişebilir.
prop_name Temel sistem mülkünün adı (ör. ro.build.date).
enum_values (yalnızca Enum, EnumList) Olası enum değerlerinden oluşan, dikey çizgiyle ayrılmış bir dize. Örneğin, value1|value2.
integer_as_bool (Yalnızca Boolean, BooleanList) Ayarlayıcıları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 mülkünün eski adı. Geter API'si, getter'ı çağırırken prop_name değerini okumaya çalışır ve prop_name yoksa legacy_prop_name değerini kullanır. Mevcut bir mülkün desteği sonlandırılırken ve yeni bir mülke taşınırken legacy_prop_name değerini kullanın.

Her mülk 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
UInt 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
Enum std::optional<{api_name}_values> Optional<{api_name}_values> {ApiName}Values
T Listesi std::vector<std::optional<T>> List<T> Vec<T>

Aşağıda, üç özelliği tanımlayan bir Sysprop Description dosyası örneği verilmiştir:

# 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 özellikleri 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 API görevi 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 API listeleri dosyalarını kaynağa eklemeniz gerekir. Bunu yapmak için API dosyaları ve bir api dizini oluşturun. api dizinini Android.bp ile aynı dizine koyun. API dosya adları <module_name>-current.txt, <module_name>-latest.txt şeklindedir. <module_name>-current.txt, geçerli kaynak kodlarının API imzalarını, <module_name>-latest.txt ise en son dondurulmuş API imzalarını içerir. 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 kodlarla eşleşmiyorsa hata mesajı ve current.txt dosyasını güncelleme talimatları gönderir. Aşağıda bir dizin ve dosya düzenleme örneği 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_libraryadının küçük harfe dönüştürülmesi, . ve -'nin _ ile değiştirilmesi ve ardından lib'nin başına _rust eklenmesiyle oluşturulduğunu unutmayın.

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

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