HIDL

HAL 인터페이스 정의 언어, 즉 HIDL은 HAL과 HAL 사용자 간의 인터페이스를 지정하는 인터페이스 설명 언어(IDL)입니다. HIDL을 통해 인터페이스 및 패키지로 수집되는 유형 및 메서드 호출을 지정할 수 있습니다. 보다 일반적으로 말하자면 HIDL은 독립적으로 컴파일될 수 있는 코드베이스 간 통신을 위한 체계입니다. Android 10부터 HIDL은 지원 중단되었으며, Android는 어디에서나 AIDL을 사용하도록 이전 중입니다.

HIDL은 프로세스 간 통신(IPC)에 사용하기 위한 언어입니다. HDL로 생성된 HAL은 바인더 프로세스 간 통신(IPC) 호출을 사용하여 다른 아키텍처 레이어와 통신할 수 있다는 점에서 바인더화된 HAL이라고 합니다. 바인더화된 HAL은 HAL을 사용하는 클라이언트와 다른 별도의 프로세스에서 실행됩니다. 프로세스에 연결되어야 하는 라이브러리의 경우 패스 스루 모드도 사용할 수 있습니다(단, 자바에서는 지원되지 않음).

HIDL은 패키지로 수집되는 (클래스와 비슷한) 인터페이스로 구성된 데이터 구조 및 메서드 서명을 지정합니다. 비록 다른 키워드 집합을 사용하지만 HIDL의 문법은 C++ 및 자바 프로그래머에게는 익숙할 것입니다. 또한 HIDL은 자바 스타일의 주석을 사용합니다.

용어

이 섹션에서는 다음 HIDL 관련 용어를 사용합니다.

바인더화 HIDL이 바인더와 유사한 메커니즘을 통해 구현되는 프로세스 간의 리모트 프로시져 콜에 사용됨을 나타냅니다. 패스 스루도 참고하세요.
콜백, 비동기식 HAL 사용자가 제공하고, HIDL 메서드를 사용하여 HAL로 전달되고, HAL에 의해 호출되어 언제든지 데이터를 반환할 수 있는 인터페이스입니다.
콜백, 동기식 서버의 HIDL 메서드 구현에서 클라이언트로 데이터를 반환합니다. void 또는 단일 원시값을 반환하는 메서드에는 사용되지 않습니다.
클라이언트 특정 인터페이스의 메서드를 호출하는 프로세스입니다. HAL 또는 Android 프레임워크 프로세스는 한 인터페이스의 클라이언트와 다른 인터페이스의 서버일 수 있습니다. 패스 스루도 참고하세요.
확장 메서드 또는 유형을 다른 인터페이스에 추가하는 인터페이스를 나타냅니다. 한 인터페이스는 다른 인터페이스 하나만 확장할 수 있습니다. 동일한 패키지 이름의 마이너 버전 증분 또는 이전 패키지에 빌드할 새 패키지(예: 공급업체 확장 프로그램)에 사용할 수 있습니다.
생성 클라이언트에 값을 반환하는 인터페이스 메서드를 나타냅니다. 하나의 비 원시값 또는 둘 이상의 값을 반환하기 위해 동기식 콜백 함수가 생성됩니다.
인터페이스 메서드 및 유형의 컬렉션입니다. C++ 또는 자바의 클래스로 변환됩니다. 인터페이스의 모든 메서드는 동일한 방향으로 호출됩니다. 클라이언트 프로세스는 서버 프로세스에 의해 구현된 메서드를 호출합니다.
편도 HIDL 메서드에 적용되면 메서드가 값을 반환하지 않고 차단하지 않음을 나타냅니다.
패키지 버전을 공유하는 인터페이스 및 데이터 유형의 컬렉션입니다.
패스 스루 서버가 공유 라이브러리인 HIDL의 모드이며, 클라이언트에 의해 dlopen이 적용됩니다. 패스 스루 모드에서 클라이언트와 서버는 동일한 프로세스지만, 별도의 코드베이스입니다. HIDL 모델에 기존 코드베이스를 가져오는 용도로만 사용됩니다. 바인더화도 참조하세요.
서버 인터페이스의 메서드를 구현하는 프로세스입니다. 패스 스루도 참고하세요.
전송 서버와 클라이언트 간에 데이터를 이동하는 HIDL 인프라입니다.
버전 패키지의 버전입니다. 두 개의 정수(메이저 버전 마이너 버전)로 구성됩니다. 마이너 버전 증분은 유형과 메서드를 추가할 수 있지만, 변경할 수는 없습니다.

HIDL 디자인

