Android System & AOSP Engineering/AOSP Framework & Custom Services

안드로이드 프레임워크 구조 분석: AMS, WMS, PMS 핵심 시스템 서비스와 AOSP 소스코드 탐구

임베디드 친구 2025. 4. 7. 09:22
반응형

우리가 매일 스마트폰 화면을 터치해 앱을 실행하고, 창을 전환하며, 음악을 듣는 모든 순간의 뒤편에서는 육안으로 보이지 않는 거대한 소프트웨어 유기체가 쉴 새 없이 움직이고 있습니다. 안드로이드 OS는 단순히 하드웨어를 제어하는 리눅스 커널 위에 자바 가상 머신(ART)을 얹어놓은 단순한 구조가 아닙니다. 그 사이 공간에는 하드웨어의 복잡한 물리 제어 규격을 완전히 추상화하여, 개발자가 단 한 줄의 고수준 API(Java/Kotlin)만 호출해도 디바이스 전체가 완벽하게 통제되도록 중재하는 거대한 통제 센터가 존재합니다.

이 핵심 계층이 바로 Android Framework(안드로이드 프레임워크)입니다. 프레임워크 계층은 시스템 부팅 시점에 SystemServer 프로세스 내부에서 수십 개 이상의 독립된 바인더 서비스 형태로 깨어나 단말의 생명주기와 자원을 독점적으로 관리합니다. 플랫폼 엔지니어링이나 커스텀 OS 개발, 혹은 상용 앱의 극단적인 성능 최적화를 달성하기 위해서는 이 시스템 서버 내부의 중추 서비스들이 구체적으로 어떤 AOSP(Android Open Source Project) 소스코드를 타고 가동되는지 물리적 실체를 알아야 합니다. 본 포스팅에서는 안드로이드 생태계를 지탱하는 대표적인 6대 코어 시스템 서비스의 역할과 핵심 내부 코드를 세밀하게 해부해 보겠습니다.

Generated by Gemini AI.

📌 핵심 요약 3줄

  1. Android Framework는 리눅스 커널 및 하드웨어 추상화 계층(HAL) 위에 상주하며, 애플리케이션 프레임워크와 시스템 코어를 유기적으로 바인딩하는 OS의 중추 신경망입니다.
  2. 시스템의 생명주기를 주관하는 AMS, 화면의 창 배치를 통제하는 WMS, 보안과 앱 설치를 관장하는 PMS 등의 핵심 서비스들이 바인더 IPC를 통해 싱글톤 형태로 소통합니다.
  3. 일반적인 애플리케이션 개발을 넘어 플랫폼을 커스텀하거나 최적화하려면, SystemServer 내부에 상주하는 자바 계층 서비스와 네이티브 오디오 믹싱 구조(AudioFlinger)의 상호작용을 AOSP 소스코드 레벨에서 분석해야 합니다.

1. 안드로이드 시스템 프레임워크 핵심 코어 서비스 매트릭스

안드로이드 단말 내부에서 독립적으로 가동되며 애플리케이션의 요청을 처리하는 중추 시스템 서비스들의 역할 및 상주 영역 비교표입니다.

시스템 서비스 명칭 자바 바인더 등록 식별자 소스코드 상주 영역 도메인 시스템 내부에서의 핵심 통제 역할 및 주권 부서
ActivityManagerService

(AMS)
activity Frameworks Java 레이어 앱 프로세스의 전체 라이프사이클 통제. 액티비티 스택 관리, 태스크 스위칭, OOM 킬러 연동 메모리 정리
WindowManagerService

(WMS)
window Frameworks Java 레이어 화면 픽셀 구역의 레이어(Z-Order) 통제. 창 배치 조정, 다중 윈도우 분할, 터치/키보드 입력 이벤트 라우팅 경로 결정
PackageManagerService

(PMS)
package Frameworks Java 레이어 애플리케이션 무결성 및 권한 통제. APK 패키지 파싱, 앱 설치/삭제, 권한 매트릭스 검증 및 샌드박스 할당
ContentProvider Mechanism (AMS 하부 매니저 통제) Frameworks Java 레이어 프로세스 간 데이터 공유 중재. SQLite 데이터베이스와 연동하여 연락처, 미디어 데이터베이스 보안 조회 및 통제
PhoneInterfaceManager

(Telephony 코어)
phone Telephony 서브시스템 모뎀 및 네트워크 가입자 식별 통제. SIM 카드 정보 래핑, 기지국 신호 모니터링, 통화 다이얼 트래픽 중계
AudioFlinger (네이티브 바인더 등록) Native C++ 레이어 하드웨어 오디오 스트림 믹싱. 여러 앱의 사운드 트랙을 물리 오디오 칩셋 규격에 맞춰 병렬 합산 및 오디오 라우팅

