바인더의 데이터 전달

원격 호출이 쓸모 있기 위해서는 함수 호출과 함께 입력 데이터를 전달하고, 다시 출력을 수신할 수 있어야 한다. 바인더는 struct binder_transaction_data 의 data.ptr.buffer 필드를 통해 입출력 데이터를 전달한다. 버퍼의 크기는 data_size 필드로 지정한다. 버퍼로 전달되는 데이터들의 형식은 온전히 보내는 쪽과 받는 쪽간의 약속이기 때문에 반드시 지켜야 할 제약은 별로 없다. 다만 이미 구현되어 있는 서비스나 개발도구들에서 설정된 관례는 일관성이 있고, 이들과 상호 운용되려면 가급적 이 관례를 따르는 것이 바람직하다. 앞으로 데이터의 형식을 논할 때는 이 관례를 기준으로 한다. 버퍼에 삽입되는 데이터들은 엔디안(Endian)변환 없이 4바이트 단위로 정렬(Alignment) 된다. 4바이트 정렬 외에 추가의 채워 넣기(Padding)는 없다. 모든 BC_TRANSACTION 커맨드의 버퍼는 서비스의 이름으로 시작한다. 서비스의 이름은 유니코드(Unicode) 문자열을 전달하는데 사용하는 String16 형식을 갖는데, 먼저 문자열의 길이를 나타내는 4바이트 정수가 저장되고, 16비트 문자들로 채워진 후에 끝을 나타내는 16비트 0이 저장된다. 서비스의 이름 뒤에는 함수의 입력(Input) 인자들이 차례로 채워진다. 입출력 모두에 사용되는 인자들 역시 인력 인자들과 마찬가지로 취급된다. BR_TRANSACTION 커맨드를 수신하는 서비스는 먼저 첫 번째 인자가 자신의 서비스이름과 동일한지 확인한 후에 입력 인자들을 처리한다. 이와는 달리 BC_REPLY 커맨드의 버퍼는 서비스의 이름 없이 먼저 반환 값(Return Value)을 버퍼에 저장한 후에 출력 인자들로 뒤를 채운다. 이 때 입출력 모두에 사용되는 인자들은 출력 인자와 동일하게 취급된다. 전달되는 인자들의 다양한 형식에 관해서는 AIDL 과 함께 다시 자세히 다루게 될 것이다. 하지만 일부 데이터들은 바인더 드라이버에 의해 관찰되고 수정된다. 즉 프로세스 경계를 넘어서 다른 프로세스로 전달될 때 드라이버가 변환작업을 수행해야 하는 경우가 있다. 이렇게 바인더 드라이버에 의해 다루어지는 특별한 종류의 데이터들을 바인더 객체(Binder Object)라고 부르는데, struct flat_binder_object 라는 구조체에 저장된다. 바인더 드라이버는 전달되는 데이터들의 형식에 대한 사전 지식이 없다. 따라서 버퍼의 어떤 부분에 바인더 객체가 포함되어 있는지에 대한 별도의 정보를 필요로 하게 되는데, 이 것이 data.ptr.offsets 의 역할이다. data.ptr.offsets 는 부호 없는 4바이트 정수의 배열이고, 총 바이트 수가 offsets_size 필드에 저장된다. data.ptr.offsets 배열에 저장된 정수들은 각각 data.ptr.buffer 내에 struct flat_binder_object 구조체가 저장되어 있는 바이트 위치를 표시한다. 이 구조체는 다음과 같이 선언되어 있다.

struct flat_binder_object {
    unsigned long type;
    unsigned long flags;
    union {
        void *binder;
        signed long handle;
    };
    void *cookie;
};

type 과 flags 에 대해서는 다시 다루기로 하고 일단은 type 에 상수 BINDER_TYPE_BINDER 또는 BINDER_TYPE_HANDLE 이 저장될 수 있다는 것만 알아두기로 하자. 한 프로세스가 함수의 반환 값으로 자신의 프로세스 내에 존재하는 바인더 객체를 전달하기로 결정했다고 하자. 이 때 type 에는 BINDER_TYPE_BINDER 를 저장하고, 바인더 객체를 가리키는 포인터를 binder 에 저장한다. 이 구조체를 struct binder_transaction_data 의 data.ptr.buffer 에 저장하고, struct flat_binder_object 구조체의 위치(이 경우는 0)를 data.ptr.offsets 에 저장함 후 BC_REPLY 커맨드에 실어 보낸다. 이제 바인더 드라이버는 struct flat_binder_object 구조체의 type 필드가 BINDER_TYPE_BINDER 이며, binder 필드에 저장된 포인터가 아직 등록되지 않은 포인터임을 파악하게 된다. 때문에 바인더 객체을 등록하고 인터페이스 핸들을 발급한다. 이 값을 handle 필드에 저장하고, type 필드를 BINDER_TYPE_HANDLE 로 변경한다. 이렇게 변경된 struct flat_binder_object 가 포함된 데이터가 수신 측에 전달되게 된다. 거꾸로 원격 프로세스에 존재하는 바인더 객체를 전송할 때는 type 필드를 BINDER_TYPE_HANDLE 로 지정하고, handle 필드에 인터페이스 핸들을 저장한다. 이 값들은 바인더 드라이버에 의해 원격 프로세스에 도착할 때는 각각 BINDER_TYPE_BINDER 와 binder 필드에 저장된 바인더 객체의 포인터 값으로 변경되어 있게 된다. 요컨대 자신의 프로세스에 존재하는 바인더 객체는 BINDER_TYPE_BINDER 와 포인터로 지정하고, 원격 프로세스에 존재하는 것은 BINDER_TYPE_HANDLE 와 인터페이스 핸들로 지정한다. 두 표현 간의 변환은 바인더 드라이버에 의해 이루어진다.   이제 인터페이스 핸들을 얻기 위한 실마리를 발견했다. 인터페이스 등록을 좀 더 자세히 살펴보자.