پیاده‌سازی ویژگی‌های سیستم به‌عنوان میاناهای برنامه‌سازی کاربردی

دارایی‌های سیستم (sysprops) روشی مناسب برای هم‌رسانی اطلاعات، معمولاً پیکربندی‌ها، در سراسر سیستم ارائه می‌دهد. هر پارتیشن می‌تواند از دارایی‌های سیستم خودش به‌صورت داخلی استفاده کند. وقتی به ویژگی‌ها در بخش‌بندی‌ها دسترسی پیدا می‌شود، ممکن است مشکلی رخ دهد، مثلاً /vendor به ویژگی‌های تعریف‌شده توسط /system دسترسی پیدا کند. از Android 8.0، برخی‌از پارتیشن‌ها، مثل /system، می‌توانند ارتقا یابند، درحالی‌که /vendor بدون تغییر باقی می‌ماند. Because system properties are just a global dictionary of string key-value pairs with no schema, it's difficult to stabilize properties. بخش /system می‌تواند بدون اطلاع قبلی ویژگی‌هایی را که بخش /vendor به آن‌ها وابسته است تغییر دهد یا بردارد.

در Android 10 و نسخه‌های بالاتر، دارایی‌های سیستم دسترسی‌یافته در سراسر پارتیشن‌ها در فایل‌های شرح sysprop طرح‌بندی می‌شوند، و میاناهای برنامه‌سازی کاربردی برای دسترسی به دارایی‌ها به‌عنوان عملکردهای عینی برای C++ و Rust، و کلاس‌ها برای Java تولید می‌شوند. استفاده از این میاناهای برنامه‌سازی کاربردی راحت‌تر است زیرا برای دسترسی به آن‌ها به رشته‌های جادویی (مثل ro.build.date) نیازی نیست و می‌توان آن‌ها را به‌صورت ایستا تایپ کرد. پایداری ABI نیز در زمان ساخت بررسی می‌شود و اگر تغییرات ناسازگار رخ دهد، ساخت متوقف می‌شود. این بررسی به‌عنوان رابط‌های تعریف‌شده صریح بین پارتیشن‌ها عمل می‌کند. این میاناهای برنامه‌سازی کاربردی می‌توانند بین Rust،‏ Java، و C++ سازگاری ایجاد کنند.

تعریف ویژگی‌های سیستم به‌عنوان میاناهای برنامه‌سازی کاربردی

خصوصیات سیستم را به‌عنوان «میاناهای برنامه‌سازی کاربردی» با فایل‌های «شرح Sysprop» (.sysprop) تعریف کنید، که از TextFormat پروتکل با طرحواره زیر استفاده می‌کنند:

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

فایل شرح sysprop حاوی پیام دارایی‌ها است که مجموعه‌ای از دارایی‌ها را شرح می‌دهد. معنی فیلدهای آن به‌صورت زیر است:

فیلد معنی
owner روی پارتیشنی تنظیم کنید که مالک این دارایی‌ها است: platform، vendor، یا odm.
module برای ایجاد فضای نام (C++) یا کلاس نهایی ایستا (Java) که در آن میاناهای برنامه‌سازی کاربردی تولیدشده قرار می‌گیرند استفاده می‌شود. برای مثال، com.android.sysprop.BuildProperties فضای نام com::android::sysprop::BuildProperties در C++‎ است، و کلاس BuildProperties در بسته در com.android.sysprop در Java است.
prop فهرست دارایی‌ها.

معانی فیلدهای پیام Property به شرح زیر است:

