Android System & AOSP Engineering/AOSP Framework & Custom Services

안드로이드 WMS 구조 분석: WindowManagerService의 윈도우 관리와 AOSP 레이어링 메커니즘

임베디드 친구 2025. 4. 9. 20:58
반응형

스마트폰 화면을 가만히 들여다보면 흥미로운 현상을 발견할 수 있습니다. 애플리케이션 화면 위에 알림 팝업 창이 뜨고, 그 위로 시스템 볼륨 조절 바가 겹치며, 최상단에는 항상 상태 바(Status Bar)가 흔들림 없이 자리를 지키고 있습니다. 이 무수히 많은 사각형 구역(Window)들은 서로의 영역을 침범하지 않고 어떻게 완벽한 앞뒤 순서(Z-Order)를 유지하며 터치 이벤트를 독점하거나 나누어 가질 수 있는 것일까요?

이 완벽한 공간 제어와 그래픽 버퍼 조율을 담당하는 심장부가 바로 WindowManagerService(WMS)입니다. WMS는 안드로이드 system_server 내부에서 가동되며, 애플리케이션의 뷰(View) 트리 구조가 물리 디스플레이 화면에 안착할 수 있도록 자리를 배치해 주는 디스플레이 공간의 총사령관입니다. 화면 회전 제어부터 로우 레벨 터치 입력 파이프라인 매핑, 그리고 하부 그래픽 합성 엔진인 서피스플링거(SurfaceFlinger)와의 직결 통신까지 담당하는 WMS의 본질을 이해해야만 진정한 플랫폼 엔지니어로 도약할 수 있습니다. 본 포스팅에서는 WMS의 핵심 아키텍처와 필수 컴포넌트, 그리고 최신 AOSP 소스코드 기반의 윈도우 추가 메커니즘을 상세히 해부해 보겠습니다.

Generated by Gemini AI.

📌 핵심 요약 3줄

  1. **WindowManagerService(WMS)**는 단말기 화면에 그려지는 모든 사각형 창(Window)의 배치, 크기, 애니메이션, Z-Order 및 터치 입력 분배를 총괄하는 그래픽스 제어 센터입니다.
  2. 내부적으로 DisplayContent, WindowState, SurfaceControl 등의 핵심 컴포넌트 체인을 유지하며, 자바 계층의 뷰 요청을 네이티브 영역의 그래픽 레이어로 가교 역할을 합니다.
  3. 애플리케이션 레이어의 WindowManager.addView() 호출은 바인더 IPC 장벽을 넘어가 WMS 내부에서 고유한 BaseLayer 가중치 기반 연산을 거쳐 물리적인 화면 우선순위를 확정 짓습니다.

1. WMS를 구성하는 5대 핵심 아키텍처 컴포넌트

WMS 아키텍처가 단말기 전역의 화면 상태와 그래픽 제어권을 유지하기 위해 유기적으로 맞물려 가동하는 5대 핵심 구성 요소 명세표입니다.

구성 요소 명칭 최신 AOSP 소스코드 경로 시스템 프레임워크 내부에서의 핵심 제어 역할
WindowManagerService com.android.server.wm.WindowManagerService 윈도우 관리 서브시스템의 총괄 컨트롤러. 시스템 서버 부팅 시점에 바인더 인스턴스로 등록되며 WMS 전체 기능 지휘
DisplayContent com.android.server.wm.DisplayContent 독립된 물리/가상 디스플레이 공간 관리자. 멀티 디스플레이 환경에서 각 디스플레이 ID에 종속된 윈도우 스택 리스트를 격리 관리
WindowState com.android.server.wm.WindowState 단일 윈도우 창의 가상 신분증. 클라이언트 앱 프로세스의 단일 창 속성, 바인더 세션 정보, 가시성 상태 및 크기 메타데이터 관리
InputMonitor com.android.server.wm.InputMonitor 터치 및 키 이벤트 라우터의 내비게이션. 현재 화면 상에서 포커스를 가진 윈도우의 구역 레이아웃을 계산하여 인풋 매니저에 좌표 경로 갱신
SurfaceControl android.view.SurfaceControl 하부 그래픽 메모리 버퍼 레이어 인터페이스. 윈도우의 실질적인 픽셀 버퍼 공간을 서피스플링거(SurfaceFlinger) 커널 메모리와 매핑하는 통로

