임베디드 하드웨어 보드나 커스텀 단말기(IVI, 키오스크, 로봇 등)를 개발하다 보면, 제조사만의 고유한 하드웨어 가속 기능이나 전역 보안 상태를 체크하는 기능을 만들어 시스템 전반에 배포해야 할 때가 있습니다. 일반적인 안드로이드 앱 빌드 방식으로는 시스템 전역을 관장하는 공용 백그라운드 서비스를 실행할 수도 없고, 다른 앱들이 내 서비스에 자유롭게 접근하도록 제어하기도 어렵습니다.
이때 선택해야 하는 정석적인 해결책이 바로 Framework Service(프레임워크 서비스)를 AOSP 소스 트리 내부 유저 공간에 직접 구현하는 것입니다. 자바 프레임워크 계층에 나만의 서비스를 심어두면, 시스템이 켜질 때 자동으로 메모리에 상주하며, 일반 서드파티 앱들이 구글 순정 API를 쓰듯 인터페이스를 통해 내 서비스를 호출할 수 있게 됩니다. 이번 포스팅에서는 프레임워크 서비스의 개념 구조와 함께 매니저 패턴의 동작 방식, 그리고 AIDL과 Binder IPC를 이용해 커스텀 서비스를 AOSP에 바인딩하는 구체적인 절차를 알아보겠습니다.

