Android System & AOSP Engineering/AOSP Framework & Custom Services

안드로이드 PMS 구조 분석: PowerManagerService의 WakeLock 제어와 배터리 도즈 모드 메커니즘

임베디드 친구 2025. 4. 11. 11:06
반응형

스마트폰을 주머니에 넣어두고 오랜 시간 사용하지 않으면 화면이 꺼지고 기기는 깊은 잠(Suspend)에 빠져듭니다. 하지만 그 순간에도 백그라운드에서는 음악이 끊김 없이 재생되고, 중요한 푸시 알림이 실시간으로 도착합니다. 제한된 배터리 용량 안에서 하드웨어 부품들의 전력을 차단하면서도, 특정 앱의 필수 작업은 중단 없이 이어가도록 조율하는 이 고도의 전력 균형 감각은 어떻게 유지되는 것일까요?

이 정밀한 전원 상태 머신과 리눅스 커널 저전력 파이프라인을 총괄하는 컨트롤 타워가 바로 PowerManagerService(이하 PMS)입니다. PMS는 안드로이드 system_server 프로세스 레이어의 중심에서 디스플레이 픽셀 백라이트, CPU 가동 클럭, 하부 시스템 서스펜드 데몬(system_suspend)의 동작 주권을 쥐고 흔드는 핵심 서비스입니다. 저전력 하드웨어를 튜닝하는 벤더 엔지니어는 물론, 배터리 소모량 최적화로 구글 플레이 스토어의 경고를 회피해야 하는 앱 개발자 모두에게 PMS 내부의 웨이크록(WakeLock) 관리 구조 이해는 필수적입니다. 본 포스팅에서는 최신 AOSP 마스터 소스코드를 바탕으로 PMS의 아키텍처 컴포넌트와 전력 상태 전환 메커니즘의 실체를 명쾌하게 해부해 보겠습니다.

Generated by Gemini AI.

📌 핵심 요약 3줄

  1. **PowerManagerService(PMS)**는 디바이스의 전원 상태 전환, 디스플레이 백라이트 가도, 앱의 전력 유지 요청(WakeLock)을 총괄 제어하는 전력 관리 사령탑입니다.
  2. 자바 레이어의 IPowerManager 바인더 인터페이스와 네이티브 영역의 SystemSuspend 가교 데몬을 결합하여 리눅스 커널의 커스텀 파워 드라이버를 직접 통제합니다.
  3. 시스템 전원 차단 시 단일 변수를 직접 수정하지 않고, mDirty 비트마스크 플래그 갱신 및 updatePowerStateLocked() 상태 머신 루프를 가동하여 하드웨어를 안전하게 절전 모드로 진입시킵니다.

1. PMS의 중심을 이루는 5대 핵심 파워 컴포넌트

PMS가 단말기 전역의 전원 런타임 토폴로지와 하드웨어 전력 등급을 유지하기 위해 연동하는 5대 핵심 아키텍처 구조입니다.

컴포넌트 명칭 최신 AOSP 소스코드 상주 경로 전력 관리 서브시스템 내부에서의 핵심 역할
PowerManagerService com.android.server.power.PowerManagerService 전력 관리 서브시스템의 메인 컨트롤러. 전원 상태 머신을 운영하며 웨이크록 리스트 및 시스템 배터리 정책 총괄 지휘
PowerManager android.os.PowerManager 공개 API 엔드포인트 게이트웨이. 애플리케이션 프로세스가 바인더 통신을 통해 시스템 파워 제어권을 간접 요청하는 가교
SuspendControlService com.android.server.power.SuspendControlService 네이티브 서스펜드 통로. C++ 레이어의 저전력 제어 드라이버와 통신하여 커널이 완전 절전 모드로 진입할 수 있도록 중재
Notifier com.android.server.power.Notifier 전원 상태 변화 브로드캐스터. 화면이 켜지거나 꺼질 때 즉각 AMS, 인풋매니저, 정책 엔진에 이벤트를 송출하여 동기화 유도
DisplayPowerController com.android.server.display.DisplayPowerController 디스플레이 파워 조율사. PMS의 상태 갱신 요청을 수신하여 실제 액정 백라이트 밝기 조정 및 픽셀 전원 차단 시퀀스 집행

2. WakeLock 유형별 하드웨어 제어 범위 및 내부 락 구조

