안드로이드 소스 코드(AOSP)를 분석하거나 커스텀 보드에 맞는 새로운 시스템 기능을 추가하다 보면 다양한 형태의 '서비스(Service)'들을 만나게 됩니다. 단순히 애플리케이션 레벨에서 구현하는 android.app.Service 외에도, OS 내부에서 백그라운드로 상주하며 시스템을 지탱하는 코어 서비스들이 존재하죠.
이 코어 서비스들은 크게 Framework Service(자바 기반)와 System Service/Native Daemon(C++ 기반)으로 나뉩니다. 두 영역 모두 안드로이드 생태계의 핵심 기능을 담당하지만, 소스 트리의 위치, 상주하는 프로세스 영역, 그리고 하드웨어(HAL)에 직접 접근할 수 있는지에 대한 권한 체계가 완전히 다릅니다. 이 계층 구조를 명확히 이해해야 바인더(Binder) 인터페이스를 설계할 때 아키텍처가 꼬이지 않습니다. 이번 포스팅에서는 두 서비스의 구조적 차이와 통신 흐름을 소스 코드 예제와 함께 자세히 짚어보겠습니다.

📌 핵심 요약 3줄
- Framework Service는 Application Framework 레이어에서 Java로 동작하며, 앱 개발자에게 상위 API(AMS, WMS 등)를 제공합니다.
- System Service (Native Daemon)는 네이티브 레이어에서 C++로 동작하며, 하드웨어(HAL) 및 커널과 직접 상호작용(SurfaceFlinger, AudioFlinger 등)합니다.
- 두 서비스 계층은 안드로이드 고유의 Binder IPC를 매개체로 유기적으로 연결되며, 상위 자바 레이어가 하위 네이티브 레이어를 호출하는 샌드위치 구조를 취합니다.
1. 한눈에 보는 서비스 계층 아키텍처 비교
안드로이드 아키텍처 맵 상에서 두 서비스가 어느 위치에 정착해 있고, 어떤 제약 사항을 갖는지 표로 정리해 보았습니다.
| 비교 항목 | Framework Service (자바 계층) | System Service / Native Daemon (네이티브 계층) |
| 주요 개발 언어 | Java / Kotlin | C / C++ |
| AOSP 소스 경로 | frameworks/base/services/ | frameworks/native/services/ 또는 hardware/ |
| 상주 프로세스 | system_server 프로세스 내부 스레드 | 독립적인 고유 네이티브 프로세스 (예: /system/bin/surfaceflinger) |
| 주요 역할 | 앱 대상의 API 엔드포인트 제공, 정책(Policy) 제어 | 실시간 하드웨어 제어, 데이터 멀티플렉싱, 렌더링 파이프라인 |
| HAL / 커널 접근 | 직접 접근 불가 (네이티브 서비스를 거쳐서 접근) | HIDL / AIDL 인터페이스를 통해 HAL 및 커널 드라이버 직접 제어 |
| 핵심 예시 | ActivityManagerService, WindowManagerService | SurfaceFlinger, AudioFlinger, InputFlinger |
2. Framework Service의 특징과 구현 방식
Framework Service는 안드로이드 시스템이 부팅될 때 system_server라는 거대한 자바 프로세스가 가동되면서 그 내부에서 함께 인스턴스화되는 자바 기반 서비스들입니다.
2.1 자바 프레임워크 서비스 구현 예시
AOSP 내부에서 시스템 서비스를 확장할 때는 com.android.server.SystemService 클래스를 상속받아 고유의 바인더 인터페이스를 구현합니다.
package com.android.server;
import android.content.Context;
import android.os.Binder;
import com.android.server.SystemService;
public class ExampleFrameworkService extends SystemService {
private final Context mContext;
public ExampleFrameworkService(Context context) {
super(context);
mContext = context;
}
@Override
public void onStart() {
// 1. 빌드 시스템 및 ServiceManager에 서비스 바인딩 등록
publishBinderService("example_framework", new BinderService());
}
// Binder IPC 통신을 처리할 실제 구현체 엔드포인트
private final class BinderService extends Binder {
// 앱이나 외부에서 바인더를 통해 호출할 시스템 메서드 정의
public void triggerAction() {
// 내부 로직 수행
}
}
}
3. System Service (Native Daemon)의 특징과 구현 방식
네이티브 시스템 서비스는 자바 가상 머신(ART)의 도움 없이, 리눅스 사용자 공간(User Space)에서 독립적인 바이너리 프로세스로 실행됩니다. 주로 연산 속도가 극도로 중요하거나, 멀티미디어 코덱, 그래픽 하드웨어 버퍼를 직접 주무르는 장치 제어에 사용됩니다.
3.1 C++ 네이티브 서비스 구현 예시
네이티브 서비스는 C++ 환경에서 바인더 통신 규격인 BnInterface를 상속받거나 AIDL이 생성해 주는 Bn 클래스를 기반으로 defaultServiceManager에 인스턴스를 등록합니다.
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
using namespace android;
// Binder 통신 인터페이스 규격을 따르는 네이티브 서비스 선언
class ExampleNativeService : public BnExampleService {
public:
static void instantiate() {
// 네이티브 ServiceManager에 서비스 전역 등록
defaultServiceManager()->addService(
String16("example_native"), new ExampleNativeService());
}
virtual status_t someHardwareControl() {
// 1. 여기서 하위 HAL 인터페이스를 호출하거나 커널 드라이버(/dev/*)에 직접 접근합니다.
return NO_ERROR;
}
};
4. 디스플레이 구동을 통해 보는 서비스 간 연계 흐름
안드로이드는 보안과 모듈화를 위해 상위 프레임워크가 하위 하드웨어를 직접 지배하지 못하게 막아두었습니다. 유저가 화면을 터치해 새로운 윈도우를 띄울 때의 아키텍처 협업 흐름을 보면 이 구조가 명확히 보입니다.
- Application: 앱이 화면을 그리거나 윈도우를 추가해달라고 요청합니다.
- Framework Service: 자바 레이어의 WindowManagerService (WMS)가 요청을 수신하여 윈도우의 배치 정책과 포커스를 계산합니다.
- System Service (Native): 계산이 끝나면 WMS는 바인더(Binder IPC)를 타고 네이티브 레벨의 SurfaceFlinger 서비스를 호출합니다.
- HAL & Kernel: SurfaceFlinger는 벤더사(Qualcomm, ARM 등)가 제공하는 그래픽 HAL(Gralloc / HWC) 인터페이스를 경유하여 최종 디스플레이 커널 드라이버에 프레임 버퍼를 플래싱합니다.
🛠️ 개발을 위한 팁 (Tips)
- service_contexts 권한 선언 필수: AOSP 환경에서 나만의 커스텀 서비스(자바든 네이티브든)를 추가했다면, 코드를 아무리 잘 짜도 부팅 시 서비스 등록 실패 에러가 납니다. 안드로이드의 강력한 보안 정책인 SELinux 때문인데요, system/sepolicy/private/service_contexts 파일(또는 장치 전용 Oem sepolicy 폴더)에 내가 만든 서비스의 바인더 이름과 SELinux 컨텍스트를 명확히 매핑해 주어야 ServiceManager가 정상적으로 등록을 수용합니다.
- dumpsys 구현으로 디버깅 환경 확보: 시스템 서비스를 새로 만들 때는 내부 바인더 클래스 안에 dump() 메서드를 반드시 오버라이드하여 현재 서비스의 핵심 멤버 변수나 상태 값을 콘솔에 프린트하도록 코드를 짜두세요. 나중에 터미널에서 adb shell dumpsys 서비스이름을 치는 것만으로 실시간 자원 상태를 파악할 수 있어 트러블슈팅 속도가 빨라집니다.
⚠️ 흔히 하는 실수 (Common Mistakes)
- system_server 내부 자바 서비스에서의 블로킹 함수 호출: Framework Service를 만들어 system_server 프로세스에 등록할 때, 서비스의 초기화(onStart) 루틴이나 바인더 인터페이스 내부에서 무거운 파일 I/O를 수행하거나 드라이버 응답을 기다리는 블로킹(Blocking) 코드를 무심코 넣는 경우가 있습니다. system_server가 단 5초만 응답을 멈춰도 안드로이드 전체 시스템이 얼어붙으며 가차 없이 ANR(Application Not Responding) 및 Watchdog 시스템 크래시가 발생해 단말기가 재부팅되므로, 무거운 작업은 반드시 별도 핸들러 스레드(HandlerThread)를 파서 위임해야 합니다.
- 네이티브 데몬 서비스의 라이프사이클 관리 누락: 독립된 C++ 프로세스로 구동되는 System Service를 설계할 때, 바인더 스레드 풀(ProcessState::self()->startThreadPool())을 활성화하지 않거나 메인 함수가 곧바로 종료되도록 방치하는 실수를 하곤 합니다. 네이티브 서비스는 자바 서비스와 달리 프로세스 생명 주기를 직접 제어해야 하므로, 등록 후 메인 스레드가 IPCThreadState::self()->joinThreadPool()을 호출해 바인더 요청을 대기하는 무한 루프 상태를 유지하도록 고정해 주어야 합니다.
5. 결론
AOSP 플랫폼 개발의 핵심은 적절한 레이어에 적절한 언어로 서비스를 배치하는 설계 능력에 있습니다. 정책을 제어하고 앱 개발자에게 안전한 자바 API 가이드라인을 제공할 때는 Framework Service가 정답이며, 데이터의 고속 처리나 실시간 하드웨어 융합이 필요할 때는 System Service (Native Daemon)로 내려가 설계해야 하죠.
오늘 정리한 두 서비스의 통신 메커니즘과 경계선을 숙지해 두시면, 하부 드라이버부터 상부 앱 레이어까지 매끄럽게 관통하는 클린 아키텍처를 구현하실 수 있을 겁니다. 신규 서비스를 추가하다가 SELinux 거부(Denial) 메시지를 만나거나 바인더 트랜잭션 에러로 먹통이 된다면, 언제든 댓글로 질문을 남겨주세요. 같이 소스를 뜯어봅시다!
'Android System & AOSP Engineering > AOSP Framework & Custom Services' 카테고리의 다른 글
| AOSP 소스 분석: SystemServer의 서비스 초기화 과정과 3대 핵심 서비스 구조 (0) | 2025.05.23 |
|---|---|
| Android Framework Service 구현 가이드: AIDL부터 Binder IPC 등록까지 (0) | 2025.05.22 |
| Android 시스템 최적화 가이드: 프로파일링, 전원 관리, 부팅 속도 개선 기법 (0) | 2025.04.23 |
| Android 커널 디버깅 가이드: dmesg, procfs부터 ftrace, kgdb 활용법 (0) | 2025.04.22 |
| Android 디버깅 가이드: Logcat, dumpsys, strace, systrace 실무 활용법 (0) | 2025.04.21 |