안드로이드 프레임워크 프로그래밍(18) [Native에서 원하는 ServiceManager 불러오기]

 요즘 안드로이드 Framework를 공부하면서 드는 생각이 있습니다. 정말 구글의 안드로이드 제작자들이 경이롭다고 생각될 정도로 안드로이드 운영체제는 심오하면서도 동작원리가 매우 신기하다는 생각이 듭니다.

 이번 포스팅에서는 Native에서 등록하였던 Service를 ServiceManager에서 불러들이는 과정에 대해 살펴보도록 하겠습니다.


 먼저 예제로 CameraService를 불러들이는 예제를 살펴보도록 하겠습니다.


/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
namespace {
    sp<ICameraService>        gCameraService;
    const int                 kCameraServicePollDelay = 500000// 0.5s
    const char*               kCameraServiceName      = "media.camera";
 
    Mutex                     gLock;
 
    class DeathNotifier : public IBinder::DeathRecipient
    {
    public:
        DeathNotifier() {
        }
 
        virtual void binderDied(const wp<IBinder>& who) {
            ALOGV("binderDied");
            Mutex::Autolock _l(gLock);
            gCameraService.clear();
            ALOGW("Camera service died!");
        }
    };
 
    sp<DeathNotifier>         gDeathNotifier;
}; // namespace anonymous
cs

/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
// establish binder interface to camera service
template <typename TCam, typename TCamTraits>
const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
{
    Mutex::Autolock _l(gLock);
    if (gCameraService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16(kCameraServiceName));
            if (binder != 0) {
                break;
            }
            ALOGW("CameraService not published, waiting...");
            usleep(kCameraServicePollDelay);
        } while(true);
        if (gDeathNotifier == NULL) {
            gDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(gDeathNotifier);
        gCameraService = interface_cast<ICameraService>(binder);
    }
    ALOGE_IF(gCameraService == 0"no CameraService!?");
    return gCameraService;
}
cs

 위에서 보시는 바와 같이 ServiceManager를 통해 getService() 함수로 CameraService의 바인더를 불러오고 있는 모습을 보실 수 있습니다. 그렇다면 위의 바인더가 어떠한 방식으로 호출이 되는지 자세히 알아보도록 하겠습니다.


/frameworks/native/libs/binder/IServiceManager.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
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
 
    virtual sp<IBinder> getService(const String16& name) const
    {
        unsigned n;
        for (n = 0; n < 5; n++){
            sp<IBinder> svc = checkService(name);
            if (svc != NULL) return svc;
            ALOGI("Waiting for service %s...\n", String8(name).string());
            sleep(1);
        }
        return NULL;
    }
 
    virtual sp<IBinder> checkService( const String16& name) const
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
        return reply.readStrongBinder();
    }
....
}
cs

 IServiceManager.cpp 내에 있는 BpServiceManager를 통해 Binder를 받는 과정을 나타내고 있습니다. 위 과정을 마치게 되면 Binder를 CameraService의 Binder를 얻을 수 있게 됩니다.

 getService() 함수를 통해 부르고자 하는 Service의 명을 전달 받은 후 그 안에 있는 checkService()함수를 호출하여 Parcel을 통해 원하는 Service의 Binder를 얻게 됩니다.


혹시 위에서 Binder를 받기 위해 활용되는 Parcel에 대해 자세히 알고 싶으신 분께서는 아래 포스팅을 참조해 주시기 바랍니다.

안드로이드 프레임워크 프로그래밍(15) [Native 단계에서의 Parcel 전송(1)]

안드로이드 프레임워크 프로그래밍(16) [Native 단계에서의 Parcel 전송(2)]


여기서 chechService() 함수의 동작 과정에 대해 살펴보도록 하겠습니다.


Parcel data, reply;

전송보낼 data와 전송을 받을 reply인 Parcel 클래스 변수를 선언합니다.


data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());

data Parcel에 인터페이스 토큰을 작성합니다. 여기서 인자값으로 들어오는 값은 "android.os.IServiceManager"입니다.

위 함수에 대해 좀 더 자세히 알고 싶으신 분은 아래 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/87


data.writeString16(name);

찾고자 하는 System Service의 이름을 입력합니다. 여기에서는 "media.camera"가 되겠군요.


remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);

지금까지 data에 기록하였던 내용을 Binder로 전송합니다. 이 과정을 통해 reply로  data의 내용에 대한 응답인 Parcel을 전송받게 됩니다.


return reply.readStrongBinder();

전달 받은 Parcel인 reply를 통해 Service의 BpBinder를 반환합니다.

여기서 수신된 Parcel로부터 binder를 얻는 과정을 살펴보도록 합니다.


/frameworks/native/libs/binder/Parcel.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
sp<IBinder> Parcel::readStrongBinder() const
{
    sp<IBinder> val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}
 
....
 
status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);
    
    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = static_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }        
    }
    return BAD_TYPE;
}
 
....
 
inline static status_t finish_unflatten_binder(
    BpBinder* proxy, const flat_binder_object& flat, const Parcel& in)
{
    return NO_ERROR;
}
cs

 위 코드의 주요 내용들을 하나씩 살펴보도록 하겠습니다.


const flat_binder_object* flat = in.readObject(false);

수신된 Parcel로부터 값을 읽어들입니다. 이 과정에서 flat_binder_object 구조체값이 넘어오게 됩니다.

이렇게 해서 넘어온 구조체값의 type는 BINDER_TYPE_HANDLE이 설정되어 있습니다.


*out = proc->getStrongProxyForHandle(flat->handle);

flat_binder_object에 저장된 서비스 핸들을 전달하여 BpBinder를 얻습니다. 위 함수의 구조는 다음과 같이 되어 있음을 확인하실 수 있습니다.

아래는 해당 함수가 동작하는 과정에 대해 확인해 보고자 하는 목적으로 첨부합니다.

위 함수에 대해 좀 더 자세한 내용을 참조하고 싶으신 분께서는 아래 포스팅을 참조해 주시길 바랍니다.


http://elecs.tistory.com/89


/frameworks/native/libs/binder/ProcessState.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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
 
    AutoMutex _l(mLock);
 
    handle_entry* e = lookupHandleLocked(handle);
 
    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.
 
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
 
            = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
 
    return result;
}
 
 
cs

return finish_unflatten_binder(

                    static_cast<BpBinder*>(out->get()), *flat, in);

위 과정을 통해 이 함수의 수행이 무사히 수행되었음을 NO_ERROR을 반환함으로서 종료합니다.



이제 이 즈음에서 우리들이 처음부터 연구하였던 소스 CameraBase.cpp의 getService() 이후의 동작들에 대해 파악해 보도록 합시다.


/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
// establish binder interface to camera service
template <typename TCam, typename TCamTraits>
const sp<ICameraService>& CameraBase<TCam, TCamTraits>::getCameraService()
{
    Mutex::Autolock _l(gLock);
    if (gCameraService.get() == 0) {
        sp<IServiceManager> sm = defaultServiceManager();
        sp<IBinder> binder;
        do {
            binder = sm->getService(String16(kCameraServiceName));
            if (binder != 0) {
                break;
            }
            ALOGW("CameraService not published, waiting...");
            usleep(kCameraServicePollDelay);
        } while(true);
        if (gDeathNotifier == NULL) {
            gDeathNotifier = new DeathNotifier();
        }
        binder->linkToDeath(gDeathNotifier);
        gCameraService = interface_cast<ICameraService>(binder);
    }
    ALOGE_IF(gCameraService == 0"no CameraService!?");
    return gCameraService;
}
cs


이제 위에서 남은 두 함수들에 대해 분석을 해 보도록 하겠습니다.


binder->linkToDeath(gDeathNotifier);

 getService() 함수를 통해 얻은 binder에 gDeathNotifier를 틍록합니다. linkToDeath() 함수는 해당 binder가 의도치 않은 상황에서 종료가 되었을 때 해당 클래스를 실행하라는 의도로 생각하시면 되겠습니다.


/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
namespace {
    sp<ICameraService>        gCameraService;
    const int                 kCameraServicePollDelay = 500000// 0.5s
    const char*               kCameraServiceName      = "media.camera";
 
