הטמעת ממשק API של Config File Schema

פלטפורמת אנדרואיד מכילה קבצי XML רבים לאחסון נתוני תצורה (לדוגמה, תצורת אודיו). רבים מקובצי ה-XML נמצאים במחיצת vendor , אך הם נקראים במחיצת system . במקרה זה, הסכימה של קובץ ה-XML משמשת כממשק על פני שתי המחיצות, ולכן יש לציין את הסכימה במפורש ועליה להתפתח באופן תואם לאחור.

לפני אנדרואיד 10, הפלטפורמה לא סיפקה מנגנונים לדרוש ציון ושימוש בסכימת XML, או כדי למנוע שינויים לא תואמים בסכימה. אנדרואיד 10 מספק מנגנון זה, הנקרא Config File Schema API. מנגנון זה מורכב מכלי שנקרא xsdc ומכלל בנייה בשם xsd_config .

הכלי xsdc הוא מהדר XML Schema Document (XSD). הוא מנתח קובץ XSD המתאר את הסכימה של קובץ XML ומייצר קוד Java ו-C++. הקוד שנוצר מנתח קובצי XML התואמים את סכימת XSD לעץ של אובייקטים, שכל אחד מהם מדגמן תג XML. תכונות XML מעוצבות כשדות של האובייקטים.

כלל הבנייה xsd_config משלב את הכלי xsdc במערכת הבנייה. עבור קובץ קלט XSD נתון, כלל הבנייה מייצר ספריות Java ו-C++. אתה יכול לקשר את הספריות למודולים שבהם קוראים ומשתמשים בקבצי ה-XML התואמים ל-XSD. אתה יכול להשתמש בכלל הבנייה עבור קבצי XML משלך המשמשים בכל מחיצות system vendor .

בניית API של סכימת קבצי תצורה

סעיף זה מתאר כיצד לבנות Config File Schema API.

הגדרת כלל הבנייה xsd_config ב-Android.bp

כלל הבנייה xsd_config יוצר את קוד המנתח עם הכלי xsdc . המאפיין package_name של כלל הבנייה xsd_config קובע את שם החבילה של קוד ה-Java שנוצר.

דוגמה לכלל בנייה של 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 שנוצרו בזמן הבנייה.
  • last_current.txt ו- last_removed.txt בודקים אם ממשקי ה-API תואמים לאחור על ידי השוואה עם קובצי API.

כדי ליצור את קבצי רשימות ה-API:

  1. צור קבצי רשימות ריקות.
  2. הפעל את הפקודה 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 or 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 or 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 ישירות. השתמש בכלל הבנייה xsd_config במקום זאת, כמתואר בהגדרת כלל הבנייה xsd_config ב-Android.bp . סעיף זה מסביר את ממשק שורת הפקודה xsdc , רק לשם השלמות. זה עשוי להיות שימושי עבור ניפוי באגים.

עליך לתת לכלי xsdc את הנתיב לקובץ XSD, וחבילה. החבילה היא שם חבילה בקוד Java ומרחב שמות בקוד C++. האפשרויות לקבוע אם הקוד שנוצר הוא 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