2. AOSP 코드로 분석하는 6대 핵심 서비스의 메커니즘

2.1 ActivityManagerService (AMS)

애플리케이션이 구동되는 최초의 순간부터 소멸할 때까지 모든 생명주기를 관장하는 프레임워크의 심장입니다.

Java
 
// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
// (최신 AOSP 아키텍처에서는 AMS 의 상당수 태스크 로직이 WindowManager 와 통합되어 동기화됩니다.)

void startSpecificActivity(ActivityRecord r, boolean andResume, boolean checkConfig) {
    // 해당 액티비티를 실행할 프로세스가 이미 메모리에 상주해 있는지 검증합니다.
    WindowProcessController wpc = mService.getProcessController(r.processName, r.info.applicationInfo.uid);

    if (wpc != null && wpc.hasThread()) {
        try {
            // 프로세스가 존재하면 즉시 해당 스레드에 액티비티 런칭 명령을 전달합니다.
            realStartActivityLocked(r, wpc, andResume, checkConfig);
            return;
        } catch (RemoteException e) {
            Slog.w(TAG, "대상 프로세스 런칭 실패로 인한 재시도 프로세스 가동", e);
        }
    }

    // 상주 프로세스가 없다면 Zygote 에 바인더를 던져 하부 리눅스 포크(Fork) 프로세스를 새로 생성합니다.
    mService.mH.post(() -> {
        mService.mAm.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                new HostingRecord("activity", r.intent.getComponent()), mService.mAm.getProcessFreezerODH(), false);
    });
}

2.2 WindowManagerService (WMS)

화면 위에 그려지는 모든 사각형의 창(Window) 영역을 쪼개고 레이어를 지정하여 서피스플링거(SurfaceFlinger)로 토스하는 공간의 마술사입니다.

Java
 
// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, ...) {
    synchronized (mGlobalLock) {
        // 이미 파괴된 디스플레이 구역인지 방어 검증 진행
        final DisplayContent displayContent = getDisplayContentOrCreate(attrs.displayId, attrs.token);
        if (displayContent == null) return WindowManagerGlobal.ADD_INVALID_DISPLAY;

        // 시스템 서버 내부에 해당 창의 상태를 대변할 WindowState 객체 캡슐화 생성
        final WindowState win = new WindowState(this, session, client, token, linkSubWindow, attrs, viewVisibility, ...);
        
        // 창의 고유 권한(예: 시스템 경고창 등)을 체크한 뒤 내부 윈도우 리스트에 정렬 삽입
        win.attach();
        mWindowMap.put(client.asBinder(), win);
        
        // 하부 그래픽 메모리 버퍼 레이아웃을 전면 재배치하도록 플래그 갱신
        mWindowPlacerLocked.performSurfacePlacement();
    }
    return WindowManagerGlobal.ADD_OK;
}

2.3 PackageManagerService (PMS)

단말에 탑재된 모든 APK 파일의 내부 구조(AndroidManifest.xml)를 완전히 분해하여 권한 지도를 완성하는 보안관입니다.

Java
 
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

@Deprecated
public void installPackageTraced(String packagePath, IPackageInstallObserver2 observer, int installFlags) {
    // 패키지 설치를 위한 세션을 생성하고 APK 바이너리의 암호화 무결성 서명을 검증합니다.
    final File packageFile = new File(packagePath);
    final ParseResult<ParsingPackage> result = ApkPackageNameParser.parsePackage(packageFile, installFlags);
    
    if (result.isError()) {
        // 무결성 검증 실패 시 시큐리티 예외를 발생시키고 설치 파이프라인을 중단합니다.
        throw new SecurityException("APK 서명 검증 실패 및 변조 패키지 감지: " + result.getErrorMessage());
    }
    
    // 검증이 완료된 바이너리를 /data/app/ 고유 데이터 샌드박스 영역으로 이송 및 등기 등록
    executeInstallPackage(result.getResult(), installFlags);
}

2.4 ContentProvider 아키텍처 연동 매커니즘

앱 간의 대형 데이터베이스 통신 트래픽을 중재하며, 무분별한 데이터 유출을 방어하기 위해 바인더 프록시를 바인딩해 줍니다.

Java
 
// frameworks/base/core/java/android/content/ContentProvider.java