    Mutex                     gLock;
 
    class DeathNotifier : public IBinder::DeathRecipient
    {
    public:
        DeathNotifier() {
        }
 
        virtual void binderDied(const wp<IBinder>& who) {
            ALOGV("binderDied");
            Mutex::Autolock _l(gLock);
            gCameraService.clear();
            ALOGW("Camera service died!");
        }
    };
 
    sp<DeathNotifier>         gDeathNotifier;
}; // namespace anonymous
cs


gCameraService = interface_cast<ICameraService>(binder);

 위 코드를 통해 CameraService가 binder를 통해 등록됩니다. 이 함수가 어떻게 동작하는지 확인해 보도록 하겠습니다. 해당 함수에서 사용되는 기술에 대해 자세히 알고 싶으신 분은 아래 포스팅을 참조해 주시길 바랍니다.


http://elecs.tistory.com/87

http://elecs.tistory.com/88


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

1
2
3
4
5
6
7
8
9
#include <binder/IInterface.h>
 
class ICameraService : public IInterface
{
....
public:
    DECLARE_META_INTERFACE(CameraService);
....
}
cs

/frameworks/native/include/binder/IInterface.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
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}
....
#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const android::String16 descriptor;                          \
    static android::sp<I##INTERFACE> asInterface(                       \
            const android::sp<android::IBinder>& obj);                  \
    virtual const android::String16& getInterfaceDescriptor() const;    \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();                                            \
 
 
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \
....
 
cs


/frameworks/av/camera/ICameraService.cpp

1
2
3
#include <binder/IInterface.h>
 
IMPLEMENT_META_INTERFACE(CameraService, "android.hardware.ICameraService");
cs


 위 내용을 종합하면 다음과 같은 내용임을 알아낼 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    static android::sp<ICameraService> asInterface(
            const android::sp<android::IBinder>& obj);
 
    const android::String16 ICameraService::descriptor("android.hardware.ICameraService");
    android::sp<ICameraService> ICameraService::asInterface(
            const android::sp<android::IBinder>& obj)
    {
        android::sp<ICameraService> intr;
        if (obj != NULL) {
            intr = static_cast<ICameraService*>(
                obj->queryLocalInterface(
                        ICameraService::descriptor).get());
            if (intr == NULL) {
                intr = new BpCameraService(obj);
            }
        }
        return intr;
    } 
cs

위 코드를 통해 gCameraService 내에 BpCameraService 클래스가 선언되는 것을 확인할 수 있다.

300x250

안드로이드 프레임워크 프로그래밍(17) [Binder 드라이버 호출과정]

 안드로이드 Binder 부분을 연구하는데 상당히 많은 지식이 필요하군요. 이제는 안드로이드 Application 단계에서는 상상도 못했던 linux 시스템 프로그래밍 단계까지 내려와보니 분석 난이도도 많이 높아졌음을 실감하고 있습니다.


 이번 포스팅에서는 안드로이드 IPC 통신의 핵심이라 할 수 있는 Binder 드라이버가 호출되는 과정을 살펴보겠습니다. 본 포스팅을 이해하기 위해서는 linux 단계에서의 시스템 프로그래밍을 이해하셔야 이해가 수월하실 듯 합니다. 그럼 분석을 시작해 보도록 하겠습니다.


 Binder 드라이버는 PrecessState가 생성될 때 함께 생성되는 구조로 되어 있습니다. 아래 코드를 보면서 이해해보도록 합시다.


/frameworks/native/libs/binder/ProcessState.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
ProcessState::ProcessState()
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
        // XXX Ideally, there should be a specific define for whether we
        // have mmap (or whether we could possibly have the kernel module
        // availabla).
#if !defined(HAVE_WIN32_IPC)
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
            close(mDriverFD);
            mDriverFD = -1;
        }
#else
        mDriverFD = -1;
#endif
    }
 
    LOG_ALWAYS_FATAL_IF(mDriverFD < 0"Binder driver could not be opened.  Terminating.");
}
cs

  현재 ProcessSate가 생성되면서 Binder 드라이버와 System Service를 등록하기 위한 mVMStart 부분을 보고 계십니다. 여기서 우리는 바인더를 호출하는 open_driver() 부분을 살펴볼 것입니다.


mDriverFD(open_driver())

 open_driver() 함수는 binder의 file descriptor을 반환해줍니다. 이 함수를 통해 Binder가 호출됩니다.


/frameworks/native/libs/binder/ProcessState.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
static int open_driver()
{
    int fd = open("/dev/binder", O_RDWR);
    if (fd >= 0) {
        fcntl(fd, F_SETFD, FD_CLOEXEC);
        int vers;
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        if (result == -1) {
            ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
            close(fd);
            fd = -1;
        }
        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
            ALOGE("Binder driver protocol does not match user space protocol!");
            close(fd);
            fd = -1;
        }
        size_t maxThreads = 15;
        result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
        if (result == -1) {
            ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
        }
    } else {
        ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno));
    }
    return fd;
}
cs

 오늘 제가 주로 설명하게 될 코드입니다. 중요한 부분마다 자세하게 설명을 해 보도록 하겠습니다.


int fd = open("/dev/binder", O_RDWR);

 open() 함수를 통해 binder 파일을 생성합니다. 보시면 아시듯이 binder는 하나의 파일에 불과했다는 것을 아실 수 있습니다.


fcntl(fd, F_SETFD, FD_CLOEXEC);

 fd에 설정된 Binder를 Control 하는 함수 입니다. F_SETFD는 Binder인 fd에 FD_CLOEXEC 플래그를 설정하라는 의미입니다. 이를 설정해주었을 경우 exec를 통해 프로그램이 실행될 때 Binder가 close 되도록 해주는 역할을 합니다.


 FD_CLOEXEC 설정에 대해 좀 더 자세히 알고 싶으신 분은 아래 링크를 통해 확인바랍니다.(영문)

http://stackoverflow.com/questions/6125068/what-does-the-fd-cloexec-fcntl-flag-do


status_t result = ioctl(fd, BINDER_VERSION, &vers);

 Binder 의 입출력 설정을 바꾸어주는 역할을 합니다. BINDER_VERSION은 요청 코드 번호를 말하며, &vers는 포인터를 보내 값을 얻기 위해 사용합니다.


/bionic/libc/kernel/common/linux/binder.h

1
2
3
4
5
6
struct binder_version {
 signed long protocol_version;
};
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
....
#define BINDER_VERSION _IOWR('b'9struct binder_version)
cs


/bionic/libc/kernel/common/asm-generic/ioctl.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
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_SIZEBITS 14
#define _IOC_DIRBITS 2
#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
#define _IOC_NONE 0U
#define _IOC_WRITE 1U
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define _IOC_READ 2U
#define _IOC(dir,type,nr,size)   (((dir) << _IOC_DIRSHIFT) |   ((type) << _IOC_TYPESHIFT) |   ((nr) << _IOC_NRSHIFT) |   ((size) << _IOC_SIZESHIFT))
extern unsigned int __invalid_size_argument_for_IOC;
#define _IOC_TYPECHECK(t)   ((sizeof(t) == sizeof(t[1]) &&   sizeof(t) < (1 << _IOC_SIZEBITS)) ?   sizeof(t) : __invalid_size_argument_for_IOC)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
....
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
cs

size_t maxThreads = 15;
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
 Binder의 최대 쓰레드 수를 15개로 한 후 이를 ioctl을 통해 설정해주는 모습입니다.

return fd;

return을 통해 Binder의 File Description을 반환해 줍니다.

300x250

안드로이드 프레임워크 프로그래밍(16) [Native 단계에서의 Parcel 전송(2)]

안드로이드/프레임워크 2015. 3. 31. 00:05

  지난 포스팅에서 Native 단계에서 Parcel 클래스가 전송되는 과정에 대하여 다루어 보았었습니다. 이번 포스팅은 전송된 Parcel을 수신하고 해당 Parcel을 풀어보는 과정에 대해 분석해 보도록 하겠습니다.

 Parcel이 Native 단계에서 전송되는 과정에 대한 자세한 사항은 이전 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/90


