HAL을 위한 AIDL

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

Android 11에서는 Android의 HAL에 AIDL을 사용할 수 있는 기능을 소개합니다. 이를 통해 HIDL 없이 Android의 일부를 구현할 수 있습니다. 가능한 경우 HAL이 AIDL만 사용하도록 전환합니다(업스트림 HAL이 HIDL을 사용하는 경우 HIDL을 사용해야 함).

system.img 와 같은 프레임워크 구성요소와 vendor.img 와 같은 하드웨어 구성요소 간에 통신하기 위해 AIDL을 사용하는 HAL은 안정적인 AIDL을 사용해야 합니다. 그러나 예를 들어 하나의 HAL에서 다른 HAL로 파티션 내에서 통신하려면 사용할 IPC 메커니즘에 대한 제한이 없습니다.

동기 부여

AIDL은 HIDL보다 오래 사용되었으며 Android 프레임워크 구성 요소 사이 또는 앱과 같은 다른 많은 위치에서 사용됩니다. 이제 AIDL에 안정성 지원이 있으므로 단일 IPC 런타임으로 전체 스택을 구현할 수 있습니다. AIDL은 또한 HIDL보다 더 나은 버전 관리 시스템을 가지고 있습니다.

  • 단일 IPC 언어를 사용한다는 것은 배우고, 디버그하고, 최적화하고, 보호할 것이 하나만 있다는 것을 의미합니다.
  • AIDL은 인터페이스 소유자를 위한 내부 버전 관리를 지원합니다.
    • 소유자는 인터페이스 끝에 메서드를 추가하거나 소포에 필드를 추가할 수 있습니다. 즉, 수년에 걸쳐 버전 코드를 작성하는 것이 더 쉽고 비용도 매년 더 적습니다(유형을 내부에서 수정할 수 있고 각 인터페이스 버전에 대한 추가 라이브러리가 필요하지 않음).
    • 확장 인터페이스는 유형 시스템이 아닌 런타임에 연결할 수 있으므로 최신 버전의 인터페이스에 다운스트림 확장을 리베이스할 필요가 없습니다.
  • 소유자가 안정화를 선택하면 기존 AIDL 인터페이스를 직접 사용할 수 있습니다. 이전에는 인터페이스의 전체 복사본을 HIDL에서 만들어야 했습니다.

AIDL HAL 인터페이스 작성

시스템과 공급업체 간에 AIDL 인터페이스를 사용하려면 인터페이스에 두 가지 변경이 필요합니다.

  • 모든 유형 정의에는 @VintfStability 로 주석을 달아야 합니다.
  • aidl_interface 선언에는 stability: "vintf", .

인터페이스 소유자만 이러한 변경을 수행할 수 있습니다.

이러한 변경을 수행할 때 인터페이스가 작동하려면 VINTF 매니페스트 에 있어야 합니다. VTS 테스트 vts_treble_vintf_vendor_test 를 사용하여 이를 테스트하고 릴리스된 인터페이스가 고정되었는지 확인하는 것과 같은 관련 요구 사항을 테스트합니다. NDK 백엔드에서 AIBinder_forceDowngradeToLocalStability , C++ 백엔드에서 android::Stability::forceDowngradeToLocalStability 또는 바인더 객체의 Java 백엔드에서 android.os.Binder#forceDowngradeToSystemStability 를 전송하기 전에 호출하여 이러한 요구 사항 없이 @VintfStability 인터페이스를 사용할 수 있습니다. 다른 프로세스로. 모든 앱이 시스템 컨텍스트에서 실행되기 때문에 서비스를 공급업체 안정성으로 다운그레이드하는 것은 Java에서 지원되지 않습니다.

또한 코드 이식성을 극대화하고 불필요한 추가 라이브러리와 같은 잠재적인 문제를 방지하려면 CPP 백엔드를 비활성화하십시오.

세 가지 백엔드(Java, NDK 및 CPP)가 있으므로 아래 코드 예제에서 backends 사용이 정확합니다. 아래 코드는 CPP 백엔드를 구체적으로 선택하여 비활성화하는 방법을 알려줍니다.

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

AIDL HAL 인터페이스 찾기

HAL용 AOSP 안정적인 AIDL 인터페이스는 HIDL 인터페이스와 동일한 기본 디렉터리의 aidl 폴더에 있습니다.

  • 하드웨어/인터페이스
  • 프레임워크/하드웨어/인터페이스
  • 시스템/하드웨어/인터페이스

확장 인터페이스는 vendor 또는 hardware 의 다른 hardware/interfaces 하위 디렉토리에 넣어야 합니다.

확장 인터페이스

Android에는 모든 릴리스와 함께 일련의 공식 AOSP 인터페이스가 있습니다. Android 파트너가 이러한 인터페이스에 기능을 추가하려는 경우 Android 런타임이 AOSP Android 런타임과 호환되지 않음을 의미하므로 이러한 인터페이스를 직접 변경해서는 안 됩니다. GMS 장치의 경우 이러한 인터페이스를 변경하지 않는 것이 GSI 이미지가 계속 작동할 수 있도록 보장하는 것이기도 합니다.

확장 프로그램은 두 가지 방법으로 등록할 수 있습니다.

  • 런타임 시 첨부된 확장 을 참조하십시오.
  • 독립 실행형, 전 세계적으로 VINTF에 등록됨.

