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