פלטפורמת Android מכילה הרבה קובצי XML לאחסון נתוני תצורה (לדוגמה, תצורת אודיו). רבים מקובצי ה-XML נמצאים בvendor
אבל נקראות במחיצה system
. במקרה הזה, הסכימה
של קובץ ה-XML משמש כממשק בין שתי המחיצות,
יש לציין את הסכימה באופן מפורש, והסכימה חייבת להתפתח תוך שמירה על תאימות לאחור
באופן כללי.
לפני Android 10, הפלטפורמה לא סיפקה
מנגנונים שמחייבים ציון של סכימת XML ושימוש בה, או כדי למנוע
שינויים שאינם תואמים בסכימה. הגרסה של Android 10 מספקת
המנגנון הזה, שנקרא Config File Schema API. המנגנון הזה מורכב מכלים שנקרא xsdc
ומכלל build שנקרא xsd_config
.
הכלי xsdc
הוא קומפילטור של מסמך סכימת XML (XSD). הוא מנתח קובץ XSD
שמתאר את הסכימה של קובץ XML ויוצר קוד Java ו-C++.
הקוד שנוצר מנתח קובצי XML שתואמים לסכימת ה-XSD לעץ של
אובייקטים, שכל אחד מהם יוצר תג XML. מאפייני ה-XML מוגדרים בתור שדות של האובייקטים.
כלל ה-build של xsd_config
משלב את הכלי xsdc
במערכת ה-build.
עבור קובץ קלט נתון XSD, כלל ה-build יוצר ספריות Java ו-C++. אפשר לקשר את הספריות למודולים שבהם קובצי ה-XML שתואמים ל-XSD נקראים ומשמשים. אפשר להשתמש בכלל ה-build בקובצי ה-XML שלכם, שנעשה בהם שימוש במחיצות system
ו-vendor
.
Build Config File Schema API
בקטע הזה מוסבר איך ליצור את Config File Schema API.
הגדרת כלל ה-build xsd_config בקובץ Android.bp
כלל ה-build xsd_config
יוצר את קוד המנתח באמצעות הכלי xsdc
.
המאפיין package_name
של כלל ה-build של xsd_config
קובע את שם החבילה של
בקוד ה-Java שנוצר.
דוגמה לכלל build מסוג xsd_config
ב-Android.bp
:
xsd_config {
name: "hal_manifest",
srcs: ["hal_manifest.xsd"],
package_name: "hal.manifest",
}
דוגמה למבנה ספרייה:
├── Android.bp
├── api
│ ├── current.txt
│ ├── last_current.txt
│ ├── last_removed.txt
│ └── removed.txt
└── hal_manifest.xsd
מערכת ה-build יוצרת רשימת ממשקי API באמצעות קוד Java שנוצר ובדיקות
מול ה-API. בדיקת ה-API הזו מתווספת ל-DroidCore ומבוצעת ב-m -j
.
יצירת קבצים של רשימות API
כדי לבצע את בדיקות ה-API, צריך לרשום את קובצי ה-API בקוד המקור.
רשימות הקבצים של ה-API כוללות:
current.txt
ו-removed.txt
בודקים אם ממשקי ה-API שונו על ידי בהשוואה לקובצי API שנוצרו בזמן ה-build.last_current.txt
ו-last_removed.txt
בודקים אם ממשקי ה-API תואמת לאחור באמצעות השוואה לקובצי API.
כדי ליצור קבצים של רשימות API:
- ליצור קובצי רשימות ריקים.
- מריצים את הפקודה
make update-api
.
שימוש בקוד הניתוח שנוצר
כדי להשתמש בקוד Java שנוצר, צריך להוסיף את :
כקידומת למודול xsd_config
במאפיין Java srcs
. החבילה של קוד ה-Java שנוצר זהה לנכס package_name
.
java_library {
name: "vintf_test_java",
srcs: [
"srcs/**/*.java"
":hal_manifest"
],
}
כדי להשתמש בקוד C++ שנוצר, יש להוסיף את שם המודול xsd_config
אל
generated_sources
ו-generated_headers
. ולהוסיף את libxml2
אל
static_libs
או shared_libs
, כי הפרמטר libxml2
נדרש במנתח שנוצר
מרחב השמות של קוד ה-C++ שנוצר זהה לנכס package_name
. לדוגמה, אם
שם המודול xsd_config
הוא hal.manifest
, מרחב השמות הוא
hal::manifest
.
cc_library{
name: "vintf_test_cpp",
srcs: ["main.cpp"],
generated_sources: ["hal_manifest"],
generated_headers: ["hal_manifest"],
shared_libs: ["libxml2"],
}
שימוש במנתח
כדי להשתמש בקוד של מנתח Java, אפשר להשתמש בפונקציה XmlParser#read
או
שיטה read{class-name}
להחזרת המחלקה של השורש
לרכיב מסוים. הניתוח מתבצע בשלב הזה.
import hal.manifest.*;
…
class HalInfo {
public String name;
public String format;
public String optional;
…
}
void readHalManifestFromXml(File file) {
…
try (InputStream str = new BufferedInputStream(new FileInputStream(file))) {
Manifest manifest = XmlParser.read(str);
for (Hal hal : manifest.getHal()) {
HalInfo halinfo;
HalInfo.name = hal.getName();
HalInfo.format = hal.getFormat();
HalInfo.optional = hal.getOptional();
…
}
}
…
}
כדי להשתמש בקוד המנתח של C++, קודם כול צריך לכלול את קובץ הכותרת. השם של
קובץ הכותרת הוא שם החבילה שהנקודות (.) מומרות לקווים תחתונים (_).
לאחר מכן משתמשים בשיטה read
או read{class-name}
כדי להחזיר את המחלקה של אלמנט הבסיס. הניתוח מתבצע בשלב הזה. הערך המוחזר הוא
std::optional<>
.
include "hal_manifest.h"
…
using namespace hal::manifest
struct HalInfo {
public std::string name;
public std::string format;
public std::string optional;
…
};
void readHalManifestFromXml(std::string file_name) {
…
Manifest manifest = *read(file_name.c_str());
for (Hal hal : manifest.getHal()) {
struct HalInfo halinfo;
HalInfo.name = hal.getName();
HalInfo.format = hal.getFormat();
HalInfo.optional = hal.getOptional();
…
}
…
}
כל ממשקי ה-API שסופקו לשימוש במנתח נמצאים ב-api/current.txt
. עבור
אחידות, כל שמות האלמנטים והמאפיינים מומרים לאותיות של גמל (במקרה של
לדוגמה, ElementName
) ומשמשים כמשתנה, שיטה ו-
שם המחלקה. אפשר לקבל את המחלקה של רכיב השורש שנותח באמצעות
read{class-name}
. אם יש רק שורש אחד
אז שם הפונקציה הוא read
. אפשר לקבל את הערך של מאפיין או רכיב משנה שעבר ניתוח באמצעות הפונקציה get{variable-name}
.
יצירת קוד מנתח
ברוב המקרים, אין צורך להריץ את xsdc
ישירות. שימוש ב-build של xsd_config
אלא יותר, כפי שמתואר
הגדרת כלל ה-build xsd_config ב-Android.bp. בקטע הזה מוסבר על ממשק שורת הפקודה xsdc
, לצורך השלמות. האפשרות הזו עשויה להיות שימושית לניפוי באגים.
צריך לספק לכלי xsdc
את הנתיב לקובץ ה-XSD ואת החבילה.
החבילה היא שם חבילה בקוד Java ומרחב שמות בקוד C++. האפשרויות לקביעת סוג הקוד שנוצר הן -j
או -c
, בהתאמה. האפשרות -o
היא הנתיב של ספריית הפלט.
usage: xsdc path/to/xsd_file.xsd [-c] [-j] [-o <arg>] [-p]
-c,--cpp Generate C++ code.
-j,--java Generate Java code.
-o,--outDir <arg> Out Directory
-p,--package Package name of the generated java file. file name of
generated C++ file and header
פקודה לדוגמה:
$ xsdc audio_policy_configuration.xsd -p audio.policy -j