그러나 확장이 등록되면 공급업체별(업스트림 AOSP의 일부가 아님) 구성 요소가 인터페이스를 사용할 때 병합 충돌 가능성이 없습니다. 그러나 업스트림 AOSP 구성 요소에 대한 다운스트림 수정이 수행되면 병합 충돌이 발생할 수 있으며 다음 전략이 권장됩니다.

  • 인터페이스 추가는 다음 릴리스에서 AOSP로 업스트림될 수 있습니다.
  • 병합 충돌 없이 추가 유연성을 허용하는 인터페이스 추가는 다음 릴리스에서 업스트림될 수 있습니다.

확장 Parcelables: ParcelableHolder

ParcelableHolder 는 다른 Parcelable 을 포함할 수 있는 Parcelable 입니다. Parcelable 의 주요 사용 사례는 ParcelableHolder 을 확장 가능하게 만드는 것입니다. 예를 들어 기기 구현자가 AOSP 정의 Parcelable , AospDefinedParcelable 을 확장하여 부가 가치 기능을 포함할 수 있을 것으로 기대하는 이미지입니다.

이전에는 ParcelableHolder 없이 기기 구현자가 AOSP에서 정의한 안정적인 AIDL 인터페이스를 수정할 수 없었습니다. 필드를 더 추가하면 오류가 발생하기 때문입니다.

parcelable AospDefinedParcelable {
  int a;
  String b;
  String x; // ERROR: added by a device implementer
  int[] y; // added by a device implementer
}

앞의 코드에서 볼 수 있듯이 Parcelable이 Android의 다음 릴리스에서 수정될 때 기기 구현자가 추가한 필드가 충돌할 수 있기 때문에 이 관행은 깨졌습니다.

ParcelableHolder를 사용하여 ParcelableHolder 의 소유자는 Parcelable 에서 확장 지점을 정의할 수 있습니다.

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

그런 다음 장치 구현자는 확장을 위해 자체 Parcelable 을 정의할 수 있습니다.

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

마지막으로 ParcelableHolder 필드를 통해 새 Parcelable 을 원래 Parcelable 에 연결할 수 있습니다.


// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;

ap.extension.setParcelable(op);

...

OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);

// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();

ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);

...

std::shared_ptr<OemDefinedParcelable> op_ptr;

ap.extension.getParcelable(&op_ptr);

// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);

...

std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);

// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });

ap.extension.set_parcelable(Rc::clone(&op));

...

let op = ap.extension.get_parcelable::<OemDefinedParcelable>();

AIDL 런타임에 대해 빌드

AIDL에는 Java, NDK, CPP의 세 가지 백엔드가 있습니다. 안정적인 AIDL을 사용하려면 항상 system/lib*/libbinder.so 에서 libbinder의 시스템 복사본을 사용하고 /dev/binder 에서 대화해야 합니다. 공급업체 이미지에 있는 코드의 경우 이것은 libbinder (VNDK의)를 사용할 수 없음을 의미합니다. 이 라이브러리에는 불안정한 C++ API와 불안정한 내부가 있습니다. 대신 기본 공급업체 코드는 AIDL의 NDK 백엔드, libbinder_ndk (시스템 libbinder.so 에서 지원)에 대한 링크 및 aidl_interface 항목으로 생성된 -ndk_platform 라이브러리에 대한 링크를 사용해야 합니다.

AIDL HAL 서버 인스턴스 이름

규칙에 따라 AIDL HAL 서비스에는 $package.$type/$instance 형식의 인스턴스 이름이 있습니다. 예를 들어 진동기 HAL의 인스턴스는 android.hardware.vibrator.IVibrator/default 로 등록됩니다.

AIDL HAL 서버 작성

@VintfStability AIDL 서버는 예를 들어 다음과 같이 VINTF 매니페스트에서 선언되어야 합니다.

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

그렇지 않으면 정상적으로 AIDL 서비스를 등록해야 합니다. VTS 테스트를 실행할 때 선언된 모든 AIDL HAL을 사용할 수 있어야 합니다.

AIDL 클라이언트 작성

AIDL 클라이언트는 예를 들어 다음과 같이 호환성 매트릭스에서 자신을 선언해야 합니다.

    <hal format="aidl" optional="true">
        <name>android.hardware.vibrator</name>
        <version>1-2</version>
        <interface>
            <name>IVibrator</name>
            <instance>default</instance>
        </interface>
    </hal>

기존 HAL을 HIDL에서 AIDL로 변환

hidl2aidl 도구를 사용하여 HIDL 인터페이스를 AIDL로 변환합니다.

hidl2aidl 기능:

  • 지정된 패키지의 .hal 파일을 기반으로 .aidl 파일을 만듭니다.
  • 모든 백엔드가 활성화된 새로 생성된 AIDL 패키지에 대한 빌드 규칙 생성
  • HIDL 유형에서 AIDL 유형으로 번역하기 위해 자바, CPP 및 NDK 백엔드에서 번역 메서드를 만듭니다.
  • 필수 종속성이 있는 번역 라이브러리에 대한 빌드 규칙 생성
  • HIDL 및 AIDL 열거자가 CPP 및 NDK 백엔드에서 동일한 값을 갖도록 정적 주장 생성

