Android 플랫폼에는 구성 데이터 저장을 위한 다수의 XML 파일이 포함되어 있습니다(예: 오디오 구성). 대부분의 XML 파일은 vendor 파티션에 있지만 읽기는 system 파티션에서 이루어집니다. 이 경우 XML 파일의 스키마가 두 파티션의 인터페이스로 기능합니다. 따라서 스키마는 명시적으로 지정되어야 하며 이전 버전과의 호환이 가능한 방식으로 진화해야 합니다.
Android 10 이전에는 플랫폼에서 XML 스키마 지정 및 사용을 요구하거나 스키마에서 호환되지 않는 변경사항을 방지하기 위한 메커니즘을 제공하지 않았습니다. Android 10에서는 이러한 메커니즘을 제공하며, 이를 Config File Schema API라고 부릅니다. 이 메커니즘은 xsdc라는 도구와 xsd_config라는 빌드 규칙으로 구성됩니다.
xsdc 도구는 XML 스키마 문서(XSD) 컴파일러입니다. 이 도구는 XML 파일의 스키마를 설명하는 XSD 파일을 파싱하고 자바 및 C++ 코드를 생성합니다. 생성된 코드는 XSD 스키마와 일치하는 XML 파일을 객체의 트리에 파싱합니다. 각 객체는 XML 태그를 모델링합니다. XML 속성은 객체의 필드로 모델링됩니다.
xsd_config 빌드 규칙은 xsdc 도구를 빌드 시스템에 통합합니다.
XSD 입력 파일의 경우 빌드 규칙에 의해 자바 및 C++ 라이브러리가 생성됩니다. 라이브러리는 XSD와 일치하는 XML 파일이 판독 및 사용되는 모듈에 연결할 수 있습니다. 빌드 규칙은 system 및 vendor 파티션에 사용되는 자체 XML 파일에 사용할 수 있습니다.
Config File Schema API 빌드
이 섹션에서는 Config File Schema API를 빌드하는 방법에 대해 설명합니다.
Android.bp에서 xsd_config 빌드 규칙 구성
xsd_config 빌드 규칙은 xsdc 도구로 파서 코드를 생성합니다. xsd_config 빌드 규칙의 package_name 속성은 생성된 자바 코드의 패키지 이름을 결정합니다.
Android.bp의 xsd_config 빌드 규칙 예시:
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
빌드 시스템은 생성된 자바 코드를 사용하여 API 목록을 생성한 후 이를 토대로 API를 검사합니다. 이 API 검사는 DroidCore에 추가되며 m -j에서 실행됩니다.
API 목록 파일 만들기
API 검사를 진행하려면 소스 코드에 API 목록 파일이 있어야 합니다.
API 목록 파일에는 다음이 포함됩니다.
current.txt및removed.txt는 빌드 시간에 생성된 API 파일과 비교하여 API가 변경되었는지 확인합니다.last_current.txt및last_removed.txt는 API 파일과 비교하여 API가 이전 버전과 호환되는지 확인합니다.
API 목록 파일 생성 방법:
- 빈 목록 파일을 만듭니다.
- 명령어
make update-api를 실행합니다.
생성된 파서 코드 사용
생성된 자바 코드를 사용하려면 :를 자바 srcs 속성의 xsd_config 모듈 이름에 접두사로 추가합니다. 생성된 자바 코드의 패키지는 package_name 속성과 동일합니다.
java_library {
name: "vintf_test_java",
srcs: [
"srcs/**/*.java"
":hal_manifest"
],
}
생성된 C++ 코드를 사용하려면 xsd_config 모듈 이름을 generated_sources 및 generated_headers 속성에 추가합니다. 그런 다음 libxml2가 생성된 파서 코드에 필요하므로 libxml2를 static_libs 또는 shared_libs에 추가합니다. 생성된 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"],
}
파서 사용
자바 파서 코드를 사용하려면 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를 직접 실행할 필요는 없습니다. Android.bp에서 xsd_config 빌드 규칙 구성에 설명된 것처럼 xsd_config 빌드 규칙을 대신 사용하세요. 이 섹션에서는 완전성을 기하기 위해 xsdc 명령줄 인터페이스에 대해 설명합니다. 이는 디버깅에 유용할 수 있습니다.
XSD 파일 경로와 패키지를 xsdc 도구에 제공해야 합니다. 패키지는 자바 코드의 패키지 이름, C++ 코드의 네임스페이스입니다. 생성된 코드가 자바인지 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