공급업체 APEX

APEX 파일 형식을 사용하여 하위 수준의 Android OS 모듈을 패키징하고 설치할 수 있습니다. APEX 파일 형식을 사용하면 네이티브 서비스 및 라이브러리, HAL 구현, 펌웨어, 구성 파일과 같은 구성요소를 독립적으로 빌드하고 설치할 수 있습니다.

공급업체 APEX는 빌드 시스템에 의해 /vendor 파티션에 자동으로 설치되며, 다른 파티션에 있는 APEX와 동일하게 apexd에 의해 런타임에 활성화됩니다.

사용 사례

공급업체 이미지의 모듈화

APEX는 기능 구현을 공급업체 이미지에 자연스럽게 번들화하고 모듈화합니다.

공급업체 이미지가 독립적으로 빌드된 공급업체 APEX의 조합으로 빌드되면 기기 제조업체는 기기에 맞는 특정 공급업체 구현을 쉽게 고르고 선택할 수 있습니다. 제조업체는 공급된 APEX 중 필요한 APEX가 없거나 새로운 맞춤 하드웨어가 있으면 새 공급업체 APEX를 만들 수 있습니다.

예를 들어, OEM은 AOSP Wi-Fi 구현 APEX, SoC 블루투스 구현 APEX, 맞춤 OEM 전화 통신 구현 APEX로 기기를 구성하도록 선택할 수 있습니다.

공급업체 APEX가 없으면 공급업체 구성요소 간에 많은 종속 항목을 사용하여 구현해야 하므로 주의 깊게 조정하고 추적해야 합니다. 교차 기능 통신의 모든 지점에서 명확하게 정의된 인터페이스로 APEX의 모든 구성요소(구성 파일 및 추가 라이브러리 포함)를 래핑하여 다양한 구성요소를 서로 교체하며 사용할 수 있게 되었습니다.

개발자의 반복 작업

공급업체 APEX를 사용하면 전체 기능 구현(예: Wi-Fi HAL)을 번들로 묶어 공급업체 APEX 내에서 공급업체 모듈을 개발하는 동안 개발자가 더 빠르게 반복 작업을 할 수 있습니다. 그런 다음 개발자는 전체 공급업체 이미지를 다시 빌드하지 않고 공급업체 APEX를 빌드하고 개별적으로 푸시하여 변경사항을 테스트할 수 있습니다.

이렇게 하면 한 가지 기능 영역에서 주로 작업하고 해당 기능 영역에서만 반복하려고 하는 개발자의 개발자 반복 주기를 단순화하고 속도를 높일 수 있습니다.

기능 영역을 APEX에 번들로 묶으면 빌드, 푸시, 기능 영역의 변경사항 테스트 절차도 간소화할 수 있습니다. 예를 들어, APEX를 재설치하면 APEX에 포함된 모든 번들 라이브러리 또는 구성 파일이 자동으로 업데이트됩니다.

또한 기능 영역을 APEX에 번들로 묶으면 잘못된 기기 동작이 관찰될 때 간단하게 디버깅하거나 되돌릴 수 있습니다. 예를 들어, 전화 통신이 새 빌드에서 올바르게 작동하지 않으면 개발자는 (전체 빌드를 플래시할 필요 없이) 기기에 기존 전화 통신 구현 APEX를 설치하여 올바르게 동작하는지 확인해 볼 수 있습니다.

워크플로 예

# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w

# Test the device.
... testing ...

# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...

# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...

기본 사항

일반 APEX 정보(기기 요구사항, 파일 형식 세부정보, 설치 단계 등)는 기본 APEX 파일 형식 페이지를 참고하세요.

Android.bp에서 vendor: true 속성을 설정하여 APEX 모듈을 공급업체 APEX로 만듭니다.

apex {
  ..
  vendor: true,
  ..
}

바이너리 및 공유 라이브러리

APEX는 안정적인 인터페이스가 없는 한 APEX 페이로드 내에 전이 종속 항목을 포함합니다.

공급업체 APEX 종속 항목의 안정적인 네이티브 인터페이스에는 stubs, ndk_library, llndk_library가 있는 cc_library가 포함됩니다. 이러한 종속 항목은 패키징에서 제외되며 종속 항목은 APEX 매니페스트에 기록됩니다. 매니페스트는 linkerconfig를 통해 처리되므로 외부 네이티브 종속 항목은 런타임에 사용할 수 있습니다.