HIDL의 목표는 HAL을 다시 빌드하지 않고도 Android 프레임워크를 대체할 수 있게 하는 것입니다. HAL은 공급업체나 SoC 제조업체가 빌드하여 기기의 /vendor 파티션에 배치되기 때문에 HAL을 다시 컴파일하지 않고도 자체 파티션의 Android 프레임워크를 OTA로 대체할 수 있습니다.

HIDL 디자인은 다음과 같은 문제를 해결합니다.

  • 상호 운용성: 다양한 아키텍처, 도구 모음, 빌드 구성으로 컴파일할 수 있는 프로세스 간에 안정적으로 상호 운용 가능한 인터페이스를 만듭니다. HIDL 인터페이스에는 버전이 지정되며 게시된 후에는 변경할 수 없습니다.
  • 효율성: HIDL은 복사 작업 수를 최소화하려고 합니다. HIDL에서 정의한 데이터는 압축을 풀지 않고도 사용할 수 있는 C++ 표준 레이아웃 데이터 구조의 C++ 코드로 전달됩니다. 또한 HIDL은 공유 메모리 인터페이스를 제공하며, RPC가 기본적으로 실행 속도가 느린 점 때문에 HIDL은 RPC 호출을 사용하지 않고 데이터를 전송하는 공유 메모리와 빠른 메시지 대기열(FMQ)의 두 가지 방법을 지원합니다.
  • 직관적 실행: HIDL은 RPC용 in 매개변수만 사용하여 까다로운 메모리 소유권 문제를 방지합니다(Android 인터페이스 정의 언어(AIDL) 참조). 메서드에서 효율적으로 반환될 수 없는 값은 콜백 함수를 통해 반환됩니다. 전송을 위해 HIDL로 데이터를 전달하거나 HIDL에서 데이터를 수신해도 데이터 소유권은 변경되지 않습니다. 즉, 소유권은 항상 호출 함수가 갖습니다. 데이터는 함수가 호출된 기간 동안만 유지되어야 하며, 호출된 함수가 반환된 직후 폐기될 수 있습니다.

패스 스루 모드 사용

이전 Android 버전을 실행하는 기기를 Android O로 업데이트하기 위해, 바인더화 및 동일 프로세스(패스 스루) 모드로 HAL을 제공하는 새 HIDL 인터페이스에서 기존 및 레거시 HAL을 모두 래핑할 수 있습니다. 이 래핑은 HAL과 Android 프레임워크 모두에 투명합니다.

패스 스루 모드는 C++ 클라이언트 및 구현에만 사용할 수 있습니다. 이전 버전의 Android를 실행하는 기기에는 자바로 작성된 HAL이 없으므로 자바 HAL이 본질적으로 바인더화됩니다.

.hal 파일이 컴파일되면 hidl-gen은 바인더 통신에 사용되는 헤더와 더불어 추가 패스 스루 헤더 파일 BsFoo.h를 생성합니다. 이 헤더는 dlopen이 적용될 함수를 정의합니다. 패스 스루 HAL은 호출된 곳과 동일한 프로세스에서 실행되므로 대부분의 경우 패스 스루 메서드는 직접 함수 호출(동일한 스레드)에 의해 호출됩니다. oneway 메서드는 HAL이 처리할 때까지 기다리지 않으므로 자체 스레드에서 실행됩니다. 즉, 패스 스루 모드로 oneway 메서드를 사용하는 HAL은 스레드로부터 안전해야 합니다.

IFoo.hal이 주어진 상태에서, BsFoo.h는 HIDL에서 생성된 메서드를 래핑하여 추가 기능을 제공합니다(예: oneway 트랜잭션이 다른 스레드에서 실행되도록 함). 이 파일은 BpFoo.h와 유사하지만, 바인더를 사용하는 IPC를 통해 호출을 전달하는 대신 원하는 함수를 직접 호출합니다. HAL의 향후 구현에서는 FooFast HAL 및 FooAccurate HAL과 같은 여러 구현을 제공할 수도 있습니다. 이러한 경우 각 추가 구현 시 파일이 생성됩니다(예: PTFooFast.cpp, PTFooAccurate.cpp).

패스 스루 HAL 바인더화

패스 스루 모드를 지원하는 HAL 구현을 바인더화할 수 있습니다. HAL 인터페이스 a.b.c.d@M.N::IFoo가 주어지면 다음과 같이 두 개의 패키지가 생성됩니다.

  • a.b.c.d@M.N::IFoo-impl: HAL의 구현을 포함하고 IFoo* HIDL_FETCH_IFoo(const char* name) 함수를 노출합니다. 레거시 기기에서 이 패키지는 dlopen이 적용되고, HIDL_FETCH_IFoo를 사용하여 구현이 인스턴스화됩니다. hidl-gen-Lc++-impl-Landroidbp-impl을 사용하면 기본 코드를 생성할 수 있습니다.
  • a.b.c.d@M.N::IFoo-service: 패스 스루 HAL을 열고 이를 바인더화된 서비스로 등록하여 동일한 HAL 구현을 패스 스루와 바인더화된 형식으로 사용할 수 있도록 지원합니다.