지난 포스팅에서 Parcel이 전송되는 과정에 이어 Parcel이 Binder를 통해 수신되는 과정을 살펴보겠습니다.


/frameworks/native/libs/binder/IPCThreadState.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
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
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;
 
    while (1) {
        if ((err=talkWithDriver()< NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0continue;
        
        cmd = mIn.readInt32();
        
        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
                << getReturnString(cmd) << endl;
        }
 
        switch (cmd) {    
....
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;
 
                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(size_t),
                            freeBuffer, this);
                    } else {
                        err = *static_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(size_t), this);
                    }
                } else {
                    freeBuffer(NULL,
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(size_t), this);
                    continue;
                }
            }
            goto finish;
 
        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }
 
finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }
    
    return err;
}
cs

 위에서 작성된 코드들 중 중요한 부분들에 대해 살펴보겠습니다.


err=talkWithDriver()

바인더 IPC 데이터가 저장된 mOut을 Binder에 전송하며 또한 수신된 데이터를 mIn에 저장하는 과정입니다.


cmd = mIn.readInt32();

 수신된 Binder를 확인하는 부분입니다. min 내에 저장된 값을 불러내어 Parcel의 상태값을 얻어냅니다. 이후 switch()문을 통해 수신된 Parcel를 처리하는 과정을 거치게 됩니다.


 cmd에는 바인더 드라이버로부터 온 Parcel 클래스를 읽어 오게 되며 해당 부분에는 BR_REPLY가 저장되 있습니다. switch() 문을 통해 BR_REPLY가 실행되며 binder_transaction_data의 값을 수신하게 됩니다.


reply->ipcSetDataReference()

포인터로 선언되었던 reply Parcel에 데이터를 저장하는 과정입니다. 해당 함수에 대해 자세히 들여다보도록 합니다.


/frameworks/native/libs/binder/Parcel.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
void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
    const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)
{
    size_t minOffset = 0;
    freeDataNoInit();
    mError = NO_ERROR;
    mData = const_cast<uint8_t*>(data);
    mDataSize = mDataCapacity = dataSize;
    //ALOGI("setDataReference Setting data size of %p to %lu (pid=%d)\n", this, mDataSize, getpid());
    mDataPos = 0;
    ALOGV("setDataReference Setting data pos of %p to %d\n"this, mDataPos);
    mObjects = const_cast<size_t*>(objects);
    mObjectsSize = mObjectsCapacity = objectsCount;
    mNextObjectHint = 0;
    mOwner = relFunc;
    mOwnerCookie = relCookie;
    for (size_t i = 0; i < mObjectsSize; i++) {
        size_t offset = mObjects[i];
        if (offset < minOffset) {
            ALOGE("%s: bad object offset %zu < %zu\n",
                  __func__, offset, minOffset);
            mObjectsSize = 0;
            break;
        }
        minOffset = offset + sizeof(flat_binder_object);
    }
    scanForFds();
}
 
 
cs


위 과정을 통해 reply Parcel의 값이 Binder에 의해 저장이 되었음을 확인하실 수 있습니다.

여기까지 실행하게 되었다면 이제 우리는 IServiceManager에서 transact() 함수를 호출했던 시점으로 다시 돌아가봅니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
....
    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

transact() 함수가 실행이 왼료되면 마지막으로 return 과정을 거치게 됩니다.


/frameworks/native/libs/binder/Parcel.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int32_t Parcel::readExceptionCode() const
{
  int32_t exception_code = readAligned<int32_t>();
  if (exception_code == EX_HAS_REPLY_HEADER) {
    int32_t header_start = dataPosition();
    int32_t header_size = readAligned<int32_t>();
    // Skip over fat responses headers.  Not used (or propagated) in
    // native code
    setDataPosition(header_start + header_size);
    // And fat response headers are currently only used when there are no
    // exceptions, so return no error:
    return 0;
  }
  return exception_code;
}
cs


 이로서 addService()가 Parcel을 마침으로서 하나의 Native System Service가 등록되는 과정을 살펴보았습니다. Parcel은 안드로이드 IPC통신의 핵심인 Binder에서 필히 사용되는 부분이므로 공부하시는 분들께서는 잘 참고해주셨으면 하네요.


 본 포스팅은 사실상 코드만 소개하다 보니 해당 내용이 상당히 복잡하게 보이실 겁니다. 해당 코드에 관련된 이미지 및 자세한 설명은 아래 출저의 책을 참조해주셨으면 합니다.


참고문헌 : 인사이드 안드로이드, 송형주 외 4인 저, 위키북스, 2010



300x250

안드로이드 프레임워크 프로그래밍(15) [Native 단계에서의 Parcel 전송(1)]

안드로이드/프레임워크 2015. 3. 30. 21:41


 지난 포스팅에서 ServiceManager에 등록이 되는 과정에 대해 살펴보았습니다. 이번 포스팅에서는 프로세스간에 데이터를 전송하는 기능인 Parcel에 대해서 알아보도록 하겠습니다.


 시작하기에 앞서 아래 링크를 클릭하여 Java 단계에서의 Parcel 전송에 대해 이해하신 후 본 포스팅을 보신다면 이해가 좀 더 쉬워질 것입니다.


 http://arsviator.blogspot.kr/2010/10/parcelable%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%98%A4%EB%B8%8C%EC%A0%9D%ED%8A%B8-%EC%A0%84%EB%8B%AC-object.html


 Parcel은 안드로이드에서 사용되는 프로세스간 데이터 전송 방식으로 자신의 프로세스에서 제작된 Parcel을 상대방의 프로세스에 전송한 후 전송받은 프로세스에서 받은 Parcel을 풀어서 안에 들어있는 데이터를 사용하는 방식이라 할 수 있습니다.

 

 A프로세스에서 Parcel을 통해 자신이 보내고자 하는 데이터를 만든 후 이를 IPC통신을 통해 B프로세스에서 Parcel 데이터를 읽을 수 있게 하고 이를 받은 B프로세서에서는 Parcel을 해체하는 작업을 거치게 됩니다. 이 때 주의할 점은 A프로세스에서 Parcel을 묶었을 때의 순서에 따라 진행해야 한다는 점입니다.


 대략적인 개념은 이 정도 한 후 실제 코드를 보면서 부연 설명을 덧붙여보도록 하겠습니다.


 지난 포스팅에서 우리는 ServiceManager에 등록되는 과정에서 Parcel이 사용되는 것을 보셨을 것입니다. 이제 Parcel이 해당 코드에서는 어떤 방식으로 사용되고 있는지 분석해봅니다.


/frameworks/native/libs/binder/IServiceManager.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
....
    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

 본 코드의 addService()는 안드로이드에 사용되는 서비스를 다른 프로세스에서 사용할 수 있게 Service Manager에 등록을 하는 과정을 나타냅니다. 이 과정을 거치면 Binder를 통해 등록된 Service를 사용할 수 있게 됩니다.


 위의 코드들을 대뜸 보면 아시듯이 data 라는 명의 Parcel 클래스에 write를 통하여 정보를 순서대로 집어넣고 있는 상황을 보고 계십니다. 여기서 각각의 줄마다 어떤 기능이 사용되고 있는지 확인해 보도록 합시다.


data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());

ServiceManager에 등록될 서비스의 RPC 헤더를 기록하는 부분입니다.

이 때 쓰이는 IServiceManager::getInterfaceDescriptor()은 해당 소스코드 내에서는 확인하기 어렵습니다. 그 이유는 IInterface.h 헤더 파일에 #define 매크로를 통해 정의되어 있기 때문인데요 이 부분에 대해서는 아래 포스팅을 참조해 주셨으면 합니다.


http://elecs.tistory.com/87


위에서 사용된 writeInterfaceToken() 함수는 Parcel 클래스에서 ServiceManager의 RPC 헤더를 저장하게 됩니다.

/frameworks/native/libs/binder/Parcel.cpp

