HIDL 데이터 선언은 C++ 표준 레이아웃 데이터 구조를 생성합니다. 이러한 구조는 자연스럽고(파일, 전역 범위 또는 힙에) 자연스럽게 배치될 수 있으며 동일한 방식으로 구성될 수 있습니다. 클라이언트 코드는 HIDL 프록시 코드를 호출하여 const 참조와 기본 유형을 전달하며, 스텁과 프록시 코드는 직렬화의 세부사항을 숨깁니다.
참고: 명시적으로 데이터 구조를 직렬화하거나 역직렬화하는 데 개발자 코드가 필요하지 않습니다.
아래 표에서는 HIDL 프리미티브를 C++ 데이터 유형에 매핑합니다.
HIDL 유형 | C++ 유형 | 헤더/라이브러리 |
---|---|---|
enum |
enum class |
|
uint8_t..uint64_t |
uint8_t..uint64_t |
<stdint.h> |
int8_t..int64_t |
int8_t..int64_t |
<stdint.h> |
float |
float |
|
double |
double |
|
vec<T> |
hidl_vec<T> |
libhidlbase |
T[S1][S2]...[SN] |
T[S1][S2]...[SN] |
|
string |
hidl_string |
libhidlbase |
handle |
hidl_handle |
libhidlbase |
safe_union |
(custom) struct |
|
struct |
struct |
|
union |
union |
|
fmq_sync |
MQDescriptorSync |
libhidlbase |
fmq_unsync |
MQDescriptorUnsync |
libhidlbase |
아래 섹션에서는 데이터 유형에 대해 자세히 설명합니다.
enum
HIDL의 enum은 C++의 enum이 됩니다. 예:
enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };
다음과 같이 변환됩니다.
enum class Mode : uint8_t { WRITE = 1, READ = 2 }; enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };
Android 10부터 enum은 ::android::hardware::hidl_enum_range
를 사용하여 반복할 수 있습니다. 이 범위에는 상위 enum에서 마지막 하위 요소에 이르기까지 HIDL 소스 코드에 나타나는 모든 열거자가 포함됩니다. 예를 들어 이 코드는 WRITE
, READ
, NONE
및 COMPARE
순서로 반복됩니다. 위 SpecialMode
의 경우 다음과 같습니다.
template <typename T> using hidl_enum_range = ::android::hardware::hidl_enum_range<T> for (SpecialMode mode : hidl_enum_range<SpecialMode>) {...}
hidl_enum_range
는 역 반복자를 구현하며 constexpr
컨텍스트에서 사용할 수 있습니다. 열거형에 여러 번 표시되는 값은 범위에 여러 번 나타납니다.
bitfield<T>
bitfield<T>
(여기서 T
는 사용자 정의된 enum)가 C++에서 enum의 기본 유형이 됩니다. 위 예에서 bitfield<Mode>
는 uint8_t
가 됩니다.
vec<T>
hidl_vec<T>
클래스 템플릿은 libhidlbase
의 일부이며 HIDL 유형의 벡터를 임의의 크기로 전달하는 데 사용할 수 있습니다. 비교 가능한 고정 크기 컨테이너는 hidl_array
입니다. hidl_vec::setToExternal()
함수를 사용하여 외부 데이터 버퍼 유형 T
를 가리키도록 hidl_vec<T>
를 초기화할 수도 있습니다.
생성된 C++ 헤더에서 구조체를 적절히 내보내거나 삽입하는 것 외에도 vec<T>
를 사용하면 std::vector
와 기본적인 T
포인터로/에서 변환하는 편리한 함수가 생성됩니다. vec<T>
를 매개변수로 사용하면 이를 사용하는 함수가 오버로드되어(프로토타입 두 개가 생성됨) 이 매개변수를 위한 HIDL 구조체와 std::vector<T>
유형을 모두 받아들이고 전달합니다.
배열
HIDL의 상수 배열은 libhidlbase
의 hidl_array
클래스로 표시됩니다. hidl_array<T, S1, S2, …,
SN>
은 N차원 고정 크기 배열 T[S1][S2]…[SN]
을 나타냅니다.
문자열
hidl_string
클래스(libhidlbase
의 일부)는 HIDL 인터페이스를 통해 문자열을 전달하는 데 사용할 수 있고 /system/libhidl/base/include/hidl/HidlSupport.h
에 정의되어 있습니다. 클래스의 첫 번째 저장소 위치는 문자 버퍼의 포인터입니다.
hidl_string
은 operator=
, 암시적 변환 및 .c_str()
함수를 사용하여 std::string and char*
(C 스타일 문자열)로/에서 변환하는 방법을 압니다.
HIDL 문자열 구조체에는 다음과 같은 적절한 복사 생성자 및 할당 연산자가 있습니다.
std::string
또는 C 문자열에서 HIDL 문자열을 로드합니다.- HIDL 문자열에서 새로운
std::string
을 만듭니다.
또한 HIDL 문자열에는 변환 생성자가 있으므로 HIDL 문자열을 사용하는 메서드에서 C 문자열(char *
) 및 C++ 문자열(std::string
)을 사용할 수 있습니다.
구조체
HIDL의 struct
에는 고정 크기 데이터 유형만 포함될 수 있으며 함수는 포함되지 않습니다. HIDL 구조체 정의는 C++에서 표준 레이아웃 struct
에 직접 매핑되므로 struct
에 일관된 메모리 레이아웃이 있도록 보장합니다. 구조체에는 별도의 가변 길이 버퍼를 가리키는 handle
, string
, vec<T>
를 비롯한 HIDL 유형이 포함될 수 있습니다.
핸들
경고: 어떤 종류의 주소(실제 기기 주소 포함)도 네이티브 핸들에 포함되어서는 안 됩니다. 프로세스 간에 이 정보를 전달하는 것은 위험하며 공격을 받기 쉽습니다. 프로세스 간에 전달되는 모든 값은 프로세스 내에서 할당된 메모리를 찾는 데 사용되기 전에 확인되어야 합니다. 그렇지 않으면 잘못된 핸들로 인해 잘못된 메모리 액세스나 메모리 손상이 발생할 수 있습니다.
handle
유형은 C++의 hidl_handle
구조로 표시되며 이는 const native_handle_t
객체 포인터를 둘러싼 간단한 래퍼입니다(이 객체는 Android에서 오랫동안 존재함).
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;
기본적으로 hidl_handle
은 래핑하는 native_handle_t
포인터의 소유권을 가져가지 않습니다. 32비트 및 64비트 프로세스에서 모두 사용할 수 있도록 native_handle_t
포인터를 안전하게 저장하기 위해 존재할 뿐입니다.
포함된 파일 설명자를 hidl_handle
이 소유하는 시나리오는 다음과 같습니다.
shouldOwn
매개변수를true
로 설정하여setTo(native_handle_t* handle, bool shouldOwn)
메서드를 호출한 후- 다른
hidl_handle
객체에서 복사 생성으로hidl_handle
객체를 만드는 경우 - 다른
hidl_handle
객체에서hidl_handle
객체를 복사 할당하는 경우
hidl_handle
은 native_handle_t*
객체로/에서 암시적 변환 및 명시적 변환을 모두 제공합니다. HIDL에서 handle
유형의 주요 용도는 HIDL 인터페이스를 통해 파일 설명자를 전달하는 것입니다. 따라서 단일 파일 설명자는 int
가 없는 native_handle_t
와 단일 fd
로 표시됩니다. 클라이언트와 서버가 다른 프로세스에 있으면 RPC 구현은 파일 설명자를 자동으로 처리하여 두 프로세스가 동일한 파일에서 작동할 수 있도록 합니다.
프로세스에 의해 hidl_handle
에서 수신된 파일 설명자는 프로세스에서 유효하지만 수신 함수를 벗어나서 지속되지 않습니다. 함수가 반환되면 닫힙니다. 파일 설명자에 지속적인 액세스를 유지하려는 프로세스는 포함된 파일 설명자를 dup()
하거나 전체 hidl_handle
객체를 복사해야 합니다.
메모리
HIDL memory
유형은 매핑되지 않은 공유 메모리를 표시하는 libhidlbase
의 hidl_memory
클래스에 매핑됩니다. HIDL에서 메모리를 공유하는 프로세스 간에 전달해야 하는 객체입니다. 공유 메모리를 사용하려면 다음 단계를 따르세요.
IAllocator
인스턴스(현재 'ashmem' 인스턴스만 사용 가능)를 가져와서 공유 메모리를 할당하는 데 사용합니다.IAllocator::allocate()
는 HIDL RPC를 통해 전달되고libhidlmemory
의mapMemory
함수를 사용하여 프로세스에 매핑될 수있는hidl_memory
객체를 반환합니다.mapMemory
는 메모리에 액세스하는 데 사용할 수 있는sp<IMemory>
객체 참조를 반환합니다.IMemory
및IAllocator
는android.hidl.memory@1.0
에 정의되어 있습니다.
IAllocator
의 인스턴스를 사용하여 메모리를 할당할 수 있습니다.
#include <android/hidl/allocator/1.0/IAllocator.h> #include <android/hidl/memory/1.0/IMemory.h> #include <hidlmemory/mapping.h> using ::android::hidl::allocator::V1_0::IAllocator; using ::android::hidl::memory::V1_0::IMemory; using ::android::hardware::hidl_memory; .... sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem"); ashmemAllocator->allocate(2048, [&](bool success, const hidl_memory& mem) { if (!success) { /* error */ } // now you can use the hidl_memory object 'mem' or pass it around }));
메모리의 실제 변경은 mem
을 만든 측이나 HIDL RPC를 통해 수신하는 측에서 IMemory
객체를 통해 실행되어야 합니다.
// Same includes as above sp<IMemory> memory = mapMemory(mem); void* data = memory->getPointer(); memory->update(); // update memory however you wish after calling update and before calling commit data[0] = 42; memory->commit(); // … memory->update(); // the same memory can be updated multiple times // … memory->commit();
인터페이스
인터페이스는 객체로 전달될 수 있습니다. 인터페이스라는 단어는 android.hidl.base@1.0::IBase
유형의 구문 슈가로 사용할 수 있습니다. 또한 현재 인터페이스와 가져온 인터페이스는 유형으로 정의됩니다.
인터페이스가 있는 변수는 강력한 포인터 sp<IName>
여야 합니다. 인터페이스 매개변수를 사용하는 HIDL 함수는 원시 포인터를 강력한 포인터로 변환하여 직관적이지 않은 동작을 유발합니다. 포인터는 예기치 않게 삭제될 수 있습니다. 문제를 방지하려면 항상 HIDL 인터페이스를 sp<>
로 저장하세요.