안드로이드 프레임워크 프로그래밍(12) [IServiceManager 등록과정]

안드로이드/프레임워크 2015. 3. 23. 01:43

 처음 안드로이드 프레임워크를 분석하게 되었을 때엔 적어도 Java Framework 단계에서 모든 작업을 해결할 수 있을 듯해 보였습니다만 최근 연구하는 Camera의 경우 안드로이드 내 리눅스 커널 단계까지 연구하고 있는 제 모습을 보고 있습니다...


 이번 포스팅에서는 CameraService와 같이 Hardware Service들을 IServiceManager에 등록되는 과정에 대해 샅샅히 살펴보고자 합니다. 본 포스팅에서는 CameraService.cpp를 예로 들어볼 것입니다.


 카메라 작동과 관련된 동작을 관리하는 CameraService는 main_mediaserver.cpp에 의해 안드로이드 시스템 내에 등록이 되어집니다.

/frameworks/av/media/mediaserver/Android.mk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
LOCAL_PATH:= $(call my-dir)
 
ifneq ($(BOARD_USE_CUSTOM_MEDIASERVEREXTENSIONS),true)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := register.cpp
LOCAL_MODULE := libregistermsext
LOCAL_MODULE_TAGS := optional
include $(BUILD_STATIC_LIBRARY)
endif
 
include $(CLEAR_VARS)
 
LOCAL_SRC_FILES:= \
    main_mediaserver.cpp 
 
LOCAL_SHARED_LIBRARIES := \
    libaudioflinger \
    libcameraservice \
    libmedialogservice \
    libcutils \
    libnbaio \
    libmedia \
    libmediaplayerservice \
    libutils \
    liblog \
    libbinder
 
LOCAL_STATIC_LIBRARIES := \
    libregistermsext
 
LOCAL_C_INCLUDES := \
    frameworks/av/media/libmediaplayerservice \
    frameworks/av/services/medialog \
    frameworks/av/services/audioflinger \
    frameworks/av/services/camera/libcameraservice
 
LOCAL_MODULE:= mediaserver
 
include $(BUILD_EXECUTABLE)
 
cs


/frameworks/av/media/mediaserver/main_mediaserver.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#define LOG_TAG "mediaserver"
....
#include <binder/IServiceManager.h>
....
#include "CameraService.h"
 
using namespace android;
 
