پیاده سازی ویژگی های سیستم به عنوان API

ویژگی های سیستم راهی مناسب برای به اشتراک گذاشتن اطلاعات، معمولاً پیکربندی ها، در سراسر سیستم فراهم می کند. هر پارتیشن می تواند از ویژگی های سیستم خود به صورت داخلی استفاده کند. هنگامی که ویژگی‌ها از طریق پارتیشن‌ها قابل دسترسی هستند، مانند /vendor دسترسی به /system -defined ویژگی‌ها، ممکن است مشکلی رخ دهد. از اندروید 8.0، برخی از پارتیشن‌ها، مانند /system را می‌توان ارتقا داد، در حالی که /vendor بدون تغییر باقی می‌ماند. از آنجایی که ویژگی‌های سیستم فقط یک فرهنگ لغت جهانی از جفت‌های کلید-مقدار رشته‌ای هستند که هیچ طرحی ندارند، تثبیت ویژگی‌ها دشوار است. پارتیشن /system می‌تواند ویژگی‌هایی را که پارتیشن /vendor به آن‌ها وابسته است، بدون اطلاع قبلی تغییر دهد یا حذف کند.

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

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

ویژگی های سیستم را به عنوان API با فایل های Sysprop Description ( .sysprop ) که از TextFormat protobuf استفاده می کنند، با طرح زیر تعریف کنید:

// 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 Description حاوی یک پیام خصوصیات است که مجموعه ای از ویژگی ها را توصیف می کند. معنی فیلدهای آن به شرح زیر است.

میدان معنی
owner پارتیشنی را که دارای خصوصیات است تنظیم کنید: Platform ، Vendor یا Odm .
module برای ایجاد فضای نام (C++) یا کلاس نهایی ثابت (جاوا) استفاده می شود که در آن APIهای تولید شده قرار می گیرند. به عنوان مثال، com.android.sysprop.BuildProperties فضای نام com::android::sysprop::BuildProperties در C++ و کلاس BuildProperties در بسته در com.android.sysprop در جاوا خواهد بود.
prop لیست املاک.

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

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

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

تایپ کنید C++ جاوا زنگ زدگی
بولی std::optional<bool> Optional<Boolean> bool
عدد صحیح std::optional<std::int32_t> Optional<Integer> i32
UInt std::optional<std::uint32_t> Optional<Integer> u32
طولانی std::optional<std::int64_t> Optional<Long> i64
ULong std::optional<std::uint64_t> Optional<Long> u64
دوبل std::optional<double> Optional<Double> f64
رشته std::optional<std::string> Optional<String> String
Enum std::optional<{api_name}_values> Optional<{api_name}_values> {ApiName}Values
لیست تی 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 به عنوان یک API برای C++، جاوا و 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 و یک دایرکتوری api ایجاد کنید. دایرکتوری api را در همان دایرکتوری Android.bp قرار دهید. نام فایل های API عبارتند از <module_name>-current.txt ، <module_name>-latest.txt . <module_name>-current.txt امضاهای API کدهای منبع فعلی را نگه می دارد و <module_name>-latest.txt آخرین امضاهای API ثابت شده را نگه می دارد. سیستم ساخت با مقایسه این فایل‌های API با فایل‌های API تولید شده در زمان ساخت، بررسی می‌کند که آیا APIها تغییر کرده‌اند یا خیر و در صورتی که current.txt با کدهای منبع مطابقت نداشته باشد، پیام خطا و دستورالعمل‌هایی برای به‌روزرسانی فایل current.txt منتشر می‌کند. در اینجا یک مثال دایرکتوری و سازماندهی فایل آمده است:

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

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

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 با تبدیل نام sysprop_library به حروف کوچک و جایگزین کردن آن ایجاد می‌شود . و - با _ ، و سپس lib و اضافه کردن _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);
    }
    
}