.hal 파일 패키지를 .aidl 파일로 변환하려면 다음 단계를 따르십시오.

  1. system/tools/hidl/hidl2aidl 에 있는 도구를 빌드합니다.

    최신 소스에서 이 도구를 빌드하면 가장 완벽한 환경을 제공합니다. 최신 버전을 사용하여 이전 릴리스에서 이전 분기의 인터페이스를 변환할 수 있습니다.

    m hidl2aidl
    
  2. 출력 디렉터리와 변환할 패키지를 사용하여 도구를 실행합니다.

    선택적으로 -l 인수를 사용하여 생성된 모든 파일의 맨 위에 새 라이센스 파일의 내용을 추가합니다. 올바른 라이센스와 날짜를 사용하십시오.

    hidl2aidl -o <output directory> -l <file with license> <package>
    

    예를 들어:

    hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
    
  3. 생성된 파일을 읽고 변환과 관련된 문제를 수정합니다.

    • conversion.log 에는 먼저 수정해야 할 처리되지 않은 문제가 포함되어 있습니다.
    • 생성된 .aidl 파일에는 조치가 필요할 수 있는 경고 및 제안이 있을 수 있습니다. 이 주석은 // 로 시작합니다.
    • 패키지를 정리하고 개선할 기회를 가지십시오.
    • toString 또는 equals 와 같이 필요할 수 있는 기능에 대해서는 @JavaDerive 주석을 확인하십시오.
  4. 필요한 대상만 빌드하십시오.

    • 사용하지 않을 백엔드를 비활성화합니다. CPP 백엔드보다 NDK 백엔드를 선호합니다. 런타임 선택 을 참조하세요.
    • 사용하지 않을 번역 라이브러리 또는 생성된 코드를 제거합니다.
  5. 주요 AIDL/HIDL 차이점 을 참조하세요.

    • AIDL의 기본 제공 Status 및 예외를 사용하면 일반적으로 인터페이스가 개선되고 다른 인터페이스별 상태 유형이 필요하지 않습니다.
    • 메서드의 AIDL 인터페이스 인수는 HIDL에서처럼 기본적으로 @nullable 이 아닙니다.

AIDL HAL용 Sepolicy

공급업체 코드에 표시되는 AIDL 서비스 유형에는 hal_service_type 속성이 있어야 합니다. 그렇지 않으면 sepolicy 구성은 다른 AIDL 서비스와 동일합니다(HAL에 대한 특수 속성이 있음). 다음은 HAL 서비스 컨텍스트의 예시 정의입니다.

    type hal_foo_service, service_manager_type, hal_service_type;

플랫폼에서 정의한 대부분의 서비스의 경우 올바른 유형의 서비스 컨텍스트가 이미 추가되어 있습니다(예: android.hardware.foo.IFoo/default 는 이미 hal_foo_service 로 표시됨). 그러나 프레임워크 클라이언트가 여러 인스턴스 이름을 지원하는 경우 장치별 service_contexts 파일에 추가 인스턴스 이름을 추가해야 합니다.

    android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0

새로운 유형의 HAL을 생성할 때 HAL 속성을 추가해야 합니다. 특정 HAL 속성은 여러 서비스 유형과 연결될 수 있습니다(방금 논의한 대로 각 서비스 유형에는 여러 인스턴스가 있을 수 있음). HAL인 foo 에는 hal_attribute(foo) 가 있습니다. 이 매크로는 hal_foo_clienthal_foo_server 속성을 정의합니다. 지정된 도메인의 경우 hal_client_domainhal_server_domain 매크로는 도메인을 지정된 HAL 속성과 연결합니다. 예를 들어, 이 HAL의 클라이언트인 시스템 서버는 hal_client_domain(system_server, hal_foo) 정책에 해당합니다. HAL 서버는 유사하게 hal_server_domain(my_hal_domain, hal_foo) 포함합니다. 일반적으로 지정된 HAL 속성에 대해 참조 또는 예제 HAL에 대한 hal_foo_default 와 같은 도메인도 생성합니다. 그러나 일부 장치는 자체 서버에 이러한 도메인을 사용합니다. 여러 서버의 도메인을 구별하는 것은 동일한 인터페이스를 제공하는 여러 서버가 있고 해당 구현에서 다른 권한 집합이 필요한 경우에만 중요합니다. 이러한 모든 매크로에서 hal_foo 는 실제로 sepolicy 개체가 아닙니다. 대신, 이 토큰은 이러한 매크로에서 클라이언트 서버 쌍과 연관된 속성 그룹을 참조하는 데 사용됩니다.

그러나 지금까지 hal_foo_servicehal_foo ( hal_attribute(foo) 의 속성 쌍)를 연결하지 않았습니다. HAL 속성은 hal_attribute_service 매크로를 사용하여 AIDL HAL 서비스와 연결됩니다(HIDL HAL은 hal_attribute_hwservice 매크로 사용). 예를 들어, hal_attribute_service(hal_foo, hal_foo_service) . 이는 hal_foo_client 프로세스가 HAL을 확보할 수 있고 hal_foo_server 프로세스가 HAL을 등록할 수 있음을 의미합니다. 이러한 등록 규칙의 시행은 컨텍스트 관리자( servicemanager )에 의해 수행됩니다. 서비스 이름이 항상 HAL 속성과 일치하지 않을 수 있습니다. 예를 들어 hal_attribute_service(hal_foo, hal_foo2_service) 볼 수 있습니다. 그러나 일반적으로 이것은 서비스가 항상 함께 사용됨을 의미하므로 hal_foo_service hal_foo2_service 사용하는 것을 고려할 수 있습니다. 여러 hal_attribute_service 를 설정하는 대부분의 HAL은 원래 HAL 속성 이름이 충분히 일반적이지 않고 변경할 수 없기 때문입니다.