2. AOSP 코드로 분석하는 WMS 초기화 및 가동 시퀀스

WMS는 시스템 부팅 단계 중 SystemServer 가동 스크립트 내에서 하드웨어 인풋 시스템 및 디스플레이 매니저 서브시스템과 동시에 결합하며 동기식으로 생성됩니다.

Java
 
// frameworks/base/services/java/com/android/server/SystemServer.java

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
    t.traceBegin("StartWindowManagerService");
    
    // [AOSP 표준 생성 규칙] 인풋매니저와 디스플레이매니저 인스턴스를 동시 주입하며 
    // WMS 싱글톤 엔진을 메인 컨텍스트 위에 가동합니다.
    WindowManagerService wm = WindowManagerService.main(context, inputManager,
            ims, displayManager);
            
    // 시스템 서비스 매니저 전역 맵에 "window" 라는 바인더 문자열 키로 서비스 등기 등록
    ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
            DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
            
    t.traceEnd();
}

생성자 직후 호출되는 main() 팩토리 메서드 내부에서는 WMS 가 내부 핸들러 스레드를 안전하게 확보할 수 있도록 런타임 초기화를 완료합니다.

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

public static WindowManagerService main(final Context context, final InputManagerService ims,
        final InputManagerCallback imsCallback, final DisplayManagerService displayManager) {
    // 디스플레이 전용 런타임 스레드 컨텍스트 컨테이너 내에서 WMS 인스턴스를 안전하게 빌드합니다.
    return DisplayThread.getHandler().runWithScissors(() -> {
        return new WindowManagerService(context, ims, imsCallback, displayManager);
    }, 0);
}

3. 앱 윈도우 추가(addView) 요청의 엔드투엔드 파이프라인

애플리케이션 개발자가 화면에 새로운 UI나 커스텀 오버레이 뷰를 올리기 위해 고수준 API를 호출했을 때, WMS 바인더 채널을 거쳐 하부 커널 구조로 이송되는 과정의 전치 소스코드 파이프라인입니다.

3.1 윈도우 매니저 임플리멘테이션 계층

Java
 
// frameworks/base/core/java/android/view/WindowManagerImpl.java

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    // 앱 프로세스 전역의 글로벌 매니저 인스턴스로 처리를 위임합니다.
    mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow, mContext.getUserId());
}

3.2 윈도우 매니저 글로벌 계층 및 ViewRootImpl 결합

Java
 
// frameworks/base/core/java/android/view/WindowManagerGlobal.java

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow, int userId) {
    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
    
    // [핵심 메커니즘] 해당 뷰 트리 구조의 중심 스레드가 될 ViewRootImpl 인스턴스를 최초로 가동합니다.
    ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
    view.setLayoutParams(wparams);
    
    // ViewRootImpl 내부로 진입하여 WMS 로의 바인더 링킹 세션을 준비시킵니다.
    root.setView(view, wparams, panelParentView, userId);
}

3.3 ViewRootImpl 세션 바인더 호출 계층

Java
 
// frameworks/base/core/java/android/view/ViewRootImpl.java

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {
    synchronized (this) {
        // mWindowSession 이라는 원격 IPC 인터페이스를 소환하여 시스템 서버의 WMS 채널로 진입합니다.
        res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                getHostVisibility(), mDisplay.getDisplayId(), userId,
                mInsetsController.getRequestedVisibleTypes(), mInputChannel, mInsetsState, mActiveControls);
    }
}

3.4 시스템 서버 내부 WMS 트래픽 접수 및 처리 계층

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

public int addWindow(Session session, IWindow client, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, ...) {
    synchronized (mGlobalLock) {
        // 들어온 디스플레이 ID에 해당하는 고유 공간 컨텍스트를 확보합니다.
        final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
        
        // 시스템 서버 내부에 이 창의 물리적 주권을 영구 대변할 WindowState 메모리 객체를 생성합니다.
        final WindowState win = new WindowState(this, session, client, token, linkSubWindow, attrs, viewVisibility, ...);
        
        // 해당 디스플레이 콘텐츠의 윈도우 트리 리스트에 안전하게 등록 처리합니다.
        displayContent.addWindow(win);
        
        // 서피스플링거와 메모리 버퍼 배치를 동기화하기 위한 윈도우 레이아웃 전면 재배치 명령 하달
        mWindowPlacerLocked.performSurfacePlacement();
    }
    return WindowManagerGlobal.ADD_OK;
}