public Cursor query(Uri uri, String[] projection, Bundle queryArgs, CancellationSignal cancellationSignal) {
    // 하부 스토리지에 격리 보관된 SQLite 데이터베이스 인스턴스를 소환합니다.
    SQLiteDatabase db = mOpenHelper.getReadableDatabase();
    
    // 들어온 URI 경로의 접근 권한이 유효한지 커널 컨텍스트 교차 검증
    validateIncomingUri(uri);
    
    // 바인더 파이프라인을 타고 타 프로세스로 넘어갈 공유 커서(CursorWindow) 메모리 버퍼 생성 및 데이터 덤프
    return db.query(mTableName, projection, queryArgs);
}

2.5 Telephony 서브시스템 (PhoneInterfaceManager)

기지국 모뎀 하드웨어와의 직결 바인더 인터페이스를 통해 통화 및 셀룰러 네트워크 파이프라인을 제어합니다.

Java
 
// packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java

@Override
public void dialForSubscriber(int subId, String callingPackage, String number, Bundle extras) {
    // 호출한 애플리케이션이 실제 전화를 걸 수 있는 하드웨어 사용 권한(CALL_PHONE)을 가졌는지 시큐리티 체크
    enforceCallPermission();

    // 원시 다이얼 텍스트 문자열을 텔레포니 고유 폰 객체 규격으로 포맷팅
    Phone phone = getPhone(subId);
    if (phone != null) {
        // 하부 라디오 인터페이스 레이어(RIL) 네이티브 가속 드라이버로 모뎀 명령 하향 전송
        phone.dial(number, new DialArgs.Builder().build());
    }
}

2.6 AudioFlinger (Native C++ 코어)

자바 계층의 명령을 받아 실제 리눅스 커널 커널 공간의 오디오 ALSA 드라이버로 소리를 합산하여 주입하는 로우 레벨 믹싱 엔진입니다.

C++
 
// frameworks/av/services/audioflinger/AudioFlinger.cpp

void AudioFlinger::PlaybackThread::threadLoop() {
    // 오디오 하드웨어 버퍼가 비어 가용 상태가 될 때까지 무한 루프 가동
    while (!exitPending()) {
        // 현재 활성화된 다양한 앱들의 개별 사운드 트랙 버퍼 메모리를 전수 스캔
        processVolume_l();
        
        // [핵심 매커니즘] 소프트웨어 오디오 믹서 엔진을 가동하여 개별 펄스 부호 변조(PCM) 데이터를 하나로 병렬 합산
        mAudioMixer->process();
        
        // 합산이 완료된 믹싱 데이터 버퍼를 하부 HAL 드라이버의 쓰기 스트림으로 다이렉트 푸시
        mOutput->stream->write(mSinkBuffer, mBufferSize);
    }
}

3. 프레임워크 핵심 서비스 간 자원 요청 흐름 및 시퀀스

사용자가 화면에서 새로운 앱의 아이콘을 터치했을 때, 시스템 프레임워크 3대장 서비스(AMS, PMS, WMS)가 바인더 드라이버를 조율하며 협력하는 라이프사이클 흐름입니다.

  1. 런처 앱의 시작 요청: 사용자가 아이콘을 탭하면 런처 앱이 ActivityManagerService로 바인더 통신을 던져 앱 가동을 요청합니다.
  2. PMS의 권한 및 경로 검증: 요청을 접수한 AMS는 즉시 PackageManagerService를 호출하여 해당 앱이 안전하게 설치되어 있는지, 실행 권한이 적법한지 디렉터리를 역추적하여 검증합니다.
  3. 프로세스 포크 및 창 생성 요청: 검증이 끝나면 AMS는 Zygote 프로세스를 통해 가상 머신 앱 프로세스를 생성하고, 동시에 WindowManagerService에 타깃 앱이 상주할 윈도우 스택 버퍼 공간 할당을 지시합니다.
  4. 그래픽 컨텍스트 렌더링 시작: WMS가 생성된 창의 Z-Order 서피스 주소 값을 확정 지어 반환하면, 비로소 앱의 메인 스레드가 깨어나 사용자 화면에 픽셀 데이터를 렌더링하기 시작합니다.

