Triển khai API lược đồ tệp cấu hình

Nền tảng Android chứa nhiều tệp XML để lưu trữ dữ liệu cấu hình (ví dụ: cấu hình âm thanh). Nhiều tệp XML nằm trong phân vùng vendor nhưng chúng được đọc trong phân vùng system . Trong trường hợp này, lược đồ của tệp XML đóng vai trò là giao diện giữa hai phân vùng và do đó lược đồ phải được chỉ định rõ ràng và phải phát triển theo cách tương thích ngược.

Trước Android 10, nền tảng này không cung cấp cơ chế yêu cầu chỉ định và sử dụng lược đồ XML hoặc ngăn chặn những thay đổi không tương thích trong lược đồ. Android 10 cung cấp cơ chế này, được gọi là API Lược đồ tệp cấu hình. Cơ chế này bao gồm một công cụ có tên xsdc và quy tắc xây dựng có tên xsd_config .

Công cụ xsdc là trình biên dịch Tài liệu Lược đồ XML (XSD). Nó phân tích tệp XSD mô tả lược đồ của tệp XML và tạo mã Java và C++. Mã được tạo ra sẽ phân tích các tệp XML tuân theo lược đồ XSD thành một cây đối tượng, mỗi đối tượng mô hình hóa một thẻ XML. Các thuộc tính XML được mô hình hóa dưới dạng các trường của đối tượng.

Quy tắc xây dựng xsd_config tích hợp công cụ xsdc vào hệ thống xây dựng. Đối với một tệp đầu vào XSD nhất định, quy tắc xây dựng sẽ tạo ra các thư viện Java và C++. Bạn có thể liên kết các thư viện với các mô-đun nơi các tệp XML phù hợp với XSD được đọc và sử dụng. Bạn có thể sử dụng quy tắc xây dựng cho các tệp XML của riêng mình được sử dụng trên toàn bộ system và các phân vùng vendor .

Xây dựng API lược đồ tệp cấu hình

Phần này mô tả cách xây dựng API lược đồ tệp cấu hình.

Định cấu hình quy tắc xây dựng xsd_config trong Android.bp

Quy tắc xây dựng xsd_config tạo mã trình phân tích cú pháp bằng công cụ xsdc . Thuộc tính package_name của quy tắc xây dựng xsd_config xác định tên gói của mã Java được tạo.

Ví dụ về quy tắc xây dựng xsd_config trong Android.bp :

xsd_config {
    name: "hal_manifest",
    srcs: ["hal_manifest.xsd"],
    package_name: "hal.manifest",
}

Cấu trúc thư mục ví dụ:

├── Android.bp
├── api
│   ├── current.txt
│   ├── last_current.txt
│   ├── last_removed.txt
│   └── removed.txt
└── hal_manifest.xsd

Hệ thống xây dựng tạo danh sách API bằng cách sử dụng mã Java được tạo và kiểm tra API dựa trên đó. Kiểm tra API này được thêm vào DroidCore và được thực thi tại m -j .

Tạo tệp danh sách API

Việc kiểm tra API yêu cầu các tệp liệt kê API trong mã nguồn.

Các tệp danh sách API bao gồm:

  • current.txtremoved.txt kiểm tra xem các API có bị thay đổi hay không bằng cách so sánh với các tệp API được tạo tại thời điểm xây dựng.
  • last_current.txtlast_removed.txt kiểm tra xem các API có tương thích ngược hay không bằng cách so sánh với các tệp API.

Để tạo tệp danh sách API:

  1. Tạo tập tin danh sách trống.
  2. Chạy lệnh make update-api .

Sử dụng mã trình phân tích cú pháp được tạo

Để sử dụng mã Java được tạo, hãy thêm : làm tiền tố cho tên mô-đun xsd_config trong thuộc tính srcs của Java. Gói mã Java được tạo giống với thuộc tính package_name .

java_library {
    name: "vintf_test_java",
    srcs: [
        "srcs/**/*.java"
        ":hal_manifest"
    ],
}

Để sử dụng mã C++ được tạo, hãy thêm tên mô-đun xsd_config vào các thuộc tính generated_sourcesgenerated_headers . Và thêm libxml2 vào static_libs hoặc shared_libs , vì libxml2 là bắt buộc trong mã trình phân tích cú pháp được tạo. Không gian tên của mã C++ được tạo giống với thuộc tính package_name . Ví dụ: nếu tên mô-đun xsd_confighal.manifest , thì không gian tên là hal::manifest .

cc_library{
    name: "vintf_test_cpp",
    srcs: ["main.cpp"],
    generated_sources: ["hal_manifest"],
    generated_headers: ["hal_manifest"],
    shared_libs: ["libxml2"],
}

Sử dụng trình phân tích cú pháp

Để sử dụng mã trình phân tích cú pháp Java, hãy sử dụng phương thức XmlParser#read hoặc read{ class-name } để trả về lớp của phần tử gốc. Phân tích cú pháp xảy ra vào thời điểm này.

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();
            …
        }
    }
    …
}

Để sử dụng mã trình phân tích cú pháp C++, trước tiên hãy bao gồm tệp tiêu đề. Tên của tệp tiêu đề là tên gói có dấu chấm (.) được chuyển thành dấu gạch dưới (_). Sau đó sử dụng phương thức read hoặc read{ class-name } để trả về lớp của phần tử gốc. Phân tích cú pháp xảy ra vào thời điểm này. Giá trị trả về là 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();
        …
    }
    …
}

Tất cả các API được cung cấp để sử dụng trình phân tích cú pháp đều có trong api/current.txt . Để thống nhất, tất cả các tên thành phần và thuộc tính đều được chuyển đổi sang dạng lạc đà (ví dụ: ElementName ) và được sử dụng làm tên biến, phương thức và tên lớp tương ứng. Lớp của phần tử gốc được phân tích cú pháp có thể được lấy bằng hàm read{ class-name } . Nếu chỉ có một phần tử gốc thì tên hàm là read . Giá trị của một phần tử con hoặc thuộc tính được phân tích cú pháp có thể được lấy bằng hàm get{ variable-name } .

Tạo mã phân tích cú pháp

Trong hầu hết các trường hợp, bạn không cần chạy xsdc trực tiếp. Thay vào đó, hãy sử dụng quy tắc xây dựng xsd_config như được mô tả trong Định cấu hình quy tắc xây dựng xsd_config trong Android.bp . Phần này giải thích giao diện dòng lệnh xsdc để hoàn thiện. Điều này có thể hữu ích cho việc gỡ lỗi.

Bạn phải cung cấp cho công cụ xsdc đường dẫn đến tệp XSD và tệp pack. Gói này là tên gói trong mã Java và một vùng tên trong mã C++. Các tùy chọn để xác định xem mã được tạo là Java hay C lần lượt là -j hoặc -c . Tùy chọn -o là đường dẫn của thư mục đầu ra.

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

Lệnh ví dụ:

$ xsdc audio_policy_configuration.xsd -p audio.policy -j