이 모든 것을 종합하면 예제 HAL은 다음과 같습니다.

    public/attributes:
    // define hal_foo, hal_foo_client, hal_foo_server
    hal_attribute(foo)

    public/service.te
    // define hal_foo_service
    type hal_foo_service, hal_service_type, protected_service, service_manager_type

    public/hal_foo.te:
    // allow binder connection from client to server
    binder_call(hal_foo_client, hal_foo_server)
    // allow client to find the service, allow server to register the service
    hal_attribute_service(hal_foo, hal_foo_service)
    // allow binder communication from server to service_manager
    binder_use(hal_foo_server)

    private/service_contexts:
    // bind an AIDL service name to the selinux type
    android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0

    private/<some_domain>.te:
    // let this domain use the hal service
    binder_use(some_domain)
    hal_client_domain(some_domain, hal_foo)

    vendor/<some_hal_server_domain>.te
    // let this domain serve the hal service
    hal_server_domain(some_hal_server_domain, hal_foo)

연결된 확장 인터페이스

서비스 관리자에 직접 등록된 최상위 인터페이스이든 하위 인터페이스이든 관계없이 모든 바인더 인터페이스에 확장을 연결할 수 있습니다. 확장을 받을 때 확장 유형이 예상대로인지 확인해야 합니다. 확장은 바인더를 제공하는 프로세스에서만 설정할 수 있습니다.

확장 프로그램이 기존 HAL의 기능을 수정할 때마다 연결된 확장 프로그램을 사용해야 합니다. 완전히 새로운 기능이 필요한 경우 이 메커니즘을 사용할 필요가 없으며 확장 인터페이스를 서비스 관리자에 직접 등록할 수 있습니다. 연결된 확장 인터페이스는 이러한 계층이 깊거나 다중 인스턴스일 수 있기 때문에 하위 인터페이스에 연결될 때 가장 적합합니다. 글로벌 확장을 사용하여 다른 서비스의 바인더 인터페이스 계층을 미러링하려면 직접 연결된 확장에 동등한 기능을 제공하기 위해 광범위한 기록이 필요합니다.

바인더에 확장을 설정하려면 다음 API를 사용하십시오.

  • NDK 백엔드에서: AIBinder_setExtension
  • Java 백엔드에서: android.os.Binder.setExtension
  • CPP 백엔드에서: android::Binder::setExtension

바인더에서 확장을 가져오려면 다음 API를 사용하십시오.

  • NDK 백엔드에서: AIBinder_getExtension
  • Java 백엔드에서: android.os.IBinder.getExtension
  • CPP 백엔드에서: android::IBinder::getExtension

해당 백엔드의 getExtension 함수 문서에서 이러한 API에 대한 자세한 정보를 찾을 수 있습니다. 확장 기능을 사용하는 방법의 예는 hardware/interfaces/tests/extension/vibrator 에서 찾을 수 있습니다.

주요 AIDL/HIDL 차이점

AIDL HAL을 사용하거나 AIDL HAL 인터페이스를 사용하는 경우 HIDL HAL 작성과의 차이점에 유의하세요.

  • AIDL 언어의 구문은 Java에 더 가깝습니다. HIDL 구문은 C++와 유사합니다.
  • 모든 AIDL 인터페이스에는 오류 상태가 내장되어 있습니다. 사용자 정의 상태 유형을 생성하는 대신 인터페이스 파일에 상수 상태 int를 생성하고 CPP/NDK 백엔드에서 EX_SERVICE_SPECIFIC 을 사용하고 Java 백엔드에서 ServiceSpecificException 을 사용합니다. 오류 처리 를 참조하십시오.
  • AIDL은 바인더 개체가 전송될 때 스레드 풀을 자동으로 시작하지 않습니다. 수동으로 시작해야 합니다( 스레드 관리 참조).
  • AIDL은 확인되지 않은 전송 오류에서 중단되지 않습니다(HIDL Return 은 확인되지 않은 오류에서 중단됨).
  • AIDL은 파일당 하나의 유형만 선언할 수 있습니다.
  • AIDL 인수는 출력 매개변수 외에 in/out/inout으로 지정할 수 있습니다("동기 콜백" 없음).
  • AIDL은 핸들 대신 기본 유형으로 fd를 사용합니다.
  • HIDL은 호환되지 않는 변경에는 주 버전을 사용하고 호환되는 변경에는 부 버전을 사용합니다. AIDL에서는 이전 버전과 호환되는 변경이 제자리에서 수행됩니다. AIDL에는 주 버전에 대한 명확한 개념이 없습니다. 대신 이것은 패키지 이름에 통합됩니다. 예를 들어 AIDL은 패키지 이름 bluetooth2 를 사용할 수 있습니다.
  • AIDL은 기본적으로 실시간 우선순위를 상속하지 않습니다. 실시간 우선 순위 상속을 활성화하려면 바인더별로 setInheritRt 함수를 사용해야 합니다.