int main(int argc, char** argv)
{
    signal(SIGPIPE, SIG_IGN);
    char value[PROPERTY_VALUE_MAX];
    bool doLog = (property_get("ro.test_harness", value, "0"> 0) && (atoi(value) == 1);
    pid_t childPid;
    if (doLog && (childPid = fork()) != 0) {
        ....
    } else {
        // all other services
        if (doLog) {
            prctl(PR_SET_PDEATHSIG, SIGKILL);   // if parent media.log dies before me, kill me also
            setpgid(00);                      // but if I die first, don't kill my parent
        }
    ....
        CameraService::instantiate();
    ....
    }
}
cs


 위의 소스코드에서 보시는 바와 같이 CameraService::instantiate() 함수가 실행됨으로서 ServiceManager에 등록되는 과정이 진행됩니다. 그런데 막상 CameraService.cpp 내에서 imstantiate() 함수는 눈을 씻고 보아도 어디에도 보이지 않습니다. 이는 즉 다른 클래스에서 상속되었다는 뜻으로 볼 수 있을텐데요. 그렇다면 이를 직접 찾아보도록 하겠습니다.


/frameworks/av/services/camera/libcameraservice/CameraService.h

1
2
3
4
5
6
7
8
9
10
11
12
....
#include <binder/BinderService.h>
....
class CameraService :
    public BinderService<CameraService>,
    public BnCameraService,
    public IBinder::DeathRecipient,
    public camera_module_callbacks_t
{
    friend class BinderService<CameraService>;
....
}
cs

/frameworks/native/include/binder/BinderService.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#ifndef ANDROID_BINDER_SERVICE_H
#define ANDROID_BINDER_SERVICE_H
 
#include <stdint.h>
 
#include <utils/Errors.h>
#include <utils/String16.h>
 
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
 
// ---------------------------------------------------------------------------
namespace android {
 
template<typename SERVICE>
class BinderService
{
public:
    static status_t publish(bool allowIsolated = false) {
        sp<IServiceManager> sm(defaultServiceManager());
        return sm->addService(
                String16(SERVICE::getServiceName()),
                new SERVICE(), allowIsolated);
    }
 
    static void publishAndJoinThreadPool(bool allowIsolated = false) {
        publish(allowIsolated);
        joinThreadPool();
    }
 
    static void instantiate() { publish(); }
 
    static status_t shutdown() { return NO_ERROR; }
 
private:
    static void joinThreadPool() {
        sp<ProcessState> ps(ProcessState::self());
        ps->startThreadPool();
        ps->giveThreadPoolName();
        IPCThreadState::self()->joinThreadPool();
    }
};
 
 
}; // namespace android
// ---------------------------------------------------------------------------
#endif // ANDROID_BINDER_SERVICE_H
 
 
cs


 위의 코드에서 보시는 바와 같이 instantiate() 함수는 BinderService 클래스에서 이루어지고 있는 모습을 보실 수 있습니다. 그리고 그 안에는 publish() 함수가 실행되고 있으며 publish() 함수는 CameraService를 IServiceManager에 등록하고 있는 과정을 보이고 있습니다.


return sm->addService(

                String16(SERVICE::getServiceName()),
                new SERVICE(), allowIsolated);


static char const* getServiceName() { return "media.camera"; }



 위 코드를 통해 CameraService는 IServiceManager에 등록됩니다.

/frameworks/native/libs/binder/IServiceManager.cpp

1
2
3
4
5
6
7
8
9
10
11
12
    virtual status_t addService(const String16& name, const sp<IBinder>& service,
            bool allowIsolated)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }
 
cs






300x250

안드로이드 프레임워크 프로그래밍(11) [JAVA 프레임워크와 Native 프레임워크 연결 원리]

안드로이드/프레임워크 2015. 3. 18. 02:49

 어느덧 안드로이드 프레임워크를 연구한지 3달 정도 되어갑니다. 초반에는 온갖 삽질이 길어져서 작업 하나를 수행하는 데에도 많은 시간이 소요되었습니다만 현재 조금 프로그램의 흐름을 볼 수 있는 눈이 생겨 어느 정도 감을 잡아가는 단계가 되었다고 조심스럽게 말해봅니다.


 이번 포스팅에서는 안드로이드 기기가 부팅된 후 Java와 Native 프레임워크가 JNI로 연결되는 과정에 대해 다루어 보도록 하겠습니다.


 이전 포스팅에서 service의 JNI 연결과정을 다룬 바 있습니다. 이번 포스팅에서는 '/framework/base/core'에  있는 프레임워크들이 JNI를 통해 Native와 서로 연결되는 과정을 다루게 됩니다. Java Service와 Native Service의 연결 과정에 대해서는 아래 포스팅을 참조해 주시길 바랍니다.


http://elecs.tistory.com/75


 본 포스팅은 안드로이드 카메라 시스템인 Camera.java와 android_hardware_Camera.cpp가 JNI로 함수가 연결되는 과정을 예로 설명드리겠습니다.


 안드로이드 Framework는 기기의 전원이 들어온 후 부팅이 완료되었을 때 init를 통해 zygote가 실행됩니다. 이 zygote가 동작하는 과정에서 AndroidRuntime::start() 함수를 소환함으로서 안드로이드 기기의 동작을 준비하는 과정을 거칩니다. 해당 코드를 살펴보면 다음과 같습니다.


/frameworks/base/core/jni/AndroidRuntime.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void AndroidRuntime::start(const char* className, const char* options)
{
    ....
 
    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
 
    ....
}
 
 
cs


 AndroidRuntime::start() 함수가 실행된 후 startReg()함수를 불러들이는 모습입니다. startReg() 함수를 통해 JNI가 동작되는 함수들을 실행하게 되는데 해당 함수를 자세히 살펴보도록 하겠습니다.