1
2
3
4
5
6
7
8
// Write RPC headers.  (previously just the interface token)
status_t Parcel::writeInterfaceToken(const String16& interface)
{
    writeInt32(IPCThreadState::self()->getStrictModePolicy() |
               STRICT_MODE_PENALTY_GATHER);
    // currently the interface identification token is just its name as a string
    return writeString16(interface);
}
cs


data.writeString16(name);

해당 Service의 이름이 이 과정을 통해 등록됩니다.


/frameworks/native/libs/binder/Parcel.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
status_t Parcel::writeString16(const String16& str)
{
    return writeString16(str.string(), str.size());
}
 
status_t Parcel::writeString16(const char16_t* str, size_t len)
{
    if (str == NULL) return writeInt32(-1);
    
    status_t err = writeInt32(len);
    if (err == NO_ERROR) {
        len *= sizeof(char16_t);
        uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
        if (data) {
            memcpy(data, str, len);
            *reinterpret_cast<char16_t*>(data+len) = 0;
            return NO_ERROR;
        }
        err = mError;
    }
    return err;
}
cs

data.writeStrongBinder(service);

Service Manager에 바인더를 등록하는 과정입니다.


/frameworks/native/libs/binder/Parcel.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
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
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}
 
status_t flatten_binder(const sp<ProcessState>& proc,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;
    
    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.handle = handle;
            obj.cookie = NULL;
        } else {
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = local->getWeakRefs();
            obj.cookie = local;
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = NULL;
        obj.cookie = NULL;
    }
    
    return finish_flatten_binder(binder, obj, out);
}
 
inline static status_t finish_flatten_binder(
    const sp<IBinder>& binder, const flat_binder_object& flat, Parcel* out)
{
    return out->writeObject(flat, false);
}
 
 
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
    const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
    const bool enoughObjects = mObjectsSize < mObjectsCapacity;
    if (enoughData && enoughObjects) {
restart_write:
        *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
        
        // Need to write meta-data?
        if (nullMetaData || val.binder != NULL) {
            mObjects[mObjectsSize] = mDataPos;
            acquire_object(ProcessState::self(), val, this);
            mObjectsSize++;
        }
        
        // remember if it's a file descriptor
        if (val.type == BINDER_TYPE_FD) {
            if (!mAllowFds) {
                return FDS_NOT_ALLOWED;
            }
            mHasFds = mFdsKnown = true;
        }
 
        return finishWrite(sizeof(flat_binder_object));
    }
 
    if (!enoughData) {
        const status_t err = growData(sizeof(val));
        if (err != NO_ERROR) return err;
    }
    if (!enoughObjects) {
        size_t newSize = ((mObjectsSize+2)*3)/2;
        size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t));
        if (objects == NULL) return NO_MEMORY;
        mObjects = objects;
        mObjectsCapacity = newSize;
    }
    
    goto restart_write;
}
cs


data.writeInt32(allowIsolated ? 1 : 0);

allowIsolated 변수의 초기값은 False로 설정되어 있습니다.


/frameworks/native/libs/binder/Parcel.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
status_t Parcel::writeInt32(int32_t val)
{
    return writeAligned(val);
}
 
template<class T>
status_t Parcel::writeAligned(T val) {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
 
    if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
        *reinterpret_cast<T*>(mData+mDataPos) = val;
        return finishWrite(sizeof(val));
    }
 
    status_t err = growData(sizeof(val));
    if (err == NO_ERROR) goto restart_write;
    return err;
}
cs


status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);

 transact() 함수를 실행함으로서 Parcel을 전송하게 됩니다. 전송보내게 될 data와 전송받게 될 reply 인 Parcel 클래스를 설정하신 후 실행하게 되면 시스템 서비스에 등록이 성공합니다.


/frameworks/native/include/binder/Binder.h

1
2
3
4
5
6
7
8
class BpRefBase : public virtual RefBase
{
protected:
....
    inline  IBinder*        remote()                { return mRemote; }
    inline  IBinder*        remote() const          { return mRemote; }
....
};
cs


위 과정을 통해 mRemote에 저장되어 있던 Binder가 반환됩니다.

여기서 ServiceManager는 BpBinder를 반환하게 됩니다.

해당 과정에 대해 자세히 알고 싶으신 분께서는 아래 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/89


BpBinder 클래스에서 transact() 함수가 동작되는 과정을 살펴보도록 하겠습니다.


/frameworks/native/include/binder/BpBinder.h
1
2
3
4
5
6
7
8
9
10
11
class BpBinder : public IBinder
{
public:
                        BpBinder(int32_t handle);
....
    virtual status_t    transact(   uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);
....
}
cs


/frameworks/native/libs/binder/BpBinder.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
BpBinder::BpBinder(int32_t handle)
    : mHandle(handle)
    , mAlive(1)
    , mObitsSent(0)
    , mObituaries(NULL)
{
    ALOGV("Creating BpBinder %p handle %d\n"this, mHandle);
 
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
    IPCThreadState::self()->incWeakHandle(handle);
}
 
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
 
    return DEAD_OBJECT;
}
cs

 위에서 보시게 되는 Parcel 클래스인 data는 정보를 받는 역할을, reply는 정보를 보내는 역할로 보시면 되겠습니다. 이 곳에서는 이번엔 IPCThreadState 클래스를 통해 Transact() 함수가 실행되고 있는 모습을 보고 계십니다. 해당 함수를 추적해 봅시다.


/frameworks/native/libs/binder/IPCThreadState.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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck();
 
    flags |= TF_ACCEPT_FDS;
 
    IF_LOG_TRANSACTIONS() {
        TextOutput::Bundle _b(alog);
        alog << "BC_TRANSACTION thr " << (void*)pthread_self() << " / hand "
            << handle << " / code " << TypeCode(code) << ": "
            << indent << data << dedent << endl;
    }
    
    if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
    
    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }
    
    if ((flags & TF_ONE_WAY) == 0) {
        #if 0
        if (code == 4) { // relayout
            ALOGI(">>>>>> CALLING transaction 4");
        } else {
            ALOGI(">>>>>> CALLING transaction %d", code);
        }
        #endif
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        #if 0
        if (code == 4) { // relayout
            ALOGI("<<<<<< RETURNING transaction 4");
        } else {
            ALOGI("<<<<<< RETURNING transaction %d", code);
        }
        #endif
        
        IF_LOG_TRANSACTIONS() {
            TextOutput::Bundle _b(alog);
            alog << "BR_REPLY thr " << (void*)pthread_self() << " / hand "
                << handle << ": ";
            if (reply) alog << indent << *reply << dedent << endl;
            else alog << "(none requested)" << endl;
        }
    } else {
        err = waitForResponse(NULL, NULL);
    }
    
    return err;
}
cs


위 코드에서 Parcel과 연관된 기능에 대해 살펴보도록 하겠습니다.

writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);

바인더를 통해 Parcel을 전송할 준비를 해주는 함수로 보시면 되겠습니다.


/frameworks/native/libs/binder/IPCThreadState.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
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;
 
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;
    
    const status_t err = data.errorCheck();
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        tr.flags |= TF_STATUS_CODE;
        *statusBuffer = err;
        tr.data_size = sizeof(status_t);
        tr.data.ptr.buffer = statusBuffer;
        tr.offsets_size = 0;
        tr.data.ptr.offsets = NULL;
    } else {
        return (mLastError = err);
    }
    
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));
    
    return NO_ERROR;
}
cs

 위의 코드에서 보시는 대로 본 함수에 전달된 인자값의 내용들이 binder_transaction_data 구조체 안에 저장되는 모습을 보실 수 있습니다.


 30번째 줄에 등장하는 mOut은 Parcel 클래스로 해당 클래스는 송신 용도로 쓰이는 Parcel 클래스로 이와 반대 역할을 하는 수신 용도로 쓰이는 Parcel 클래스인 mIn 클래스가 존재합니다.


/frameworks/native/include/binder/IPCThreadState.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class IPCThreadState
{
public:
    static  IPCThreadState*     self();
    static  IPCThreadState*     selfOrNull();  // self(), but won't instantiate
....
private:
                                IPCThreadState();
                                ~IPCThreadState();
....            
            Parcel              mIn;
            Parcel              mOut;
            status_t            mLastError;
            pid_t               mCallingPid;
            uid_t               mCallingUid;
            int32_t             mStrictModePolicy;
            int32_t             mLastTransactionBinderFlags;
....
};
 
 
cs


