데이터 유형

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, NONECOMPARE 순서로 반복됩니다. 위 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의 상수 배열은 libhidlbasehidl_array 클래스로 표시됩니다. hidl_array<T, S1, S2, …, SN>은 N차원 고정 크기 배열 T[S1][S2]…[SN]을 나타냅니다.

문자열

hidl_string 클래스(libhidlbase의 일부)는 HIDL 인터페이스를 통해 문자열을 전달하는 데 사용할 수 있고 /system/libhidl/base/include/hidl/HidlSupport.h에 정의되어 있습니다. 클래스의 첫 번째 저장소 위치는 문자 버퍼의 포인터입니다.

hidl_stringoperator=, 암시적 변환 및 .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_handlenative_handle_t* 객체로/에서 암시적 변환 및 명시적 변환을 모두 제공합니다. HIDL에서 handle 유형의 주요 용도는 HIDL 인터페이스를 통해 파일 설명자를 전달하는 것입니다. 따라서 단일 파일 설명자는 int가 없는 native_handle_t와 단일 fd로 표시됩니다. 클라이언트와 서버가 다른 프로세스에 있으면 RPC 구현은 파일 설명자를 자동으로 처리하여 두 프로세스가 동일한 파일에서 작동할 수 있도록 합니다.

프로세스에 의해 hidl_handle에서 수신된 파일 설명자는 프로세스에서 유효하지만 수신 함수를 벗어나서 지속되지 않습니다. 함수가 반환되면 닫힙니다. 파일 설명자에 지속적인 액세스를 유지하려는 프로세스는 포함된 파일 설명자를 dup()하거나 전체 hidl_handle 객체를 복사해야 합니다.

메모리

HIDL memory 유형은 매핑되지 않은 공유 메모리를 표시하는 libhidlbasehidl_memory 클래스에 매핑됩니다. HIDL에서 메모리를 공유하는 프로세스 간에 전달해야 하는 객체입니다. 공유 메모리를 사용하려면 다음 단계를 따르세요.

  1. IAllocator 인스턴스(현재 'ashmem' 인스턴스만 사용 가능)를 가져와서 공유 메모리를 할당하는 데 사용합니다.
  2. IAllocator::allocate()는 HIDL RPC를 통해 전달되고 libhidlmemorymapMemory 함수를 사용하여 프로세스에 매핑될 수있는 hidl_memory 객체를 반환합니다.
  3. mapMemory는 메모리에 액세스하는 데 사용할 수 있는 sp<IMemory> 객체 참조를 반환합니다. IMemoryIAllocatorandroid.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<>로 저장하세요.