IFoo 형식이 주어지면 sp<IFoo> IFoo::getService(string name, bool getStub)를 호출하여 IFoo의 인스턴스에 액세스할 수 있습니다. getStub이 참인 경우 getService는 패스 스루 모드에서만 HAL을 열려고 시도합니다. getStub이 거짓인 경우 getService는 바인더화된 서비스를 찾으려고 시도하며, 이에 실패하는 경우 패스 스루 서비스를 찾으려고 시도합니다. defaultPassthroughServiceImplementation을 제외하고는 getStub 매개변수를 사용해서는 안 됩니다. Android O를 실행하는 기기는 완전히 바인더화된 기기이므로 서비스를 패스 스루 모드로 여는 것은 허용되지 않습니다.

HIDL 문법

HIDL 언어는 설계상 C와 유사하지만, C 전처리기를 사용하지 않습니다. =|의 일반적인 사용을 제외하면 아래에 설명되지 않은 모든 구두점은 문법의 일부입니다.

참고: HIDL 코드 스타일에 관한 자세한 내용은 코드 스타일 가이드를 참조하세요.

  • /** */는 문서 주석을 나타냅니다. 이는 유형, 메서드, 필드 및 enum 값 선언에만 적용할 수 있습니다.
  • /* */는 여러 줄로 된 주석을 나타냅니다.
  • //는 줄 끝의 주석을 나타냅니다. //를 제외하면 줄 바꿈은 다른 공백과 동일합니다.
  • 아래의 문법 예시에서 //부터 줄 끝 사이의 텍스트는 문법의 일부가 아니라 문법에 관한 주석입니다.
  • [empty]는 항이 비어 있을 수 있음을 의미합니다.
  • ?는 리터럴 또는 항 뒤에 붙어 선택사항임을 나타냅니다.
  • ...은 구분 구두점 표시를 통해 0개 이상의 항목을 포함하는 시퀀스를 나타냅니다. HIDL에는 variadic 인수가 없습니다.
  • 쉼표는 시퀀스 요소를 구분합니다.
  • 세미콜론은 마지막 요소를 포함하여 각 요소를 종료합니다.
  • 대문자는 논터미널(nonterminal)입니다.
  • italicsinteger 또는 identifier(표준 C 파싱 규칙)와 같은 토큰 계열입니다.
  • constexpr 은 C 스타일의 상수 표현식입니다(예: 1 + 11L << 3).
  • import_name은 패키지 또는 인터페이스 이름이며, HIDL 버전 관리에 설명된 대로 정규화됩니다.
  • 소문자 words는 리터럴 토큰입니다.

예를 들면 다음과 같습니다.

ROOT =
    PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... }  // not for types.hal
  | PACKAGE IMPORTS ITEM ITEM...  // only for types.hal; no method definitions

ITEM =
    ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?;
  |  safe_union identifier { UFIELD; UFIELD; ...};
  |  struct identifier { SFIELD; SFIELD; ...};  // Note - no forward declarations
  |  union identifier { UFIELD; UFIELD; ...};
  |  enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar
  |  typedef TYPE identifier;

VERSION = integer.integer;

PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION;

PREAMBLE = interface identifier EXTENDS

EXTENDS = <empty> | extends import_name  // must be interface, not package

GENERATES = generates (FIELD, FIELD ...)

// allows the Binder interface to be used as a type
// (similar to typedef'ing the final identifier)
IMPORTS =
   [empty]
  |  IMPORTS import import_name;

TYPE =
  uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t |
 float | double | bool | string
|  identifier  // must be defined as a typedef, struct, union, enum or import
               // including those defined later in the file
|  memory
|  pointer
|  vec<TYPE>
|  bitfield<TYPE>  // TYPE is user-defined enum
|  fmq_sync<TYPE>
|  fmq_unsync<TYPE>
|  TYPE[SIZE]

FIELD =
   TYPE identifier

UFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...};
  |  struct identifier { FIELD; FIELD; ...};
  |  union identifier { FIELD; FIELD; ...};
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SIZE =  // Must be greater than zero
     constexpr

ANNOTATIONS =
     [empty]
  |  ANNOTATIONS ANNOTATION

ANNOTATION =
  |  @identifier
  |  @identifier(VALUE)
  |  @identifier(ANNO_ENTRY, ANNO_ENTRY  ...)

ANNO_ENTRY =
     identifier=VALUE

VALUE =
     "any text including \" and other escapes"
  |  constexpr
  |  {VALUE, VALUE ...}  // only in annotations

ENUM_ENTRY =
     identifier
  |  identifier = constexpr