waitForResponse(NULL, NULL);

본 함수가 실행됨으로서 바인더를 통해 Parcel이 전송됩니다!


/frameworks/native/libs/binder/IPCThreadState.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
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
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;
 
    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0continue;
        
        cmd = mIn.readInt32();
        
        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
                << getReturnString(cmd) << endl;
        }
 
        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        
        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
            goto finish;
 
        case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;
            goto finish;
        
        case BR_ACQUIRE_RESULT:
            {
                ALOG_ASSERT(acquireResult != NULL, "Unexpected brACQUIRE_RESULT");
                const int32_t result = mIn.readInt32();
                if (!acquireResult) continue;
                *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
            }
            goto finish;
        
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;
 
                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(size_t),
                            freeBuffer, this);
                    } else {
                        err = *static_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(size_t), this);
                    }
                } else {
                    freeBuffer(NULL,
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(size_t), this);
                    continue;
                }
            }
            goto finish;
 
        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }
 
finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }
    
    return err;
}
cs

 이번 코드는 전체적인 맥락에서 상당히 중요하다 판단하여 모두 기재해 보았습니다. 해당 함수에서 중요한 부분에 대해 자세히 설명해 드리도록 하겠습니다.

err=talkWithDriver()

바인더 IPC 데이터가 저장된 mOut을 Binder에 전송하며 또한 수신된 데이터를 mIn에 저장하는 과정입니다.

아래는 talkWithDriver() 함수가 구현된 모습입니다. 상당히 복잡하게 구성되어 있으므로 전체적인 기능만 보시고 넘어가시면 되겠습니다.


/frameworks/native/libs/binder/IPCThreadState.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
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
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }
    
    binder_write_read bwr;
    
    // Is the read buffer empty?
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    
    // We don't want to write anything if we are still reading
    // from data left in the input buffer and the caller
    // has requested to read the next data.
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    
    bwr.write_size = outAvail;
    bwr.write_buffer = (long unsigned int)mOut.data();
 
    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (long unsigned int)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
 
    IF_LOG_COMMANDS() {
        TextOutput::Bundle _b(alog);
        if (outAvail != 0) {
            alog << "Sending commands to driver: " << indent;
            const void* cmds = (const void*)bwr.write_buffer;
            const void* end = ((const uint8_t*)cmds)+bwr.write_size;
            alog << HexDump(cmds, bwr.write_size) << endl;
            while (cmds < end) cmds = printCommand(alog, cmds);
            alog << dedent;
        }
        alog << "Size of receive buffer: " << bwr.read_size
            << ", needRead: " << needRead << ", doReceive: " << doReceive << endl;
    }
    
    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
 
    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        IF_LOG_COMMANDS() {
            alog << "About to read/write, write size = " << mOut.dataSize() << endl;
        }
#if defined(HAVE_ANDROID_OS)
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
        IF_LOG_COMMANDS() {
            alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
        }
    } while (err == -EINTR);
 
    IF_LOG_COMMANDS() {
        alog << "Our err: " << (void*)err << ", write consumed: "
            << bwr.write_consumed << " (of " << mOut.dataSize()
                        << "), read consumed: " << bwr.read_consumed << endl;
    }
 
    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < (ssize_t)mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        IF_LOG_COMMANDS() {
            TextOutput::Bundle _b(alog);
            alog << "Remaining data size: " << mOut.dataSize() << endl;
            alog << "Received commands from driver: " << indent;
            const void* cmds = mIn.data();
            const void* end = mIn.data() + mIn.dataSize();
            alog << HexDump(cmds, mIn.dataSize()) << endl;
            while (cmds < end) cmds = printReturnCommand(alog, cmds);
            alog << dedent;
        }
        return NO_ERROR;
    }
    
    return err;
}
cs


cmd = mIn.readInt32();

 수신된 Binder를 확인하는 부분입니다. min 내에 저장된 값을 불러내어 Parcel의 상태값을 얻어냅니다. 이후 switch()문을 통해 수신된 Parcel를 처리하는 과정을 거치게 됩니다.


=====

 이번 포스팅을 작성하다보니 내용이 상당히 길어져 버렸군요. 수신 과정만 분석하는데만 무려 1주일이라는 시간이 지나버렸군요. 사실 이 부분까지 접근을 하다보면 필연적으로 안드로이드 내의 linux 커널 부분까지 접근하게 되는데요 커널을 다루는 것은 본 포스팅에서 다루지 않도록 하겠습니다. 안드로이드 커널과 관련된 부분은 전문 서적등을 통해 정보를 얻으시면 좀 더 정확하고 빠른 이해가 가능하실 것이라 생각합니다.


다음포스팅에서는 전송된 Parcel을 수신한 후 처리하는 과정에 대해 알아보도록 하겠습니다.

http://elecs.tistory.com/91

300x250

안드로이드 프레임워크 프로그래밍(14) [서비스 매니저 생성 및 등록과정]

안드로이드/프레임워크 2015. 3. 29. 22:57

 이번 포스팅에서는 CameraService와 같이 중요한 Native System Service를 담당하는 ServiceManager가 생성되어 동작되는 과정에 대해 살펴보도록 하겠습니다.

 

 Native 단계에서 동작하는 ServiceManager가 초기에 생성되는 과정을 살펴보던 도중 이러한 코드를 발견하게 되었습니다.


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

1
2
3
4
5
6
7
8
9
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
....
}
cs

 위에서 보시는 바와 같이 BpServiceManager가 public으로 Constructor가 선언되어 있는 것을 보실 수 있습니다. 이로 보아 어딘가에서 이를 통해 ServiceManager를 생성할 것으로 추정하였으나 좀처럼 쉽지가 않았습니다.


 그렇다면 ServiceManager는 어떠한 방식으로 생성이 되는지 차근차근 살펴보도록 하겠습니다.


 먼저 우리는 이전 포스팅에서 CameraService 클래스가 ServiceManager에 등록되어지는 과정을 확인한 바 있습니다.


http://elecs.tistory.com/83 - 안드로이드 프레임워크 프로그래밍(12) [IServiceManager 등록과정]


사실 위 코드에서는 ServiceManager의 생성과정이 포함되어 있음을 확인할 수 있었습니다.

/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
int main(int argc, char** argv)
{
....
    if (doLog && (childPid = fork()) != 0) {
....
    } else {
        // all other services
        if (doLog) {
            prctl(PR_SET_PDEATHSIG, SIGKILL);   
            setpgid(00);                      
        }
        sp<ProcessState> proc(ProcessState::self());
        sp<IServiceManager> sm = defaultServiceManager();
        ALOGI("ServiceManager: %p", sm.get());
        AudioFlinger::instantiate();
        MediaPlayerService::instantiate();
        CameraService::instantiate();
        AudioPolicyService::instantiate();
        registerExtensions();
        ProcessState::self()->startThreadPool();
        IPCThreadState::self()->joinThreadPool();
    }
}
cs

위에서 중요한 코드 별로 하나씩 살펴 보도록 하겠습니다.


sp<ProcessState> proc(ProcessState::self());

 ProcessState 클래스 변수를 선언합니다. 이 때 ProcessState의 Constructor의 선언은 public이 아니기 때문에 따로 마련된 함수 self()를 통해 선언합니다.


/frameworks/native/libs/binder/ProcessState.cpp

1
2
3
4
5
6
7
8
9
sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState;
    return gProcess;
}
cs

 위의 방식으로 ProcessState가 존재하면 바로 return으로 값을 넘기고 없을 경우 Constructor를 새로 생성해냅니다.