فیلد معنی
api_name نام API تولیدشده.
type نوع این ملک.
access Readonly: فقط API دریافت‌کننده تولید می‌کند
Writeonce، ReadWrite: API دریافت‌کننده و تنظیم‌کننده تولید می‌کند
scope ‫Internal: فقط مالک می‌تواند دسترسی داشته باشد.
Public: همه می‌توانند به آن دسترسی داشته باشند، به‌جز واحدهای NDK.
prop_name نام ویژگی سیستم پایه، برای مثال ro.build.date.
enum_values (فقط Enum، EnumList) رشته‌ای که با خط عمودی(|) از هم جدا شده است و از مقادیر شمارشی ممکن تشکیل شده است. برای مثال، value1|value2.
integer_as_bool (فقط Boolean،‏ BooleanList) تنظیم‌کننده‌ها را به‌جای false و true از 0 و 1 استفاده کنند.
legacy_prop_name (اختیاری، فقط Readonly دارایی) نام قدیمی دارایی سیستم زیرین. وقتی تابع دریافت‌کننده فراخوانی می‌شود، میانای برنامه‌سازی کاربردی دریافت‌کننده سعی می‌کند prop_name را بخواند و اگر prop_name وجود نداشته باشد از legacy_prop_name استفاده می‌کند. هنگام منسوخ کردن یک ویژگی موجود و انتقال به یک ویژگی جدید، از legacy_prop_name استفاده کنید.

هر نوع ویژگی به انواع زیر در C++‎،‏ Java، و Rust نگاشت می‌شود:

نوع C++‎ (پوچ‌شدنی) Java (nullable) Rust (اختیاری یا تهی)
بولی std::optional<bool> Optional<Boolean> Option<bool>
عدد صحیح std::optional<std::int32_t> Optional<Integer> Option<i32>
UInt std::optional<std::uint32_t> Optional<Integer> Option<u32>
طولانی std::optional<std::int64_t> Optional<Long> Option<i64>
ULong std::optional<std::uint64_t> Optional<Long> Option<u64>
دوبرابر std::optional<double> Optional<Double> Option<f64>
رشته std::optional<std::string> Optional<String> Option<String>
Enum std::optional<{api\_name}\_values> Optional<{api\_name}\_values> Option<{ApiName}Values>
فهرست T std::vector<std::optional<T>> List<T> Vec<T>

در اینجا نمونه‌ای از فایل Sysprop Description آورده شده است که سه ویژگی را تعریف می‌کند:

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

تعریف کتابخانه‌های خواص سیستم

می‌توانید sysprop_library ماژول را با فایل‌های Sysprop Description تعریف کنید. sysprop_library به‌عنوان میانای برنامه‌سازی کاربردی برای C++‎،‏ Java، و Rust عمل می‌کند. سیستم ساخت به‌طور داخلی یک rust_library، یک java_library، و یک cc_library برای هر نمونه از sysprop_library تولید می‌کند.

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

برای بررسی‌های میانای برنامه‌سازی کاربردی، باید فایل‌های فهرست میانای برنامه‌سازی کاربردی را در منبع اضافه کنید. برای انجام این کار، فایل‌های API و فهرست راهنمای api ایجاد کنید. دایرکتوری api را در همان دایرکتوری Android.bp قرار دهید. نام فایل‌های API عبارت‌اند از <module_name>-current.txt و <module_name>-latest.txt. <module_name>-current.txt حاوی امضاهای میانای برنامه‌سازی کاربردی کدهای منبع فعلی است و <module_name>-latest.txt حاوی جدیدترین امضاهای میانای برنامه‌سازی کاربردی ثابت‌شده است. سیستم ساخت با مقایسه این فایل‌های API با فایل‌های API تولیدشده در زمان ساخت، بررسی می‌کند که آیا APIها تغییر کرده‌اند یا نه و اگر current.txt با کدهای منبع مطابقت نداشته باشد، پیام خطا و دستورالعمل‌هایی برای به‌روزرسانی فایل current.txt صادر می‌کند. در اینجا نمونه‌ای از سازمان‌دهی فایل و پوشه آورده شده است:

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

واحدهای کارخواه Rust،‏ Java، و C++ می‌توانند با sysprop_library پیوند برقرار کنند تا از میاناهای برنامه‌سازی کاربردی تولیدشده استفاده کنند. سیستم ساخت پیوندهایی از کارخواه‌ها به کتابخانه‌های تولیدشده C++‎، Java، و Rust ایجاد می‌کند و ازاین‌طریق به کارخواه‌ها امکان دسترسی به میاناهای برنامه‌سازی کاربردی تولیدشده را می‌دهد.

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:

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

  …
}

نمونه جاوا:

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++‎:

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