세월호 참사 1주기 합동분향소 주변풍경

흔치않은일상 2015. 4. 16. 19:13

작년 이맘때 즈음,

저는 학교 PC실 한구석에서 작업을 하고 있었습니다.

잠시 인터넷 포털사이트를 접속하였을 때 보게 된 보게된 사진 한 장.

바로 세월호가 침몰하여 배의 일부분만 바다 위에 떠있던 장면이었습니다.


그 안에 수많은 학생들이 있었다는 사실을 접하게 되었을 때 경악을 금치 못하였습니다.

부디 배 안에 갇힌 학생들이 단 한명이라도 좋으니 무사히 구출되길 간절히 바랐지만

그러한 바람을 마음에 품고 지낸지 벌써 1년이라는 시간이 지나버렸습니다.


여전히 세월호 유족분들은 진상규명을 외치며 거리에 나와계시지만

1주기가 된 지금까지도 해결되지 못한 점이 참으로 마음이 아픕니다.




오늘 구글 메인 화면에는 세월호 유족들을 애도하는 검은 리본이 우리를 반기고 있군요.

이를 보고 오늘이 1주기임을 느낄 수 있었습니다.

아이들이 배와 함께 가라앉았던 사건이 불과 어제의 일인것만 같았던데 말이지요.


그렇게 바쁘게 시간이 흘러 1주기가 된 오늘 세월호 합동분향소에 다녀왔습니다.






안산 세월호 합동분향소는 회랑유원지에 위치해 있습니다.

지도를 보니 4호선 초지역에서 걸어서 갈 만한 거리에 있길래 직접 발걸음을 옮겨보기로 하였습니다.



역에서 내리자마자 제 눈앞에 등장한 현수막입니다.

여전히 부모는 세월호에 오른 아이들을 한없이 기다리고 있습니다.



열심히 걷다보니 어느덧 초지역에서 이만큼 걸어왔습니다.



정신없는 공사현장을 지나 앞으로 쭈욱 나아가다 보면 사거리가 눈에 보일 겁니다.



안산시민이 건 현수막입니다.

현수막 뒤로는 합동분향소 추모행사에 참가하러 가는 아이들이 눈에 보입니다.



아이들의 가슴에는 자신들이 직접 만든 리본을 달고 있더군요.



한 아이의 가방에 보이는 세월호 문구가 제 마음을 짠하게 만들고 있습니다.



가는 길마다 보이는 현수막들은

여전히 작년 이맘때 즈음의 사건을 잊지 않게 해줍니다.



작년 11월 세월호 실종자 수식이 종료된 이후 실종자 9명은 아직까지도 가족들 품에 돌아오지 못하고 있습니다.

실종자 9분들이 모두 가족의 품으로 돌아가기를 간절히 바랍니다.



드디어 합동분양소 입구에 도착하였습니다.



세월호 사고 희생자 합동분향소 전면입니다.



그날을 잊지 않기 위해 사람들이 그린 그림들이 전시되고 있습니다.



그 많은 그림들 중 갖아 인상에 남는 그림중 하나였습니다.

광화문 앞에서 단식농성을 벌이던 유민아빠의 모습을 보고

순간 눈시울이 붉어집니다.



1주기 추모식 행사가 한창 준비중인 모습입니다.

날씨가 좋지 않던데 무사히 진행되었으면 좋겠습니다.




분향소 앞에서 피켓을 들고 서있는 모습입니다.

세월호 참사 1주기가 되는 지금까지도 유가족들은 여전히 밝혀지지 않은 진상규명을 정부에게 요청하고 있습니다.



분향소 한쪽에서는 관련 추모 행사의 포스터가 걸려있는 것을 볼 수 있었습니다.



합동분향소 인근에 위치한 호수입니다.,

아이들이 살아있다면 이 곳에서 정다운 이야기를 하며 하루를 보냈을텐데...



