Android System & AOSP Engineering/AOSP Framework & Custom Services

AOSP 실무: frameworks/base 내부 커스텀 시스템 서비스 추가 및 Android.bp 빌드 가이드

임베디드 친구 2025. 6. 8. 12:36
반응형

안드로이드 OS 기반의 독자적인 셋톱박스, 스마트 가전, 혹은 로봇 플랫폼을 빌드할 때 프레임워크 엔지니어들이 가장 빈번하게 드나드는 아지트가 있습니다. 바로 안드로이드 운영체제의 거대한 뼈대를 이루는 frameworks/base/ 디렉터리입니다.

순정 안드로이드가 제공하는 표준 API 외에 단말기 고유의 하드웨어나 특화 비즈니스 로직을 OS 전역에 뿌려주려면, 이 frameworks/base/ 레이아웃 내부에 나만의 AIDL 통신 명세서와 서비스 클래스를 정확히 주입해 주어야 합니다. 하지만 안드로이드의 빌드 시스템인 숭(Soong, Android.bp)은 구조가 매우 방대하고 엄격해서, 컴파일 규칙을 조금만 잘못 건드려도 소스 트리 전체가 꼬이며 빌드 에러의 늪에 빠지기 쉽죠. 오늘은 순정 소스 트리의 무결성을 깨뜨리지 않으면서 깔끔하게 커스텀 시스템 서비스를 빌드 체인에 태우고, adb shell 명령어로 원격 제어 컴포넌트를 호출하는 실전 공정을 완벽하게 정리해 드리겠습니다.

Generated by Gemini AI.

📌 핵심 요약 3줄

  1. frameworks/base/core/ 레이어에 AIDL 인터페이스를 추가할 때는 별도 모듈을 파지 말고, 순정 Android.bp 파일의 전역 소스 리스트에 경로를 매핑해야 빌드 충돌이 없습니다.
  2. 구현체 자바 코드는 frameworks/base/services/core/ 내부 표준 패키지 구조에 배치하여 시스템 서버 자바 아카이브(services.jar)에 자연스럽게 컴파일 병합되도록 유도합니다.
  3. 빌드 완료 후에는 m services 명령어로 빌드 시간을 절약하고, adb shell service call 명령어를 통해 원격 바인더 트랜잭션이 성공하는지 교차 검증합니다.

1. 프레임워크 확장 시 기억해야 할 핵심 파일 레이아웃 및 여정

커스텀 서비스를 구성하는 소스 코드들의 정확한 파일 위치와 컴파일 대상 매핑 테이블입니다.

단계 구분 소스 트리 내 실질적 절대 경로 수행하는 핵심 역할 및 수정 방향 빌드 타깃 모듈 명칭
1. IPC 인터페이스 선언 frameworks/base/core/java/android/os/ICustomService.aidl 앱 레이어와 시스템 서버 레이어가 서로 소통할 전역 바인더 메서드 명세 정의 framework-minus-apex
2. 비즈니스 로직 구현 frameworks/base/services/core/java/com/android/server/CustomService.java AIDL Stub 인터페이스를 상속받아 실질적인 디바이스 제어 및 연산 로직 구현 services.core
3. 부팅 시퀀스 빌트인 frameworks/base/services/java/com/android/server/SystemServer.java startOtherServices() 단계에서 커스텀 서비스 인스턴스를 메모리에 로드 services

2. 표준 컴파일 규격을 준수하는 프레임워크 서비스 이식

안드로이드 빌드 시스템 표준에 맞추어 충돌 없이 컴파일을 통과하는 소스 코드 구현 및 환경 설정 단계입니다.

2.1 AIDL 인터페이스 정의 및 프레임워크 등록

먼저 시스템 전역에 노출할 IPC 통신용 매커니즘 인터페이스를 배치합니다. 패키지 경로는 반드시 순정 규격인 android.os를 따르는 것이 시스템 빌드 상 안전합니다.

Java
 
// frameworks/base/core/java/android/os/ICustomService.aidl
package android.os;

interface ICustomService {
    void customMethod();
}

이제 이 AIDL 파일이 컴파일러 시스템에 포착되도록 frameworks/base/Android.bp 메인 빌드 파일을 열어 소스 목록에 기재해 줍니다. 수많은 자바 파일 경로가 모여있는 srcs: 섹션을 검색하여 내 AIDL 경로를 쏙 끼워 넣어 줍니다.

코드 스니펫
 
// frameworks/base/Android.bp (순정 파일의 프레임워크 소스 리스트 섹션 수정)
java_defaults {
    name: "framework-minus-apex-defaults",
    srcs: [
        // ... 수많은 기존 순정 aidl 및 java 파일들 ...
        "core/java/android/os/ICustomService.aidl", // 내 커스텀 AIDL 경로 정식 등록
    ],
}

