توفّر سمات النظام طريقة ملائمة لمشاركة المعلومات، وعادةً ما تكون تلك المعلومات تتعلّق بالإعدادات على مستوى النظام. يمكن لكل قسم استخدام خصائص النظام الخاصة به
داخليًا. يمكن أن تحدث مشكلة عند الوصول إلى المواقع على مستوى الأقسام،
مثل /vendor
الوصول إلى المواقع المحدّدة من قِبل /system
. منذ الإصدار 8.0 من Android،
يمكن ترقية بعض الأقسام، مثل /system
، مع إبقاء /vendor
بدون تغيير. وبما أنّ سمات النظام ليست سوى قاموس شامل من سلاسل تشكل ثنائيات ملفوظة مفاتيح وقيم بدون مخطّط، من الصعب استقرار السمات. يمكن أن يغيّر القسم
/system
أو يزيل السمات التي يعتمد عليها القسم /vendor
بدون أي إشعار.
بدءًا من الإصدار 10 من Android، يتم تحويل خصائص النظام التي يتم الوصول إليها في جميع الأقسام إلى مخططات في ملفات Sysprop Description، ويتم إنشاء واجهات برمجة التطبيقات للوصول إلى الخصائص كوظائف محدّدة للغة C++ وRust،
وطبقات للغة Java. إنّ واجهات برمجة التطبيقات هذه أكثر ملاءمةً للاستخدام لأنّها لا تتطلّب استخدام سلاسل رمزية سحرية (مثل ro.build.date
) للوصول إليها، ويمكن استخدامها مع أنواع ثابتة. يتم أيضًا التحقّق من ثبات واجهة التطبيق الثنائية (ABI) في وقت الإصدار، ويتعطّل الإصدار في حال حدوث تغييرات غير متوافقة. يعمل هذا الفحص كموصّلات محدّدة بوضوح بين الأقسام. ويمكن أن توفّر واجهات برمجة التطبيقات هذه أيضًا اتساقًا بين
Rust وJava وC++.
تحديد خصائص النظام كواجهات برمجة تطبيقات
حدِّد خصائص النظام كواجهات برمجة تطبيقات باستخدام ملفات 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 واحد على رسالة خصائص واحدة تصف مجموعة من الخصائص. في ما يلي معنى حقول هذا الجدول:
الحقل | المعنى |
---|---|
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 : إنشاء واجهة برمجة تطبيقات للحصول على البيانات فقط
Writeonce وReadWrite : إنشاء واجهات برمجة تطبيقات للحصول على البيانات وضبطها
ملاحظة: قد لا تستخدم السمات التي تحتوي على البادئة ro. إذن الوصول
ReadWrite .
|
scope
|
Internal : يمكن للمالك فقط الوصول إلى هذا المحتوى.
Public : يمكن لجميع المستخدمين الوصول إلى المحتوى، باستثناء وحدات NDK.
|
prop_name
|
تمثّل هذه السمة اسم السمة الأساسية للنظام، مثل ro.build.date .
|
enum_values
|
(Enum وEnumList فقط) سلسلة مفصولة بشريط(|)
تتألف من قيم التعداد المحتملة على سبيل المثال، value1|value2 .
|
integer_as_bool
|
(Boolean وBooleanList فقط) اجعل وظائف الإعداد تستخدم
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) |
---|---|---|---|
قيمة منطقية | 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
|
طويل | 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
|
قائمة T | 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 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
في الدليل
نفسه الذي يتضمّن الدليل Android.bp
. أسماء ملفات واجهة برمجة التطبيقات هي <module_name>-current.txt
،
<module_name>-latest.txt
. يحتوي <module_name>-current.txt
على توقيعات واجهة برمجة التطبيقات
للرموز المصدر الحالية، ويحتوي <module_name>-latest.txt
على أحدث توقيعات واجهة برمجة التطبيقات
المجمّدة. يتحقّق نظام الإنشاء ممّا إذا تم تغيير واجهات برمجة التطبيقات من خلال مقارنة ملفات واجهات برمجة التطبيقات هذه بملفات واجهات برمجة التطبيقات التي تم إنشاؤها في وقت الإنشاء، ويُرسِل current.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: ["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);
}
…
}
مثال على 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);
}
…
}
…