,

Android 11에서는 Android의 HAL에 AIDL을 사용할 수 있는 기능을 소개합니다. 이를 통해 HIDL 없이 Android의 일부를 구현할 수 있습니다. 가능한 경우 HAL이 AIDL만 사용하도록 전환합니다(업스트림 HAL이 HIDL을 사용하는 경우 HIDL을 사용해야 함).

system.img 와 같은 프레임워크 구성요소와 vendor.img 와 같은 하드웨어 구성요소 간에 통신하기 위해 AIDL을 사용하는 HAL은 안정적인 AIDL을 사용해야 합니다. 그러나 예를 들어 하나의 HAL에서 다른 HAL로 파티션 내에서 통신하려면 사용할 IPC 메커니즘에 대한 제한이 없습니다.

동기 부여

AIDL은 HIDL보다 오래 사용되었으며 Android 프레임워크 구성 요소 사이 또는 앱과 같은 다른 많은 위치에서 사용됩니다. 이제 AIDL에 안정성 지원이 있으므로 단일 IPC 런타임으로 전체 스택을 구현할 수 있습니다. AIDL은 또한 HIDL보다 더 나은 버전 관리 시스템을 가지고 있습니다.

  • 단일 IPC 언어를 사용한다는 것은 배우고, 디버그하고, 최적화하고, 보호할 것이 하나만 있다는 것을 의미합니다.
  • AIDL은 인터페이스 소유자를 위한 내부 버전 관리를 지원합니다.
    • 소유자는 인터페이스 끝에 메서드를 추가하거나 소포에 필드를 추가할 수 있습니다. 즉, 수년에 걸쳐 버전 코드를 작성하는 것이 더 쉽고 비용도 매년 더 적습니다(유형을 내부에서 수정할 수 있고 각 인터페이스 버전에 대한 추가 라이브러리가 필요하지 않음).
    • 확장 인터페이스는 유형 시스템이 아닌 런타임에 연결할 수 있으므로 최신 버전의 인터페이스에 다운스트림 확장을 리베이스할 필요가 없습니다.
  • 소유자가 안정화를 선택하면 기존 AIDL 인터페이스를 직접 사용할 수 있습니다. 이전에는 인터페이스의 전체 복사본을 HIDL에서 만들어야 했습니다.

AIDL HAL 인터페이스 작성

시스템과 공급업체 간에 AIDL 인터페이스를 사용하려면 인터페이스에 두 가지 변경이 필요합니다.

  • 모든 유형 정의에는 @VintfStability 로 주석을 달아야 합니다.
  • aidl_interface 선언에는 stability: "vintf", .

인터페이스 소유자만 이러한 변경을 수행할 수 있습니다.

이러한 변경을 수행할 때 인터페이스가 작동하려면 VINTF 매니페스트 에 있어야 합니다. VTS 테스트 vts_treble_vintf_vendor_test 를 사용하여 이를 테스트하고 릴리스된 인터페이스가 고정되었는지 확인하는 것과 같은 관련 요구 사항을 테스트합니다. NDK 백엔드에서 AIBinder_forceDowngradeToLocalStability , C++ 백엔드에서 android::Stability::forceDowngradeToLocalStability 또는 바인더 객체의 Java 백엔드에서 android.os.Binder#forceDowngradeToSystemStability 를 전송하기 전에 호출하여 이러한 요구 사항 없이 @VintfStability 인터페이스를 사용할 수 있습니다. 다른 프로세스로. 모든 앱이 시스템 컨텍스트에서 실행되기 때문에 서비스를 공급업체 안정성으로 다운그레이드하는 것은 Java에서 지원되지 않습니다.

또한 코드 이식성을 극대화하고 불필요한 추가 라이브러리와 같은 잠재적인 문제를 방지하려면 CPP 백엔드를 비활성화하십시오.

세 가지 백엔드(Java, NDK 및 CPP)가 있으므로 아래 코드 예제에서 backends 사용이 정확합니다. 아래 코드는 CPP 백엔드를 구체적으로 선택하여 비활성화하는 방법을 알려줍니다.

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

AIDL HAL 인터페이스 찾기

HAL용 AOSP 안정적인 AIDL 인터페이스는 HIDL 인터페이스와 동일한 기본 디렉터리의 aidl 폴더에 있습니다.

  • 하드웨어/인터페이스
  • 프레임워크/하드웨어/인터페이스
  • 시스템/하드웨어/인터페이스

확장 인터페이스는 vendor 또는 hardware 의 다른 hardware/interfaces 하위 디렉토리에 넣어야 합니다.

확장 인터페이스

Android에는 모든 릴리스와 함께 일련의 공식 AOSP 인터페이스가 있습니다. Android 파트너가 이러한 인터페이스에 기능을 추가하려는 경우 Android 런타임이 AOSP Android 런타임과 호환되지 않음을 의미하므로 이러한 인터페이스를 직접 변경해서는 안 됩니다. GMS 장치의 경우 이러한 인터페이스를 변경하지 않는 것이 GSI 이미지가 계속 작동할 수 있도록 보장하는 것이기도 합니다.

확장 프로그램은 두 가지 방법으로 등록할 수 있습니다.

  • 런타임 시 첨부된 확장 을 참조하십시오.
  • 독립 실행형, 전 세계적으로 VINTF에 등록됨.