📌 핵심 요약 3줄
- Framework Service는 AOSP 자바 레이어에 상주하며, 시스템 전반의 핵심 기능을 다수의 애플리케이션에 API 형태로 중계하는 가교 역할을 합니다.
- 프로세스 경계를 넘어 안전하게 데이터를 주고받기 위해 AIDL 명세서를 기반으로 Binder IPC 통신 인터페이스를 자동 생성하여 연동합니다.
- 커스텀 서비스는 부팅 시점에 ServiceManager.addService()를 통해 시스템 데몬에 등록되어야 비로소 클라이언트 앱들이 검색하여 호출할 수 있습니다.
1. 매니저(Manager) 패턴과 시스템 서비스의 관계
안드로이드 프레임워크는 보안과 샌드박싱 정책 때문에 앱 프로세스가 OS 핵심 서비스에 직접 깃깃하게 접근하는 것을 철저히 차단합니다. 대신, 클라이언트 역할을 하는 '매니저(Manager) 클래스'와 실질적인 연산을 처리하는 '시스템 서비스(System Service) 클래스'를 1:1로 매핑하는 매니저 아키텍처를 취합니다.
| 앱 참조 매니저 (Client 인터페이스) | 실제 구동 시스템 서비스 (Server 내부 로직) | 주요 담당 시스템 역할 |
| ActivityManager (android.app) | ActivityManagerService (AMS) | 애플리케이션 프로세스 생성, 액티비티 생명 주기 및 태스크 관리 |
| WindowManager (android.view) | WindowManagerService (WMS) | 화면 상의 윈도우 창 배치, 레이어 순서(Z-Order), 입력 이벤트 분배 |
| PackageManager (android.content.pm) | PackageManagerService (PKMS) | 디바이스 내 .apk 패키지 설치, 삭제, 앱 권한 검증 및 관리 |
| PowerManager (android.os) | PowerManagerService (PMS) | 화면 켜짐/꺼짐 제어, CPU 웨이크록(Wakelock) 상태 및 배터리 절전 관리 |
2. Framework Service의 핵심 구성 요소 및 구현 가이드
내가 원하는 커스텀 프레임워크 서비스를 AOSP 소스 내부에 안착시키려면 인터페이스 정의(AIDL) -> 실제 서비스 비즈니스 로직 구현 -> 시스템 등록이라는 3단계 파이프라인을 밟아야 합니다.
2.1 1단계: AIDL을 이용한 인터페이스 정의 (IMyFrameworkService.aidl)
프로세스가 서로 다른 앱과 서비스가 통신하려면 공용 규격 명세서가 필요합니다. AIDL 파일에 상호작용할 메서드를 선언하면 컴파일러가 바인더 프록시(Proxy)와 스텁(Stub) 코드를 알아서 빌드해 줍니다.
// frameworks/base/core/java/com/example/framework/IMyFrameworkService.aidl
package com.example.framework;
interface IMyFrameworkService {
void setValue(int value);
int getValue();
}
2.2 2단계: 서비스 구체화 구현 (MyFrameworkService.java)
AIDL 컴파일러가 만들어준 내부 추상 클래스인 IMyFrameworkService.Stub을 상속받아, 실제 단말기에서 돌아갈 자바 비즈니스 로직을 구현합니다.
// frameworks/base/services/core/java/com/android/server/MyFrameworkService.java
package com.android.server;
import android.os.RemoteException;
import com.example.framework.IMyFrameworkService;
/**
* AIDL Stub을 상속받아 실질적인 바인더 엔드포인트를 구현합니다.
*/
public class MyFrameworkService extends IMyFrameworkService.Stub {
private int mCurrentValue = 0;
@Override
public void setValue(int value) throws RemoteException {
// 내부 정적 멤버 변수나 시스템 파일 제어 로직 수행
this.mCurrentValue = value;
}
@Override
public int getValue() throws RemoteException {
return mCurrentValue;
}
}
2.3 3단계: 시스템 서비스 매니저 등록 (SystemServer.java)
아무리 코드를 잘 짜도 시스템 부팅 시점에 ServiceManager에 등록되지 않으면 유령 코드가 됩니다. AOSP의 심장부인 SystemServer.java에 서비스를 안착시킵니다.
// frameworks/base/services/java/com/android/server/SystemServer.java
import android.os.ServiceManager;
import com.android.server.MyFrameworkService;
public class SystemServer {
// 안드로이드 핵심 서비스들이 초기화되는 메서드 내부 공간
private void startOtherServices() {
try {
// 커스텀 프레임워크 서비스를 인스턴스화하고 전역 컨텍스트에 등록
MyFrameworkService myService = new MyFrameworkService();
ServiceManager.addService("my_framework_service", myService);
} catch (Throwable e) {
Slog.e("SystemServer", "커스텀 프레임워크 서비스 등록 실패!", e);
}
}
}
3. Binder IPC를 통한 서비스 호출 프로세스
서비스 등록을 마쳤다면 외부 앱(클라이언트) 영역에서 바인더 토신을 가동하여 시스템 서비스를 원격 제어할 수 있습니다.
3.1 클라이언트 앱단 구현 예시
package com.example.myapp;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import com.example.framework.IMyFrameworkService;
public class FrameworkServiceClient {
private IMyFrameworkService mService;
public void connectToSystem() {
// 1. ServiceManager에서 등록된 바인더 토큰인 IBinder를 가져옵니다.
IBinder binder = ServiceManager.getService("my_framework_service");
// 2. 가져온 바인더 객체를 클라이언트가 쓸 수 있는 AIDL 인터페이스로 형변환(Marshalling)합니다.
mService = IMyFrameworkService.Stub.asInterface(binder);
}
public void runControl() {
try {
if (mService != null) {
// 3. 프로세스 경계를 넘어 실제 system_server 내부 로직을 직접 호출합니다.
mService.setValue(255);
int response = mService.getValue();
android.util.Log.d("ClientApp", "시스템 서비스 원격 반환 값: " + response);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
🛠️ 개발을 위한 팁 (Tips)
- Context.getSystemService() 매핑으로 편의성 증대: 앱 개발자들이 ServiceManager.getService()를 직접 호출하게 만드는 구조는 다소 투박합니다. 구글 순정 방식처럼 context.getSystemService("my_service") 구조로 우아하게 호출할 수 있도록 만드세요. frameworks/base/core/java/android/app/SystemServiceRegistry.java 파일 내부에 내 커스텀 매니저 클래스와 바인더 스텁을 매핑하는 static 블록을 한 줄 추가해 두면, 상위 앱 개발 환경의 코드 가독성이 극적으로 좋아집니다.
- AIDL 빌드 후 make update-api 수행: 안드로이드 파이프라인 소스 트리 내부에서 새로운 .aidl 파일을 추가하거나 기존 인터페이스 메서드 매개변수를 수정하고 나면 단순 컴파일 시 API 가이드라인 위반 빌드 에러가 터집니다. 당황하지 마시고 터미널 환경에서 make update-api 명령어를 실행하여 프레임워크의 public/current API 텍스트 명세서 파일(current.txt)을 갱신해 주어야 빌드 시스템이 정상적으로 컴파일을 완수합니다.
⚠️ 흔히 하는 실수 (Common Mistakes)
- Binder.clearCallingIdentity() 호출 누락으로 인한 권한 거부(SecurityException): 서드파티 앱이 내 프레임워크 서비스를 호출하고, 내 서비스가 다시 하부 시스템 파일이나 기기 설정값을 수정하는 복합 시나리오에서 자주 발생하는 치명적인 실수입니다. 바인더 통신이 연결되면 커널은 '앱의 권한(UID)'을 그대로 유지한 채 서비스를 실행합니다. 이때 앱에게 허용되지 않은 시스템 자원에 접근하면 권한 에러가 나므로, 서비스 내부 로직 진입 직전 반드시 long token = Binder.clearCallingIdentity();를 호출하여 시스템(system_server) 고유 권한으로 신분을 일시 세탁한 뒤, 로직이 끝나면 Binder.restoreCallingIdentity(token);으로 복구해 주어야 안전합니다.
- 다중 스레드(Thread-Safe) 대책 없는 공유 변수 사용: 프레임워크 서비스의 바인더 메서드들은 싱글 스레드로 순차 실행되지 않습니다. 여러 개의 외부 앱이 동시에 내 서비스의 setValue()나 특정 자원을 요청하면 바인더 스레드 풀(Binder Thread Pool) 내의 서로 다른 스레드들이 동시다발적으로 내부 로직을 덮어씁니다. 서비스 내부 멤버 변수나 파일 객체를 제어할 때는 필히 자바의 synchronized 블록이나 Atomic 계열 변수, ReentrantLock을 걸어두지 않으면 런타임에 데이터가 오염되는 레이스 컨디션(Race Condition) 지옥을 경험하게 됩니다.
4. 결론
안드로이드 Framework Service를 올바르게 아키텍처링하는 것은 단순 기능 구현을 넘어 OS의 계층적 보안 가이드라인과 바인더 통신의 생명 주기를 완벽하게 다룰 줄 알아야 하는 상급 BSP 영역입니다.
AIDL 명세서를 나침반 삼아 단단한 바인더 통신 채널을 뚫고, SystemServer에 내 코드가 안착하여 단말기 앱들과 유기적으로 소통하기 시작하면 단순 앱 개발자와는 차원이 다른 안드로이드 풀 스택 엔지니어로서의 시야를 확보하게 됩니다. 플랫폼 커스텀 빌드를 진행하다가 TransactionTooLargeException 같은 바인더 에러를 만나거나 SELinux 정책 컴파일 도중 의존성이 꼬이는 문제가 생긴다면 언제든 댓글 남겨주세요. 같이 원인을 분석해 봅시다!
'Android System & AOSP Engineering > AOSP Framework & Custom Services' 카테고리의 다른 글
| AOSP 커스텀 서비스 개발: 사용자 정의 Framework Service 설계부터 Android.bp 빌드까지 (0) | 2025.05.24 |
|---|---|
| AOSP 소스 분석: SystemServer의 서비스 초기화 과정과 3대 핵심 서비스 구조 (0) | 2025.05.23 |
| Android AOSP 아키텍처 이해: Framework Service vs System Service 차이점 (0) | 2025.05.20 |
| Android 시스템 최적화 가이드: 프로파일링, 전원 관리, 부팅 속도 개선 기법 (0) | 2025.04.23 |
| Android 커널 디버깅 가이드: dmesg, procfs부터 ftrace, kgdb 활용법 (0) | 2025.04.22 |