가족들은 여전히 세월호 침몰의 정확한 원인을 알지 못합니다.



그렇기에 이렇게 정부에게 진상규명을 요구하지만

세월호의 진상은 여전히 밝혀지지 않은채 제자리 걸음을 하고 있습니다.



인근 주민센터에는 세월호 노란리본 깃발이 태극기와 함께 조기로 걸려있었습니다.





호수에서 단원고등학교로 가던 길에 갑자기 소나기가 내리기 시작합니다.

지금 아이들이 살아있었다면 이 길을 자기 친구들과 함께 해맑은 표정으로 학교에 가고 있을 모습이 문듯 스쳐갑니다.



2015년 4월 16일 단원고등학교 정문의 풍경입니다.

학교 분위기는 작년과 크게 달라보이지 않습니다.



정문 건너편에서의 풍경입니다. 빗줄기가 제법 굵어졌습니다.



학교 건너편에서는 세월호 관련 시민단체 분들이 모여 희생자 분의 이야기를 듣고 있습니다.

단원고 마지막 생존자가 물속에서 나오는 순간에 대한 이야기를 들으니

배 안에서 짫은 인생을 마감한 아이들의 슬픈 얼굴이 떠올랐습니다.



그렇게 합동분향소를 떠나 버스정류장에 도착한 오늘

2015년 4월 16일 목요일입니다......



▶◀ 세월호에 탑승하였다가 희생된 분들의 명복을 빕니다

300x250

안드로이드 프레임워크 프로그래밍(19) [Native 단계의 ServiceManager 동작원리]

안드로이드/프레임워크 2015. 4. 10. 00:33

 지금까지 우리는 안드로이드 내의 System service가 ServiceManager에 의해 관리되고 있음을 알 수 있었습니다. IServiceManager를 통해 BpServiceManager를 생성하여 Binder를 통해 서비스를 등록하거나 찾는 과정 또한 확인했습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
android::sp<IServiceManager> IServiceManager::asInterface(                           
    const android::sp<android::IBinder>& obj)                       
    {
        android::sp<IServiceManager> intr;
        if (obj != NULL) {
            intr = static_cast<IServiceManager*>(
                obj->queryLocalInterface(
                    IServiceManager::descriptor).get());
        if (intr == NULL) {
            intr = new BpServiceManager(obj);
        }
    }
    return intr;
cs


이러한 과정을 거치는 한 편으로는 이런 생각을 가지신 분들도 계시리라 생각합니다.


"BpServiceManager와 BnServiceManager가 존재한다면 ServiceManager 클래스도 존재하지 않을까?"


 대부분의 분들이라면 분명 있으리라 생각하실 겁니다. 그러나 놀랍게도 ServiceManager는 Java 단계에서는 클래스가 존재합니다만 Native 단계에서는 ServiceManager 클래스를 확인하실 수 없습니다. 그렇다면 Native 단계에서는 ServiceManager가 사용되지 않는걸까요?

 사실 Native 단계에서 ServiceManager는 daemon 프로세스와 같이 백그라운드에서 지속적으로 동작하는 프로세스로 존재합니다. 비록 ServiceManager라는 이름은 아니지만 Binder를 등록하거나 검색을 할 수 있는 기능을 갖추어 놓고 있습니다. ServiceManager는 다음과 같은 파일들로 구성되어 있습니다.


/frameworks/native/cmds/servicemanager/binder.h

/frameworks/native/cmds/servicemanager/binder.c

/frameworks/native/cmds/servicemanager/service_manager.c


 시작하기에 앞서 Android.mk에 설정된 모습을 보도록 하겠습니다.

/frameworks/native/cmds/servicemanager/Android.mk

1
2
3
4
5
6
7
8
9
10
11
12
LOCAL_PATH:= $(call my-dir)
 
#include $(CLEAR_VARS)
#LOCAL_SRC_FILES := bctest.c binder.c
#LOCAL_MODULE := bctest
#include $(BUILD_EXECUTABLE)
 
include $(CLEAR_VARS)
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := service_manager.c binder.c
LOCAL_MODULE := servicemanager
include $(BUILD_EXECUTABLE)
cs


 이제 ServiceManager가 실행되는 모습을 보도록 합시다.


/frameworks/native/cmds/servicemanager/service_manager.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void *svcmgr_handle;
 
int main(int argc, char **argv)
{
    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;
 
    bs = binder_open(128*1024);
 
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
 
    svcmgr_handle = svcmgr;
    binder_loop(bs, svcmgr_handler);
    return 0;
}
cs


/frameworks/native/cmds/servicemanager/binder.h

1
2
/* the one magic object */
#define BINDER_SERVICE_MANAGER ((void*0)
cs


 처음엔 binder_state 구조체 변수와 void 변수 svcmgr이 선언됩니다. binder_state에는 이름 그대로 바인더의 상태를 저장하기 위해 사용되는 구조체임을 알 수 있습니다. 여기서 main() 함수의 내용을 하나씩 살펴보겠습니다.


    bs = binder_open(128*1024);


 binder_open() 함수가 선언되어 있고 이를 통해 binder_state 구조체를 return 하는 모습을 보이고 있습니다. binder_open() 함수를 통해 binder가 설정되며 인자로 메모리에 할당할 용량을 설정합니다.


/frameworks/native/cmds/servicemanager/binder.c

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
struct binder_state
{
    int fd;
    void *mapped;
    unsigned mapsize;
};
 
struct binder_state *binder_open(unsigned mapsize)
{
    struct binder_state *bs;
 
    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return 0;
    }
 
    bs->fd = open("/dev/binder", O_RDWR);
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open device (%s)\n",
                strerror(errno));
        goto fail_open;
    }
 
    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }
 
        /* TODO: check version */
 
    return bs;
 
fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return 0;
}
cs

 binder_open() 함수의 내부를 살펴보도록 합시다. open() 함수가 호출됨으로서 바딘더의 File descriptor를 얻어낸 후 mmap() 함수를 호출하여 실제 메모리에 바인더를 할당하는 작업을 진행합니다. 모든 것이 완료되면 binder_state를 저장한 구조체 변수의 포인터를 return 합니다.


binder_become_context_manager(bs)


binder_state의 값을 통하여 컨텍스트 매니저(ServiceManager)을 설정해줍니다.

/frameworks/native/cmds/servicemanager/binder.c

1
2
3
4
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
cs

binder.h

1
#define BINDER_SET_CONTEXT_MGR _IOW('b'7int)
cs

 해당 함수는 ioctl() 함수를 사용하여 바인더의 입출력을 제어합니다.


binder_loop(bs, svcmgr_handler);


 이 함수가 실행됨으로서 ServiceManager의 실행은 loop에 들어갑니다. 즉, 시스템에 특별한 이상이 발생하지 않는 한 작동이 계속 되는 것으로 이해하시면 좋을 듯 합니다.


/frameworks/native/cmds/servicemanager/binder.c

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    unsigned readbuf[32];
 
    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    
    readbuf[0= BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(unsigned));
 
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (unsigned) readbuf;
 
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
 
        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
 
        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}
 
int binder_write(struct binder_state *bs, void *data, unsigned len)
{
    struct binder_write_read bwr;
    int res;
    bwr.write_size = len;
    bwr.write_consumed = 0;
    bwr.write_buffer = (unsigned) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}
 
 