/frameworks/base/core/jni/AndroidRuntime.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
 * Register android native functions with the VM.
 */
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
    /*
     * This hook causes all future threads created in this process to be
     * attached to the JavaVM.  (This needs to go away in favor of JNI
     * Attach calls.)
     */
    androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
 
    ALOGV("--- registering native functions ---\n");
 
    /*
     * Every "register" function calls one or more things that return
     * a local reference (e.g. FindClass).  Because we haven't really
     * started the VM yet, they're all getting stored in the base frame
     * and never released.  Use Push/Pop to manage the storage.
     */
    env->PushLocalFrame(200);
 
    if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }
    env->PopLocalFrame(NULL);
 
    //createJavaThread("fubar", quickTest, (void*) "hello");
 
    return 0;
}
cs


 startReg() 함수 내에서 JNI를 등록하는 register_jni_procs()함수가 동작하는 것을 확인하실 수 있습니다. 그 안에 있는 인자인 gRegJNI는 JNI로 연결시킬 Register 함수들이 등록되어있는 구조체입니다. 코드를 통해 좀 더 자세히 구성요소를 확인해 보도록 하겠습니다.


/frameworks/base/core/jni/AndroidRuntime.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
extern int register_android_hardware_Camera(JNIEnv *env);
 
#ifdef NDEBUG
    #define REG_JNI(name)      { name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
    };
#else
    #define REG_JNI(name)      { name, #name }
    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
        const char* mName;
    };
#endif
 
typedef void (*RegJAMProc)();
 
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 0) {
#ifndef NDEBUG
            ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
            return -1;
        }
    }
    return 0;
}
 
static const RegJNIRec gRegJNI[] = {
 
    ....
 
    REG_JNI(register_android_hardware_Camera),
 
    ....
 
};
 
 
cs


 위 코드 내용을 읽어보니 상당히 흥미로운 부분을 확인할 수 있었습니다. 각 함수들을 하나씩 살펴보도록 하겠습니다.


static const RegJNIRec gRegJNI[]

 구조체 RegJNIRec를 배열로 선언하고 있습니다. 배열은 초기화가 진행중인데 그 안에 있는 REG_JNI()는 코드 내에서 define 되어 있는 것을 확인하실 수 있습니다.


#define REG_JNI(name)      { name }

    struct RegJNIRec {
        int (*mProc)(JNIEnv*);
};


위에서 보시면 아시듯 'REG_JNI(name)'으로 선언된 부분이 { name }으로 변경되고 있는 것을 확인하실 수 있습니다. 그래고 바로 아래에는 구조체 RegJNIRec가 선언되고 있는 것을 보실 수 있습니다. 구조체 내부에 있는 int(*mProc)(JNIEnv*)는 포인터 함수로서 extern으로 선언되어 있던 함수 register_android_hardware_Camera()를 등록하는 구조임을 알 수 있습니다.


static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)

위 함수를 통해 드디어 본격적으로 JNI가 동작하게 됩니다. 함수 내부의 코드를 살펴보면 다음과 같은 것을 보실 수 있습니다.

if (array[i].mProc(env) < 0)

 위에서 설명한 gRegJNI[] 배열 내에 있던 구조체 RegJNIRec 내에 설정했던 함수 포인터들이 위 코드를 통해 반복문으로 모두 실행하는 것을 보실 수 있습니다. 위 과정을 실행하게 되면 프레임워크 내에 존재하는 모든 Java와 Native 함수들이 연결됩니다.

 다음으로 우리들이 등록하였던 register_android_hardware_Camera()함수를 보도록 하겠습니다.


/frameworks/base/core/jni/android_hardware_Camera.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static JNINativeMethod camMethods[] = {
 
   ....
 
  { "native_setup",
    "(Ljava/lang/Object;ILjava/lang/String;)V",
    (void*)android_hardware_Camera_native_setup },
 
    ....
 
};
 