애플리케이션이나 시스템 컴포넌트가 CPU와 화면을 강제로 깨워두기 위해 가동하는 웨이크록의 유형별 물리 스펙 매트릭스입니다.

웨이크록 레벨 상수 (WakeLock Level) CPU 가동 여부 (CPU State) 화면 백라이트 상태 (Display State) 실제 활용되는 주요 시스템 시나리오
PARTIAL_WAKE_LOCK 정상 가동 (ON) 완전히 꺼짐 (OFF) 백그라운드 음악 재생, 파일 다운로드, GPS 위치 추적 및 데이터 동기화
SCREEN_DIM_WAKE_LOCK 정상 가동 (ON) 어두운 상태 유지 (DIM) 레거시 앱 내부의 읽기 모드 지원 시 화면 꺼짐 방지 용도
SCREEN_BRIGHT_WAKE_LOCK 정상 가동 (ON) 최고 밝기 유지 (BRIGHT) 모바일 게임 플레이, 동영상 스트리밍 플레이어 구동 시 화면 유지
PROXIMITY_SCREEN_OFF_WAKE_LOCK 근접 센서 연동 제어 센서 가림 시 꺼짐 (OFF) 음성 통화 진행 시 귀가 화면에 닿으면 액정을 꺼서 오터치를 방지하는 보안 구역

3. AOSP 코드로 파헤치는 PMS 핵심 동작 매커니즘

3.1 부트스트랩 엔진 가동 및 서비스 레지스트리 등록

PMS는 시스템 서버가 점화되는 Bootstrap 초기 레이어에서 가장 먼저 인스턴스화되어 바인더 원격 채널에 등기됩니다.

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

private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
    t.traceBegin("StartPowerManagerService");
    
    // [AOSP 표준 생성 규칙] 시스템 내부 컨텍스트 위에 파워 매니저 서비스 객체를 라이프사이클에 태웁니다.
    mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
    
    t.traceEnd();
}

실제 PMS 서비스 바인딩 구문은 자신의 이너 바인더 객체(mBinder)를 커널 바인더 레지스트리에 주입하여 외부 IPC 호출을 수용합니다.

Java
 
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

@Override
public void onStart() {
    // 외부 앱 프로세스가 Context.POWER_SERVICE 키워드로 원격 바인더 프록시를 획득할 수 있도록 바인딩 처리
    publishBinderService(Context.POWER_SERVICE, mBinder);
    
    // 시스템 서버 내부 전용 로컬 고속 인터페이스 엔진 등록
    publishLocalService(PowerManagerInternal.class, new LocalService());
}

3.2 WakeLock 등록 및 리스트 적재 연산 구조

애플리케이션이 acquire()를 외치며 하드웨어 전력 유지를 청구할 때, PMS 내부 힙 메모리 테이블에 락 토큰을 적재하는 핵심 내부 공정입니다.

Java
 
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

private void acquireWakeLockInternal(IBinder lock, int flags, String tag, WorkSource ws,
        String packageName, int uid, int pid) {
    synchronized (mLock) {
        // 1. 전달받은 고유 바인더 토큰 주소를 기반으로 기존에 등록된 락인지 역추적
        int index = findWakeLockIndexLocked(lock);
        WakeLock wakeLock;
        
        if (index >= 0) {
            wakeLock = mWakeLocks.get(index);
            // 이미 존재하는 락이라면 속성 값을 최신 정보로 업데이트
            wakeLock.updateProperties(flags, tag, packageName, ws, uid, pid);
        } else {
            // 2. 신규 요청인 경우 새로운 내부 WakeLock 인스턴스를 캡슐화하여 생성
            wakeLock = new WakeLock(lock, flags, tag, packageName, ws, uid, pid);
            
            // 3. 전력 통제 테이블인 mWakeLocks 리스트 리지스트리에 영구 추가
            mWakeLocks.add(wakeLock);
        }
        
        // 4. [핵심 플래그 세팅] 웨이크록 상태가 변경되었음을 파워 매니저 상태 비트에 선언
        setDirtyLocked(DIRTY_WAKE_LOCKS);
        
        // 5. 실시간 전원 상태 머신 루프를 가동하여 CPU를 강제 기동 상태로 유지
        updatePowerStateLocked();
    }
}

3.3 비트마스크 기반 전원 상태 머신 루프와 절전 제어

