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