Android/Framework

Native 코드와 Android Framework 연동

임베디드 친구 2025. 4. 6. 09:48
728x90
반응형

Native 코드와 Android Framework 연동

Android 애플리케이션 개발에서는 일반적으로 Java 또는 Kotlin과 같은 고수준 언어를 사용하지만, 보다 성능이 중요한 작업이나 기존 C/C++ 기반의 네이티브 라이브러리를 활용해야 하는 경우에는 Native 코드와 Android Framework을 연동해야 하는 경우가 많습니다. 본 글에서는 네이티브 라이브러리를 프레임워크 서비스에서 활용하는 방법과 JNI를 사용한 System API 확장 방법을 AOSP 코드와 함께 살펴보겠습니다.

1. Android에서 Native 코드 활용 개요

Android 시스템은 네이티브 코드를 활용할 수 있도록 다양한 메커니즘을 제공합니다. 대표적인 방식은 다음과 같습니다.

  • JNI (Java Native Interface): Java 코드에서 C/C++ 코드를 호출하는 표준 방식
  • Android NDK (Native Development Kit): 네이티브 코드를 작성하여 Android 애플리케이션에서 사용할 수 있도록 하는 도구
  • HIDL (Hardware Interface Definition Language): HAL을 통해 네이티브 드라이버와 프레임워크를 연결
  • AIDL (Android Interface Definition Language): 프로세스 간 통신(IPC)을 위해 사용되며, 네이티브 서비스와 연동 가능

이 글에서는 프레임워크 서비스 내에서 네이티브 라이브러리를 활용하는 방법과 JNI를 이용하여 System API를 확장하는 방법을 중점적으로 다룹니다.

2. 네이티브 라이브러리를 프레임워크 서비스에서 활용하는 방법

Android 프레임워크 서비스는 Java 기반으로 작성되지만, 네이티브 라이브러리를 로드하여 사용할 수 있습니다. 이를 위해서는 다음과 같은 과정이 필요합니다.

2.1 네이티브 라이브러리 빌드 및 배포

네이티브 라이브러리는 일반적으로 Android.bp 파일을 이용하여 빌드할 수 있습니다. 예제 코드를 통해 살펴보겠습니다.

Android.bp 파일 작성

cc_library_shared {
    name: "libnative_example",
    srcs: ["native_example.cpp"],
    shared_libs: ["liblog"],
    cflags: ["-Wall"],
}

위와 같이 설정하면 libnative_example.so라는 공유 라이브러리가 생성됩니다.

2.2 Java 프레임워크에서 네이티브 라이브러리 로드

Android의 Java 서비스에서 네이티브 라이브러리를 로드하기 위해 System.loadLibrary()를 사용할 수 있습니다.

public class NativeService {
    static {
        System.loadLibrary("native_example");
    }

    public native String getNativeMessage();
}

여기서 getNativeMessage() 메서드는 네이티브 코드에서 구현되어야 합니다.

2.3 JNI를 이용한 네이티브 함수 구현

C++ 코드에서 JNI 함수를 구현하는 방식은 다음과 같습니다.

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_NativeService_getNativeMessage(JNIEnv *env, jobject /* this */) {
    std::string message = "Hello from Native Code";
    return env->NewStringUTF(message.c_str());
}

이제 Java 서비스에서 getNativeMessage()를 호출하면 네이티브 코드에서 반환된 문자열을 사용할 수 있습니다.

3. JNI를 사용한 System API 확장

Android 시스템 API를 확장하려면 frameworks/base 내의 Java 서비스와 native 코드 간의 연동이 필요합니다.

3.1 System API 정의

우선, IExampleService.aidl 파일을 생성하여 시스템 서비스를 정의합니다.

interface IExampleService {
    String getNativeMessage();
}

3.2 프레임워크 서비스 구현

ExampleService.java 파일을 작성하여 AIDL 인터페이스를 구현합니다.

public class ExampleService extends IExampleService.Stub {
    static {
        System.loadLibrary("native_example");
    }

    public native String getNativeMessage();
}

3.3 네이티브 코드 구현

JNI를 사용하여 시스템 API를 구현합니다.

#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_android_server_ExampleService_getNativeMessage(JNIEnv *env, jobject /* this */) {
    return env->NewStringUTF("System API Extended via JNI");
}

3.4 서비스 등록

프레임워크에 새로운 서비스를 등록하려면 SystemServer.java에서 다음과 같이 추가해야 합니다.

private void startExampleService() {
    ExampleService exampleService = new ExampleService();
    ServiceManager.addService("example", exampleService);
}

이제 Android 시스템이 부팅될 때 ExampleService가 시작되며, AIDL을 통해 네이티브 코드와 연동된 API를 호출할 수 있습니다.

4. 결론

본 글에서는 Android Framework에서 네이티브 코드를 활용하는 방법에 대해 설명하였습니다. 네이티브 라이브러리를 Java 기반의 프레임워크 서비스에서 로드하는 방법과 JNI를 이용하여 System API를 확장하는 과정도 살펴보았습니다.

네이티브 코드를 활용하면 Android 애플리케이션에서 고성능 연산이 가능하며, 기존의 C/C++ 기반 코드를 효과적으로 재사용할 수 있습니다. 그러나 JNI를 사용할 때는 메모리 관리와 성능 이슈를 고려해야 하며, 보안 취약점이 발생하지 않도록 신중하게 설계해야 합니다.

반응형