그러나 확장이 등록되면 공급업체별(업스트림 AOSP의 일부가 아님) 구성 요소가 인터페이스를 사용할 때 병합 충돌 가능성이 없습니다. 그러나 업스트림 AOSP 구성 요소에 대한 다운스트림 수정이 수행되면 병합 충돌이 발생할 수 있으며 다음 전략이 권장됩니다.

  • 인터페이스 추가는 다음 릴리스에서 AOSP로 업스트림될 수 있습니다.
  • 병합 충돌 없이 추가 유연성을 허용하는 인터페이스 추가는 다음 릴리스에서 업스트림될 수 있습니다.

확장 Parcelables: ParcelableHolder

ParcelableHolder 는 다른 Parcelable 을 포함할 수 있는 Parcelable 입니다. Parcelable 의 주요 사용 사례는 ParcelableHolder 을 확장 가능하게 만드는 것입니다. 예를 들어 기기 구현자가 AOSP 정의 Parcelable , AospDefinedParcelable 을 확장하여 부가 가치 기능을 포함할 수 있을 것으로 기대하는 이미지입니다.

이전에는 ParcelableHolder 없이 기기 구현자가 AOSP에서 정의한 안정적인 AIDL 인터페이스를 수정할 수 없었습니다. 필드를 더 추가하면 오류가 발생하기 때문입니다.

parcelable AospDefinedParcelable {
  int a;
  String b;
  String x; // ERROR: added by a device implementer
  int[] y; // added by a device implementer
}

앞의 코드에서 볼 수 있듯이 Parcelable이 Android의 다음 릴리스에서 수정될 때 기기 구현자가 추가한 필드가 충돌할 수 있기 때문에 이 관행은 깨졌습니다.

ParcelableHolder를 사용하여 ParcelableHolder 의 소유자는 Parcelable 에서 확장 지점을 정의할 수 있습니다.

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

그런 다음 장치 구현자는 확장을 위해 자체 Parcelable 을 정의할 수 있습니다.

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

마지막으로 ParcelableHolder 필드를 통해 새 Parcelable 을 원래 Parcelable 에 연결할 수 있습니다.


// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;

ap.extension.setParcelable(op);

...

OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);

// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();

ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);

...

std::shared_ptr<OemDefinedParcelable> op_ptr;

ap.extension.getParcelable(&op_ptr);

// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);

...

std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);

// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });

ap.extension.set_parcelable(Rc::clone(&op));

...

let op = ap.extension.get_parcelable::<OemDefinedParcelable>();

AIDL 런타임에 대해 빌드

AIDL에는 Java, NDK, CPP의 세 가지 백엔드가 있습니다. 안정적인 AIDL을 사용하려면 항상 system/lib*/libbinder.so 에서 libbinder의 시스템 복사본을 사용하고 /dev/binder 에서 대화해야 합니다. 공급업체 이미지에 있는 코드의 경우 이것은 libbinder (VNDK의)를 사용할 수 없음을 의미합니다. 이 라이브러리에는 불안정한 C++ API와 불안정한 내부가 있습니다. 대신 기본 공급업체 코드는 AIDL의 NDK 백엔드, libbinder_ndk (시스템 libbinder.so 에서 지원)에 대한 링크 및 aidl_interface 항목으로 생성된 -ndk_platform 라이브러리에 대한 링크를 사용해야 합니다.

AIDL HAL 서버 인스턴스 이름

규칙에 따라 AIDL HAL 서비스에는 $package.$type/$instance 형식의 인스턴스 이름이 있습니다. 예를 들어 진동기 HAL의 인스턴스는 android.hardware.vibrator.IVibrator/default 로 등록됩니다.

AIDL HAL 서버 작성

@VintfStability AIDL 서버는 예를 들어 다음과 같이 VINTF 매니페스트에서 선언되어야 합니다.

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

그렇지 않으면 정상적으로 AIDL 서비스를 등록해야 합니다. VTS 테스트를 실행할 때 선언된 모든 AIDL HAL을 사용할 수 있어야 합니다.

AIDL 클라이언트 작성

AIDL 클라이언트는 예를 들어 다음과 같이 호환성 매트릭스에서 자신을 선언해야 합니다.

    <hal format="aidl" optional="true">
        <name>android.hardware.vibrator</name>
        <version>1-2</version>
        <interface>
            <name>IVibrator</name>
            <instance>default</instance>
        </interface>
    </hal>

기존 HAL을 HIDL에서 AIDL로 변환

hidl2aidl 도구를 사용하여 HIDL 인터페이스를 AIDL로 변환합니다.

hidl2aidl 기능:

  • 지정된 패키지의 .hal 파일을 기반으로 .aidl 파일을 만듭니다.
  • 모든 백엔드가 활성화된 새로 생성된 AIDL 패키지에 대한 빌드 규칙 생성
  • HIDL 유형에서 AIDL 유형으로 번역하기 위해 자바, CPP 및 NDK 백엔드에서 번역 메서드를 만듭니다.
  • 필수 종속성이 있는 번역 라이브러리에 대한 빌드 규칙 생성
  • HIDL 및 AIDL 열거자가 CPP 및 NDK 백엔드에서 동일한 값을 갖도록 정적 주장 생성