int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uint32_t *ptr, uint32_t size, binder_handler func)
{
    int r = 1;
    uint32_t *end = ptr + (size / 4);
 
    while (ptr < end) {
        uint32_t cmd = *ptr++;
#if TRACE
        fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
        switch(cmd) {
        case BR_NOOP:
            break;
        case BR_TRANSACTION_COMPLETE:
            break;
        case BR_INCREFS:
        case BR_ACQUIRE:
        case BR_RELEASE:
        case BR_DECREFS:
#if TRACE
            fprintf(stderr,"  %08x %08x\n", ptr[0], ptr[1]);
#endif
            ptr += 2;
            break;
        case BR_TRANSACTION: {
            struct binder_txn *txn = (void *) ptr;
            if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
                ALOGE("parse: txn too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;
 
                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn);
                res = func(bs, txn, &msg, &reply);
                binder_send_reply(bs, &reply, txn->data, res);
            }
            ptr += sizeof(*txn) / sizeof(uint32_t);
            break;
        }
        case BR_REPLY: {
            struct binder_txn *txn = (void*) ptr;
            if ((end - ptr) * sizeof(uint32_t) < sizeof(struct binder_txn)) {
                ALOGE("parse: reply too small!\n");
                return -1;
            }
            binder_dump_txn(txn);
            if (bio) {
                bio_init_from_txn(bio, txn);
                bio = 0;
            } else {
                    /* todo FREE BUFFER */
            }
            ptr += (sizeof(*txn) / sizeof(uint32_t));
            r = 0;
            break;
        }
        case BR_DEAD_BINDER: {
            struct binder_death *death = (void**ptr++;
            death->func(bs, death->ptr);
            break;
        }
        case BR_FAILED_REPLY:
            r = -1;
            break;
        case BR_DEAD_REPLY:
            r = -1;
            break;
        default:
            ALOGE("parse: OOPS %d\n", cmd);
            return -1;
        }
    }
 
    return r;
}
cs


binder.h

1
2
3
4
5
6
7
8
9
10
struct binder_write_read {
 signed long write_size;
 signed long write_consumed;
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
 unsigned long write_buffer;
 signed long read_size;
 signed long read_consumed;
 unsigned long read_buffer;
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
};
cs



 제 이전 포스팅에서 Native 단계에서의 Parcel의 전송과정에 대해 다루었던 과정을 보셨던 분이라면 위의 소스코드를 읽었을 때 '앗!'이라는 생각을 하시는 분들이 계시리라 생각합니다. 그렇습니다. Parcel에서 파일이 전송되려 할 때 transact() 함수를 통해 전송되어 오는 Parcel의 값들을 처리하고 있는 것임을 본 포스팅을 통해 확실히 알게 되셨으리라 생각합니다.


 사실 위의 과정에서 좀 더 많은 설명을 해드리고 싶습니다만 글을 더 진행하기엔 포스팅의 분량도 많아질 뿐더러 내용 또한 어려워지기 때문에 이후 시간이 된다면 Parcel이 Binder와 어떻게 동작하는 지에 대해 자세히 다루어 보는 시간을 가져보도록 하겠습니다.

300x250

[C/C++] typedef 함수 포인터 구현원리

프로그래밍 팁 2015. 4. 6. 13:33

 최근 안드로이드 프레임워크를 공부하다보니 JAVA는 물론 JNI를 통해 연결되는 C/C++ 코드들에 대해 빠삭하게 공부를 하고 있습니다. 정말이지 흔히 쓰는 저 언어들에 슬슬 도가 트고 있지 않은가 싶을정도로 자신의 실력에 대해 자만심이 들기도 할 정도입니다.

 소스코드들을 공부하는 과정에서 어려운 부분이 있다면 바로 흔히 사용하지 않는 방식으로 구현된 소스코드를 해석하는 때라고 생각합니다. 특히 수업시간에는 이론만 알고 넘어가는 함수 포인터라는 생소한 개념이 쓰였을 때는 처음엔 이것의 정체 조차 모르는 경우도 허다하지요.


 본론으로 들어가기에 앞서 간단한 소스코드를 통하여 함수 포인터에 대한 기념을 알아보도록 하겠습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<stdio.h>
 
void (*ptrfunc)(int);
 