/frameworks/native/libs/binder/ProcessState.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
ProcessState::ProcessState()
    : mDriverFD(open_driver())
    , mVMStart(MAP_FAILED)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    if (mDriverFD >= 0) {
        // XXX Ideally, there should be a specific define for whether we
        // have mmap (or whether we could possibly have the kernel module
        // availabla).
#if !defined(HAVE_WIN32_IPC)
        // mmap the binder, providing a chunk of virtual address space to receive transactions.
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
        if (mVMStart == MAP_FAILED) {
            // *sigh*
            ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n");
            close(mDriverFD);
            mDriverFD = -1;
        }
#else
        mDriverFD = -1;
#endif
    }
 
    LOG_ALWAYS_FATAL_IF(mDriverFD < 0"Binder driver could not be opened.  Terminating.");
}
cs

 위 코드를 살펴보았을 때 mmap() 를 통하여 메모리를 할당하고 있는 모습을 보실 수 있는데, 이 과정을 통해 바인더 드라이버가 프로세스에게 바인더 RPC 데이터를 저장할 때 사용하는 영역을 확보하실 수 있습니다.


sp<IServiceManager> sm = defaultServiceManager();

 위 코드를 통하여 BpServiceManager를 생성하게 됩니다. 해당 과정을 좀 더 자세히 살펴보면 다음과 같습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
    
    {
        AutoMutex _l(gDefaultServiceManagerLock);
        while (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
            if (gDefaultServiceManager == NULL)
                sleep(1);
        }
    }
    
    return gDefaultServiceManager;
}
cs

 위에서 볼 수 있는 gDefaultServiceManager는 ServiceManager의 값을 가지는 변수입니다. 이 변수에 BpServiceManager가 저장되는데 해당 코드가 구현되는 과정을 면밀히 살펴보도록 하겠습니다.


ProcessState::self()->getContextObject(NULL)

/frameworks/native/libs/binder/ProcessState.cpp

1
2
3
4
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)
{
    return getStrongProxyForHandle(0);
}
cs

 내부를 살펴보니 또다시 return 값으로 함수가 주어져 있는 것을 보실 수 있습니다.


getStrongProxyForHandle(0);

/frameworks/native/libs/binder/ProcessState.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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;
 
    AutoMutex _l(mLock);
 
    handle_entry* e = lookupHandleLocked(handle);
 
    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                // Special case for context manager...
                // The context manager is the only object for which we create
                // a BpBinder proxy without already holding a reference.
                // Perform a dummy transaction to ensure the context manager
                // is registered before we create the first local reference
                // to it (which will occur when creating the BpBinder).
                // If a local reference is created for the BpBinder when the
                // context manager is not present, the driver will fail to
                // provide a reference to the context manager, but the
                // driver API does not return status.
                //
                // Note that this is not race-free if the context manager
                // dies while this code runs.
                //
                // TODO: add a driver API to wait for context manager, or
                // stop special casing handle 0 for context manager and add
                // a driver API to get a handle to the context manager with
                // proper reference counting.
 
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }
 
            = new BpBinder(handle); 
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
 
    return result;
}
cs

/frameworks/native/include/binder/ProcessState.h

1
2
3
4
5
6
7
8
9
10
11
12
13
            struct handle_entry {
                IBinder* binder;
                RefBase::weakref_type* refs;
            };
            
            handle_entry*       lookupHandleLocked(int32_t handle);
 
            int                 mDriverFD;
            void*               mVMStart;
            
    mutable Mutex               mLock;  // protects everything below.
            
            Vector<handle_entry>mHandleToObject;
cs


/frameworks/native/libs/binder/ProcessState.cpp

1
2
3
4
5
6
7
8
9
10
11
12
ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{
    const size_t N=mHandleToObject.size();
    if (N <= (size_t)handle) {
        handle_entry e;
        e.binder = NULL;
        e.refs = NULL;
        status_t err = mHandleToObject.insertAt(e, N, handle+1-N);
        if (err < NO_ERROR) return NULL;
    }
    return &mHandleToObject.editItemAt(handle);
}
cs

 위 함수는 인자값으로 서비스의 handle값을 전달해 호출하는 역할을 하고 있습니다. 해당 코드에서 사용된 핸들값인 0은 ServiceManager에 고정된 서비스의 핸들입니다. 이 함수를 통해 ServiceManager의 Service Handle을 가진 BpBinder의 instance를 생성한 후에 포인터를 반환하는 작업이 이루어집니다. 즉, 해당 함수를 호출함으로서 mHandle 변수의 값이 0인 BpBinder 객체가 생성되는 것이지요.


 일단 위 과정을 통해 ProcessState::self()->getContextObject(NULL)이 BpBinder가 생성되었음을 직접 확인할 수 있었습니다. 이번에는 BpServiceManager가 생성되는 과정에 대해 알아보도록 하겠습니다.


gDefaultServiceManager = interface_cast<IServiceManager>(

                ProcessState::self()->getContextObject(NULL));


 이전에 보았던 IServiceManager에서 작성되었던 코드입니다. 이번에는 이 곳에서 interface_cast() 코드 부분을 살펴보도록 하겠습니다.

/frameworks/native/include/binder/IInterface.h

1
2
3
4
5
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
    return INTERFACE::asInterface(obj);
}
cs

 현재 interface_cast() 함수가 template로 선언되어 있는 모습을 보고 계십니다. 여기서 INTERFACE는 정황상 IServiceManager이고, 즉 이 곳에서는 IServiceManager의 asInterface() 함수가 실행되고 있는 모습을 보실 수 있습니다. asInterface() 함수가 실행되는 과정에 대해서는 아래 링크를 참조해 주시길 바랍니다.


http://elecs.tistory.com/87

http://elecs.tistory.com/88


/frameworks/native/include/binder/IServiceManager.h

 static android::sp<IServiceManager> asInterface(                      

            const android::sp<android::IBinder>& obj);

/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

위 과정을 통해 ServiceManager가 BpServiceManager의 상태로 서비스에 등록되고 있는 과정을 확인하였습니다.

여기서 잠시 맨 처음에 제시하였던 코드로 다시 돌아가 보도록 하겠습니다.


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

1
2
3
4
5
6
7
8
9
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
....
}
cs

 위에서 선언하였던 BpServiceManager() 생성자가 new 를 통해 생성되고 있는데 이 이후의 과정을 살펴보도록 합시다.


/frameworks/native/include/binder/IInterface.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase
{
public:
                                BpInterface(const sp<IBinder>& remote);
 
protected:
    virtual IBinder*            onAsBinder();
};
 
template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
    : BpRefBase(remote)
{
}
cs

/frameworks/native/libs/binder/Binder.cpp
1
2
3
4
5
6
7
8
9
10
BpRefBase::BpRefBase(const sp<IBinder>& o)
    : mRemote(o.get()), mRefs(NULL), mState(0)
{
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
 
    if (mRemote) {
        mRemote->incStrong(this);           // Removed on first IncStrong().
        mRefs = mRemote->createWeak(this);  // Held for our entire lifetime.
    }
}
cs


위 BpRefBase에서 mRemote가 초기화 되고 있는 모습을 보실 수 있습니다. 이 과정을 통해 BpBinder가 mRemote에 저장됩니다. 이때

mRemote(o.get())

 에서 get() 함수는 sp<>인 Smart Pointer입니다. Smart Pointer에 대한 자세한 사항은 아래 포스팅을 참조해 주시기 바랍니다.


http://elecs.tistory.com/79


/frameworks/rs/cpp/util/StrongPointer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename T>
class sp
{
public:
    inline sp() : m_ptr(0) { }
....
    // Accessors
 
    inline  T&      operator* () const  { return *m_ptr; }
    inline  T*      operator-> () const { return m_ptr;  }
    inline  T*      get() const         { return m_ptr; }
....
private:
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;
    void set_pointer(T* ptr);
    T* m_ptr;
};
 
 
cs

 위 Smart Pointer인 sp 클래스 내의 함수 get()의 정의 대로 Smart Pointer 내에 저장되어 있는 BpBinder의 포인터를 mRemote에 저장하고 있는 상황인 것을 아실 수 있습니다.


참고문헌 : 인사이드 안드로이드, 송형주 외 4인 저, 위키북스, 2010

300x250

안드로이드 Native 코드 분석 : DECLARE_META_INTERFACE()