.hal 파일 패키지를 .aidl 파일로 변환하려면 다음 단계를 따르십시오.

  1. system/tools/hidl/hidl2aidl 에 있는 도구를 빌드합니다.

    최신 소스에서 이 도구를 빌드하면 가장 완벽한 환경을 제공합니다. 최신 버전을 사용하여 이전 릴리스에서 이전 분기의 인터페이스를 변환할 수 있습니다.

    m hidl2aidl
    
  2. 출력 디렉터리와 변환할 패키지를 사용하여 도구를 실행합니다.

    선택적으로 -l 인수를 사용하여 생성된 모든 파일의 맨 위에 새 라이센스 파일의 내용을 추가합니다. 올바른 라이센스와 날짜를 사용하십시오.

    hidl2aidl -o <output directory> -l <file with license> <package>
    

    예를 들어:

    hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
    
  3. 생성된 파일을 읽고 변환과 관련된 문제를 수정합니다.

    • conversion.log 에는 먼저 수정해야 할 처리되지 않은 문제가 포함되어 있습니다.
    • 생성된 .aidl 파일에는 조치가 필요할 수 있는 경고 및 제안이 있을 수 있습니다. 이 주석은 // 로 시작합니다.
    • 패키지를 정리하고 개선할 기회를 가지십시오.
    • toString 또는 equals 와 같이 필요할 수 있는 기능에 대해서는 @JavaDerive 주석을 확인하십시오.
  4. 필요한 대상만 빌드하십시오.

    • 사용하지 않을 백엔드를 비활성화합니다. CPP 백엔드보다 NDK 백엔드를 선호합니다. 런타임 선택 을 참조하세요.
    • 사용하지 않을 번역 라이브러리 또는 생성된 코드를 제거합니다.
  5. 주요 AIDL/HIDL 차이점 을 참조하세요.

    • AIDL의 기본 제공 Status 및 예외를 사용하면 일반적으로 인터페이스가 개선되고 다른 인터페이스별 상태 유형이 필요하지 않습니다.
    • 메서드의 AIDL 인터페이스 인수는 HIDL에서처럼 기본적으로 @nullable 이 아닙니다.

AIDL HAL용 Sepolicy

공급업체 코드에 표시되는 AIDL 서비스 유형에는 hal_service_type 속성이 있어야 합니다. 그렇지 않으면 sepolicy 구성은 다른 AIDL 서비스와 동일합니다(HAL에 대한 특수 속성이 있음). 다음은 HAL 서비스 컨텍스트의 예시 정의입니다.

    type hal_foo_service, service_manager_type, hal_service_type;

플랫폼에서 정의한 대부분의 서비스의 경우 올바른 유형의 서비스 컨텍스트가 이미 추가되어 있습니다(예: android.hardware.foo.IFoo/default 는 이미 hal_foo_service 로 표시됨). 그러나 프레임워크 클라이언트가 여러 인스턴스 이름을 지원하는 경우 장치별 service_contexts 파일에 추가 인스턴스 이름을 추가해야 합니다.

    android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0

새로운 유형의 HAL을 생성할 때 HAL 속성을 추가해야 합니다. 특정 HAL 속성은 여러 서비스 유형과 연결될 수 있습니다(방금 논의한 대로 각 서비스 유형에는 여러 인스턴스가 있을 수 있음). HAL인 foo 에는 hal_attribute(foo) 가 있습니다. 이 매크로는 hal_foo_clienthal_foo_server 속성을 정의합니다. 지정된 도메인의 경우 hal_client_domainhal_server_domain 매크로는 도메인을 지정된 HAL 속성과 연결합니다. 예를 들어, 이 HAL의 클라이언트인 시스템 서버는 hal_client_domain(system_server, hal_foo) 정책에 해당합니다. HAL 서버는 유사하게 hal_server_domain(my_hal_domain, hal_foo) 포함합니다. 일반적으로 지정된 HAL 속성에 대해 참조 또는 예제 HAL에 대한 hal_foo_default 와 같은 도메인도 생성합니다. 그러나 일부 장치는 자체 서버에 이러한 도메인을 사용합니다. 여러 서버의 도메인을 구별하는 것은 동일한 인터페이스를 제공하는 여러 서버가 있고 해당 구현에서 다른 권한 집합이 필요한 경우에만 중요합니다. 이러한 모든 매크로에서 hal_foo 는 실제로 sepolicy 개체가 아닙니다. 대신, 이 토큰은 이러한 매크로에서 클라이언트 서버 쌍과 연관된 속성 그룹을 참조하는 데 사용됩니다.

그러나 지금까지 hal_foo_servicehal_foo ( hal_attribute(foo) 의 속성 쌍)를 연결하지 않았습니다. HAL 속성은 hal_attribute_service 매크로를 사용하여 AIDL HAL 서비스와 연결됩니다(HIDL HAL은 hal_attribute_hwservice 매크로 사용). 예를 들어, hal_attribute_service(hal_foo, hal_foo_service) . 이는 hal_foo_client 프로세스가 HAL을 확보할 수 있고 hal_foo_server 프로세스가 HAL을 등록할 수 있음을 의미합니다. 이러한 등록 규칙의 시행은 컨텍스트 관리자( servicemanager )에 의해 수행됩니다. 서비스 이름이 항상 HAL 속성과 일치하지 않을 수 있습니다. 예를 들어 hal_attribute_service(hal_foo, hal_foo2_service) 볼 수 있습니다. 그러나 일반적으로 이것은 서비스가 항상 함께 사용됨을 의미하므로 hal_foo_service hal_foo2_service 사용하는 것을 고려할 수 있습니다. 여러 hal_attribute_service 를 설정하는 대부분의 HAL은 원래 HAL 속성 이름이 충분히 일반적이지 않고 변경할 수 없기 때문입니다.

