Sistem özelliklerini API olarak uygulama

Sistem özellikleri, genellikle yapılandırmalar olmak üzere bilgileri sistem genelinde paylaşmanın kolay bir yolunu sunar. Her bölüm, kendi sistem özelliklerini dahili olarak kullanabilir. Mülklere bölümler arasında erişildiğinde (ör. /vendor, /system tanımlı mülklere eriştiğinde) sorun oluşabilir. Android 8.0'dan itibaren /system gibi bazı bölümler yükseltilebilirken /vendor bölümü değiştirilmez. Sistem özellikleri, şeması olmayan yalnızca genel bir dize anahtar-değer çiftleri sözlüğü olduğundan özellikleri 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 Açıklama dosyaları olarak şematize edilir ve özelliklere erişmek için API'ler C++ ve Rust için somut işlevler, Java için ise sınıflar olarak oluşturulur. Bu API'ler, erişim için sihirli dizeler (ör. ro.build.date) gerekmediğinden ve statik olarak yazılabildiklerinden daha kolay kullanılır. ABI kararlılığı, derleme sırasında da kontrol edilir ve uyumsuz değişiklikler olursa derleme bozulur. Bu kontrol, bölümler arasında açıkça tanımlanmış arayüzler olarak işlev görür. Bu API'ler, Rust, Java ve C++ arasında tutarlılık da sağlayabilir.

Sistem özelliklerini API olarak tanımlama

Sistem özelliklerini, aşağıdaki şemaya sahip protobuf'un TextFormat'ını kullanan Sysprop Açıklama dosyaları (.sysprop) ile 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 Açıklama dosyası, bir özellikler mesajı içerir. Bu mesaj, bir özellikler grubunu açıklar. Alanlarının anlamı aşağıdaki gibidir.

Alan Anlamı
owner Platform, Vendor veya Odm özelliklerinin sahibi olan bölüme ayarlayın.
module Oluşturulan API'lerin yerleştirildiği bir ad alanı (C++) veya statik nihai sınıf (Java) oluşturmak için kullanılır. Örneğin, com.android.sysprop.BuildProperties C++'da com::android::sysprop::BuildProperties ad alanı, Java'da ise com.android.sysprop paketindeki BuildProperties sınıfı olur.
prop Tesislerin 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'si oluşturur.
Writeonce, ReadWrite: Getter ve setter API'leri oluşturur.
Not: ro. önekine sahip özellikler ReadWrite erişimini kullanamayabilir.
scope Internal: Yalnızca sahibi erişebilir.
Public: NDK modülleri hariç 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) 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 özellikleri) Temel sistem özelliğinin eski adı. Getter çağrıldığında, getter API'si prop_name değerini okumaya çalışır ve prop_name değeri yoksa legacy_prop_name değerini kullanır. Mevcut bir mülkün desteğini sonlandırıp yeni bir mülke geçerken legacy_prop_name özelliğini 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
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 List std::vector<std::optional<T>> List<T> Vec<T>

Üç özelliği tanımlayan bir Sysprop Açıklama dosyası örneğini aşağıda 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 özellikleri kitaplıklarını tanımlama

Artık Sysprop Açıklama dosyalarıyla sysprop_library modüllerini tanımlayabilirsiniz. sysprop_library, C++, Java ve Rust için bir 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 kaynakta API listeleri dosyalarını 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. <module_name>-current.txt, mevcut kaynak kodlarının API imzalarını, <module_name>-latest.txt ise en son dondurulmuş API imzalarını içerir. Derleme sistemi, bu API dosyalarını derleme zamanında oluşturulan API dosyalarıyla karşılaştırarak API'lerin değiştirilip değiştirilmediğini kontrol eder ve current.txt kaynak kodlarla eşleşmezse current.txt dosyasını güncellemek için bir hata mesajı ve talimatlar yayınlar. Aşağıda bir dizin ve dosya düzeni örneği verilmiştir:

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

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

java_library {
    name: "JavaClient",
    srcs: ["foo/bar.java"],
    libs: ["PlatformProperties"],
}

cc_binary {
    name: "cc_client",
    srcs: ["baz.cpp"],
    shared_libs: ["libPlatformProperties"],
}

rust_binary {
    name: "rust_client",
    srcs: ["src/main.rs"],
    rustlibs: ["libplatformproperties_rust"],
}

Rust kitaplık adının, sysprop_library adını küçük harfe dönüştürerek, . ve - yerine _ koyarak, ardından lib ekleyip _rust ekleyerek oluşturulduğunu unutmayın.

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

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