안드로이드(Android)는 오늘날 모바일 운영체제의 대명사로 자리 잡았지만, 그 본질은 거대한 오픈소스 프로젝트에 뿌리를 두고 있습니다. 바로 AOSP(Android Open Source Project)입니다. AOSP는 구글이 주도하고 전 세계 수많은 개발자가 참여하여 만들어가는 안드로이드의 심장부이며, 스마트폰 제조사부터 임베디드 시스템 개발자에 이르기까지 누구나 안드로이드의 소스 코드를 활용하여 자신만의 운영체제를 구축하거나 특정 목적에 맞는 디바이스를 제작할 수 있는 기반을 제공합니다. 이 글에서는 AOSP의 핵심 구조를 이해하고, 일반적인 앱 개발의 경계를 넘어 시스템의 핵심 기능과 통합되는 '시스템 앱'을 개발하는 과정 전반을 심도 있게 탐구합니다. AOSP의 빌드 시스템인 Make와 Soong(.bp 파일)의 차이점을 분석하고, 실제 시스템 앱을 개발하여 AOSP 이미지에 통합하는 과정, 그리고 보안 및 호환성과 같은 고급 주제까지 다룰 것입니다.
1. AOSP와 안드로이드 생태계의 이해
AOSP를 단순히 '안드로이드의 소스 코드'라고만 이해하면 그 잠재력의 극히 일부만 보는 것입니다. AOSP는 운영체제의 근간을 이루는 코드베이스일 뿐만 아니라, 하드웨어를 제어하고, 소프트웨어를 실행하며, 보안을 유지하는 정교한 아키텍처와 생태계 전체를 의미합니다.
1.1. AOSP의 다층적 아키텍처
안드로이드 시스템은 여러 개의 계층으로 구성된 소프트웨어 스택(Software Stack)입니다. 각 계층은 하위 계층의 서비스를 이용하여 상위 계층에 기능을 제공하는 구조로 설계되어 있습니다. 이는 시스템의 모듈성과 확장성을 높이는 핵심적인 설계 철학입니다.
- 리눅스 커널 (Linux Kernel): 안드로이드 아키텍처의 가장 아래에 위치하며, 시스템의 핵심입니다. 프로세스 관리, 메모리 관리, 전원 관리, 디바이스 드라이버 등 하드웨어와 직접적으로 소통하는 모든 저수준(low-level) 작업을 담당합니다. AOSP는 장기 지원(LTS) 버전의 리눅스 커널을 기반으로 하며, Binder IPC, 절전 기능(wakelocks) 등 안드로이드에 특화된 기능을 추가하여 사용합니다.
- 하드웨어 추상화 계층 (HAL - Hardware Abstraction Layer): HAL은 상위의 자바 API 프레임워크가 하드웨어 드라이버의 구체적인 구현을 몰라도 되도록 표준화된 인터페이스를 제공합니다. 예를 들어, 카메라 앱 개발자는 삼성의 카메라 센서와 소니의 카메라 센서가 어떻게 다른지 알 필요 없이, 안드로이드 프레임워크가 제공하는 표준 카메라 API만 호출하면 됩니다. HAL이 각 제조사(vendor)의 하드웨어 드MAR이 드라이버와 프레임워크 사이의 통신을 중개하기 때문입니다. 이는 안드로이드가 수많은 종류의 하드웨어에서 일관되게 동작할 수 있도록 하는 핵심적인 요소입니다.
- 안드로이드 런타임 (ART - Android Runtime): 안드로이드 5.0 롤리팝부터 기존의 Dalvik 가상 머신을 대체한 런타임 환경입니다. 앱의 바이트코드(.dex 파일)를 기기의 네이티브 기계어로 변환하여 실행하는 역할을 합니다. ART는 앱 설치 시점에 코드를 미리 컴파일하는 AOT(Ahead-Of-Time) 컴파일 방식을 주로 사용하여 앱 실행 속도와 시스템 성능을 크게 향상시켰습니다.
- 네이티브 C/C++ 라이브러리 (Native C/C++ Libraries): 안드로이드 시스템의 핵심 기능 중 상당수는 C/C++로 작성된 네이ティブ 라이브러리를 통해 제공됩니다. OpenGL ES(3D 그래픽 처리), WebKit(웹 브라우저 엔진), SQLite(데이터베이스), SSL(보안 통신) 등이 여기에 해당합니다. 이러한 라이브러리들은 자바 프레임워크를 통해 개발자에게 노출되거나, NDK(Native Development Kit)를 통해 C/C++ 앱에서 직접 사용할 수 있습니다.
- 자바 API 프레임워크 (Java API Framework): 일반적인 안드로이드 앱 개발자들이 가장 많이 상호작용하는 계층입니다. 액티비티 매니저, 윈도우 매니저, 콘텐츠 프로바이더, 뷰 시스템 등 앱 개발에 필요한 모든 핵심적인 도구와 서비스를 API 형태로 제공합니다. 개발자들은 이 프레임워크를 사용하여 풍부하고 일관된 사용자 경험을 제공하는 앱을 만들 수 있습니다.
- 시스템 앱 (System Apps): 안드로이드 아키텍처의 최상위 계층으로, 사용자가 직접 상호작용하는 부분입니다. 전화, 주소록, 캘린더, 웹 브라우저, 설정 등 기본적으로 탑재된 앱들이 여기에 속합니다. 이 앱들은 운영체제의 핵심 기능을 사용자에게 제공하는 역할을 하며, 일반 앱과 달리 특별한 권한을 가지고 시스템 깊숙한 기능에 접근할 수 있습니다.
1.2. AOSP와 구글 안드로이드의 관계
우리가 흔히 '안드로이드'라고 부르는 것은 사실 '구글 모바일 서비스(GMS - Google Mobile Services)'가 포함된 상용 버전의 안드로이드입니다. AOSP는 GMS가 포함되지 않은 순수한 오픈소스 운영체제입니다. 이 둘의 관계를 이해하는 것은 매우 중요합니다.
- AOSP: 완전한 기능을 갖춘 운영체제이지만, 구글 플레이 스토어, Gmail, 구글 지도, 유튜브 등 구글의 독점적인 앱과 서비스가 포함되어 있지 않습니다. 아마존의 Fire OS나 수많은 중국 내수용 안드로이드 기기들이 AOSP를 기반으로 독자적인 생태계를 구축한 대표적인 예입니다.
- GMS 안드로이드: 삼성, LG 등 대부분의 글로벌 스마트폰 제조사는 AOSP를 기반으로 자사의 하드웨어에 맞게 수정하고, 구글과의 라이선스 계약을 통해 GMS를 탑재하여 출시합니다. GMS는 구글의 서비스와 API를 제공하며, 이를 통해 사용자들은 익숙한 구글 생태계를 이용할 수 있습니다. GMS를 탑재하기 위해서는 구글이 정의한 호환성 정의 문서(CDD - Compatibility Definition Document)를 준수하고, 호환성 테스트 스위트(CTS - Compatibility Test Suite)를 통과해야 합니다.
따라서, AOSP를 기반으로 시스템 앱을 개발한다는 것은, GMS에 의존하지 않는 순수한 안드로이드 환경, 또는 특정 목적(예: 산업용 키오스크, 차량용 인포테인먼트 시스템)을 위해 맞춤 제작된 안드로이드 환경에서 동작하는 앱을 만드는 것을 의미하는 경우가 많습니다.
2. 시스템 앱의 특권과 책임
시스템 앱은 일반적인 서드파티 앱(사용자가 플레이 스토어 등에서 직접 설치하는 앱)과 근본적으로 다릅니다. 이들은 시스템 파티션(/system, /system_ext, /product, /vendor 등)에 설치되며, 운영체제의 일부로 간주됩니다. 이러한 특별한 지위는 강력한 권한과 함께 무거운 책임을 동반합니다.
2.1. 시스템 앱의 종류와 권한 수준
AOSP 내에서 시스템 앱은 설치 위치와 서명에 따라 다시 여러 종류로 나뉩니다. 각 종류는 서로 다른 수준의 권한을 가집니다.
- 일반 시스템 앱 (
/system/app): 이 위치에 설치된 앱은 사용자가 삭제할 수 없다는 점 외에는 일반 앱과 큰 차이가 없습니다. 일부 시스템 레벨의 권한을 가질 수는 있지만, 가장 민감한 작업은 수행할 수 없습니다. - 특권 앱 (Privileged App,
/system/priv-app): 시스템 앱의 핵심입니다./system/priv-app디렉토리에 설치된 앱들은 'privileged' 플래그를 부여받아, 일반 앱은 절대 사용할 수 없는 강력한 시스템 권한을 획득할 수 있습니다. 이는 시스템 설정을 변경하거나, 다른 앱의 데이터를 관리하거나, 하드웨어를 직접 제어하는 등 운영체제의 핵심 동작에 깊이 관여하는 기능에 필수적입니다. - 서명 기반 권한 (Signature Permissions): 안드로이드는 'signature' 보호 수준을 가진 권한을 정의합니다. 이 권한은 동일한 인증서로 서명된 앱들끼리만 서로 부여하고 사용할 수 있습니다. AOSP의 핵심 프레임워크와 시스템 앱들은 대부분 '플랫폼 인증서(platform key)'로 서명됩니다. 따라서 플랫폼 인증서로 서명된 시스템 앱은 안드로이드 시스템의 가장 내밀한 부분에 접근할 수 있는 최고 수준의 권한을 얻게 됩니다.
2.2. 시스템 앱만이 가질 수 있는 강력한 기능들
이러한 특권 덕분에 시스템 앱은 일반 앱이 상상할 수 없는 다양한 기능을 구현할 수 있습니다. 몇 가지 대표적인 예시는 다음과 같습니다.
android.permission.WRITE_SECURE_SETTINGS: 보안 설정(Secure Settings)을 직접 읽고 쓸 수 있는 권한입니다. 비행기 모드, 데이터 로밍, 화면 잠금 방식 등 사용자의 민감한 설정을 코드 레벨에서 제어할 수 있습니다. 모바일 기기 관리(MDM) 솔루션이나 특정 기능을 강제하는 산업용 기기에서 필수적인 권한입니다.android.permission.INSTALL_PACKAGES: 사용자 동의 없이 백그라운드에서 다른 앱(APK)을 설치하거나 제거할 수 있습니다. 자체적인 앱 스토어를 구현하거나, 기업 환경에서 필수 앱을 자동으로 배포하는 데 사용됩니다.android.permission.REBOOT: 기기를 프로그래밍 방식으로 재부팅하거나 종료할 수 있습니다. 원격 관리 시스템이나 시스템 업데이트를 적용하는 과정에서 필요합니다.android.permission.MANAGE_USERS: 기기 내에 여러 사용자 계정을 생성, 삭제, 전환할 수 있습니다. 다중 사용자를 지원하는 키오스크나 공유 태블릿 환경에서 핵심적인 기능입니다.android.permission.DEVICE_POWER: 화면을 켜거나 끄고, 기기를 절전 모드로 전환하는 등 전원 상태를 세밀하게 제어합니다.- 백그라운드 서비스 제한 우회: 최신 안드로이드 버전에서는 배터리 수명을 위해 백그라운드에서 실행되는 서비스에 엄격한 제한을 가합니다. 하지만 시스템 앱은 이러한 제한에서 예외 처리되어, 필요에 따라 지속적으로 백그라운드에서 동작할 수 있습니다.
이처럼 시스템 앱 개발은 안드로이드 플랫폼의 잠재력을 최대한으로 끌어낼 수 있는 강력한 도구입니다. 하지만 이는 동시에 시스템의 안정성과 보안에 직접적인 영향을 미치므로, 신중한 접근과 깊이 있는 이해가 반드시 필요합니다.
3. AOSP 빌드 시스템의 진화: Make에서 Soong으로
AOSP의 거대한 소스 코드를 컴파일하고, 수많은 라이브러리와 앱을 연결하여 최종적으로 디바이스에서 실행 가능한 시스템 이미지(system.img, boot.img 등)를 만들어내는 과정을 '빌드(build)'라고 합니다. 이 복잡한 빌드 과정을 자동화하고 관리하는 것이 바로 빌드 시스템의 역할입니다. AOSP의 빌드 시스템은 역사적으로 큰 변화를 겪어왔으며, 현재는 두 가지 시스템이 공존하고 있습니다.
3.1. 전통의 강자: GNU Make와 Android.mk 파일
AOSP 초기부터 오랫동안 사용되어 온 빌드 시스템은 GNU Make입니다. Make는 특정 파일(Makefile)에 정의된 규칙에 따라 소스 코드를 컴파일하고 결과물을 생성하는 도구입니다. AOSP에서는 Android.mk라는 이름의 파일을 사용하여 각 모듈(앱, 라이브러리, 실행 파일 등)의 빌드 방법을 정의합니다.
Android.mk 파일은 다음과 같은 구조를 가집니다.
# 현재 디렉토리의 경로를 설정합니다. 필수적인 시작점입니다.
LOCAL_PATH := $(call my-dir)
# 이전 빌드에서 사용된 LOCAL_ 변수들을 초기화합니다.
# 새로운 모듈을 정의하기 전에 반드시 호출해야 합니다.
include $(CLEAR_VARS)
# 빌드될 모듈의 고유한 이름을 지정합니다.
# APK의 경우, 이 이름이 최종 파일 이름이 됩니다. (예: MyApp.apk)
LOCAL_MODULE := MyApp
# 컴파일할 소스 코드 파일들을 지정합니다.
# .java, .kt, .c, .cpp 파일 등을 포함할 수 있습니다.
LOCAL_SRC_FILES := $(call all-java-files-under, java) \
src/com/example/myapp/util/Utils.java
# 이 모듈이 의존하는 정적 자바 라이브러리를 지정합니다.
# (예: 'android-support-v4')
LOCAL_STATIC_JAVA_LIBRARIES := lib-foo lib-bar
# 이 모듈이 의존하는 공유 라이브러리 (네이티브 .so 파일)를 지정합니다.
LOCAL_SHARED_LIBRARIES := libutils libcutils
# 플랫폼 API가 아닌 비공개 API를 사용해야 할 경우 지정합니다.
LOCAL_PRIVATE_PLATFORM_APIS := true
# 앱의 패키지 이름 (AndroidManifest.xml의 package와 일치해야 함)
LOCAL_PACKAGE_NAME := MyApp
# 앱 서명에 사용할 인증서를 지정합니다.
# 'platform'은 시스템의 핵심 권한을 가질 수 있는 플랫폼 키를 의미합니다.
# 'shared', 'media', 'testkey' 등도 사용 가능합니다.
LOCAL_CERTIFICATE := platform
# 이 모듈을 특권 앱으로 지정합니다. /system/priv-app 에 설치됩니다.
LOCAL_PRIVILEGED_MODULE := true
# 빌드 시 이 모듈을 포함할 조건을 태그로 지정합니다.
# 'eng': 엔지니어링 빌드에만 포함
# 'user': 일반 사용자 빌드에 포함
# 'debug': 디버그 빌드에 포함
# 'optional': 기본적으로 포함되지 않으며, PRODUCT_PACKAGES에 명시해야 함
LOCAL_MODULE_TAGS := optional
# 이 모듈이 안드로이드 앱 패키지(APK)임을 빌드 시스템에 알립니다.
include $(BUILD_PACKAGE)
# 만약 이 모듈이 네이티브 실행 파일이라면 아래와 같이 작성합니다.
# include $(CLEAR_VARS)
# LOCAL_MODULE := my_native_executable
# LOCAL_SRC_FILES := main.c
# include $(BUILD_EXECUTABLE)
Make 시스템은 매우 유연하고 강력하지만, 몇 가지 본질적인 단점을 가지고 있습니다.
- 속도 저하: AOSP와 같이 수십만 개의 파일로 구성된 거대한 프로젝트에서 Make는 빌드 설정을 해석하는 데만 상당한 시간을 소요합니다. 모든
.mk파일을 읽고 파싱하는 과정이 비효율적입니다. - 복잡성과 오류 가능성: Makefile의 문법은 변수, 조건문, 함수 등을 지원하지만, 이는 곧 복잡한 로직을 작성하기 쉽게 만들어 오류 발생 가능성을 높입니다. 변수가 예기치 않게 덮어쓰이거나, 의존성 관계가 꼬이기 쉽습니다.
- 선언적이지 않은 구조: Make는 '어떻게' 빌드할 것인지를 기술하는 절차적인 방식에 가깝습니다. 이는 빌드 과정을 이해하고 수정하기 어렵게 만듭니다.
3.2. 현대적인 대안: Soong과 Android.bp (Blueprint) 파일
이러한 Make의 단점을 극복하기 위해 구글은 Soong이라는 새로운 빌드 시스템을 도입했습니다. Soong은 Android.bp (Blueprint 파일)라는 설정 파일을 사용합니다. Android.bp 파일은 Make와 달리 프로그래밍 로직(조건문, 반복문 등)을 포함할 수 없으며, 순수하게 빌드 모듈의 속성을 '선언'하는 데에만 사용됩니다.
Soong의 작동 방식은 다음과 같습니다. 1. Soong은 전체 소스 트리를 스캔하여 모든 `Android.bp` 파일을 찾습니다. 2. 이 파일들을 파싱하여 빌드 규칙의 내부 표현을 생성합니다. 3. 이 정보를 바탕으로 매우 빠르고 효율적인 Ninja 빌드 파일을 생성합니다. 4. 실제 컴파일 및 링크 작업은 Ninja가 담당하여 실행합니다. 이러한 구조 덕분에 Soong은 Make에 비해 빌드 설정 분석 속도가 월등히 빠르고, 빌드 규칙이 명확하며, 오류 발생 가능성이 낮습니다.
앞서 Android.mk로 작성했던 시스템 앱을 Android.bp로 변환하면 다음과 같습니다.
// android_app 모듈 타입을 사용하여 안드로이드 앱을 정의합니다.
android_app {
// 모듈의 고유 이름
name: "MyApp",
// 소스 코드 파일 목록
// filegroup을 사용하거나 직접 파일 경로를 나열할 수 있습니다.
srcs: [
"java/**/*.java",
"src/com/example/myapp/util/Utils.java",
],
// 의존하는 정적 자바 라이브러리
static_libs: [
"lib-foo",
"lib-bar",
"androidx.appcompat_appcompat", // 예시: androidx 라이브러리
],
// 앱의 패키지 이름
package_name: "com.example.myapp",
// 플랫폼 API 사용 여부 (true일 경우 안정적인 public API만 사용)
// 시스템 앱은 종종 내부 API를 사용해야 하므로 false로 설정하거나
// platform_apis: true 와 함께 sdk_version을 명시해야 합니다.
// 여기서는 플랫폼 소스와 함께 빌드되므로 이 속성은 생략 가능합니다.
// 앱 서명에 사용할 인증서
certificate: "platform",
// 특권 앱으로 지정
privileged: true,
// product 파티션에 설치할지 여부
// true로 설정하면 /product/priv-app 에 설치됩니다.
product_specific: true,
// 다른 앱을 이 앱으로 대체 (오버라이드)
// 예를 들어, AOSP의 기본 Dialer 앱을 이 앱으로 대체하고 싶을 때 사용
// overrides: ["Dialer"],
}
Android.bp 파일은 JSON과 유사한 간결하고 선언적인 문법을 사용합니다. 각 속성(property)은 명확한 의미를 가지며, 빌드에 필요한 모든 정보를 구조적으로 표현합니다. AOSP는 점진적으로 모든 Android.mk 파일을 Android.bp 파일로 전환하고 있으며, 새로운 모듈을 추가할 때는 Android.bp를 사용하는 것이 강력히 권장됩니다.
4. 실전: AOSP에 시스템 앱 통합하기
이제 이론을 바탕으로 실제 AOSP 소스 코드에 새로운 시스템 앱을 추가하고 빌드하여 시스템 이미지에 포함시키는 전 과정을 단계별로 살펴보겠습니다.
Step 1: AOSP 빌드 환경 준비
가장 먼저 AOSP 소스 코드를 다운로드하고 빌드할 수 있는 환경을 구축해야 합니다. 이 과정은 수백 기가바이트의 디스크 공간과 상당한 시간이 소요됩니다.
- 필수 패키지 설치:
build-essential,git,curl,python등 AOSP 빌드에 필요한 다양한 도구를 설치합니다. 필요한 패키지 목록은 AOSP 공식 문서에서 확인할 수 있습니다. - Repo 도구 설치: Repo는 여러 Git 저장소로 구성된 AOSP 프로젝트를 쉽게 관리하기 위한 도구입니다.
- AOSP 소스 코드 다운로드:
# 작업 디렉토리 생성 mkdir aosp && cd aosp # 원하는 안드로이드 버전(브랜치)을 지정하여 소스 코드 초기화 # 예시: android-12.1.0_r27 repo init -u https://android.googlesource.com/platform/manifest -b android-12.1.0_r27 --depth=1 # 소스 코드 동기화 (다운로드) repo sync -c -j$(nproc) - 빌드 환경 설정:
source build/envsetup.sh - 빌드 타겟 선택:
# lunch 명령어로 빌드할 타겟 디바이스와 빌드 유형을 선택합니다. # 예시: Cuttlefish (가상 머신) 64비트 userdebug 빌드 lunch aosp_cf_x86_64_phone-userdebug
Step 2: 시스템 앱 소스 코드 및 디렉토리 구조 생성
AOSP 내에서 앱은 보통 packages/apps/ 디렉토리 아래에 위치합니다. 새로운 시스템 앱을 위한 디렉토리를 생성합니다.
# AOSP 루트 디렉토리에서 실행
mkdir -p packages/apps/MySystemApp/src/com/example/mysystemapp
cd packages/apps/MySystemApp
이제 기본적인 안드로이드 앱 구성 요소들을 생성합니다.
AndroidManifest.xml: 앱의 기본 정보를 정의합니다. 시스템 앱이므로, 특별한 권한을 사용하려면uses-permission태그를 추가해야 합니다.src/: 자바 또는 코틀린 소스 코드를 위치시킵니다.res/: 레이아웃, 문자열, 이미지 등 리소스 파일을 위치시킵니다.
packages/apps/MySystemApp/AndroidManifest.xml (예시):
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mysystemapp">
<!-- 시스템 재부팅 권한 요청 -->
<uses-permission android:name="android.permission.REBOOT" />
<!-- 보안 설정 쓰기 권한 요청 -->
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application
android:label="MySystemApp">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<intent-filter>
</activity>
</application>
</manifest>
Step 3: Android.bp 파일 작성
앱의 루트 디렉토리에 Android.bp 파일을 생성하여 빌드 규칙을 정의합니다. 이 파일이 AOSP 빌드 시스템에게 이 디렉토리의 코드를 어떻게 처리해야 할지 알려주는 역할을 합니다.
packages/apps/MySystemApp/Android.bp:
android_app {
name: "MySystemApp",
// 모든 자바 소스 파일 포함
srcs: ["src/**/*.java"],
// 리소스 디렉토리 지정 (기본값이 res이므로 생략 가능)
// resource_dirs: ["res"],
// AndroidManifest.xml 파일 경로 (기본값이 파일명이므로 생략 가능)
// manifest: "AndroidManifest.xml",
// 플랫폼 소스와 함께 빌드되므로 sdk_version을 명시할 필요 없음
// 만약 prebuilt된 라이브러리에 의존한다면 sdk_version을 지정해야 함
// 플랫폼 인증서로 서명
certificate: "platform",
// 특권 앱으로 만들어 /system/priv-app 에 설치
privileged: true,
// product 파티션에 설치 (최신 AOSP 버전에서는 권장됨)
product_specific: true,
}
이 간단한 설정만으로도 MySystemApp은 플랫폼 키로 서명된 특권 앱으로 빌드될 준비가 끝납니다.
Step 4: 제품(Product) 설정 파일에 앱 추가
Android.bp 파일을 작성했다고 해서 이 앱이 자동으로 시스템 이미지에 포함되는 것은 아닙니다. 어떤 패키지를 최종 이미지에 포함할지는 각 제품의 설정 파일(Product Makefile)에서 결정합니다. lunch에서 선택했던 타겟(aosp_cf_x86_64_phone)의 설정 파일을 찾아 새로 만든 앱을 추가해야 합니다.
해당 파일은 보통 device/<vendor>/<device>/ 경로에 있습니다. Cuttlefish의 경우 device/google/cuttlefish/shared/phone.mk 와 같은 파일을 수정할 수 있습니다.
device/google/cuttlefish/shared/phone.mk 파일을 열고 PRODUCT_PACKAGES 리스트에 모듈 이름을 추가합니다.
# ... 기존 패키지 목록 ...
PRODUCT_PACKAGES += \
Calculator \
Calendar \
Camera2 \
Contacts \
DeskClock \
Dialer \
Gallery2 \
Launcher3 \
Messaging \
Music \
Settings \
SystemUI \
# 여기에 새로운 시스템 앱 추가
MySystemApp
이제 빌드 시스템은 전체 시스템 이미지를 생성할 때 `MySystemApp` 모듈을 찾아 빌드하고, 결과물인 `MySystemApp.apk`를 /product/priv-app/ 디렉토리에 포함시킬 것입니다.
Step 5: 빌드 및 실행
모든 설정이 완료되었습니다. 이제 AOSP 전체를 빌드하거나, 개발 중에는 특정 모듈만 빠르게 빌드할 수 있습니다.
# AOSP 루트 디렉토리에서 실행
# 방법 1: 특정 모듈만 빌드 (개발 및 테스트에 유용)
# 결과물은 out/target/product/<device>/product/priv-app/MySystemApp/ 에 생성됨
m MySystemApp
# 방법 2: 전체 시스템 이미지 빌드 (수 시간이 소요될 수 있음)
# -j 플래그는 병렬로 실행할 작업 수를 지정 (CPU 코어 수에 맞게 설정)
m -j$(nproc)
빌드가 성공적으로 완료되면, 생성된 이미지들을 에뮬레이터나 실제 디바이스에 플래시하여 실행할 수 있습니다. Cuttlefish의 경우, `launch_cvd` 명령어로 쉽게 가상 디바이스를 실행하고, 새로 추가된 'MySystemApp'이 앱 서랍에 나타나고 정상적으로 실행되는 것을 확인할 수 있습니다.
5. 고급 주제: 시스템 앱 개발의 실질적인 과제
시스템 앱을 성공적으로 빌드하고 이미지에 포함시키는 것은 시작에 불과합니다. 실제 제품 수준의 시스템 앱을 개발하기 위해서는 보안, 호환성, 성능 등 훨씬 더 복잡한 문제들을 고려해야 합니다.
5.1. 보안: SELinux 정책과의 싸움
최신 안드로이드에서는 권한(permission) 시스템 외에 SELinux(Security-Enhanced Linux)라는 강력한 강제적 접근 통제(MAC) 시스템이 적용됩니다. 단순히 AndroidManifest.xml에 권한을 선언하고 플랫폼 키로 서명했다고 해서 모든 시스템 리소스에 접근할 수 있는 것이 아닙니다.
SELinux는 모든 프로세스(앱 포함)와 모든 시스템 리소스(파일, 디바이스 드라이버, 소켓 등)에 '보안 컨텍스트(레이블)'를 부여합니다. 그리고 어떤 컨텍스트를 가진 프로세스가 어떤 컨텍스트를 가진 리소스에 어떤 작업을(읽기, 쓰기, 실행 등) 할 수 있는지를 정의한 '정책(policy)'을 시스템에 탑재합니다. 만약 허용되지 않은 작업을 시도하면, 권한이 있더라도 커널 수준에서 해당 작업이 차단되고 로그가 남습니다(denial message).
시스템 앱이 파일 시스템의 특정 경로에 접근하거나, 특정 시스템 속성(system property)을 변경하거나, 하드웨어 드라이버와 직접 통신해야 하는 경우, 반드시 적절한 SELinux 정책을 추가해야 합니다.
- 도메인 정의: 시스템 앱을 위한 새로운 SELinux 도메인을 정의합니다. (
my_system_app.te) - 타입 정의: 앱이 생성하는 파일이 있다면 해당 파일의 타입을 정의합니다.
- 허용 규칙(Allow Rule) 추가:
allow my_system_app_t system_property:property_service set;와 같이 어떤 도메인이 어떤 타입의 리소스에 어떤 작업을 할 수 있는지 명시적인 규칙을 추가해야 합니다. - File Contexts 설정: 앱의 실행 파일이 어떤 보안 컨텍스트를 가져야 하는지 정의합니다. (
file_contexts)
SELinux 정책 디버깅은 logcat이나 dmesg에서 'avc: denied' 메시지를 분석하는 것부터 시작됩니다. 이는 시스템 앱 개발에서 가장 어렵고 시간이 많이 소요되는 작업 중 하나이지만, 시스템의 보안을 유지하기 위해 반드시 거쳐야 하는 과정입니다.
5.2. 호환성과 안정성
시스템 앱은 운영체제의 일부이므로, 그 안정성이 전체 시스템의 안정성에 직접적인 영향을 미칩니다. 시스템 앱의 버그 하나가 기기 전체를 부팅 불능(boot loop) 상태로 만들 수도 있습니다.
- AOSP 버전 변화 대응: AOSP는 지속적으로 발전하며 내부 API가 변경되거나 제거될 수 있습니다. 시스템 앱이 비공개(hidden) API에 의존하는 경우, 새로운 안드로이드 버전이 나올 때마다 호환성 문제가 발생할 수 있습니다. 따라서 가능한 한 안정적인 API를 사용하고, 변경 사항을 꾸준히 추적해야 합니다.
- CTS (Compatibility Test Suite): GMS 인증을 받아야 하는 상용 제품의 경우, 시스템 앱의 추가나 변경이 CTS 테스트 통과에 영향을 미치지 않는지 반드시 확인해야 합니다. CTS는 안드로이드의 API와 기능이 호환성 정의 문서(CDD)에 따라 올바르게 구현되었는지를 검증하는 수십만 개의 자동화된 테스트 케이스로 구성됩니다.
- OTA (Over-the-Air) 업데이트: 시스템 앱은 플레이 스토어를 통해 업데이트되지 않습니다. 오직 전체 시스템 펌웨어를 업데이트하는 OTA 방식을 통해서만 업데이트될 수 있습니다. 이는 배포 주기가 길고, 한번 배포된 버그를 수정하기 어렵다는 것을 의미합니다. 따라서 출시 전 훨씬 더 엄격하고 철저한 테스트가 요구됩니다.
5.3. 디버깅 및 성능 최적화
시스템 프로세스나 시스템 앱을 디버깅하는 것은 일반 앱보다 까다롭습니다. Android Studio의 디버거를 시스템 프로세스에 직접 연결하거나, dumpsys, logcat 등 커맨드라인 도구를 적극적으로 활용하여 시스템의 내부 상태를 분석하는 기술이 필요합니다.
또한, 시스템 앱은 종종 백그라운드에서 상시 동작하며 시스템 리소스를 소모합니다. 불필요한 CPU 사용, 과도한 메모리 점유, 빈번한 Wakelock 획득 등은 기기의 배터리 수명과 전반적인 성능에 치명적인 영향을 줄 수 있습니다. 따라서 Systrace, Perfetto와 같은 시스템 수준의 프로파일링 도구를 사용하여 앱의 성능을 분석하고 최적화하는 과정이 필수적입니다.
결론
AOSP를 통해 시스템 앱을 개발하는 것은 안드로이드의 가장 깊은 곳을 탐험하는 여정과 같습니다. 이는 단순히 코드를 작성하는 것을 넘어, 운영체제의 아키텍처, 빌드 시스템, 보안 모델, 하드웨어와의 상호작용까지 이해해야 하는 복합적인 작업입니다. Make와 Soong이라는 두 빌드 시스템의 차이를 이해하고, Android.bp를 통해 빌드 규칙을 선언하며, 제품 설정 파일을 수정하여 마침내 나만의 앱을 시스템 이미지에 통합하는 과정은 큰 성취감을 줍니다.
그러나 이 강력한 권한에는 시스템 전체의 안정성과 보안을 책임져야 하는 무거운 의무가 따릅니다. SELinux 정책의 복잡함을 해결하고, 까다로운 호환성 문제를 관리하며, 시스템 전체에 미치는 성능 영향을 최소화하려는 노력은 시스템 앱 개발자를 진정한 시스템 엔지니어로 성장하게 만듭니다. 이 글이 AOSP와 시스템 앱 개발이라는 흥미롭고 도전적인 세계로 첫발을 내딛는 모든 개발자에게 훌륭한 나침반이 되기를 바랍니다.
Post a Comment