💡 안드로이드 프레임워크 최적화를 위한 실전 팁

  1. 시스템 서비스 튜닝 시 LocalServices를 활용한 프로세스 내부 직접 호출 최적화: AOSP 소스코드 내부에서 신규 프레임워크 서비스를 구현하거나 기존 코드를 수정할 때, 무심코 모든 컴포넌트 간 통신을 ServiceManager.getService() 기반의 바인더 인터페이스로 처리하는 실수를 범하곤 합니다. 같은 SystemServer 프로세스 영역 내부에서 구동되는 자바 서비스끼리 통신할 때는 무거운 바인더 IPC 트랜잭션을 일으킬 필요가 전혀 없습니다. 프레임워크 내부 커스텀 시에는 구글이 제공하는 인프로세스 싱글톤 관리자인 LocalServices.getService(ActivityManagerInternal.class) 인터페이스를 사용해 보세요. 바인더 드라이버의 직렬화/역직렬화 오버헤드를 100% 우회하여, 단순 자바 객체 포인터 참조 속도로 내부 코어 메서드를 다이렉트 스위칭할 수 있습니다.
  2. dumpsys 명령어를 활용한 실시간 시스템 서비스 내부 덤프 상태 모니터링: 커스텀 프레임워크 코드가 런타임 환경에서 시스템 자원을 얼마나 소모하고 있는지 확인하기 위해 로그캣에 무수히 많은 로깅 코드를 심는 행위는 프레임워크 자체의 가동 속도를 떨어뜨립니다. 이럴 때는 안드로이드 프레임워크 서비스의 표준 디버그 훅인 dump() 메서드를 적극 활용해 보세요. 서비스 내부에 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) 구조를 오버라이딩해 두면, 개발 터미널 창에서 adb shell dumpsys activity 혹은 adb shell dumpsys window 명령어를 입력하는 순간, 가동 중인 시스템 서비스의 내부 컬렉션 변수와 힙 메모리 상태 구조가 텍스트 파일 형식으로 실시간 가시화되어 덤프됩니다.

⚠️ 흔히 하는 실수

  1. SystemServer 초기화 루프 블로킹으로 인한 전체 OS 부팅 데드락(Deadlock) 유발: AOSP 컴포넌트를 직접 제작하는 초보 임베디드 개발자들이 가장 많이 범하는 치명적인 아키텍처 오류입니다. SystemServer.java 스크립트 빌드 과정에 내가 만든 서비스를 추가하면서, 서비스의 생성자 함수나 초기화(onStart()) 구문 내부에 대용량 파일 동기식 파일 읽기나 외부 네트워크 응답 대기 등의 지연 코드를 그대로 심어버리는 경우입니다. 안드로이드 프레임워크의 부팅 파이프라인은 단일 메인 스레드 유기체처럼 순차 구동되므로, 하나의 커스텀 서비스가 초기화 단계에서 런타임 락(Lock)을 잡고 늘어지면 뒤이어 실행되어야 할 AMS, WMS 등 국가 대표급 코어 서비스들이 동달아 깨어나지 못해 단말기가 무한 부팅 애니메이션만 출력하다가 멈춰버리는 부팅 데드락 시나리오로 이어집니다. 무조건 시간이 걸리는 서브 로직은 비동기 헨들러(mH.post()) 스레드로 격리하여 실행해야 합니다.
  2. 네이티브 오디오 및 그래픽 공유 버퍼 미해제에 따른 커널 레벨의 영구적 메모리 고갈(Leaked Pages): AudioFlinger나 하부 HAL 도메인과 링킹되는 C++ 프레임워크 코어를 만질 때 고질적으로 터지는 리소스 관리 실패 사례입니다. 자바 프레임워크 객체는 가비지 컬렉터(GC)가 주기적으로 청소해 주지만, JNI 장벽 아래 네이티브 영역의 공유 오디오 트랙 메모리 버퍼나 하드웨어 연동 구조체 힙 영역은 수동으로 클리어 루틴을 타지 않으면 리눅스 커널 페이지 공간에 영구 누적됩니다. 특히 시스템 서비스는 폰이 꺼질 때까지 계속 상주하는 '롱 러닝 프로세스'이므로, 이곳에서 유발된 단 몇 바이트의 네이티브 누수가 시간이 흐르면 시스템 가용 RAM 전체를 갉아먹어 결국 단말기 전체가 엄청난 버벅임(Throttling)에 시달리다가 강제 커널 패닉 리셋으로 치닫게 만듭니다.

4. 결론

Android Framework 계층은 모바일 운영체제의 가상화 기술과 원시 하드웨어 제어권이 완벽한 하모니를 이루며 맞물려 돌아가는 소프트웨어 공학의 걸작입니다. 단순히 구글이 제공하는 SDK API를 가져다 쓰는 수준을 넘어, AMS의 스레드 포크 매커니즘, WMS의 서피스 배치 규칙, 그리고 네이티브 오디오 엔진의 합산 루프 구조를 AOSP 소스코드 레벨에서 정밀하게 추적하고 제어할 수 있을 때, 비로소 플랫폼의 한계를 뛰어넘어 완벽한 최적화를 이루어내는 고결한 마스터 엔지니어로 거듭날 수 있습니다.

반응형