4. Z-Order 결정 메커니즘과 Policy 내부 가중치 스펙

WMS는 여러 윈도우가 화면 위에 겹칠 때 고유한 가중치(Layer) 연산을 수행합니다. AOSP 소스코드 내부에서 윈도우의 앞뒤 정렬 순서를 최종 픽스하는 핵심 공식과 물리 스펙 매트릭스입니다.

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

int getLayer() {
    // 윈도우 고유의 베이스 레이어 값에 서브 윈도우 가중치(팝업 등)를 합산하여 
    // 서피스플링거가 인식할 최종 레이어 인덱스를 추출해 냅니다.
    return mBaseLayer + mSubLayer;
}

최신 안드로이드 AOSP 표준 프레임워크 기준의 가중치 변환 맵 구조는 다음과 같이 안전하게 계층화되어 운영됩니다.

윈도우 타입 명세 (Window Type) AOSP 내부 가중치 상수 (Base Layer Weight) 화면 상의 실제 적재 및 시각적 노출 순서 (Z-Order)
TYPE_BASE_APPLICATION 2 가장 하단의 기본 터전. 일반적인 서드파티 앱들의 메인 액티비티 화면 레이어
TYPE_APPLICATION_PANEL 3 앱 내부의 종속 레이어. 액티비티 위에 종속되어 생성되는 기본 다이얼로그 및 컨텍스트 메뉴 창
TYPE_SYSTEM_DIALOG 15 시스템 전역 경고. 시스템 내부 알림, 배터리 부족 경고, 사용자 인증 팝업 등의 배치 영역
TYPE_STATUS_BAR 19 상단의 부동 레이어. 단말 최상단에 상시 고정되어 배터리 및 네트워크 상태를 보여주는 구역
TYPE_NAVIGATION_BAR 20 하단의 최우선 통제 레이어. 제스처 핸들이나 소프트웨어 소프트 키가 상주하는 영역으로, 웬만한 앱 윈도우에 절대 가려지지 않음
TYPE_POINTER 24 (최상위 가중치) 마우스 커서 및 터치 포인터 픽셀 영역. 사용자 입력 피드백 레이어로 디스플레이 전체 구역의 최고 전면에 고정 배치

5. 앱 실행에 따른 WMS 내부 데이터 갱신 시퀀스

애플리케이션 레이어부터 WMS를 거쳐 시스템 하부 그래픽 엔진까지 연결되는 런타임 서피스 매핑 흐름입니다.

  1. 윈도우 할당 요청: 애플리케이션의 ViewRootImpl이 바인더 채널을 타고 WMS의 addWindow() 원격 인터페이스를 노킹합니다.
  2. 윈도우 자격 검증 및 상태 캡슐화: WMS가 전달받은 인텐트와 파라미터를 기반으로 전용 WindowState 인스턴스를 구조화하고, 타깃 DisplayContent 리스트 영역에 정렬 주입합니다.
  3. 그래픽 컨텍스트 샌드박스 빌드: 자리가 확정되면 WMS는 SurfaceControl API를 호출하여 해당 윈도우가 가질 독점적인 그래픽 메모리 캔버스 공간 생성을 처리합니다.
  4. 하부 하드웨어 레이어 합성: 생성된 서피스 토큰 제어권이 최종적으로 SurfaceFlinger 네이티브 드라이버 계층으로 하향 전달되며, 디스플레이 하드웨어 하향 주입을 위한 픽셀 레이어 믹싱 공정이 실시간으로 가동됩니다.

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

  1. WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED 속성의 명시적 튜닝을 통한 프레임 드랍 방지: 프레임워크 내부에 시스템 커스텀 윈도우 오버레이 뷰를 수동으로 삽입할 때, 무심코 기본 플래그 배열만 주입하면 하드웨어 GPU 가속 혜택을 받지 못하고 CPU 소프트웨어 렌더링 파이프라인으로 강제 후퇴할 수 있습니다. 시스템 UI를 커스텀 빌드할 때는 윈도우 특성 파라미터에 반드시 해당 가속 플래그 속성을 수동으로 비트 연산(|=) 합산해 주세요. WMS 가 이 플래그를 감지해야만 SurfaceControl 생성 시 하부 하드웨어의 GPU 오픈GLES/Vulkan 하드웨어 가속 링킹 파이프라인을 다이렉트로 매핑해 주므로 대형 애니메이션 구동 시 프레임 드랍(Jank) 현상을 완벽하게 방어할 수 있습니다.
  2. adb shell dumpsys window windows 명령어를 활용한 실시간 Z-Order 적재 리스트 추적: 화면에 커스텀 레이아웃이나 시스템 경고창을 추가했는데 다른 시스템 UI에 가려 보이지 않거나 터치 이벤트가 먹통이 되는 원인을 규명하고 싶다면 창 상태 추적 명령어를 사용해 보세요. 단말기 터미널 창에 해당 명령을 입력하면 현재 가동 중인 모든 디스플레이 영역(DisplayContent)의 탑-다운 Z-Order 정렬 덤프 리스트는 물론, 개별 WindowState가 가지고 있는 mHasSurface 플래그 유무와 정확한 픽셀 공간 좌표 매트릭스(mFrame) 값을 실시간 가시화하여 가치 판단을 내릴 수 있습니다.