이 모든 것을 종합하면 예제 HAL은 다음과 같습니다.

    public/attributes:
    // define hal_foo, hal_foo_client, hal_foo_server
    hal_attribute(foo)

    public/service.te
    // define hal_foo_service
    type hal_foo_service, hal_service_type, protected_service, service_manager_type

    public/hal_foo.te:
    // allow binder connection from client to server
    binder_call(hal_foo_client, hal_foo_server)
    // allow client to find the service, allow server to register the service
    hal_attribute_service(hal_foo, hal_foo_service)
    // allow binder communication from server to service_manager
    binder_use(hal_foo_server)

    private/service_contexts:
    // bind an AIDL service name to the selinux type
    android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0

    private/<some_domain>.te:
    // let this domain use the hal service
    binder_use(some_domain)
    hal_client_domain(some_domain, hal_foo)

    vendor/<some_hal_server_domain>.te
    // let this domain serve the hal service
    hal_server_domain(some_hal_server_domain, hal_foo)

연결된 확장 인터페이스

서비스 관리자에 직접 등록된 최상위 인터페이스이든 하위 인터페이스이든 관계없이 모든 바인더 인터페이스에 확장을 연결할 수 있습니다. 확장을 받을 때 확장 유형이 예상대로인지 확인해야 합니다. 확장은 바인더를 제공하는 프로세스에서만 설정할 수 있습니다.

확장 프로그램이 기존 HAL의 기능을 수정할 때마다 연결된 확장 프로그램을 사용해야 합니다. 완전히 새로운 기능이 필요한 경우 이 메커니즘을 사용할 필요가 없으며 확장 인터페이스를 서비스 관리자에 직접 등록할 수 있습니다. 연결된 확장 인터페이스는 이러한 계층이 깊거나 다중 인스턴스일 수 있기 때문에 하위 인터페이스에 연결될 때 가장 적합합니다. 글로벌 확장을 사용하여 다른 서비스의 바인더 인터페이스 계층을 미러링하려면 직접 연결된 확장에 동등한 기능을 제공하기 위해 광범위한 기록이 필요합니다.

바인더에 확장을 설정하려면 다음 API를 사용하십시오.

  • NDK 백엔드에서: AIBinder_setExtension
  • Java 백엔드에서: android.os.Binder.setExtension
  • CPP 백엔드에서: android::Binder::setExtension

바인더에서 확장을 가져오려면 다음 API를 사용하십시오.

  • NDK 백엔드에서: AIBinder_getExtension
  • Java 백엔드에서: android.os.IBinder.getExtension
  • CPP 백엔드에서: android::IBinder::getExtension

해당 백엔드의 getExtension 함수 문서에서 이러한 API에 대한 자세한 정보를 찾을 수 있습니다. 확장 기능을 사용하는 방법의 예는 hardware/interfaces/tests/extension/vibrator 에서 찾을 수 있습니다.

주요 AIDL/HIDL 차이점

AIDL HAL을 사용하거나 AIDL HAL 인터페이스를 사용하는 경우 HIDL HAL 작성과의 차이점에 유의하세요.

  • AIDL 언어의 구문은 Java에 더 가깝습니다. HIDL 구문은 C++와 유사합니다.
  • 모든 AIDL 인터페이스에는 오류 상태가 내장되어 있습니다. 사용자 정의 상태 유형을 생성하는 대신 인터페이스 파일에 상수 상태 int를 생성하고 CPP/NDK 백엔드에서 EX_SERVICE_SPECIFIC 을 사용하고 Java 백엔드에서 ServiceSpecificException 을 사용합니다. 오류 처리 를 참조하십시오.
  • AIDL은 바인더 개체가 전송될 때 스레드 풀을 자동으로 시작하지 않습니다. 수동으로 시작해야 합니다( 스레드 관리 참조).
  • AIDL은 확인되지 않은 전송 오류에서 중단되지 않습니다(HIDL Return 은 확인되지 않은 오류에서 중단됨).
  • AIDL은 파일당 하나의 유형만 선언할 수 있습니다.
  • AIDL 인수는 출력 매개변수 외에 in/out/inout으로 지정할 수 있습니다("동기 콜백" 없음).
  • AIDL은 핸들 대신 기본 유형으로 fd를 사용합니다.
  • HIDL은 호환되지 않는 변경에는 주 버전을 사용하고 호환되는 변경에는 부 버전을 사용합니다. AIDL에서는 이전 버전과 호환되는 변경이 제자리에서 수행됩니다. AIDL에는 주 버전에 대한 명확한 개념이 없습니다. 대신 이것은 패키지 이름에 통합됩니다. 예를 들어 AIDL은 패키지 이름 bluetooth2 를 사용할 수 있습니다.
  • AIDL은 기본적으로 실시간 우선순위를 상속하지 않습니다. 실시간 우선 순위 상속을 활성화하려면 바인더별로 setInheritRt 함수를 사용해야 합니다.