화면을 끄고 시스템 서스펜드 상태로 하향 진입하는 goToSleep() 시퀀스는 단일 상태 변수를 직접 바꾸지 않고, 비트마스크 플래그 변경 후 중앙 집중식 전원 갱신 루프를 구동하는 정밀 아키텍처를 따릅니다.

Java
 
// frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java

public void goToSleepInternal(long eventTime, int reason, int flags, int uid) {
    synchronized (mLock) {
        // 화면 진입 하향 정책 마일스톤 변수 변경 및 슬립 타임스탬프 기록
        mLastSleepTime = eventTime;
        mSandmanSummoned = true; // 절전 모드 전환 유도 플래그 활성화
        
        // 전원 등급 및 화면 밝기 상태 엔진을 전면 리셋해야 함을 선언하는 비트마스크 주입
        setDirtyLocked(DIRTY_USER_ACTIVITY | DIRTY_BOOT_COMPLETED);
        
        // [중앙 파워 갱신 메인 루프 호출]
        updatePowerStateLocked();
    }
}

// PMS 의 모든 전력 제어 연산 결과가 최종 수렴되는 거대 중앙 집중식 상태 머신
private void updatePowerStateLocked() {
    if (!mSystemReady || mRequestedTransformations == 0) return;
    
    // 1. 현재 mWakeLocks 테이블에 적재된 모든 락의 전력 등급 가중치 전수 연산
    updateIsPoweredLocked(mDirty);
    updateStayOnSettingLocked(mDirty);
    
    // 2. 스크린 표시 상태 머신 정책 결정 (DisplayPowerController 로 전달할 요청서 래핑)
    boolean displayChanged = updateDisplayPowerStateLocked(mDirty);
    
    // 3. [최상위 핵심 리눅스 서스펜드 중재] 더 이상 살아있는 웨이크록이 없고, 화면이 완벽히 꺼졌다면
    if (mWakeLockSummary == 0 && mDisplayReady && !mSandmanSummoned) {
        // 네이티브 SuspendControl 데몬에게 가동 승인 신호를 날려 리눅스 커널을 완전 Suspend(절전) 로 탈바꿈시킵니다.
        mNativeWrapper.nativeSendPowerHint(POWER_HINT_LOW_POWER, 1);
    }
    
    // 연산이 완료된 처리 비트마스크 플래그 청소
    mDirty = 0;
}

4. 안드로이드 시스템 전역 3대 저전력 규제 정책

PMS는 단순히 웨이크록 관리만 하지 않고, 운영체제 레이어의 상위 배터리 관리 모듈과 협력하여 3대 저전력 제약 시스템을 엄격히 집행합니다.

저전력 정책 명칭 프레임워크 내부 가동 타이밍 및 발동 조건 애플리케이션 및 하드웨어 자원에 가해지는 물리 제약 스펙
도즈 모드 (Doze Mode) 단말의 움직임이 없고 디스플레이가 꺼진 채 장시간 방치될 때 일반 앱의 PARTIAL_WAKE_LOCK 효력을 강제 정지시키고 네트워크 폴링 및 알람 윈도우 허용 주기를 극단적으로 제한
앱 대기 모드 (App Standby) 사용자가 특정 앱을 수일 동안 단 한 번도 직접 실행하지 않았을 때 해당 앱을 비활성 버킷(Bucket) 등급으로 강등시켜 백그라운드 작업 예약(JobScheduler) 및 동기화 트래픽 차단
배터리 절약 모드 (Battery Saver) 배터리 잔량이 설정된 임계치(예: 15% 이하) 미만으로 도달 시 AP의 최대 가동 클럭 스케줄러(DVFS) 한계치를 강제 하향 조정하고, 디스플레이 60Hz/120Hz 주사율을 강제로 다운그레이드