/system 파티션의 APEX와 달리 공급업체 APEX는 일반적으로 특정 VNDK 버전에 연결됩니다. VNDK 라이브러리는 버전 내의 ABI 안정성을 보장하므로 VNDK 라이브러리를 안정적인 것으로 취급하고 use_vndk_as_stable 속성을 사용해 APEX에서 이를 제외하여 공급업체 APEX의 크기를 줄일 수 있습니다.

아래의 스니펫에서 APEX는 바이너리(my_service)와 안정적이지 않은 종속 항목(*.so 파일)을 모두 포함합니다. my_servicelibbase와 같은 VNDK 라이브러리로 빌드된 경우에는 APEX가 VNDK 라이브러리를 포함하지 않습니다. 대신 런타임에 my_service는 시스템에서 제공하는 VNDK 라이브러리의 libbase를 사용합니다.

apex {
  ..
  vendor: true,
  use_vndk_as_stable: true,
  binaries: ["my_service"],
  ..
}

아래 스니펫에서 APEX는 공유 라이브러리 my_standalone_lib과 안정적이지 않은 종속 항목을 포함합니다(위의 설명 참고).

apex {
  ..
  vendor: true,
  use_vndk_as_stable: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

HAL 구현

HAL 구현을 정의하려면 다음 예와 비슷하게 공급업체 APEX 내의 바이너리 및 라이브러리 중 대응하는 바이너리와 라이브러리를 제공합니다.

HAL 구현을 완전히 캡슐화하려면 APEX는 관련 VINTF 프래그먼트와 init 스크립트도 지정해야 합니다.

VINTF 프래그먼트

VINTF 프래그먼트는 APEX 내부에 포함되지 않고 /vendor/etc/vintf에 설치됩니다.

다음과 같이 표준 vintf_fragments 속성을 사용합니다.

apex {
  ..
  vendor: true,
  vintf_fragments: ["fragment.xml"],
  ..
}

Init 스크립트

APEX는 두 가지 방법으로 init 스크립트를 포함할 수 있습니다. (A) APEX 페이로드 내에 사전 빌드된 텍스트 파일 또는 (B) /vendor/etc에 있는 일반 init 스크립트를 사용하면 됩니다. 동일한 APEX에 둘 다 설정할 수 있습니다.

APEX의 init 스크립트는 다음과 같습니다.

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

APEX 내의 init 스크립트에는 service 정의만 포함될 수 있으며 on <property> 지시어는 포함되지 않습니다. 서비스가 일부 조건에 종속되어야 하는 경우 /vendor에 init 스크립트를 설치해야 합니다.

/vendor의 init 스크립트는 다음과 같습니다.

apex {
  ..
  vendor: true,
  init_rc: ["myinit.in.vendor.rc"],
  ..
}

init 스크립트 내의 바이너리 경로에 관한 모든 참조는 APEX 내 경로를 반영해야 합니다. init 스크립트 소스 파일에 경로를 하드코딩하거나 genrule을 사용하여 기존 소스 파일에서 경로를 대체할 수 있습니다.

펌웨어

예:

다음과 같이 prebuilt_firmware 모듈 유형을 사용하여 공급업체 APEX에 펌웨어를 삽입합니다.

prebuilt_firmware {
  name: "my.bin",
  src: "path_to_prebuilt_firmware",
  vendor: true,
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.bin"],  // installed inside APEX as /etc/firmware/my.bin
  ..
}

prebuilt_firmware 모듈은 APEX의 <apex name>/etc/firmware 디렉터리에 설치됩니다. ueventd/apex/*/etc/firmware 디렉터리를 스캔하여 펌웨어 모듈을 찾습니다.

APEX의 file_contexts는 런타임에 ueventd에서 이러한 파일에 액세스할 수 있도록 모든 펌웨어 페이로드 항목에 올바르게 라벨을 지정해야 합니다. 일반적으로 vendor_file 라벨이면 충분합니다. 예:

(/.*)? u:object_r:vendor_file:s0

커널 모듈

다음과 같이 커널 모듈을 사전 빌드된 모듈로 공급업체 APEX에 삽입합니다.

prebuilt_etc {
  name: "my.ko",
  src: "my.ko",
  vendor: true,
  sub_dir: "modules"
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.ko"],  // installed inside APEX as /etc/modules/my.ko
  ..
}

APEX의 file_contexts는 모든 커널 모듈 페이로드 항목에 올바르게 라벨을 지정해야 합니다. 예:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

커널 모듈은 명시적으로 설치해야 합니다. 다음의 공급업체 파티션 init 스크립트 예는 insmod를 사용하여 설치하는 방법을 보여줍니다.

my_init.rc:

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

런타임 리소스 오버레이

예:

rros 속성을 사용하여 공급업체 APEX에 런타임 리소스 오버레이를 삽입합니다.

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}

apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

기타 구성 파일

공급업체 APEX는 구성 파일이 사전에 공급업체 APEX 내에 빌드되어 있으므로 일반적으로 공급업체 파티션에서 볼 수 있는 다양한 구성 파일을 지원하며 이러한 구성 파일은 계속 추가되고 있습니다.

예:

추가 개발 기능

부팅 시 APEX 선택

예:

개발자는 같은 APEX 이름과 키를 공유하는 공급업체 APEX의 여러 버전을 설치할 수 있고 그런 다음 영구 sysprop를 사용하여 부팅할 때마다 활성화할 버전을 선택할 수도 있습니다. 특정 개발자 사용 사례의 경우, 이렇게 하는 것이 adb install을 사용하여 APEX의 새 사본을 설치하는 것보다 간단할 수 있습니다.

사용 사례 예:

  • Wi-Fi HAL 공급업체 APEX의 3가지 버전 설치: QA팀은 한 버전을 사용하여 수동 테스트 또는 자동 테스트를 실행한 후 다른 버전으로 재부팅하고 테스트를 다시 실행한 후 최종 결과를 비교할 수 있습니다.
  • 카메라 HAL 공급업체 APEX의 두 가지 버전, currentexperimental 설치: Google 내부 테스터는 추가로 파일을 다운로드 및 설치하지 않고도 시험용 버전을 사용할 수 있으므로 쉽게 두 버전 간에 전환할 수 있습니다.

부팅하는 동안 apexd는 올바른 APEX 버전을 활성화하기 위해 특정 형식에 따라 sysprop를 찾습니다.

속성 키의 예상 형식은 다음과 같습니다.

  • Bootconfig
    • BoardConfig.mk에 기본값을 설정하는 데 사용됩니다.
    • androidboot.vendor.apex.<apex name>
  • 영구 sysprop
    • 기본값을 변경하는 데 사용되며 이미 부팅된 기기에 설정됩니다.
    • bootconfig 값이 있는 경우 해당 값을 재정의합니다.
    • persist.vendor.apex.<apex name>

속성 값은 활성화되어야 하는 APEX의 파일 이름이어야 합니다.

APEX Android.bp 정의의 유일한 변경사항은 multi_install_skip_symbol_files 필드이며, 이 필드는 다중 설치된 APEX 정의(기본 버전이 아닌 경우)에서 모두 true여야 합니다.

// Default version.
apex {
  name: "com.oem.camera.hal.my_apex_default",
  apex_name: "com.oem.camera.hal",
  vendor: true,
  ..
}

// Non-default version.
apex {
  name: "com.oem.camera.hal.my_apex_experimental",
  apex_name: "com.oem.camera.hal",
  vendor: true,
  multi_install_skip_symbol_files: true,
  ..
}

또한, 기본 버전은 BoardConfig.mk의 bootconfig를 사용하여 구성해야 합니다.

# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
    androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default

기기가 부팅되면 영구 sysprop를 설정하여 활성화된 버전을 변경합니다.

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

기기가 플래시 후 bootconfig 업데이트를 지원하는 경우(fastboot oem 명령어 사용) 다중 설치된 APEX의 bootconfig 속성이 변경되면 부팅 시 활성화된 버전도 변경됩니다.

Cuttlefish 기반의 가상 참조 기기의 경우 --extra_bootconfig_args 명령어를 사용하여 실행하는 동안 bootconfig 속성을 직접 설정할 수 있습니다. 예:

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";