توفّر سمات النظام (sysprops) طريقة سهلة لمشاركة المعلومات، وعادةً ما تكون الإعدادات، على مستوى النظام. يمكن لكل قسم استخدام سمات النظام الخاصة به داخليًا. قد تحدث مشكلة عند الوصول إلى السمات في أقسام مختلفة، مثل وصول /vendor إلى السمات المحدّدة في /system. اعتبارًا من Android 8.0، يمكن ترقية بعض الأقسام، مثل /system، بينما يظل القسم /vendor بدون تغيير. بما أنّ سمات النظام هي مجرد قاموس عالمي لأزواج القيم الرئيسية من السلاسل بدون مخطط، فمن الصعب تثبيت السمات. يمكن أن يغيّر القسم /system أو يزيل السمات التي يعتمد عليها القسم /vendor بدون إرسال أي إشعار.
في Android 10 والإصدارات الأحدث، يتم وضع مخطط لسمات النظام التي يتم الوصول إليها في أقسام مختلفة في ملفات وصف سمات النظام، ويتم إنشاء واجهات برمجة التطبيقات للوصول إلى السمات كدوال ملموسة للغتَي C++ وRust، وفئات للغة Java. تكون واجهات برمجة التطبيقات هذه أسهل في الاستخدام لأنّها لا تتطلّب سلاسل سحرية (مثل ro.build.date) للوصول، ولأنّه يمكن كتابتها بشكل ثابت. يتم أيضًا التحقّق من ثبات واجهة التطبيق الثنائية (ABI) في مدّة التصميم، ويتوقف الإنشاء إذا حدثت تغييرات غير متوافقة. يعمل هذا التحقّق كواجهات محدّدة بشكل صريح بين الأقسام. يمكن أن توفّر واجهات برمجة التطبيقات هذه أيضًا الاتساق بين لغات Rust وJava وC++.
تحديد سمات النظام كواجهات برمجة تطبيقات
يمكن تحديد سمات النظام كواجهات برمجة تطبيقات باستخدام ملفات وصف سمات النظام (.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;
}
يحتوي ملف وصف سمات النظام على رسالة `properties` تصف مجموعة من السمات. في ما يلي معاني حقولها:
| الحقل | المعنى |
|---|---|
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
|
اسم واجهة برمجة التطبيقات التي تم إنشاؤها |
type
|
نوع هذه السمة |
access
|
Readonly: يؤدي إلى إنشاء واجهة برمجة تطبيقات getter فقط
Writeonce, ReadWrite: يؤدي إلى إنشاء واجهات برمجة تطبيقات getter وsetter
|
scope
|
Internal: يمكن للمالك فقط الوصول إليها.
Public: يمكن للجميع الوصول إليها، باستثناء وحدات NDK.
|
prop_name
|
اسم سمة النظام الأساسية، مثل
ro.build.date.
|
enum_values
|
(Enum, EnumList فقط) سلسلة مفصولة بشريط عمودي(|)
تتألف من قيم التعداد المحتملة على سبيل المثال، value1|value2
|
integer_as_bool
|
(Boolean، BooleanList فقط) يجعل دوال setter تستخدم 0 و1 بدلاً من false وtrue
|
legacy_prop_name
|
(اختياري، سمات Readonly فقط) الاسم القديم لسمة النظام الأساسية عند استدعاء دالة getter، تحاول واجهة برمجة التطبيقات getter قراءة
prop_name وتستخدم legacy_prop_name إذا
prop_name لم يكن موجودًا. يُستخدَم legacy_prop_name عند
إيقاف سمة حالية والانتقال إلى سمة جديدة.
|
يرتبط كل نوع من السمات بالأنواع التالية في لغات C++ وJava وRust:
| النوع | C++ (يمكن أن تكون القيمة فارغة) | Java (يمكن أن تكون القيمة فارغة) | Rust (اختيارية أو يمكن أن تكون القيمة فارغة) |
|---|---|---|---|
| Boolean | std::optional<bool> |
Optional<Boolean> |
Option<bool> |
| Integer | std::optional<std::int32_t> |
Optional<Integer> |
Option<i32> |
| UInt | std::optional<std::uint32_t> |
Optional<Integer> |
Option<u32> |
| Long | std::optional<std::int64_t> |
Optional<Long> |
Option<i64> |
| ULong | std::optional<std::uint64_t> |
Optional<Long> |
Option<u64> |
| Double | std::optional<double> |
Optional<Double> |
Option<f64> |
| String | std::optional<std::string> |
Optional<String> |
Option<String> |
| Enum | std::optional<{api\_name}\_values> |
Optional<{api\_name}\_values> |
Option<{ApiName}Values> |
| T List | std::vector<std::optional<T>> |
List<T> |
Vec<T> |
في ما يلي مثال على ملف وصف سمات النظام الذي يحدّد ثلاث سمات:
# 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_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 في الدليل نفسه الذي يحتوي على Android.bp. يكون اسما ملفَي واجهة برمجة التطبيقات هما <module_name>-current.txt و
<module_name>-latest.txt. يحتوي <module_name>-current.txt على تواقيع واجهة برمجة التطبيقات
للرموز البرمجية المصدر الحالية، ويحتوي <module_name>-latest.txt على أحدث تواقيع واجهة برمجة التطبيقات الثابتة. يتحقّق نظام الإنشاء مما إذا تم تغيير واجهات برمجة التطبيقات من خلال مقارنة ملفات واجهة برمجة التطبيقات هذه بملفات واجهة برمجة التطبيقات التي تم إنشاؤها في وقت الإنشاء، ويُصدر رسالة خطأ وتعليمات لتعديل الملف 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);
}
…
}
مثال على لغة Java:
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);
}
…
}
…