מאפייני מערכת מספקים דרך נוחה לשתף מידע, בדרך כלל תצורות, בכל המערכת. כל מחיצה יכולה להשתמש במאפייני מערכת משלה באופן פנימי. בעיה יכולה להתרחש כאשר נגישות למאפיינים על פני מחיצות, כגון /vendor
גישה למאפיינים מוגדרים /system
. מאז אנדרואיד 8.0, ניתן לשדרג חלק מהמחיצות, כגון /system
, בעוד ש- /vendor
נותר ללא שינוי. מכיוון שמאפייני מערכת הם רק מילון גלובלי של צמדי מפתח/ערך מחרוזת ללא סכמה, קשה לייצב מאפיינים. מחיצת /system
עשויה לשנות או להסיר מאפיינים שהמחיצה /vendor
תלוי בהם ללא כל הודעה מוקדמת.
החל מהגרסה של אנדרואיד 10, מאפייני מערכת שאליהם ניגש במחיצות מסוכמים לקובצי Sysprop Description, וממשקי API לגישה למאפיינים נוצרים כפונקציות קונקרטיות עבור C++ ו-Rust, ומחלקות עבור Java. ממשקי 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++) או מחלקה סופית סטטית (Java) שבה ממוקמים ממשקי API שנוצרו. לדוגמה, com.android.sysprop.BuildProperties יהיה namespace com::android::sysprop::BuildProperties ב-C++, והמחלקה BuildProperties בחבילה ב- com.android.sysprop ב-Java. |
prop | רשימת נכסים. |
המשמעויות של שדות הודעת Property
הן כדלקמן.
שדה | מַשְׁמָעוּת |
---|---|
api_name | שם ה-API שנוצר. |
type | סוג הנכס הזה. |
access | Readonly : מייצר ממשק API של getter בלבד הערה: מאפיינים עם הקידומת |
scope | Internal : רק הבעלים יכול לגשת. |
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 של getter מנסה לקרוא prop_name ומשתמש legacy_prop_name אם prop_name לא קיים. השתמש legacy_prop_name בעת הוצאה משימוש של נכס קיים ומעבר לנכס חדש. |
כל סוג של נכס ממפה לסוגים הבאים ב-C++, Java ו-Rust.
סוּג | C++ | Java | חֲלוּדָה |
---|---|---|---|
בוליאנית | 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 |
רשימת 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
משמשת כ-API עבור C++, Java ו-Rust. מערכת ה-build מייצרת באופן פנימי 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++, Java ו-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);
}
…
}
דוגמה של 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);
}
…
}
…