ویژگیهای سیستم (sysprops) راهی مناسب برای به اشتراک گذاشتن اطلاعات، معمولاً پیکربندیها، در سطح سیستم فراهم میکنند. هر پارتیشن میتواند از ویژگیهای سیستمی خود به صورت داخلی استفاده کند. زمانی که ویژگیها در بین پارتیشنها قابل دسترسی باشند، ممکن است مشکلی پیش بیاید، مانند دسترسی /vendor به ویژگیهای تعریفشده /system . از اندروید ۸.۰، برخی از پارتیشنها، مانند /system ، میتوانند ارتقا یابند، در حالی که /vendor بدون تغییر باقی میماند. از آنجا که ویژگیهای سیستم فقط یک دیکشنری سراسری از جفتهای کلید-مقدار رشتهای بدون هیچ طرحوارهای هستند، تثبیت ویژگیها دشوار است. پارتیشن /system میتواند ویژگیهایی را که پارتیشن /vendor به آنها وابسته است، بدون هیچ اطلاع قبلی تغییر دهد یا حذف کند.
در اندروید ۱۰ و بالاتر، ویژگیهای سیستم که از طریق پارتیشنها قابل دسترسی هستند، در فایلهای توصیفی sysprop شماتیک میشوند و APIهای دسترسی به ویژگیها به صورت توابع عینی برای C++ و Rust و کلاسهایی برای Java تولید میشوند. استفاده از این APIها راحتتر است زیرا برای دسترسی به هیچ رشته جادویی (مانند ro.build.date ) نیاز نیست و میتوان آنها را به صورت استاتیک تایپ کرد. پایداری ABI نیز در زمان ساخت بررسی میشود و در صورت بروز تغییرات ناسازگار، ساخت متوقف میشود. این بررسی به عنوان رابطهای تعریفشده صریح بین پارتیشنها عمل میکند. این APIها همچنین میتوانند سازگاری بین Rust، Java و C++ را فراهم کنند.
تعریف ویژگیهای سیستم به عنوان API
ویژگیهای سیستم را به عنوان API با فایلهای توضیحات Sysprop ( .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 شامل یک پیام ویژگیها است که مجموعهای از ویژگیها را توصیف میکند. معنای فیلدهای آن به شرح زیر است:
| میدان | معنی |
|---|---|
owner | روی پارتیشنی که مالک ویژگیها است تنظیم کنید: platform ، vendor یا odm . |
module | برای ایجاد یک فضای نام (C++) یا کلاس نهایی استاتیک (Java) که APIهای تولید شده در آن قرار میگیرند، استفاده میشود. برای مثال، com.android.sysprop.BuildProperties در C++ یک فضای نام com::android::sysprop::BuildProperties و در جاوا، کلاس BuildProperties در پکیج com.android.sysprop است. |
prop | فهرست املاک. |
معانی فیلدهای پیام Property به شرح زیر است:
| میدان | معنی |
|---|---|
api_name | نام API تولید شده. |
type | نوع این ملک. |
access | Readonly : فقط API گیرنده تولید میکندWriteonce ، ReadWrite : رابطهای برنامهنویسی کاربردی (API) مربوط به getter و setter را تولید میکند. |
scope | Internal : فقط مالک میتواند دسترسی داشته باشد.Public : همه میتوانند به آن دسترسی داشته باشند، به جز ماژولهای NDK. |
prop_name | نام ویژگی سیستم اصلی، برای مثال ro.build.date . |
enum_values | (فقط Enum ، EnumList ) یک رشته جدا شده با bar(|) که شامل مقادیر enum ممکن است. برای مثال، value1|value2 . |
integer_as_bool | (فقط Boolean و BooleanList ) تنظیمکنندهها را مجبور کنید که به جای false و true از 0 و 1 استفاده کنند. |
legacy_prop_name | (اختیاری، فقط برای ویژگیهای Readonly ) نام قدیمی ویژگی سیستم اصلی. هنگام فراخوانی getter، API getter سعی میکند prop_name بخواند و اگر prop_name وجود نداشته باشد legacy_prop_name استفاده میکند. هنگام منسوخ کردن یک ویژگی موجود و انتقال به یک ویژگی جدید، legacy_prop_name استفاده کنید. |
هر نوع ویژگی به انواع زیر در C++، جاوا و Rust نگاشت میشود:
| نوع | سی پلاس پلاس (تهیپذیر) | جاوا (قابل تهیسازی) | زنگ (اختیاری یا قابل تهیسازی) |
|---|---|---|---|
| بولی | std::optional<bool> | Optional<Boolean> | Option<bool> |
| عدد صحیح | std::optional<std::int32_t> | Optional<Integer> | Option<i32> |
| یوینت | std::optional<std::uint32_t> | Optional<Integer> | Option<u32> |
| طولانی | std::optional<std::int64_t> | Optional<Long> | Option<i64> |
| یو لانگ | std::optional<std::uint64_t> | Optional<Long> | Option<u64> |
| دو برابر | std::optional<double> | Optional<Double> | Option<f64> |
| رشته | std::optional<std::string> | Optional<String> | Option<String> |
| شمارشی | std::optional<{api\_name}\_values> | Optional<{api\_name}\_values> | Option<{ApiName}Values> |
| فهرست تی | std::vector<std::optional<T>> | List<T> | Vec<T> |
در اینجا مثالی از یک فایل توضیحات Sysprop که سه ویژگی را تعریف میکند، آورده شده است:
# 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 تعریف کنید. 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++ میتوانند برای استفاده از APIهای تولید شده، به sysprop_library لینک شوند. سیستم ساخت، لینکهایی از کلاینتها به کتابخانههای تولید شده C++، Java و Rust ایجاد میکند و به این ترتیب به کلاینتها امکان دسترسی به APIهای تولید شده را میدهد.
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"],
}
در مثال قبلی، میتوانستید به صورت زیر به ویژگیهای تعریفشده دسترسی داشته باشید.
مثال زنگ زدگی:
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
);
}
…
}
…
مثال سی++:
#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);
}
…
}
…