void testprint(int n){
    printf("Number : %d\n", n);
}
 
int main(){
    testprint(100);
    ptrfunc = testprint;
    ptrfunc(77);
 
    return 0;
}
cs



위의 결과 출력을 보시면 대략적인 함수 포인터의 동작 원리를 이해하실 수 있을 것이라 생각합니다.

여기서 잠시 코드를 좀 더 자세히 설명 드리도록 하겠습니다.


void (*ptrfunc)(int);

함수 포인터는 위에서 보시는 바와 같은 구조로 이루어져 있습니다. 각 부분의 기능은 다음과 같습니다.

return값의 자료형 (*포인터 함수의 이름) (인자값)


 함수 포인터를 사용하실 때 주의하실 점은 함수 포인터가 이용하고자 하는 함수의 return값의 자료형과 인자값의 자료형 및 갯수가 일치해야 사용할 수 있다는 점입니다. 다음 코드를 확인해 봅시다.


ptrfunc = testprint;


 함수 포인터에 사용하고자 하는 함수의 이름을 입력합니다. 위 과정을 통해 기존 포인터와 같이 함수의 주소값이 포인터에 저장됨으로서 해당 함수 포인터는 자신이 가지고 있는 주소값의 함수와 같은 기능을 구현하게 됩니다.


 다음으로 typedef가 사용된 함수 포인터에 대해 살펴보도록 합시다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>
 
typedef void (*ptrfunc)(int);
 
void testprint(int n){
    printf("Number : %d\n", n);
}
 
int main(){
    testprint(100);
    ptrfunc elecs;
    elecs = testprint;
    elecs(77);
 
    return 0;
}
cs


 위에서 설명하였던 소스코드에 typedef를 적용하여 보았습니다. typedef문이 이곳에서는 어떻게 적용되고 있는지 살펴보도록 합시다.


typedef void (*ptrfunc)(int);


 보시는 대로 기존에 있던 함수 포인터가 선언된 부분 앞쪽에 typedef가 선언되어 있는 모습을 보고 계십니다. typedef문은 빈번하게 사용되는 소스코드가 복잡하거나 길 경우 이를 간결하게 사용하기 위한 목적으로 사용되는데요 함수 포인터에서의 typedef문은 지금껏 보았던 typedef문과는 약간 사용되는 방법이 다르지만 결국은 사용되는 목적은 같습니다.


 다음으로 typedef 함수 포인터가 응용되는 부분을 보여드리도록 하겠습니다.


    ptrfunc elecs;
    elecs = testprint;


 응용이라고 말씀드려서 뭔가 거창한 걸 하려나 하겠습니다만 사실 typedef로 선언된 함수 포인터는 위에서 보시는 바와 같이 매우 간결하게 쓰이고 있음을 아실 수 있습니다. ptrfunc로 정의된 typedef문의 함수 포인터를 elecs라는 이름의 함수 포인터 하나를 만들었다고 보시면 됩니다. 쉽게 설명해서 함수포인터인 변수 하나가 생겼다고 생각하시면 됩니다. 아직도 이해가 안되신다면 아래의 간단한 소스코드를 보시면 아하! 하고 이해하실 겁니다.


int elecs;