안드로이드/프레임워크 2015. 3. 28. 02:29

 안드로이드 Binder 부분에 대해 공부를 하시다 보면 다음과 같은 함수를 만나게 되는 경우가 있습니다.

/frameworks/native/include/binder/IServiceManager.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
class IServiceManager : public IInterface
{
public:
    DECLARE_META_INTERFACE(ServiceManager);
 
    /**
     * Retrieve an existing service, blocking for a few seconds
     * if it doesn't yet exist.
     */
    virtual sp<IBinder>         getService( const String16& name) const = 0;
 
    /**
     * Retrieve an existing service, non-blocking.
     */
    virtual sp<IBinder>         checkService( const String16& name) const = 0;
 
    /**
     * Register a service.
     */
    virtual status_t            addService( const String16& name,
                                            const sp<IBinder>& service,
                                            bool allowIsolated = false= 0;
 
    /**
     * Return list of all existing services.
     */
    virtual Vector<String16>    listServices() = 0;
 
    enum {
        GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
        CHECK_SERVICE_TRANSACTION,
        ADD_SERVICE_TRANSACTION,
        LIST_SERVICES_TRANSACTION,
    };
};
cs

 위 함수를 처음 만났을 때 많은 분들께서 처음에는 상당히 의야해 하실 것으로 예상합니다. 사실 저것은 함수가 아니라 #define으로 정의된 매크로 함수입니다.

 해당 부분에서 MACRO 함수를 사용하는 이유는 Native의 System Service들의 Binder를 설계하다 보면 각 서비스 별로 바인더를 설계해야 하는 경우가 발생하게 됩니다. 즉, 위에 사용된 매크로 함수는 해당 서비스들의 코드를 작성하기엔 양이 많기 때문에 위와 같은 방법을 사용하여 코드 용량을 줄인 것이라고 이해하셔도 될 듯 합니다.

 이와 같은 용도로 IMPLEMENT_META_INTERFACE() 함수도 안드로이드에서는 쓰이고 있습니다. 그렇다면 DECLARE_META_INTERFACE() 함수가 어떻게 선언되어 있는지 살펴보도록 하겠습니다.


/frameworks/native/include/binder/IInterface.h

1
2
3
4
5
6
7
#define DECLARE_META_INTERFACE(INTERFACE)                               \
    static const android::String16 descriptor;                          \
    static android::sp<I##INTERFACE> asInterface(                       \
            const android::sp<android::IBinder>& obj);                  \
    virtual const android::String16& getInterfaceDescriptor() const;    \
    I##INTERFACE();                                                     \
    virtual ~I##INTERFACE();                                            \
cs


위 Macro 코드가 위 코드에서는 다음과 같이 적용됨을 알 수 있습니다.

/frameworks/native/include/binder/IServiceManager.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
class IServiceManager : public IInterface
{
public:
    static const android::String16 descriptor;                         
    static android::sp<IServiceManager> asInterface(                      
            const android::sp<android::IBinder>& obj);                 
    virtual const android::String16& getInterfaceDescriptor() const;   
    IServiceManager();                                                    
    virtual ~IServiceManager();                                          
 
    /**
     * Retrieve an existing service, blocking for a few seconds
     * if it doesn't yet exist.
     */
    virtual sp<IBinder>         getService( const String16& name) const = 0;
 
    /**
     * Retrieve an existing service, non-blocking.
     */
    virtual sp<IBinder>         checkService( const String16& name) const = 0;
 
    /**
     * Register a service.
     */
    virtual status_t            addService( const String16& name,
                                            const sp<IBinder>& service,
                                            bool allowIsolated = false= 0;
 
    /**
     * Return list of all existing services.
     */
    virtual Vector<String16>    listServices() = 0;
 
    enum {
        GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION,
        CHECK_SERVICE_TRANSACTION,
        ADD_SERVICE_TRANSACTION,
        LIST_SERVICES_TRANSACTION,
    };
};
cs


 위와 같은 원리로 안드로이드 Binder 관련 MACRO 함수로 IMPLEMENT_META_INTERFACE() 함수가 있습니다. 해당 매크로함수에 대한 정리는 아래 포스팅을 확인해주시기 바랍니다.

http://elecs.tistory.com/87

300x250

안드로이드 Native 코드 분석 : IMPLEMENT_META_INTERFACE()

안드로이드/프레임워크 2015. 3. 27. 00:46

 안드로이드 프레임워크를 분석하는 과정에서 시스템 서비스와 관련된 부분에 대해 분석을 하다보면 다음과 같은 이름의 함수를 만나보실 수 있을 것입니다.


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

IMPLEMENT_META_INTERFACE(ServiceManager, "android.os.IServiceManager");


 위 함수는 겉으로 보기에는 뭔가 거창한 듯한 함수처럼 보입니다만 사실은 #define으로 정의된 매크로입니다. 이 매크로는 header 파일인 IInterface.h에 선언되어 있음을 확인하실 수 있습니다.


/frameworks/native/include/binder/IInterface.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME)                       \
    const android::String16 I##INTERFACE::descriptor(NAME);             \
    const android::String16&                                            \
            I##INTERFACE::getInterfaceDescriptor() const {              \
        return I##INTERFACE::descriptor;                                \
    }                                                                   \
    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }                                                                   \
    I##INTERFACE::I##INTERFACE() { }                                    \
    I##INTERFACE::~I##INTERFACE() { }                                   \
 
cs

 보시는 바와 같이 #define을 통해 정의되어 있는 것을 확인하실 수 있습니다. 이 매크로에서 맨 오른쪽 부분에 BackSlash가 처리되어 있는 것은 해당 매크로 #define이 한 줄에서 끝나지 않고 있음을 의미합니다. 그리고 내용을 확인하시다 보면 다음과 같은 내용이 있습니다.


I##INTERFACELLI##INTERFACE() { }


 여기서 '##'이란 해당 기호 앞뒤에 있는 글자를 곧바로 붙여쓰라는 의미로 이해해 주신다면 쉽게 넘어가실 수 있으리라 생각합니다.

 INTERFACE가 'ServiceManager', Name이 '"android.os.IServiceManager"'로 정의되었다는 가정하에 Macro의 결과물은 다음과 같이 나타낼 수 있습니다.


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    const android::String16 IServiceManager::descriptor("android.os.IServiceManager");
    const android::String16&                                            
            IServiceManager::getInterfaceDescriptor() const {              
        return IServiceManager::descriptor;                                
    }                                                                   
    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;                                                    
    }                                                                   
    IServiceManager::IServiceManager() { }                                    
    IServiceManager::~IServiceManager() { }                                  
cs

Macro를 해독하면 다음과 같은 코드가 적용되고 있음을 알 수 있습니다.

 이 함수가 어떤 부분에서 중요하다는 의미인지 지금까지 설명드린 것 만으로는 충분히 이해하실 수 없으실 겁니다. 하지만 아래의 코드를 확인해 보신다면 단박에 위 매크로의 용도를 눈치채실 수 있을 겁니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
    BpServiceManager(const sp<IBinder>& impl)
        : BpInterface<IServiceManager>(impl)
    {
    }
....
 
    virtual sp<IBinder> checkService( const String16& name) const
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
        return reply.readStrongBinder();
    }
....
}
cs


 처음에는 저 IServiceManager 내에 있는 getInterfaceDescriptor() 함수를 찾아보았는데 전혀 찾아낼 수가 없어서 다른 곳에서 이리저리 찾아봤건만 저렇게 Macro를 통해 구현되어 있다는 것은 꿈에도 생각하지 못했습니다! 참으로 안드로이드의 세계는 알거 같으면서도 어려운 구조로군요.

 안드로이드 프레임워크 소스 내에는 위에서 소개드렸던 매크로 외에도 다음과 같이 2종류의 Macro가 존재합니다.


#define DECLARE_META_INTERFACE(INTERFACE) 

#define CHECK_INTERFACE(interface, data, reply)


 위 두 매크로 또한 제가 위에서 설명드린 바와 같이 해석을 해두신다면 원리를 쉽게 파악하실 수 있을 것입니다!

300x250

우분투 사용중 안보이거나 깨진 언어가 나올 때 언어 설치방법

