이 섹션에서는 HIDL 데이터 유형에 관해 설명합니다. 구현 세부정보는 HIDL C++(C++ 구현 시) 또는 HIDL Java(Java 구현 시)를 참고하세요.
C++와의 유사점은 다음과 같습니다.
structs
는 C++ 구문을 사용하며unions
는 기본적으로 C++ 구문을 지원합니다. 둘 다 이름을 지정해야 하며 익명 구조체 및 공용체는 지원되지 않습니다.- typedef는 C++에서와 마찬가지로 HIDL에서 허용됩니다.
- C++ 스타일 주석이 허용되며 생성된 헤더 파일에 복사됩니다.
Java와의 유사점은 다음과 같습니다.
- HIDL은 각각의 파일에 관해 Java 스타일의 네임스페이스를 정의하며 네임스페이스는
android.hardware.
로 시작해야 합니다. 생성된 C++ 네임스페이스는::android::hardware::…
입니다. - 파일의 모든 정의는 Java 스타일
interface
래퍼 내에 포함됩니다. - HIDL 배열 선언은 C++ 스타일이 아닌 Java 스타일을 따릅니다. 예:
struct Point { int32_t x; int32_t y; }; Point[3] triangle; // sized array
- 주석은 javadoc 형식과 유사합니다.
데이터 표현
표준 레이아웃(간단한 기존 데이터 유형 요구사항의 하위 집합)으로 구성된 struct
또는 union
은 생성된 C++ 코드에서 일관된 메모리 레이아웃을 가지며 struct
및 union
멤버에 명시적인 정렬 속성이 적용되어 있습니다.
항상 기본형에서 파생되는 enum
, bitfield
유형을 비롯한 기본 HIDL 유형은 cstdint의 std::uint32_t
와 같은 표준 C++ 유형에 매핑됩니다.
Java는 부호가 없는 유형을 지원하지 않으므로 부호가 없는 HIDL 유형은 이에 상응하는 부호 있는 Java 유형에 매핑됩니다. 구조체는 Java 클래스에 매핑되며 배열은 Java 배열에 매핑됩니다. 현재 Java에서 공용체는 지원되지 않습니다. 문자열은 UTF8로 내부 저장됩니다. Java는 UTF16 문자열만 지원하므로 Java 구현으로 또는 Java 구현에서 전송된 문자열 값은 변환되며 문자 집합이 항상 원활하게 매핑되는 것은 아니기 때문에 재변환 시 동일하지 않을 수 있습니다.
C++에서 IPC를 통해 수신된 데이터는 const
로 표시되며 함수 호출 동안만 지속되는 읽기 전용 메모리에 위치합니다. Java에서 IPC를 통해 수신된 데이터는 이미 Java 객체에 복사된 상태이므로 추가 복사 없이도 보관할 수 있습니다(수정할 수도 있음).
주석
Java 스타일 주석은 유형 선언에 추가될 수 있습니다. 주석은 HIDL 컴파일러의 공급업체 테스트 도구 모음(VTS) 백엔드에 의해 파싱되지만 이렇게 파싱된 주석 중 HIDL 컴파일러에서 실제로 이해하는 주석은 없습니다. 대신, 파싱된 VTS 주석은 VTS 컴파일러(VTSC)에서 처리됩니다.
주석은 Java에서와 마찬가지로 값이 상수 표현식, 문자열, {}
내부의 값 목록 중 하나일 수 있는 @annotation
또는 @annotation(value)
또는 @annotation(id=value, id=value…)
등의 Java 구문을 사용합니다. 이름이 같은 여러 주석이 동일한 항목에 연결될 수 있습니다.
전방 선언
HIDL에서 구조체는 전방 선언되지 않을 수 있기 때문에 사용자가 지정한 자체 참조 데이터 유형이 불가능해집니다. 예를 들어 HIDL에서 연결된 목록이나 트리를 설명할 수 없습니다. 기존(Android 8.x 이전) HAL의 대부분은 데이터 구조 선언의 재정렬을 통해 삭제될 수 있는 전방 선언의 사용을 제한합니다.
이 제한으로 인해 데이터 구조는 자체 참조 데이터 구조에서 여러 번 발생할 수 있는 포인터 값을 추적하는 대신 간단한 깊은 복사를 통해 값별로 복사될 수 있습니다. 동일한 데이터를 가리키는 두 메서드 매개변수 또는 vec<T>
의 경우와 같이 동일한 데이터가 두 번 전달되는 경우 개별 사본 두 개가 생성되어 전달됩니다.
중첩 선언
HIDL은 중첩 선언을 필요한 수준만큼 지원합니다(단, 아래에 한 가지 예외가 있음). 예:
interface IFoo { uint32_t[3][4][5][6] multidimArray; vec<vec<vec<int8_t>>> multidimVector; vec<bool[4]> arrayVec; struct foo { struct bar { uint32_t val; }; bar b; } struct baz { foo f; foo.bar fb; // HIDL uses dots to access nested type names } …
예외적으로 인터페이스 유형은 vec<T>
에 한 수준 깊이만큼만 삽입할 수 있습니다(vec<vec<IFoo>>
아님).
원시 포인터 구문
HIDL 언어는 *를 사용하지 않으며 C/C++ 원시 포인터의 완전한 유연성을 지원하지 않습니다. HIDL이 포인터와 배열/벡터를 캡슐화하는 방법은 vec<T> 템플릿을 참고하세요.
인터페이스
interface
키워드에는 두 가지 용도가 있습니다.
- .hal 파일에서 인터페이스의 정의를 엽니다.
- 구조체/공용체 필드, 메서드 매개변수, 반환의 특수한 유형으로 사용할 수 있습니다. 일반 인터페이스이자
android.hidl.base@1.0::IBase
의 동의어로 여겨집니다.
예를 들어 IServiceManager
에는 다음과 같은 메서드가 있습니다.
get(string fqName, string name) generates (interface service);
메서드는 일부 인터페이스를 이름으로 조회하고자 합니다. 이는 인터페이스를 android.hidl.base@1.0::IBase
로 교체하는 것과도 동일합니다.
인터페이스의 경우 최상위 수준 매개변수 또는 vec<IMyInterface>
의 멤버로 전달하는 두 가지 방법만 가능합니다. 중첩 벡터, 구조체, 배열 또는 공용체의 멤버는 될 수 없습니다.
MQDescriptorSync 및 MQDescriptorUnsync
MQDescriptorSync
및 MQDescriptorUnsync
유형은 HIDL 인터페이스에서 동기화되거나 비동기화된 빠른 메시지 큐(FMQ) 설명자를 전달합니다. 자세한 내용은 HIDL C++를 참고하세요(FMQ는 Java에서 지원되지 않음).
메모리 유형
memory
유형은 HIDL에서 매핑되지 않은 공유 메모리를 표현하는 데 사용됩니다. 이 유형은 C++에서만 지원됩니다. 수신 지점에서 IMemory
객체를 초기화할 때 이 유형의 값을 사용하여 메모리를 매핑하고 사용 가능한 상태로 만들 수 있습니다. 자세한 내용은 HIDL C++를 참고하세요.
경고: 공유 메모리에 배치된 구조화된 데이터는 memory
를 전달하는 인터페이스 버전의 수명 동안 형식이 변경되지 않는 유형이어야 합니다. 그렇지 않으면 HAL에 치명적인 호환성 문제가 발생할 수 있습니다.
포인터 유형
pointer
유형은 HIDL 내부 전용입니다.
비트필드<T> 형식 템플릿
T
가 사용자 정의된 enum 값인 bitfield<T>
의 경우 이 값이 T
에서 정의된 enum 값의 비트 단위 OR임을 나타냅니다. 생성된 코드에서 bitfield<T>
는 T의 기본 유형으로 표시됩니다. 예:
enum Flag : uint8_t { HAS_FOO = 1 << 0, HAS_BAR = 1 << 1, HAS_BAZ = 1 << 2 }; typedef bitfield<Flag> Flags; setFlags(Flags flags) generates (bool success);
컴파일러는 uint8_t
와 동일한 유형 플래그를 처리합니다.
(u)int8_t
/(u)int16_t
/(u)int32_t
/(u)int64_t
를 사용하면 어떨까요? bitfield
를 사용하면 setFlags
가 플래그의 비트 단위 OR 값을 취한다는 것을 아는 리더(예: 16으로 setFlags
를 호출하는 것이 유효하지 않음을 인지)에 HAL 정보를 추가로 제공합니다. bitfield
가 없으면 이 정보는 문서를 통해서만 전달됩니다. 또한 VTS는 플래그 값이 플래그의 비트 단위 OR인지 실제로 확인할 수 있습니다.
핸들 기본형
경고: 어떤 종류의 주소(실제 기기 주소 포함)도 네이티브 핸들에 포함되어서는 안 됩니다. 프로세스 간에 이 정보를 전달하는 것은 위험하며 공격을 받기 쉽습니다. 프로세스 간에 전달되는 모든 값은 프로세스 내에서 할당된 메모리를 찾는 데 사용되기 전에 확인되어야 합니다. 그렇지 않으면 잘못된 핸들로 인해 잘못된 메모리 액세스나 메모리 손상이 발생할 수 있습니다.
HIDL 의미 체계는 값을 복사하며 이는 매개변수가 복사됨을 의미합니다.
대용량 데이터 또는 동기화 펜스처럼 프로세스 간에 공유되어야 하는 데이터는 영구 객체를 가리키는 파일 설명자를 전달하여 처리됩니다(공유 메모리, 실제 파일 또는 파일 설명자 뒤에 숨을 수 있는 기타 모든 항목의 경우 ashmem
). 바인더 드라이버는 파일 설명자를 다른 프로세스로 복제합니다.
native_handle_t
Android는 libcutils
에 정의된 일반 핸들 개념인 native_handle_t
를 지원합니다.
typedef struct native_handle { int version; /* sizeof(native_handle_t) */ int numFds; /* number of file-descriptors at &data[0] */ int numInts; /* number of ints at &data[numFds] */ int data[0]; /* numFds + numInts ints */ } native_handle_t;
네이티브 핸들은 값으로 전달되는 정수 및 파일 설명자의 모음입니다. 단일 파일 설명자는 정수가 없고 단일 파일 설명자는 있는 네이티브 핸들에 저장할 수 있습니다. handle
기본형으로 캡슐화된 네이티브 핸들을 사용하여 핸들을 전달하면 네이티브 핸들이 HIDL에 직접 포함됩니다.
native_handle_t
는 크기가 가변적이기 때문에 구조체에 직접 포함될 수 없습니다. 핸들 필드는 별도 할당된 native_handle_t
를 가리키는 포인터를 생성합니다.
이전 버전의 Android에서는 libcutils에 있는 함수와 동일한 함수를 사용하여 네이티브 핸들을 생성했습니다.
Android 8.0 이상에서는 이러한 함수가 android::hardware::hidl
네임스페이스에 복사되거나 NDK로 이동됩니다. HIDL 자동 생성 코드는 사용자 작성 코드의 관여 없이 이러한 함수를 자동으로 직렬화하고 역직렬화합니다.
핸들 및 파일 설명자 소유권
hidl_handle
객체(최상위 수준 또는 복합 유형의 일부)를 전달하거나 반환하는 HIDL 인터페이스 메서드를 호출하는 경우, 여기에 포함되는 파일 설명자의 소유권은 다음과 같습니다.
hidl_handle
객체를 인수로 전달하는 호출자는 객체가 래핑하는native_handle_t
에 포함되는 파일 설명자의 소유권을 보유합니다. 호출자는 파일 설명자 작업이 끝나면 해당 파일 설명자를 닫아야 합니다.hidl_handle
객체를_cb
함수로 전달하여 반환하는 프로세스는 객체가 래핑하는native_handle_t
에 포함된 파일 설명자의 소유권을 보유합니다. 프로세스는 파일 설명자 작업이 끝나면 해당 파일 설명자를 닫아야 합니다.hidl_handle
을 수신하는 전송은 객체가 래핑하는native_handle_t
내부에 있는 파일 설명자의 소유권을 보유합니다. 수신자는 트랜잭션 콜백 중에 이러한 파일 설명자를 그대로 사용할 수 있지만 콜백 이후 파일 설명자를 사용하려면 네이티브 핸들을 클론해야 합니다. 전송은 트랜잭션이 완료되면 자동으로 파일 설명자를 자동으로close()
합니다.
HIDL은 Java에서 핸들을 지원하지 않습니다(Java에서 핸들을 전혀 지원하지 않으므로).
크기가 지정된 배열
HIDL 구조체 내 크기가 지정된 배열의 경우, 해당 요소는 구조체에 포함 가능한 모든 유형이 될 수 있습니다.
struct foo { uint32_t[3] x; // array is contained in foo };
문자열
문자열은 C++와 Java에서 다르게 표시되지만 기본 전송 저장 유형은 C++ 구조입니다. 자세한 내용은 HIDL C++ 데이터 유형 또는 HIDL Java 데이터 유형을 참고하세요.
참고: HIDL 인터페이스를 통해 Java로 또는 Java에서 문자열을 전달하면(Java에서 Java로의 전달 포함) 원본 인코딩을 그대로 보존하지 못할 수도 있는 문자 집합 변환이 발생합니다.
벡터<T> 유형 템플릿
vec<T>
템플릿은 T
의 인스턴스를 포함하는 가변 크기의 버퍼를 나타냅니다.
T
는 다음 중 하나일 수 있습니다.
- 기본형(예: uint32_t)
- 문자열
- 사용자 정의 enum
- 사용자 정의 구조체
- 인터페이스 또는
interface
키워드(vec<IFoo>
,vec<interface>
는 최상위 수준 매개변수로만 지원됨) - 핸들
- 비트필드<U>
- U가 인터페이스를 제외한 목록에 있는 벡터<U>(예:
vec<vec<IFoo>>
는 지원되지 않음) - U가 인터페이스를 제외한 목록에 있는 U[](U의 크기가 지정된 배열)
사용자 정의 유형
이 섹션에서는 사용자 정의 유형에 대해 설명합니다.
Enum
HIDL은 익명 enum을 지원하지 않습니다. 이를 제외하면 HIDL의 enum은 C++11과 유사합니다.
enum name : type { enumerator , enumerator = constexpr , … }
기본 enum은 HIDL의 정수 유형 중 하나로 정의됩니다. 정수 유형을 기반으로 하는 enum의 첫 번째 열거자에 값이 지정되지 않은 경우, 값은 0으로 기본 설정됩니다. 이후 열거자에 지정된 값이 없으면 값은 이전 값에 1을 더한 값으로 기본 설정됩니다. 예:
// RED == 0 // BLUE == 4 (GREEN + 1) enum Color : uint32_t { RED, GREEN = 3, BLUE }
enum은 이전에 정의된 enum에서 상속할 수도 있습니다. 하위 enum(여기에서는 FullSpectrumColor
)의 첫 번째 열거자에 값이 지정되지 않은 경우 기본값은 상위 enum의 마지막 열거자 값에 1을 더한 값입니다. 예:
// ULTRAVIOLET == 5 (Color:BLUE + 1) enum FullSpectrumColor : Color { ULTRAVIOLET }
경고: enum 상속은 대부분의 다른 상속 유형과 역방향으로 작동합니다. 하위 enum 값은 상위 enum 값으로 사용할 수 없습니다. 하위 enum이 상위 enum보다 더 많은 값을 포함하기 때문입니다. 그러나 하위 enum 값은 정의상 상위 enum 값의 상위 집합이므로 상위 enum 값을 하위 enum 값으로 사용해도 무방합니다. 이는 상위 enum을 참조하는 유형이 인터페이스의 후속 반복에서 하위 enum을 참조할 수 없음을 의미하기 때문에 인터페이스를 설계할 때 이 점을 염두에 두시기 바랍니다.
enum 값은 중첩 유형으로서의 도트 구문이 아닌 콜론 구문과 함께 참조됩니다. 해당 구문은 Type:VALUE_NAME
입니다. 값이 동일한 enum 유형 또는 하위 유형에서 참조되는 경우 유형을 지정할 필요가 없습니다. 예를 들면 다음과 같습니다.
enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 }; enum Color : Grayscale { RED = WHITE + 1 }; enum Unrelated : uint32_t { FOO = Color:RED + 1 };
Android 10부터 enum에는 상수 표현식에 사용할 수 있는 len
속성이 있습니다.
MyEnum::len
은 이 열거에 있는 총 항목 수입니다.
이는 값이 중복될 때 줄어들 수 있는 값의 총 개수와는 다릅니다.
구조체
HIDL은 익명 구조체를 지원하지 않습니다. 이를 제외하면 HIDL의 구조체는 C와 매우 유사합니다.
HIDL은 구조체 내에 전부 포함된 가변 길이 데이터 구조를 지원하지 않습니다. 여기에는 C/C++에 있는 구조체의 마지막 필드로 종종 사용되며 크기가 [0]
으로 보이기도 하는 무제한 길이 배열이 포함됩니다. HIDL vec<T>
는 데이터를 별도의 버퍼에 저장한 동적 크기 배열을 나타냅니다. 이러한 인스턴스는 struct
에 있는 vec<T>
의 인스턴스로 표현됩니다.
마찬가지로 string
은 struct
에 포함될 수 있습니다(관련 버퍼는 별도임). 기본 데이터 유형의 인스턴스가 가변 길이이므로, 생성된 C++에서 HIDL 핸들 유형의 인스턴스는 실제 네이티브 핸들을 가리키는 포인터를 통해 표시됩니다.
노동조합
HIDL은 익명 공용체를 지원하지 않습니다. 이를 제외하면 공용체는 C와 유사합니다.
공용체는 포인터, 파일 설명자, 바인더 객체 등의 픽스업 유형을 포함할 수 없습니다. 특수 필드나 관련 유형을 필요로 하지 않으며 memcpy()
또는 이와 동등한 것을 통해 복사됩니다. 공용체는 바인더 오프셋(즉, 핸들 또는 바인더 인터페이스 참조) 설정이 필요한 것을 직접 또는 다른 데이터 구조를 통해 포함할 수 없습니다. 예:
union UnionType { uint32_t a; // vec<uint32_t> r; // Error: can't contain a vec<T> uint8_t b;1 }; fun8(UnionType info); // Legal
공용체는 구조체 내에서 선언될 수도 있습니다. 예:
struct MyStruct { union MyUnion { uint32_t a; uint8_t b; }; // declares type but not member union MyUnion2 { uint32_t a; uint8_t b; } data; // declares type but not member }