elecs = 199;


 이제 감이 오시는지요? 그렇습니다! typedef문으로 선언된 함수 포인터는 마치 자료형을 선언하는 것과 같이 간단하게 함수 포인터 변수를 선언한다고 생각하시면 되는 것입니다! 혹시나 해서 아직도 이해하지 못하신 분들을 위해 저 위에 typedef 함수 포인터가 실제로는 어떻게 구현되어 있는지 보여드리겠습니다.


    void (*elecs)(int);
    elecs = testprint;


 위에서 보시는 바와 같이 ptrfunc 부분이 elecs로 치환된 것이라고 생각하시면 제 설명을 정확히 이해하시는 것입니다!


 혹시 typedef 함수 포인터의 원리에 대해 알고자 하셔서 오신 분들이라면 포스팅을 여기까지만 읽어주셔도 자신의 실력으로 함수 포인터를 활용하실 수 있으리라 생각합니다. 아래에서 부터는 다소 어려우니 기죽지 마시고 이렇게 활용되고 있구나 하는 생각으로 읽어주셨으면 합니다.


 그렇다면 이제 실전에서 사용되고 있는 코드를 보도록 하겠습니다. 아래의 소스코드는 안드로이드 내에서 구현된 함수 포인터 입니다. 언어는 C++로 구성되어 있습니다만 함수포인터를 설멍하는데 큰 어려움은 없을 것입니다. 소스는 다음과 같습니다.


/frameworks/av/camera/CameraBase.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
template <typename TCam, typename TCamTraits>
sp<TCam> CameraBase<TCam, TCamTraits>::connect(int cameraId,
                                               const String16& clientPackageName,
                                               int clientUid)
{
    ALOGV("%s: connect", __FUNCTION__);
    sp<TCam> c = new TCam(cameraId);
    sp<TCamCallbacks> cl = c;
    status_t status = NO_ERROR;
    const sp<ICameraService>& cs = getCameraService();
 
    if (cs != 0) {
        TCamConnectService fnConnectService = TCamTraits::fnConnectService;
        status = (cs.get()->*fnConnectService)(cl, cameraId, clientPackageName, clientUid,
                                             /*out*/ c->mCamera);
    }
    if (status == OK && c->mCamera != 0) {
        c->mCamera->asBinder()->linkToDeath(c);
        c->mStatus = NO_ERROR;
    } else {
        ALOGW("An error occurred while connecting to camera: %d", cameraId);
        c.clear();
    }
    return c;
}
cs


 여기서 참으로 특이한 구조의 소스코드를 만나게 되었습니다.

TCamConnectService fnConnectService = TCamTraits::fnConnectService;


이제 이 부분이 어떻게 구현되었는지 자세히 보도록 합시다.

/frameworks/av/include/camera/CameraBase.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
template <typename TCam>
struct CameraTraits {
};
 
template <typename TCam, typename TCamTraits = CameraTraits<TCam> >
class CameraBase : public IBinder::DeathRecipient
{
public:
    typedef typename TCamTraits::TCamListener       TCamListener;
    typedef typename TCamTraits::TCamUser           TCamUser;
    typedef typename TCamTraits::TCamCallbacks      TCamCallbacks;
    typedef typename TCamTraits::TCamConnectService TCamConnectService;
....
}
cs

위 코드를 통하여 다음과 같은 사실을 알아내었습니다.

    typedef typename TCamTraits::TCamConnectService TCamConnectService;


 TCamConnectService로 정의된 부분이 CameraTraits<TCam>::TCamConnectService와 동일하다는 것을 알고 다음으로 CameraTraits에 대해 확인해 보도록 하겠습니다.


/frameworks/av/include/camera/Camera.h

1
2
3
4
5
6
7
8
9
10
11
12
template <>
struct CameraTraits<Camera>
{
    typedef CameraListener        TCamListener;
    typedef ICamera               TCamUser;
    typedef ICameraClient         TCamCallbacks;
    typedef status_t (ICameraService::*TCamConnectService)(const sp<ICameraClient>&,
                                                           intconst String16&, int,
                                                           /*out*/
                                                           sp<ICamera>&);
    static TCamConnectService     fnConnectService;
};
cs


위 코드에서 정말 요상하게 친구가 하나 보이는군요.

typedef status_t (ICameraService::*TCamConnectService)(const sp<ICameraClient>&,

                                                           intconst String16&, int,
                                                           /*out*/
                                                           sp<ICamera>&);


 위에서 배운 바와 같이 해당 코드는 typedef 함수 포인터입니다. 다만 포인터 함수의 이름이 참으로 독특한데 이는 멤버 포인터라는 C++에서 사용되고 있는 기능입니다. 멤버 포인터에 대해 좀 더 자세히 알고 싶으신 분은 아래 포스팅을 참조해 주시기 바랍니다.