💡 안드로이드 전력 관리를 위한 실전 팁

  1. WorkSource 객체 결합을 통한 배터리 소모 주체 명확화: 시스템 백그라운드 서비스나 시스템 프레임워크 단에서 특정 앱을 대신해 대리 런타임 작업을 수행하기 위해 PARTIAL_WAKE_LOCK을 획득하는 경우가 있습니다. 이때 단순히 웨이크록만 잡으면 배터리 소모 통계 인덱스가 내 시스템 서비스의 UID로 누적되어 오염됩니다. 웨이크록 객체 생성 시 반드시 요청한 앱의 UID 정보를 담은 WorkSource 객체를 생성하여 wakeLock.setWorkSource(ws)로 바인딩해 주세요. 그래야 PMS가 실제 배터리를 소모한 앱의 지분으로 정확히 계산하여 세팅해 주므로 배터리 설정 창의 프로파일링 정밀도가 완벽해집니다.
  2. adb shell dumpsys power 명령어를 통한 좀비 웨이크록 탐지: 내가 제작한 시스템 모듈이나 애플리케이션이 유휴 상태에서도 전류를 계속 잡아먹는 현상을 계측하고 싶다면 전력 덤프 명령어를 활용해 보세요. 개발 보드 셸에서 명령어를 실행하면 현재 PMS 내부 메모리에 살아 움직이는 모든 mWakeLocks 리스트의 태그 명칭, 락을 유지한 누적 타임스탬프(duration), 락을 소유한 프로세스의 물리 PID 및 배터리 절약 모드 등급 정보가 트리 구조로 시각화되어 병목 지점을 단숨에 색출해 낼 수 있습니다.

⚠️ 흔히 하는 실수

  1. try-finally 블록 누락으로 인한 영구적 '좀비 웨이크록(Unreleased WakeLock)' 유발: 앱이나 플랫폼 코드를 설계하는 개발자들이 가장 많이 범하는 전력 치명상 예외 사고입니다. wakeLock.acquire()를 실행하여 CPU를 깨워둔 뒤, 연산 비즈니스 로직 내부에서 뜻하지 않은 런타임 익셉션(NullPointerException 등)이 터지거나 중간에 반환(return) 코드가 작동하면서 하단의 wakeLock.release() 구문을 밟지 못하고 빠져나가는 시나리오입니다. PMS는 주인의 명시적 릴리즈 신호가 없으면 해당 앱 프로세스가 완전히 킬(Kill)되기 전까지 CPU를 최고 주파수 휴식 없는 가동 상태로 묶어두게 되며, 단 몇 시간 만에 사용자의 스마트폰 배터리를 방전시켜 기기를 뜨끈한 핫팩으로 전락시킵니다. 웨이크록 제어 코드는 무조건 자바의 try { ... } finally { wakeLock.release(); } 블록 구조 내부에 안전하게 격리 배정해야만 합니다.
  2. 비동기 핸들러 메시지 처리 중 타임아웃 없는 acquire() 선언으로 워치독 리부팅 트리거: PMS 내부 상태 머신이나 이와 직결된 백그라운드 핸들러 메커니즘에서 타임아웃 제한 조건이 명시되지 않은 전역 웨이크록을 남발할 때 발생하는 하부 데드락 참사입니다. 특정 네트워크 패킷 수신이나 IPC 응답을 무한정 기다리는 구조에 웨이크록을 걸어두었다가 상대 모듈이 뻗어버리면, PMS의 중앙 동기화 루프가 풀리지 않고 고착됩니다. 이 상태가 지속되면 시스템 서버 전체의 자원 정체를 감시하는 안드로이드 워치독(Watchdog) 스레드가 프레임워크 자체의 전원 통제 불능 상태로 간주하여 시스템 서버 프로세스를 강제로 폭파시키고 단말기를 불시에 강제 재부팅(Soft Reboot)시키는 대형 장애로 이어집니다. 장시간 소요가 예상되는 락은 반드시 acquire(long timeout) 형태로 최소한의 방어 타임아웃 타임라인을 주입해야 안전합니다.

5. 결론

Android 운영체제의 전력 효율성을 제어하는 절대적 컨트롤러인 PowerManagerService는 최상위 애플리케이션 계층의 비즈니스 요구 사항을 면밀히 검토하고, 리눅스 커널의 물리적인 저전력 서스펜드 드라이버 영역으로 안전하게 연착륙시키는 고도의 중재자입니다. mDirty 비트 연산에 기반한 전원 상태 머신의 유기적인 갱신 메커니즘을 AOSP 소스코드 레벨에서 유연하게 통제하고, 무분별하게 소모되는 웨이크록 토큰들의 라이프사이클을 완벽히 격리 제어할 수 있을 때, 비로소 배터리 효율을 극대화하는 동시에 단 1ms의 반응 지연도 없는 무결점 성능의 임베디드 운영체제 엔진을 완성해 낼 수 있습니다.

반응형