검색결과 리스트
글
안드로이드 프레임워크 프로그래밍(15) [Native 단계에서의 Parcel 전송(1)]
지난 포스팅에서 ServiceManager에 등록이 되는 과정에 대해 살펴보았습니다. 이번 포스팅에서는 프로세스간에 데이터를 전송하는 기능인 Parcel에 대해서 알아보도록 하겠습니다.
시작하기에 앞서 아래 링크를 클릭하여 Java 단계에서의 Parcel 전송에 대해 이해하신 후 본 포스팅을 보신다면 이해가 좀 더 쉬워질 것입니다.
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 매크로를 통해 정의되어 있기 때문인데요 이 부분에 대해서는 아래 포스팅을 참조해 주셨으면 합니다.
위에서 사용된 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에 바인더를 등록하는 과정입니다.
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를 반환하게 됩니다.
해당 과정에 대해 자세히 알고 싶으신 분께서는 아래 포스팅을 참조해 주시기 바랍니다.
BpBinder 클래스에서 transact() 함수가 동작되는 과정을 살펴보도록 하겠습니다.
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() == 0) continue; 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을 수신한 후 처리하는 과정에 대해 알아보도록 하겠습니다.
'안드로이드 > 프레임워크' 카테고리의 다른 글
안드로이드 프레임워크 프로그래밍(17) [Binder 드라이버 호출과정] (0) | 2015.04.01 |
---|---|
안드로이드 프레임워크 프로그래밍(16) [Native 단계에서의 Parcel 전송(2)] (0) | 2015.03.31 |
안드로이드 프레임워크 프로그래밍(14) [서비스 매니저 생성 및 등록과정] (0) | 2015.03.29 |
안드로이드 Native 코드 분석 : DECLARE_META_INTERFACE() (0) | 2015.03.28 |
안드로이드 Native 코드 분석 : IMPLEMENT_META_INTERFACE() (0) | 2015.03.27 |
설정
트랙백
댓글
글
안드로이드 프레임워크 프로그래밍(14) [서비스 매니저 생성 및 등록과정]
이번 포스팅에서는 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(0, 0); } 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; } b = 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>(
이전에 보았던 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() 함수가 실행되는 과정에 대해서는 아래 링크를 참조해 주시길 바랍니다.
/frameworks/native/include/binder/IServiceManager.h
static android::sp<IServiceManager> asInterface(
/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에 대한 자세한 사항은 아래 포스팅을 참조해 주시기 바랍니다.
/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
'안드로이드 > 프레임워크' 카테고리의 다른 글
안드로이드 프레임워크 프로그래밍(16) [Native 단계에서의 Parcel 전송(2)] (0) | 2015.03.31 |
---|---|
안드로이드 프레임워크 프로그래밍(15) [Native 단계에서의 Parcel 전송(1)] (0) | 2015.03.30 |
안드로이드 Native 코드 분석 : DECLARE_META_INTERFACE() (0) | 2015.03.28 |
안드로이드 Native 코드 분석 : IMPLEMENT_META_INTERFACE() (0) | 2015.03.27 |
안드로이드 프레임워크 프로그래밍(13) [커널이미지 적용후 부팅화면만 나올 때 대처법] (0) | 2015.03.24 |
설정
트랙백
댓글
글
안드로이드 Native 코드 분석 : DECLARE_META_INTERFACE()
안드로이드 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() 함수가 있습니다. 해당 매크로함수에 대한 정리는 아래 포스팅을 확인해주시기 바랍니다.
'안드로이드 > 프레임워크' 카테고리의 다른 글
안드로이드 프레임워크 프로그래밍(15) [Native 단계에서의 Parcel 전송(1)] (0) | 2015.03.30 |
---|---|
안드로이드 프레임워크 프로그래밍(14) [서비스 매니저 생성 및 등록과정] (0) | 2015.03.29 |
안드로이드 Native 코드 분석 : IMPLEMENT_META_INTERFACE() (0) | 2015.03.27 |
안드로이드 프레임워크 프로그래밍(13) [커널이미지 적용후 부팅화면만 나올 때 대처법] (0) | 2015.03.24 |
안드로이드 프레임워크 프로그래밍(12) [IServiceManager 등록과정] (1) | 2015.03.23 |