2.2 시스템 서비스 핵심 클래스 비즈니스 로직 구현

시스템 서버 백그라운드 스레드에서 실질적으로 명령을 받아 연산할 클래스를 추가합니다.

Java
 
// frameworks/base/services/core/java/com/android/server/CustomService.java
package com.android.server;

import android.content.Context;
import android.os.ICustomService; // 빌드 시스템이 AIDL을 파싱해 자동 생성해 준 클래스
import android.os.RemoteException;
import android.util.Slog;

/**
 * AIDL 내부의 Stub 클래스를 상속받아 실질적인 시스템 특권 기능을 가동합니다.
 */
public class CustomService extends ICustomService.Stub {
    private static final String TAG = "CustomServiceCore";
    private final Context mContext;

    public CustomService(Context context) {
        mContext = context;
    }

    @Override
    public void customMethod() throws RemoteException {
        // 이 구역은 앱 프로세스의 벽을 넘어 호출되는 특권 영역입니다.
        Slog.i(TAG, "CustomService 바인더 링크 연동 성공: customMethod() 로직이 가동됩니다.");
    }
}

주의: frameworks/base/services/core/Android.bp 파일은 이미 하위 자바 소스들을 자동으로 통째로 긁어가도록 빌드 규칙이 짜여있으므로, 소스 파일을 경로에 맞게 잘 생성해 두기만 하면 빌드 스크립트를 수동으로 더 건드릴 필요가 없습니다.

2.3 SystemServer.java에 부팅 라이프사이클 바인딩

안드로이드가 켜질 때 이 서비스를 주소록에 등재하도록 프레임워크 초기화 시퀀스에 코드를 추가합니다.

Java
 
// frameworks/base/services/java/com/android/server/SystemServer.java
import com.android.server.CustomService;

public final class SystemServer {

    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
        // ... 기본 순정 서비스 가동 흐름 ...

        t.traceBegin("StartCustomSystemService");
        try {
            Slog.i(TAG, "커스텀 프레임워크 확장 시스템 서비스 로드 시작");
            
            // 전역 ServiceManager 레지스트리에 내 서비스의 고유 문자열 주소 키값을 등록합니다.
            ServiceManager.addService("custom_service", new CustomService(mSystemContext));
            
        } catch (Throwable e) {
            Slog.e(TAG, "커스텀 서비스를 부팅 시퀀스에 올리는 도중 예외 에러 발생", e);
        }
        t.traceEnd();

        // ... 나머지 순정 서비스 계속 진행 ...
    }
}

3. 효율적인 AOSP 빌드 및 에뮬레이터 교차 검증 공정

전체 플랫폼 소스를 다 빌드하면 검색 시간이 너무 오래 걸리므로, 영리하게 증분 빌드를 수행하고 adb 터미널 명령어로 링킹 상태를 스캔해 봅니다.

3.1 증분 타깃 빌드 커맨드 가동

전체 AOSP 빌드 환경 셸을 로드한 뒤, 프레임워크 수정 본만 빠르게 교체 빌드하는 명령어를 실행합니다.

Bash
 
# 1. 안드로이드 빌드 유틸 셸 함수 로드
source build/envsetup.sh

# 2. 내 타깃 기기 또는 에뮬레이터 환경 선택 (예시: x86_64 가상 에뮬레이터 엔지니어링 버전)
lunch aosp_car_x86_64-eng

# 3. [시간 단축 팁] 전체 빌드를 돌리지 말고, 수정된 시스템 서버 모듈만 콕 집어서 고속 컴파일합니다.
m services

3.2 에뮬레이터 런타임 내 서비스 등록 확인 상태 스캔

빌드가 끝나고 단말기(에뮬레이터)를 띄운 다음 시스템 주소록을 스캔하여 내 서비스가 살아서 숨 쉬는지 점검합니다.

Bash
 
# 에뮬레이터 혹은 단말 가동 후 주소록 전체 스캔 필터링
adb shell service list | grep custom_service

정상적으로 커널 바인더 레지스트리에 명단이 등록되었다면 아래와 같이 내 패키지 명세 경로를 담은 리턴 라인이 시원하게 출력됩니다.

Plaintext
 
87  custom_service: [android.os.ICustomService]

3.3 로우 바인더 트랜잭션 수동 강제 구동 테스트

클라이언트 앱을 아직 짜지 않은 상태여도 adb 셸 명령어의 service call 기능을 활용하면 커널 단에서 내 원격 함수를 강제로 찔러볼 수 있습니다.

Bash
 
# custom_service의 1번 메서드(첫 번째로 선언한 customMethod)를 원격 강제 호출합니다.
adb shell service call custom_service 1

호출 후 OS 시스템 로그를 필터링하여 우리가 심어놓은 Slog 문구가 알맞게 통과했는지 교차 검증을 마칩니다.