공대생의 팁 2015. 3. 26. 12:23

 우분투가 상당히 매력적인 부분 중 하나라면 다양한 언어를 지원해준다는 점입니다. 심지어 이렇게 한글도 제공하는 환경인 것을 보면 우분투는 이름 그대로 공동체 정신을 담고 있는 것은 아닌가 싶습니다.


 우분투를 사용하다가 종종 괴랄하게 등장하는 언어들이 보입니다. 이는 자신의 우분투에 해당 언어가 설치되어 있지 않아 보이지 않는 현상입니다.


 위키피디아를 사용하던 중 생전 처음보는 괴랄한 글자들이 보시는 바와 같이 튀어나오고 있습니다. 과연 이 언어는 무엇일까요



 잠시 한글위키로 넘어온 후 다른 언어로 된 위키백과 모두 보기를 눌러봅니다.



 언어 목록들 중 유독 글자가 깨져서 보이는 것이 하나 보입니다. 써있기로는 Burmese라 되어 있군요,



 확인해보니 해당 글은 버마어(미얀마어)였습니다. 이렇게 자신의 우분투 환경에서 존재하지 않는 언어를 확인하셨다면 해당 언어를 찾아 우분투에 설치하도록 합니다.



우분투의 시스템 설정 메뉴로 들어가신 후 언어 지원을 클릭합니다.



다음으로 '언어 설치/제거' 버튼을 눌러줍니다.



설치할 언어들의 목록들이 펼쳐지는 것을 보실 수 있습니다. 우리는 이 곳에서 버마어(미얀마어)를 추가합니다.



자신의 슈퍼유저 암호를 입력하시면 해당 언어의 설치가 시작됩니다.



해당 언어를 설치하신 후 우분투를 리셋하시면 깨져 보이지 않던 언어가 드디어 모습을 드러내게 됩니다.



300x250

Timer를 활용하여 특정 파형간 간격 측정방법

임베디드/MCU 2015. 3. 25. 00:02

 아날로그 회로를 사용할 때 알고 싶은 정보들 중 하나로 특정 파형 간의 간격을 시간으로 구하는 것일겁니다. 그런데 이 파형이 간헐적이라면 사람의 감으로 대략적으로 알 수 있겠습니다만 1MHz 정도 되는 파형으로는 절대 사람의 힘으로는 알 수 없을 것입니다. 이러한 파형을 측정하기 위해 사용되는 것이 바로 MCU에서 사용되는 Timer와 Counter이지요.


 2015년 현재 웨어러블이 주목받고 있습니다. 사람의 몸에 직접 착용하여 사용하는 기기이다 보니 사람의 생체를 활용한 기능들이 속속 선보이고 있습니다. 삼성전자의 갤럭시워치에는 심전도 측정 기능이 등장하였는데요 심박수를 측정할 때도 이 Timer가 쓰이는 것이지요.



  

 2015년 현재의 대표적인 상징물이라 할 수 있는 스마트워치

심전도, 만보계 등 생체를 활용한 기능들이 활용되고 있다.


 이번 포스팅에서는 특정 파형에서 원하는 구간을 측정하여 해당 구간의 시간 간격을 타이머로 구하는 방법에 대해 이야기하려 합니다. 파형으로 심전도 측정값을 활용하여 진행해 보도록 하겠습니다.




 다음과 같이 특정한 파형이 있다고 가정해 봅시다. 우리는 위 파형에서 보이고 있는 Peak 값을 구해볼 것입니다. MCU에서 해당 부분만 검출하기 위해서 아날로그 비교기(Analog Comparator)를 통해 검출해내 보겠습니다.


 여기서 잠시 Analog Comparator의 동작 방식에 대해 간단하게 이해를 하고 넘어가겠습니다.


 Analog Comparator은 기본적으로 2개의 입력과 1개의 출력으로 구성되어 있습니다. 이 때 Vin+을 Input1로, Vin-을 Input2라 가정합니다. 쉽게 설명하자면 Input1과 Input2의 전압차이가 +일 경우 Output의 값은 1이 되며 Input1과 Input2의 전압차이가 -가 될 경우 Output의 값은 0이 됩니다. 아래는 Analog Comparator을 사용한 입출력값과 결과값을 나타냅니다.




 출력결과를 확인하시면 우리들이 흔히 접하는 0과 1의 디지털 파형이 되는 것을 보실 수 있습니다. 이제 이 Output값을 이용해서 우리는 각 1이되는 값 사이의 간격을 측정해 볼 것입니다. 여기서 Output의 파형을 상세하게 보도록 하겠습니다.



 아날로그 파형에서 자신이 측정하고자 하던 파형 사이의 간격은 다음과 같이 측정하면 될 것입니다. 이 때 파형을 측정하는 방법을 다음과 같이 4가지로 보실 수 있습니다.


1. 파형이 1이 될 때

2. 파형이 0이 될 때

3. 파형이 Positive Edge(0에서 1로 올라가는 순간)이 될 때

4. 파형이 Negative Edge(1에서 0으로 내려가는 순간)이 될 때


 Interrupt를 공부하신 분이라면 이 부분에서 '아하!'하고 이해하실 수 있을 것입니다. 그렇습니다. 출력 부분을 Interrupt 처리가 가능한 입력 부분에 연결하실 후 Positive Edge가 발생하는 순간부터 Timer를 동작시켜 시간을 측정하신 후 그 다음 Interrupt가 들어오는 순간 Timer의 동작을 종료시킨 후 해당 값을 확인하시면 파형간 간격을 측정하실 수 있으실 것입니다.

300x250

안드로이드 프레임워크 프로그래밍(13) [커널이미지 적용후 부팅화면만 나올 때 대처법]

안드로이드/프레임워크 2015. 3. 24. 02:16

 AOSP를 알면 알수록 참으로 심오한 안드로이드의 신비를 몸소 느끼고 있습니다. 아마 이 포스팅을 보시는 여러분들도 안드로이드의 구조에 대해 열심히 공부하고 계시리라 생각합니다. 이번 포스팅에서는 프로그래밍 도중 일어날 수 있는 무한 부팅모드에 대해 이야기를 해보려 합니다.


 안드로이드 기기를 켰을 때 부팅화면이 나오게 되는데 이 상태에서 안드로이드 기기는 설정되어 있는 시스템들을 모두 구동될 때 까지 준비중이라는 의미로 부팅화면을 띄우는데요. 간혹 특정 코드 부분을 코딩하다보면 의도치 않게 보시는 바와 같이 무한 로딩 상태에 빠지는 경우가 있습니다.



 아마 위의 화면처럼 안드로이드의 부팅화면이 반복되는 것을 본다면 혹시 고장은 아닌가 하는 걱정이 들 것입니다. 이는 eclipse를 실행 후 Logcat 화면을 확인해 보시면 분명 기기는 동작중이긴 한데 특정 부분에서 exception이 벌어지고 있는 것을 보실 수 있습니다.


 과연 이 경우는 어떤 경우일까요? 가장 큰 요인은 자신이 수정한 부분에서 문제가 발생하였을 때 이러한 현상이 주로 발생합니다. 비록 단순하지만 다음과 같이 대처하시면 원인을 찾으실 수 있으실 겁니다.


1. 일단 자신의 기기의 전원버튼을 계속 누르셔서 기기를 종료합니다. 어떤 기기의 경우 전원이 꺼지자마자 바로 부팅모드에 진입하는 경우가 있는데 그런 경우 그냥 처음부터 bootloader 모드로 진입하도록 합니다.


2. 커널 이미지를 적용하기 직전 자신이 작성한 소스코드 부분들을 모두 주석처리합니다. 그런 다음 다시 컴파일을 시도하신 후 이미지를 bootloader에 진입한 기기에 적용합니다.


3. 주석처리한 코드를 꼼꼼히 살피면서 주석을 한 줄씩 지워가며 문제가 발생한 부분을 찾습니다. 거의 이 단계에서 무안 부팅모드에 빠지는 코드를 찾아내실 수 있으실 겁니다.

300x250

안드로이드 프레임워크 프로그래밍(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