⚠️ 흔히 하는 실수

  1. performSurfacePlacement() 강제 루프 유발에 따른 시스템 서버의 극심한 CPU 스래싱(Thrashing) 발생: WMS 코드를 내부적으로 핸들링하는 주니어 플랫폼 엔지니어들이 화면 갱신을 확실하게 처리하겠다는 명목으로, 커스텀 이벤트 콜백 구문 내부에 레이아웃 재계산 명령어인 mWindowPlacerLocked.performSurfacePlacement() 로직을 무조건 동기식으로 무차별 연사하는 경우입니다. 이 레이아웃 재배치 메서드는 디스플레이 콘텐츠 내부의 전체 윈도우 좌표와 Z-Order 레이어를 전수 스캔하여 연산하는 무거운 작업입니다. 이를 이벤트 루프 제어 없이 연사하면 WMS 스레드 풀 전체의 CPU 점유율이 폭등하면서, 뒤이어 가동되어야 할 터치 입력 이벤트 분배(InputMonitor) 파이프라인까지 마비되어 단말기 터치 반응 속도가 수 초 이상 먹통이 되는 치명적인 런타임 지연을 유발합니다.
  2. 원격 앱 프로세스 비정상 종료 시 바인더 링킹 세션(Session.java)의 고스트 자원 방치: 애플리케이션 측에서 WindowManager.removeViewImmediate() 리소스 해제 절차를 정상적으로 밟지 않은 채, 강제 크래시나 커널 OOM 등으로 프로세스가 갑자기 소멸해 버릴 때 자주 발생하는 프레임워크 누수 예외입니다. WMS 측 내부 맵 구조에서 해당 앱 클라이언트 바인더의 IWindow 세션 토큰을 명시적으로 정리(windowDeletedLocked)해 주지 않고 방치하면, 화면에는 보이지 않지만 메모리 상에는 유효한 것으로 처리되는 좀비 WindowState 객체가 시스템 서버 내에 영구 고착됩니다. 이 누수 자원들은 사용자가 폰을 재부팅하기 전까지 WMS 전역 스캔 루프 돌 때마다 불필요하게 가동되어 프레임워크 자원을 지속적으로 잠식합니다.

6. 결론

안드로이드의 윈도우 관리 컨트롤러인 WindowManagerService는 고수준 자바 프레임워크 설계와 로우 레벨 임베디드 그래픽스 파이프라인이 교차하는 OS 아키텍처의 정수입니다. ViewRootImpl에서 건너오는 원격 바인더 요청 트래픽의 내부 이동 경로를 정확히 간파하고, PhoneWindowManager 가중치 정책 규칙에 근거한 Z-Order 연산 메커니즘을 AOSP 코드로 통제할 수 있을 때, 비로소 화면의 지연 시간을 완벽히 정복하고 극한의 모바일 멀티 디스플레이 가상화 기술을 완벽히 제어해 낼 수 있습니다.

반응형