안드로이드 시스템 소스코드를 깊숙이 파고들다 보면 결국 모든 길은 한 곳으로 통한다는 사실을 깨닫게 됩니다. 바로 프로세스 간 통신(IPC)의 핵심인 바인더(Binder) 메커니즘인데요. 하지만 똑같은 바인더를 쓰면서도 어떤 모듈은 .aidl 파일로 소통하고, 어떤 벤더 드라이버 레이어는 .hal(HIDL) 파일로 인터페이스를 정의하고 있어서 소스 분석 초기에 큰 혼란을 겪곤 합니다.
"앱 개발할 때 쓰던 AIDL을 왜 HAL 레이어 소스에서 또 쓰고 있지?", "HIDL은 이제 안 쓰이는 걸까?" 같은 의문이 드는 것은 당연합니다. 구글이 안드로이드 버전을 거듭하며 플랫폼 내부의 통신 표준 아키텍처를 계속해서 진화시켜 왔기 때문인데요. 이번 포스팅에서는 AIDL과 HIDL의 태생적인 목적 차이부터 시작해, 각각의 동작 방식과 실제 코드 구조, 그리고 최신 AOSP 버전에서 전개되고 있는 중요한 아키텍처 변화까지 명확하게 비교해 보겠습니다.

📌 핵심 요약 3줄
- AIDL은 원래 안드로이드 앱 앱 간 또는 앱과 프레임워크 서비스 간의 Java 중심 IPC를 위해 설계된 인터페이스 정의 언어입니다.
- HIDL은 안드로이드 8.0 트레블(Treble) 프로젝트와 함께 도입되어 벤더 하드웨어(HAL) 프로세스를 분리하는 전용 규격으로 사용되었습니다.
- 현재 최신 안드로이드 아키텍처는 성능과 하위 호환성을 모두 잡은 **'Stable AIDL'**을 표준으로 채택하면서, 하드웨어 레이어(HAL)까지 AIDL 구조로 빠르게 대통합하는 추세입니다.
1. AIDL과 HIDL 핵심 아키텍처 비교
두 기술은 바인더(Binder)라는 동일한 하부 엔진을 공유하지만, 관리하는 범위와 인터페이스의 특성에서 뚜렷한 차이를 보입니다.
| 기술 비교 항목 | AIDL (Android Interface Definition Language) | HIDL (Hardware Interface Definition Language) |
| 기본 목적 | 앱 프로세스 간 통신 및 프레임워크 시스템 서비스 연결 | 하드웨어 추상화 계층(HAL)과 프레임워크의 격리 분리 |
| 주요 실행 레이어 | 유저 애플리케이션 프레임워크 레이어 | 벤더 하드웨어 및 시스템 서비스 레이어 |
| 타겟 소스 언어 | Java, C++, NDK (최신 아키텍처 통합 지원) | C++, Java (주로 네이티브 C++ 중심 구현) |
| 네임 서비스 관리자 | servicemanager (통합 관리) | hwservicemanager (독립 실행형) |
| 하위 호환성 (Stability) | Stable AIDL 도입으로 빌드 타임 하위 호환 강력 보장 | 패키지 버전 명시(@1.0)를 통해 인터페이스 파편화 방지 |
| 현재 기술적 위치 | 안드로이드 11 이후 표준 HAL 인터페이스로 전면 채택 | 안드로이드 최신 버전 기준으로 구글 가이드상 Legacy 지정 |
2. AIDL 동작 메커니즘과 코드 구조
AIDL은 프레임워크 바인더 서비스의 근간입니다. 상위 자바 생태계와 링킹하기 매우 편한 구조로 설계되어 있습니다.
2.1 인터페이스 규격 선언 (IExampleService.aidl)
// com/example/aidl/IExampleService.aidl
package com.example.aidl;
interface IExampleService {
// 클라이언트가 원격으로 메시지를 요청하는 메서드
String getMessage();
}
2.2 서비스 구현부 (Java 서비스 예시)
서버 단에서는 Stub 구조체를 상속받아 내부 알맹이 추상 메서드를 구체화합니다.
public class ExampleService extends Service {
// AIDL 컴파일러가 생성해준 Stub을 통해 인터페이스 비즈니스 로직 구현
private final IExampleService.Stub binder = new IExampleService.Stub() {
@Override
public String getMessage() {
return "Hello from AIDL Remote Service!";
}
};
@Override
public IBinder onBind(Intent intent) {
// 클라이언트에게 바인더 통신 채널 헨들러를 토스합니다.
return binder;
}
}
2.3 클라이언트 바인딩 구조
private IExampleService exampleService;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 전달받은 날것의 커널 바인더 객체를 AIDL 인터페이스 형태로 프록시 변환합니다.
exampleService = IExampleService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
exampleService = null;
}
};
// 서비스 바인딩 구동
Intent intent = new Intent(this, ExampleService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
3. HIDL 동작 메커니즘과 코드 구조
HIDL은 하드웨어 제조사(OEM)가 소스코드를 꽁꽁 숨긴 채 하드웨어 드라이버 라이브러리만 독립 프로세스로 띄울 수 있도록 기획된 전용 언어입니다.
3.1 벤더 인터페이스 선언 (IExample.hal)
// vendor.example.hardware.example@1.0/IExample.hal
package vendor.example.hardware.example@1.0;
interface IExample {
// 벤더 프로세스가 응답 메시지를 생성하도록 규격 설정
getMessage() generates (string message);
};
3.2 네이티브 구현부 (Example.cpp)
네이티브 단의 C++ 코드로 상속받아 실체화합니다.
#include "IExample.h"
namespace vendor::example::hardware::example::V1_0::implementation {
// HIDL 툴체인이 만들어준 C++ 클래스를 구현합니다.
Return<void> Example::getMessage(getMessage_cb _hidl_cb) {
// 비동기/동기 연산을 모두 수용하기 위해 콜백 구조를 기본적으로 애용합니다.
_hidl_cb("Hello from HIDL Binderized Vendor Daemon!");
return Void();
}
} // namespace
3.3 독립 서버 데몬 등록
int main() {
// 1. 하드웨어 구동 인스턴스 할당
sp<IExample> service = new Example();
// 2. 바인더 수신 전용 RPC 스레드 가동
configureRpcThreadpool(1, true);
// 3. hwservicemanager 네임서버에 서비스 정식 등록
status_t status = service->registerAsService();
joinRpcThreadpool();
return 0;
}
4. 왜 지금은 하드웨어 레이어(HAL)까지 AIDL로 가고 있을까?
예전에는 "성능의 HIDL, 사용성의 AIDL"이라는 이분법적인 시각이 존재했습니다. HIDL이 네이티브 C++ 영역에서 성능 최적화가 더 잘 어우러진다고 판단했기 때문인데요. 하지만 두 구조를 동시에 유지하다 보니 구글 플랫폼 엔지니어들과 파트너사 개발자들의 빌드 관리 공수가 두 배로 들어갔습니다.
이에 구글은 안드로이드 11부터 'Stable AIDL' 체제를 도입했습니다. 네이티브 C++ 컴파일 성능을 보장하는 libbinder_ndk 인프라를 구축하면서, 굳이 복잡한 HIDL 언어를 따로 쓰지 않아도 AIDL 파일 하나만으로 앱 IPC부터 최하단 하드웨어 HAL 프로세스 제어까지 유연하게 구현할 수 있게 되었습니다. 즉, 통신 통로의 대통합이 일어난 셈입니다.
💡 안드로이드 IPC 시스템 개발을 위한 실전 팁
- 신규 벤더 모듈 설계 시 무조건 Stable AIDL 선택: 새 하드웨어 부품(예: 특수 생체인식 센서, 커스텀 컨트롤 보드 등)의 HAL 레이어를 설계해야 한다면 과감히 HIDL을 배제하고 AIDL HAL 구조로 프로젝트를 시작하시는 것을 권장합니다. 구글은 이미 AOSP 오디오, 진동, 조도 센서 등 표준 코어 HAL을 순차적으로 AIDL 구조로 리팩토링 완료한 상태이며, 향후 버전에서는 HIDL 컴파일 도구 체인 자체를 폐기할 예정이기 때문에 미래지향적인 선택이 필수적입니다.
- 동기식 바인더 호출 시 타임아웃 방어 코드 탑재: AIDL로 동기식 원격 메서드를 호출할 때, 호출당한 상대방 프로세스가 무한 루프에 빠지거나 드라이버 IO 블로킹에 묶이면 내 프로세스의 호출 스레드도 함께 먹통이 됩니다. 이를 막기 위해 네이티브 레이어 통신 시 linkToDeath 리스너를 바인딩하여 상대방 프로세스의 생사 여부를 상시 감시하고, 중요한 제어 구문은 메인 런타임 스레드가 아닌 백그라운드 Worker 스레드로 격리하여 호출하는 구조를 설계해야 시스템이 단단해집니다.
⚠️ 흔히 하는 실수
- onWay 비동기 키워드 남용으로 인한 데이터 누락 현상: AIDL에서 리턴 값이 없는 메서드에 oneway 키워드를 붙이면 바인더는 상대방이 명령을 받았는지 확인도 안 하고 제어권을 내 프로세스로 즉시 반환하므로 성능상 이득을 봅니다. 하지만 하드웨어 제어 명령(예: "모터 구동 각도 설정")을 촘촘하게 연속으로 oneway 호출을 날려버리면, 하단 바인더 드라이버의 트랜잭션 큐가 가득 차거나(Buffer Overflow) 전달 순서가 뒤틀려 일부 하드웨어 제어 명령이 공중에서 분해되는 기괴한 하드웨어 오작동 버그를 마주하게 됩니다. 상태 동기화가 필요한 명령은 반드시 oneway를 빼고 동기식 호출로 설계하셔야 합니다.
- AIDL 인터페이스 무단 수정으로 인한 런타임 빌드 크래시: 시스템 펌웨어 업데이트 시 .aidl 파일에 새로 필드를 추가하거나 메서드 순서를 임의로 바꾸고 컴파일하여 단말기에 밀어 넣는 실수가 잦습니다. 바인더 통신은 컴파일러가 생성한 고유 테이블 번호(Index) 배열 구조로 상대방 메서드를 식별하는데요. 순서가 바뀌거나 규격이 틀어지면 런타임에 완전히 뚱딴지같은 메서드가 실행되거나 메모리 오프셋 미스매치로 프로세스가 즉시 사망(SIGSEGV)합니다. 인터페이스 규격서를 변경할 때는 반드시 패키지 버전 관리 룰을 준수하거나 하단에 순차적으로 메서드를 추가하는 방식을 취해야 크래시 대참사를 막을 수 있습니다.
5. 결론
AIDL과 HIDL은 안드로이드가 거대한 파편화의 늪을 탈출해 단단하고 깔끔한 컴포넌트 OS로 진화해 온 발자취 그 자체입니다. 과거의 이분법적인 경계를 넘어, 오늘날의 안드로이드 생태계는 "Stable AIDL을 기반으로 한 단일 인터페이스 월드"로 단일화되고 있습니다. 각 언어가 가진 바인더 내부의 상호작용 흐름과 한계점을 정확히 식별할 줄 아는 안목이 있다면, 어떤 까다로운 임베디드 도메인 프로젝트를 만나더라도 명쾌한 시스템 아키텍처를 뽑아내실 수 있을 것입니다.
'Android System & AOSP Engineering > AOSP Framework & Custom Services' 카테고리의 다른 글
| 안드로이드 미디어 스택의 양대 산맥: Camera HAL3와 Audio HAL 구조 및 AOSP 코드 완벽 분석 (0) | 2025.03.25 |
|---|---|
| 안드로이드 HAL과 바인더(Binder) IPC: 아키텍처 결합과 데이터 흐름 완벽 분석 (0) | 2025.03.24 |
| 안드로이드 8.0의 혁신 HIDL 이란? .hal 소스코드로 배우는 Binderized HAL 완벽 가이드 (0) | 2025.03.22 |
| 안드로이드 HAL과 리눅스 커널 드라이버의 연결 고리: 시스템 콜 기반 하드웨어 제어 분석 (0) | 2025.03.21 |
| 안드로이드 HAL의 진화: 레거시 구조부터 HIDL을 거쳐 AIDL HAL까지 완벽 분석 (0) | 2025.03.20 |