http://showmiso.tistory.com/210


바로 그 아래에는 typedef로 선언된 함수 포인터에 대한 변수를 static으로 선언되었음을 확인하실 수 있습니다.


 static TCamConnectService     fnConnectService;


 이제 여기서 다시 앞에서 확인하였던 선언문을 다시 한 번 보도록 합니다.


TCamConnectService fnConnectService = TCamTraits::fnConnectService;


 위 소스코드는 TCamConnectService로 선언된 typedef 함수 포인터를 가진 변수명 fnConnectService 안에 TCamTratis::fnConnectService 함수의 주소값을 넣겠다는 의미로 이해해 주시면 되겠습니다. 그렇다면 여기서 TCamTratis::fnConnectService 함수는 어떻게 구현되었는지 찾아보도록 하겠습니다.


/frameworks/av/camera/Camera.cpp

1
2
CameraTraits<Camera>::TCamConnectService CameraTraits<Camera>::fnConnectService =
        &ICameraService::connect;
cs


 Camera.cpp 소스 코드 내에서 'CameraTraits<Camera>::fnConnectService' 라는 이름의 포인터 함수 변수가 선언되었고 해당 포인터 함수에 ICameraService::connect 함수의 주소를 넣어준다고 이해하시면 되겠습니다.


/frameworks/av/include/camera/ICameraService.h

1
2
3
4
5
6
7
8
9
10
11
12
class ICameraService : public IInterface
{
public:
....
    virtual status_t connect(const sp<ICameraClient>& cameraClient,
            int cameraId,
            const String16& clientPackageName,
            int clientUid,
            /*out*/
            sp<ICamera>& device) = 0;
....
}
cs


/frameworks/av/camera/ICameraService.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
class BpCameraService: public BpInterface<ICameraService>
{
public:
    BpCameraService(const sp<IBinder>& impl)
        : BpInterface<ICameraService>(impl)
    {
    }
....
    // connect to camera service (android.hardware.Camera)
    virtual status_t connect(const sp<ICameraClient>& cameraClient, int cameraId,
                             const String16 &clientPackageName, int clientUid,
                             /*out*/
                             sp<ICamera>& device)
    {
        Parcel data, reply;
        data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
        data.writeStrongBinder(cameraClient->asBinder());
        data.writeInt32(cameraId);
        data.writeString16(clientPackageName);
        data.writeInt32(clientUid);
        remote()->transact(BnCameraService::CONNECT, data, &reply);
 
        if (readExceptionCode(reply)) return -EPROTO;
        status_t status = reply.readInt32();
        if (reply.readInt32() != 0) {
            device = interface_cast<ICamera>(reply.readStrongBinder());
        }
        return status;
    }
....
}
cs

 드디어 우리는 connect 함수를 찾아내는 데 성공하였습니다. 이로서 함수 포인터가 정의 되는 과정을 모두 살펴보았습니다. 마지막으로 이 기나긴 여정을 코드로 간결하게 요약하자면 다음과 같습니다.


TCamConnectService fnConnectService = TCamTraits::fnConnectService;


위 코드는 아래와 같이 변동이 됨을 확인하실 수 있습니다.


status_t (ICameraService::*TCamConnectService)(const sp<ICameraClient>&,

                                                           intconst String16&, int,
                                                           /*out*/
                                                           sp<ICamera>&);

TCamConnectService =
&ICameraService::connect;



 위의 코드를 처음 보시는 분들은 이 시점에서도 모두 이해가 안 되실 수 있습니다. 하지만 위 코드에서 일정 부분 만이라도 이해하셨다면 여러분들은 성공하신 겁니다!


300x250