// Get all the required offsets in java class and register native functions
int register_android_hardware_Camera(JNIEnv *env)
{
    ....
 
    // Register native functions
    return AndroidRuntime::registerNativeMethods(env, "android/hardware/Camera",
                                              camMethods, NELEM(camMethods));
}
cs


 위 코드를 보았을 때 JNINativeMethod 배열을 선언하는 과정을 보실 수 있는데 해당 배열 내에 선언된 값을 하나 보시면 다음과 같습니다.

  { "native_setup",
    "(Ljava/lang/Object;ILjava/lang/String;)V",
    (void*)android_hardware_Camera_native_setup }

 위의 구조를 보았을 때 JNINativeMethod가 struct 구조체로 구성되어 있다는 사실을 어느 정도 직감하실 수 있으실 겁니다! 해당 구조체 내에는 3개의 변수를 저장할 수 있는대 내용은 다음과 같습니다.


{ Java에서 선언된 native method, Signature, C++에서 연결하고자 하는 함수 }

위와 같은 방식으로 값들을 선언해 주시면 되겠습니다. 위에서 Signature는 Java에서 선안된 함수의 인자와 return을 간단하게 나타낸 것을 말합니다. Signature에 대해 좀 더 자세히 알고 싶으신 분들은 아래 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/75


다음으로 AndroidRuntime.cpp에서 연결했었던 register_android_hardware_Camera(JNIEnv *env)함수를 확인해 보면 리턴형으로 다시 AndroidRuntime 내의 함수를 호출하고 있는 모습을 보실 수 있습니다. 이를 다시 AndroidRuntime.cpp 에서 확인해 보도록 하겠습니다.



/frameworks/base/core/jni/AndroidRuntime.cpp

1
2
3
4
5
6
7
8
/*
 * Register native methods using JNI.
 */
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
    const char* className, const JNINativeMethod* gMethods, int numMethods)
{
    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
cs


 드디어 이번 포스팅의 핵심이라 할 수 있는 jniRegisterNativeMethods()함수가 등장하였습니다! 해당 함수가 실행되게 되면 C++과 Java와의 함수가 최종적으로 연결됩니다. 해당 함수의 인자는 다음과 같이 구성되어 있습니다.

(Java VM 환경변수, 연결하고자 하는 Class의 경로, Native와 Java 사이에 연결하고자 하는 함수들의 내역이 있는 구조체 JNINativeMethod, gMethod의 길이);


위의 과정을 통해 연결된 Java 매소드는 해당 코드를 통하여 확실하게 확인하실 수 있습니다!


/frameworks/base/core/java/android/hardware/Camera.java

1
2
3
4
5
6
7
8
9
public class Camera {
 
    ....
    private native final void native_setup(Object camera_this, int cameraId,
                                           String packageName);
 
    ....
 
}
cs


/frameworks/base/core/jni/android_hardware_Camera.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// connect to camera service
static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
    jobject weak_this, jint cameraId, jstring clientPackageName)
{
    // Convert jstring to String16
    const char16_t *rawClientName = env->GetStringChars(clientPackageName, NULL);
    jsize rawClientNameLen = env->GetStringLength(clientPackageName);
    String16 clientName(rawClientName, rawClientNameLen);
    env->ReleaseStringChars(clientPackageName, rawClientName);
 
    sp<Camera> camera = Camera::connect(cameraId, clientName,
            Camera::USE_CALLING_UID);
 
    if (camera == NULL) {
        jniThrowRuntimeException(env, "Fail to connect to camera service");
        return;
    }
 
    // make sure camera hardware is alive
    if (camera->getStatus() != NO_ERROR) {
        jniThrowRuntimeException(env, "Camera initialization failed");
        return;
    }
 
    jclass clazz = env->GetObjectClass(thiz);
    if (clazz == NULL) {
        jniThrowRuntimeException(env, "Can't find android/hardware/Camera");
        return;
    }
 
    // We use a weak reference so the Camera object can be garbage collected.
    // The reference is only used as a proxy for callbacks.
    sp<JNICameraContext> context = new JNICameraContext(env, weak_this, clazz, camera);
    context->incStrong((void*)android_hardware_Camera_native_setup);
    camera->setListener(context);
 
    // save context in opaque field
    env->SetIntField(thiz, fields.context, (int)context.get());
}
cs


