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