Bash
 
adb logcat -s CustomServiceCore
Plaintext
 
--------- beginning of main
05-17 18:42:11.302  1522  1540 I CustomServiceCore: CustomService 바인더 링크 연동 성공: customMethod() 로직이 가동됩니다.

🛠️ 개발을 위한 팁 (Tips)

  1. AIDL 수정 후 가끔 빌드가 꼬인다면 out/ 디렉터리 부분 청소: ICustomService.aidl 파일의 메서드 구조를 변경(매개변수 추가 등)한 뒤 m services를 돌렸을 때, 자바 컴파일러가 구형 스텁 인터페이스 구조를 기억하고 빌드 깨짐 현상을 일으킬 때가 있습니다. 이럴 때는 전체 빌드를 다시 돌려 아까운 시간을 날리지 마시고 rm -rf out/target/common/obj/JAVA_LIBRARIES/framework-minus-apex_intermediates/ 경로를 지워준 뒤 다시 컴파일을 시도해 보세요. AIDL 뼈대 자바 파일만 깨끗하게 갱신되므로 고속 빌드 타이밍을 유지할 수 있습니다.
  2. 트랜잭션 코드 호출 번호는 AIDL 선언 순서대로: adb shell service call 명령어로 로우 바인더 함수를 찌를 때 붙는 뒤의 번호(위 예제의 1)는 AIDL 파일 내에 정의된 메서드의 순서입니다. 위에서 아래 방향으로 순차적으로 1번, 2번, 3번 인덱싱 번호가 매겨집니다. 나중에 메서드를 추가하거나 순서를 바꿀 때 이 트랜잭션 콜 번호도 함께 매칭되므로 순서 관리에 유념하셔야 디버깅 삽질을 예방할 수 있습니다.

⚠️ 흔히 하는 실수 (Common Mistakes)

  1. frameworks/base/core/ 하위에 무단으로 독립 Android.bp 파일 생성: 프레임워크 확장 초보 단계에서 제일 많이 저지르는 대표적인 컴파일 붕괴 원인입니다. ICustomService.aidl을 추가하면서 그 폴더 안에 단독으로 aidl_interface 빌드 블록을 만들고 빌드를 누르는 행위죠. 안드로이드 Soong 빌드 아키텍처는 frameworks/base 하위의 공용 API 노출 영역을 단 하나의 거대한 중앙 빌드 트리로 묶어서 검증합니다. 중간 경로에 사설 컴파일러 모듈을 따로 선언해 버리면 소스 패키지가 중복 빌드 토큰으로 얽히면서 프레임워크 전체 빌드가 마비되니, 메인 Android.bp 소스 리스트에 경로 한 줄만 추가해 주는 규격을 무조건 지켜주세요.
  2. ServiceManager.addService() 수행 시 컨텍스트 주의: SystemServer.java 파일 안에서 내 커스텀 서비스를 적재할 때 인자값으로 그냥 일반 컨텍스트를 넘기거나 mSystemContext 주입 시점을 잘못 설계하는 실수입니다. SystemServer 내부 초기화 루틴 중 startOtherServices() 단계는 시스템의 여러 인프라가 완전히 정착하기 전 단계이므로, 내 서비스 내부 생성자에서 시스템 컨텍스트의 자원을 너무 일찍 끌어다 쓰려고 하면(예: 로드 직후 바로 SharedPreferences 접근 등) NullPointerException이 나면서 스마트폰 부팅 시퀀스가 로고 화면에서 영원히 멈춰 서는 무한 부팅 벽돌 현상이 발생합니다. 무거운 초기화는 부팅 완료 브로드캐스트 시점으로 빼주어야 합니다.

4. 결론

안드로이드의 핵심 골격인 frameworks/base/ 내부를 직접 수정하고 컴파일 체인을 연결해 나만의 시스템 서비스를 주입하는 일은 플랫폼 운영체제를 온전히 내 손아귀에 넣고 통제하는 엔지니어링의 시작점입니다.

빌드 아키텍처가 제안하는 Android.bp 소스 리스트 링킹 가이드라인을 엄격하게 지키고, 시스템 서버 자바 프레임워크 팩토리 레이아웃에 소스코드를 정확히 안착시켜 주어야 부팅 무결성을 해치지 않는 깔끔한 맞춤형 OS 펌웨어가 탄생합니다. 프레임워크 소스 수정 후 코드 생성 단에서 파싱 오류(Ninja Build Error)가 나며 진전이 안 되거나, 에뮬레이터에서 서비스 호출 시 권한 제어 문제로 로그캣에 빨간 에러 메시지가 도배된다면 주저 말고 하단 댓글 창에 빌드 로그 조각을 던져주세요. 로우 레이어 트랙에서 막힌 혈을 깔끔하게 짚어보겠습니다!

반응형