300x250

티스토리 블로그를 반년간 하면서...

공대생의 팁 2015. 3. 12. 00:28

 안녕하세요. '늦깎이 공대생의 좌충우돌 이야기'를 연재하고 있는 흔한 공대생입니다!


 블로그를 시작한 지도 어느덧 반년이 넘어가고 있습니다. 그 동안 글을 쓰면서 많은 생각을 해 보았고 어떻게 하면 사람들에게 나의 주장을 좀 더 쉽게 이해할 수 있게 적을 수 있을까 하는 고민도 해보기도 합니다. 글을 쓰면서도 이 글이 과연 사람들에게 도움을 줄 수는 있을까 하는 생각도 해보고 내가 쓴 글을 통해 사람들이 제 글을 읽어주시길 바라는 마음도 들기도 했습니다.


 지금껏 블로그를 운영하면서 이렇게 블로그에 큰 애정을 쏟아본 것도 처음이고 블로그라는 사람들과 소통할 수 있다는 것도 저에게는 큰 재미 중 하나 이기도 합니다. 초기에 블로그가 개설되었을 당시에는 이틀에 한 명 들어올까 말까 하던 제 블로그가 현재는 하루에 50명 정도 방문해 주시고 계십니다. 지금까지 작성된 포스팅이 80개 정도 되는 것을 생각한다면 참으로 많은 분들에 제 블로그에 와주신다는 것에 가끔은 행복하기도 합니다.


 비록 하루에 만 명 단위로 사람들이 오는 블로그들에 비하면 초라하지만 제 포스팅이 하나 둘 늘어날 수록 방문해주시는 분들이 점점 늘어나고 있는 모습을 볼 때마다 항상 행복한 생각이 많이 듭니다. 이 자리를 빌어 제 블로그를 찾아와주신 분들께 진심으로 감사의 말씀을 드리고 싶습니다!


 처음 블로그를 만들던 당시에는 제 취미 중 하나인 여행기도 함께 포스팅을 해보려 했습니다만, 역시 공대생 답게 공부한 내용들에 대한 포스팅이 태반인건 다소 아쉬운 느낌이 듭니다. 저도 가끔은 계속 미루기만 하던 여행기를 다른 분들이 생각치 못한 관점으로 사람들에게 보여주고자 하는 생각도 조금은 들기도 합니다.


 그 덕분인지 제 포스팅을 읽고 제게 도움을 청하시는 공대생 분들의 사연을 접하기도 합니다. 심지어 같은 학교에 다니는 같은 작품을 만들고 있는 동료가 저에게 도움을 요청하였던 재미있는 일도 있었습니다. 저 또한 그 분들의 심정을 매우 잘 알기에 자신의 능력 내에서 많은 도움이 될 수 있게 도와주는 것도 어떻게 보면 블로그를 통한 소통의 방법이 아닐까 하는 생각이 듭니다.


 티스토리, 저에게 티스토리는 정말로 좋은 추억을 만들어 주었습니다. 다른 사람들에게 제 지식을 피력할 수 있었던 곳이었고, 저에게 다른 사람들의 이야기를 들려주는 장소가 되어주기도 했었습니다. 저에게 있어 티스토리는 제 일상에서 보지 못했던 사람의 일상을 보여주는 곳이 아닌가 생각합니다.


 비록 지금 저의 필력은 많이 모자릅니다만, 티스토리를 통해 글을 쓰는 데에 좀 더 많은 노력을 하게 되는 기회가 되었으면 합니다. 그리고 좀 더 많은 사람들과 교류하며 다양한 사람사는 이야기를 공유할 수 있는 곳이 되어 주기를 바랍니다!


 ※제게 큰 즐거움을 주셨던 티스토리 블로거 여러분들께 진심으로 감사드립니다!

 

300x250