Android Binder 번역한것.

Android : 2009. 10. 9. 12:29
반응형
Android Binder pdf 문서를 번역한 것이다.
내가 한거라 잘못된 부분이 있을 수 있음을 감안해야 한다.



안드로이드 IPC 시스템이 어떻게 작동하는지 보기 위해 IAudioFlinger::serMode API 를 사용한다.

AudioFlinger media_server 프로그램의 서비스다.

 

Service Manager Run

 

sevice_manager는 다른 프로세스에게 sevive manager 서비스를 제공한다. 이것은 반드시 다른 어떤 서비스의 실행 보다 먼저 실행되어야만 한다.

 

int main(int argc, char **argv)

{

    struct binder_state *bs;

    void *svcmgr = BINDER_SERVICE_MANAGER;

 

    bs = binder_open(128*1024);

 

    if (binder_become_context_manager(bs)) {

        LOGE("cannot become context manager (%s)\n", strerror(errno));

        return -1;

    }

 

    svcmgr_handle = svcmgr;

    binder_loop(bs, svcmgr_handler);

    return 0;

}

 

우선 /dev/binder 드라이버를 오픈하고 BINDER_SET_CONTEXT_MGR ioctl 을 호출해서 바인더 커널 드라이버가 manager처럼 작동하게 한다. 그런 다음 루프로 들어가서 다른 프로세스로부터 어떤 데이터가 오기를 기다리게 된다.

 

void binder_loop(struct binder_state *bs, binder_handler func)

{

    int res;

    struct binder_write_read bwr;

    unsigned readbuf[32];

 

    bwr.write_size = 0;

    bwr.write_consumed = 0;

    bwr.write_buffer = 0;

    

    readbuf[0] = BC_ENTER_LOOPER;

    binder_write(bs, readbuf, sizeof(unsigned));

 

    for (;;) {

        bwr.read_size = sizeof(readbuf);

        bwr.read_consumed = 0;

        bwr.read_buffer = (unsigned) readbuf;

 

        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

 

        if (res < 0) {

            LOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));

            break;

        }

 

        res = binder_parse(bs, 0, readbuf, bwr.read_consumed, func);

        if (res == 0) {

            LOGE("binder_loop: unexpected reply?!\n");

            break;

        }

        if (res < 0) {

            LOGE("binder_loop: io error %d %s\n", res, strerror(errno));

            break;

        }

    }

}

 

BINDER_SERVICE_MANAGER 를 주의깊게 봐라.

 

/* the one magic object */

#define BINDER_SERVICE_MANAGER ((void*) 0)

 

BINDER_SERVICE_MANAGER service_manager를 위해 등록된 핸들이다. 다른 프로세스들은 반드시 이 핸들을 사용해서 service_manager와 통신해야 한다.

 

Get IServiceManager

 

IServiceManager 인스턴스를 얻기 위한 단 하나의 방법은 IServiceManager.cpp에 있는 defaultServiceManager를 호출하는 것이다.

sp<IServiceManager> defaultServiceManager()

{

    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;

    

    {

        AutoMutex _l(gDefaultServiceManagerLock);

        if (gDefaultServiceManager == NULL) {

            gDefaultServiceManager = interface_cast<IServiceManager>(

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

        }

    }

    

    return gDefaultServiceManager;

}

 

gDefaultServiceManager libutil에 정의되어 있다. 그래서 어떤 프로그램이나 라이브러리가 libutil을 포함하고 있으면 이 심볼을 갖고 있을 것이다. 이것은 한 프로세스에 단 하나가 있다. 처음 gDegaultServiceManager NULL 값을 갖고 있다. 그래서 이것은 처음에 ProcessState::self()를 통해 ProcessState 인스턴스를 얻는다. 한 프로세스는 단 하나의 ProcessState 인스턴스를 갖는다. ProcessState IPCThreadState를 사용하기위해 /dev/binder 드라이버를 오픈할것이다.

 

ProcessState::ProcessState()

    : mDriverFD(open_driver())

 

이제 우리는 ProcessState의 인스턴스 하나를 얻었다.

이제 getContextObject를 보자

 

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& caller)

{

    if (supportsProcesses()) {

        return getStrongProxyForHandle(0);

    } else {

        return getContextObject(String16("default"), caller);

    }

}

 

보드는 binder driver를 제공한다. 그래서 우리는 getStrongProxyForHandle 에 도착할것이다. (Handle 0 service manager를 위해 예약되었다. 다음에 설명될 것이다.)

 

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)

{

    sp<IBinder> result;

 

    AutoMutex _l(mLock);

 

    return result;

}

 

처음에 b NULL로 한다. BpBinder remote binder object를 위한 base proxy class이다.

 

BpBinder::BpBinder(int32_t handle)

    : mHandle(handle)

    , mAlive(1)

    , mObitsSent(0)

    , mObituaries(NULL)

{

    LOGV("Creating BpBinder %p handle %d\n", this, mHandle);

 

    extendObjectLifetime(OBJECT_LIFETIME_WEAK);

    IPCThreadState::self()->incWeakHandle(handle);

}

 

IPCThreadState::incWeakHandle output buffer에 있는 BC_INCREFS 명령을 추가한다.

 

void IPCThreadState::incWeakHandle(int32_t handle)

{

    LOG_REMOTEREFS("IPCThreadState::incWeakHandle(%d)\n", handle);

    mOut.writeInt32(BC_INCREFS);

    mOut.writeInt32(handle);

}

 

이제 getContextObject BpBinder 인스턴스를 리턴한다. interface_cast IInterface.h에 정의되어 있다.

 

inline sp<IServiceManager> interface_cast(const sp<IBinder>& obj)

{

    return IServiceManager::asInterface(obj);

}

 

IServiceManager의 정의 부분이다.

 

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) = 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,

    };

};

DEVLARE_META_INTERFACE 매크로는 IInterface.h에 다음처럼 정의되어 있다. 그리고 이것은 다음 코드로 확장될 것이다.

 

static const String16 descriptor;                                   

    static sp<IServiceManager> asInterface(const sp<IBinder>& obj);        

    virtual String16 getInterfaceDescriptor() const;

 

보는 것 처럼 DECLARE_META_INTERFACE 매크로는 두개의 함수를 선언하는데  IServiceManager.cpp에서 IMPLEMENT_META_INTERFACE 매크로에 의해 실행된다.

 

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

 

코드는 다음처럼 확장된다.

const String16 IServiceManager::descriptor(NAME);                      

    String16 IServiceManager::getInterfaceDescriptor() const {             

        return IServiceManager::descriptor;                                

    }                                                                    

    sp<IServiceManager> IServiceManager::asInterface(const sp<IBinder>& obj)  

    {                                                                   

        sp<IServiceManager> intr;                                           

        if (obj != NULL) {                                              

            intr = static_cast<IServiceManager*>(                          

                obj->queryLocalInterface(                               

                        IServiceManager::descriptor).get());               

            if (intr == NULL) {                                         

                intr = new BpServiceManager(obj);                          

            }                                                            

        }                                                               

        return intr;                                                    

 

    }

 

그래서 IServiceManager::asInterface BpServiceManager 인스턴스를 리턴한다. BpServiceManager 원격 BnServiceManagsser를 위한 proxy처럼 작동한다. IServiceManager의 연산은 실제적으로 BpServiceManager의 상응하는 가상 함수를 호출한다.

 

Summary:

이 섹션은 remote 오브젝트를 위한 proxy 오브젝트를 어떻게 얻을수 있는지 알려준다.

여러분의 서비스-IFunnyTest를 수행하려면 다음을 그대로 따라하면 된다.

l  DECLARE_META_INTERFACE(FunnyTest) 매크로를 여러분의 header 파일에 넣는다.

l  IMPLEMENT_META_INTERFACE(Funnytest, “your unuque name”) 매크로를 여러분의 interface source file 에 넣는다.

l  여러분의 BpFunnyTest 클래스를 실행합니다.

 

Generate AudioFlinger Service

Media_server 프로그램은 AudioFlinger 서비스를 시작할 것입니다.

int main(int argc, char** argv)

 

{

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

    sp<IServiceManager> sm = defaultServiceManager();

    LOGI("ServiceManager: %p", sm.get());

    AudioFlinger::instantiate();

    MediaPlayerService::instantiate();

    CameraService::instantiate();

    ProcessState::self()->startThreadPool();

    IPCThreadState::self()->joinThreadPool();

}

 

AudioFlinger IServiceManager::addService를 호출하는 RPC Call 이다.

 

void AudioFlinger::instantiate() {

    defaultServiceManager()->addService(

            String16("media.audio_flinger"), new AudioFlinger());

}

 

AudioFlinger BnAudioFlinger로부터 상속받는다. 이것은 BnInterface 클래스의 템플릿이다.

 

class BnAudioFlinger : public BnInterface<IAudioFlinger>

{

public:

virtual status_t onTransact( uint32_t code,

const Parcel& data,

Parcel* reply,

uint32_t flags = 0);

};

 

BnInterface BBinder로부터 상속받는다.

template<typename INTERFACE>

class BnInterface : public INTERFACE, public BBinder

{

public:

virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);

virtual String16 getInterfaceDescriptor() const;

protected:

virtual IBinder* onAsBinder();

};

template<typename INTERFACE>

IBinder* BnInterface<INTERFACE>::onAsBinder()

{

return this;

}

 

BnInterface의 실행에 따르면 파라메터가 IserviceManager::addService 를 통과하는 것을 알 수 있는데 이것은 새로운 AudioFlinger 인스턴스의 주소이다. BBinderIbinder로 부터 파생되었고 이  transact 함수는 onTracsact 가상함수를 호출한다.

 

status_t BBinder::transact(

uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

data.setDataPosition(0);

 

status_t err = NO_ERROR;

switch (code) {

case PING_TRANSACTION:

reply->writeInt32(pingBinder());

break;

default:

err = onTransact(code, data, reply, flags);

break;

}

if (reply != NULL) {

reply->setDataPosition(0);

}

return err;

}

 

가장 중요한 가상함수는 onTransact이다. BnAudioFlinger 가 가상함수를 실행했다. 시나리오 대로라면 우리는 단지 SET_MODE 분기에 대한 포커스만 필요할 것이다.

 

status_t BnAudioFlinger::onTransact(

uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

switch(code) {

case SET_MODE: {

CHECK_INTERFACE(IAudioFlinger, data, reply);

int mode = data.readInt32();

reply->writeInt32( setMode(mode) );

return NO_ERROR;

} break;

 

media_server IPCThreadState::joinThreadPool를 통해 루프로 들어갈 것이다. service_manager talkWithDriver 에서 다른 프로세세로부터 데이터를 기다릴 것이다.

 

void IPCThreadState::joinThreadPool(bool isMain)

{

mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

 

status_t result;

do {

int32_t cmd;

result = talkWithDriver();

if (result >= NO_ERROR) {

size_t IN = mIn.dataAvail();

if (IN < sizeof(int32_t)) continue;

cmd = mIn.readInt32();

result = executeCommand(cmd);

}

// Let this thread exit the thread pool if it is no longer

// needed and it is not the main process thread.

if(result == TIMED_OUT && !isMain) {

break;

}

} while (result != -ECONNREFUSED && result != -EBADF);

 

mOut.writeInt32(BC_EXIT_LOOPER);

talkWithDriver(false);

}

 

여러분의 IFunnyTest를 실행하려면 다음과 같이 하면 된다.

l  여러분의 BnFunnyTest 클래스를 실행하라

l  프로세스에서 여러분의 서비스가 실행중일 때 IPCThreadState::joinThreadPool 을 호출하여 binder를 위한 루프를 시작한다.

 

RPC Call IServiceManager::addService

우리가 IServiceManager::addService 호출했다. 이것은 BpServiceManager::addService를 호출했다는 의미이다.

 

virtual status_t addService(const String16& name, const sp<IBinder>& service)

{

Parcel data, reply;

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

data.writeString16(name);

data.writeStrongBinder(service);

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

return err == NO_ERROR ? reply.readInt32() : err;

}

 

Parcel은 간단하다. 우리는 이것을 연속적인 버퍼라고 생각할 수 있다. 서비스 파라메터가 BBinder 오브젝트(Bn 으로부터 파생된 AudioFlinger)를 가리키고 있다는 것을 주목해라.

 

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)

{

return flatten_binder(ProcessState::self(), val, this);

}

 

flatten_binder는 하나의 Binder 명령을 생성한다. BBinder가 지역 binder 객체이기 때문에, 코드는 빨간색으로 마크된 라인으로 갈라진다.

 

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) {

LOGE("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;

}

}

return finish_flatten_binder(binder, obj, out);

}

 

빨간 부분을 주목하라. 지역 주소가 packet으로 들어간다.(이것은 다음에 쓰일것이다.) addService RPC 호출을 위한 패킷을 만든다음 BpServiceManager::addService BpBindertransact를 호출할것이다.

 

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;

}

 

BpBinder IPCThreadState::transact를 호출하여 mHandle에 해당하는 바인더 오브젝트로 트랜잭션을 시작한다. 시나리오에서 mHandle 0 이다.

 

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 (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 (reply) {

err = waitForResponse(reply);

} else {

Parcel fakeReply;

err = waitForResponse(&fakeReply);

}

} else {

err = waitForResponse(NULL, NULL);

}

return err;

}

 

IPCThreadState::transact 는 첫번째로 writeTransactionData를 호출하여 바인더커널드라이버를 위한 트랜잭션 structure를 조립한다.

 

다음 라인을 주목해라. 이 부분은 바인더커널드라이버가 트랜잭션 타겟을 확인하기 위해 매우 중요하다.

 

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;

 

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;

}

 

그러면 waitForResponse talkWithDriver를 호출하여 BINDER_WRITE_READ ioctl을 가져온다.

 

#if defined(HAVE_ANDROID_OS)

if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)

err = NO_ERROR;

else

err = -errno;

#else

 

그러면 이제 트랜잭션 데이터가 바인더커널드라이버로 배달되었다.

 

Summary:

Proxy 오브젝트는 RPC 호출을 위해 필요한 패킷을 생성한다. 그리고 바인더커널드라이버에 패킷을 쓰기위해 BINDER_WRITE_READ를 불러낸다. 그 패킷은 형식이 정의된 패킷이다. RPC호출을 위해서 BC_TRANSACTION 패킷 타입을 사용한다.

여러분의 IFunnyTest를 실행하려면 다음을 따라해라.

l  여러분의 서비스가 실행중인 프로세스에서 IServiceManager::addService를 호출해서 servier_manager에 서비스를 등록해라.

 

Transaction in Binder Kernel Driver

 

어떤 프로세스가 /dev/binder 드라이버를 열 때, 상응하는 binder_proc structure binder_open에서 할당될 것이다.

 

static int binder_open(struct inode *nodp, struct file *filp)

{

struct binder_proc *proc;

 

proc = kzalloc(sizeof(*proc), GFP_KERNEL);

if (proc == NULL)

return -ENOMEM;

get_task_struct(current);

proc->tsk = current;

INIT_LIST_HEAD(&proc->todo);

init_waitqueue_head(&proc->wait);

proc->default_priority = task_nice(current);

mutex_lock(&binder_lock);

binder_stats.obj_created[BINDER_STAT_PROC]++;

hlist_add_head(&proc->proc_node, &binder_procs);

proc->pid = current->group_leader->pid;

INIT_LIST_HEAD(&proc->delivered_death);

filp->private_data = proc;

mutex_unlock(&binder_lock);

if (binder_proc_dir_entry_proc) {

char strbuf[11];

snprintf(strbuf, sizeof(strbuf), "%u", proc->pid);

create_proc_read_entry(strbuf, S_IRUGO, binder_proc_dir_entry_proc,

binder_read_proc_proc, proc);

}

return 0;

}

 

그래서 어떤 ioctl이 도착했을 때, 드라이버는 프로세스 정보를 알 수 있다. 트랜젝션 데이터는 BINDER_WRITE_READ ioctl을 통해 전송된다.

 

case BINDER_WRITE_READ: {

  struct binder_write_read bwr;

    if (size != sizeof(struct binder_write_read)) {

   ret = -EINVAL;

   goto err;

  }

  if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {

   ret = -EFAULT;

   goto err;

  }

    if (bwr.write_size > 0) {

      ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);

   if (ret < 0) {

    bwr.read_consumed = 0;

    if (copy_to_user(ubuf, &bwr, sizeof(bwr)))

     ret = -EFAULT;

    goto err;

   }

  }

    if (bwr.read_size > 0) {

   ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer,

bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);

   if (!list_empty(&proc->todo))

    wake_up_interruptible(&proc->wait);

   if (ret < 0) {

    if (copy_to_user(ubuf, &bwr, sizeof(bwr)))

     ret = -EFAULT;

    goto err;

   }

  }

  if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {

   ret = -EFAULT;

   goto err;

  }

  break;

 }

 

Driver first handles는 쓰고, 읽는다. binder_thread_write를 보자. binder_thread_write의 핵심은 write 버퍼의 명령어를 parse 하고, 해당 커맨드를 실행하는 루프이다.

 

uint32_t cmd;

  void __user *ptr = buffer + *consumed;

  void __user *end = buffer + size; 

  while (ptr < end && thread->return_error == BR_OK) {

  if (get_user(cmd, (uint32_t __user *)ptr))

   return -EFAULT;

  ptr += sizeof(uint32_t);

  if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {

   binder_stats.bc[_IOC_NR(cmd)]++;

   proc->stats.bc[_IOC_NR(cmd)]++;

   thread->stats.bc[_IOC_NR(cmd)]++;

  }

  switch (cmd) {

  case ***:

 

  default:

      printk(KERN_ERR "binder: %d:%d unknown command %d\n", proc->pid, thread->pid, cmd);

   return -EINVAL;

  }

    *consumed = ptr - buffer;

 }

 

우리는 두개의 커맨드가 시나리오에 관련됨을 볼수 있다. 그 중 하나는 BC_INCREFS 이다.

 

case BC_INCREFS:

  case BC_ACQUIRE:

  case BC_RELEASE:

  case BC_DECREFS: {

   uint32_t target;

   struct binder_ref *ref;

   const char *debug_string;

 

   if (get_user(target, (uint32_t __user *)ptr))

    return -EFAULT;

   ptr += sizeof(uint32_t);

      if (target == 0 && binder_context_mgr_node &&

              (cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {

    ref = binder_get_ref_for_node(proc,

            binder_context_mgr_node);

   } else

    ref = binder_get_ref(proc, target);

      if (ref == NULL) {

    binder_user_error("binder: %d:%d refcou"

     "nt change on invalid ref %d\n",

     proc->pid, thread->pid, target);

    break;

   }    switch (cmd) {

   case BC_INCREFS:

    debug_string = "IncRefs";

    binder_inc_ref(ref, 0, NULL);

    break;

   }

   break;

 

시나리오에서 target 0이 될것이라고 우리가 전에 언급했던 것을 기억해라 system_manager BINDER_SET_CONTEXT_MGR ioctl을 호출할 때 binder_context_mgr_mode handle 0 을 나타내도록 생성된다. 그래서 여기서는 단지 binder_context_mgr_node 노드를 약한 참조 하는 것을 증가시킬 뿐이다.

 

binder_context_mgr_node = binder_new_node(proc, NULL);

 

다른 한가지는 BC_TRANSACTION 이다.

 

case BC_TRANSACTION:

  case BC_REPLY: {

   struct binder_transaction_data tr;

 

   if (copy_from_user(&tr, ptr, sizeof(tr)))

    return -EFAULT;

   ptr += sizeof(tr);

      binder_transaction(proc, thread, &tr, cmd == BC_REPLY);

   break;

}

 

만일 패킷이 하나의 BINDER_TYPE_BINDER 객체를 갖고 있다면 binder_transaction은 하나의 새로운 바인더 노드를 생성한다.

 

fp = (struct flat_binder_object *)(t->buffer->data + *offp);

  switch (fp->type) {

  case BINDER_TYPE_BINDER:

  case BINDER_TYPE_WEAK_BINDER: {

   struct binder_ref *ref;

   struct binder_node *node = binder_get_node(proc, fp->binder);

      if (node == NULL) {

    node = binder_new_node(proc, fp->binder);

    if (node == NULL) {

     return_error = BR_FAILED_REPLY;

     goto err_binder_new_node_failed;

    }

     node->cookie = fp->cookie;

   }

   ref = binder_get_ref_for_node(target_proc, node);

      if (ref == NULL) {

    return_error = BR_FAILED_REPLY;

    goto err_binder_get_ref_for_node_failed;    }

   if (fp->type == BINDER_TYPE_BINDER)

    fp->type = BINDER_TYPE_HANDLE;

   else

    fp->type = BINDER_TYPE_WEAK_HANDLE;

   fp->handle = ref->desc;

   binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE, &thread->todo);

   if (binder_debug_mask & BINDER_DEBUG_TRANSACTION)

        printk(KERN_INFO "                node %d u%p -> ref %d desc %d\n",

           node->debug_id, node->ptr, ref->debug_id, ref->desc);

} break;

 

binder_transaction 은 타켓이 handle 0 이라는 것을 알 것이다. 그래서 이것은 target_node, target_proc target_thread를 얻기 위해서 다음 분기를 실행한다.

 

} else {

   target_node = binder_context_mgr_node;

      if (target_node == NULL) {

    return_error = BR_DEAD_REPLY;

    goto err_no_context_mgr_node;

   }

  }

  e->to_node = target_node->debug_id;

  target_proc = target_node->proc;

  if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {

   struct binder_transaction *tmp;

   tmp = thread->transaction_stack;

   while (tmp) {

    if (tmp->from && tmp->from->proc == target_proc)

     target_thread = tmp->from;

    tmp = tmp->from_parent;

   }

}

 

마침내 binder_transaction은 이 request를 리스트에 집어넣을 것이고 binder_thread_read waiting thread를 깨운다.

 

t->work.type = BINDER_WORK_TRANSACTION;

 list_add_tail(&t->work.entry, target_list);

  tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;

 list_add_tail(&tcomplete->entry, &thread->todo);

 if (target_wait)

   wake_up_interruptible(target_wait);

 

이제 binder_thread_read 를 보자. service_manager 가 동작할 때, 하나의 request가 배달될때까지 여기에서 기다리고 있을 것이다.

 

ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));

 

media_server 로부터 미리 썼기 때문에 프로세스는 깨어났고 실행 됐다. 다음 코드는 media_server write 버퍼의 데이터를 system_menager read 버퍼로 복사한다.

 

tr.data_size = t->buffer->data_size;

  tr.offsets_size = t->buffer->offsets_size;

    tr.data.ptr.buffer = (void *)((void *)t->buffer->data + proc->user_buffer_offset);

    tr.data.ptr.offsets = tr.data.ptr.buffer + ALIGN(t->buffer->data_size, sizeof(void *));

 

  if (put_user(cmd, (uint32_t __user *)ptr))

   return -EFAULT;

  ptr += sizeof(uint32_t);

  if (copy_to_user(ptr, &tr, sizeof(tr)))

   return -EFAULT;

ptr += sizeof(tr);

 

Summary:

이 섹션은 RPC 호출의 클라이언트 사이드에서 서버사이드로의 데이터 흐름을 설명했다.

 

Service Manager Handle Add Service

 

지금 까지 service_manager media_server로부터 BR_TRANSACTION의 패킷을 얻었다. 그래서 이것은 binder_paser를 호출해서 패킷을 조작한다.

 

case BR_TRANSACTION: {

            if (func) {

                unsigned rdata[256/4];

                struct binder_io msg;

                struct binder_io reply;

                int res;

 

                bio_init(&reply, rdata, sizeof(rdata), 4);

                bio_init_from_txn(&msg, txn);

                res = func(bs, txn, &msg, &reply);

                binder_send_reply(bs, &reply, txn->data, res);

            }

            ptr += sizeof(*txn) / sizeof(uint32_t);

            break;

        }

 

binder_parser svcmgr_hadler를 호출하여 BR_TRANSACTION 패킷을 분석하고 이것은 BpServerManager의 프로세스를 예약한다. 다음 binder_txn 구조체는 실제적으로 binder_transaction_data 구조체와 같다. 우리의 시나리오상 트랜잭션 코드는 SVC_MGR_ADD_SERVICE이다.

 

int svcmgr_handler(struct binder_state *bs,

                   struct binder_txn *txn,

                   struct binder_io *msg,                    struct binder_io *reply)

{

    struct svcinfo *si;

    uint16_t *s;

    unsigned len;

    void *ptr;

 

    if (txn->target != svcmgr_handle)

        return -1;

 

    s = bio_get_string16(msg, &len);

 

        if ((len != (sizeof(svcmgr_id) / 2)) ||

        memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {

        fprintf(stderr,"invalid id %s\n", str8(s));

        return -1;

    }

 

    switch(txn->code) {

    case SVC_MGR_ADD_SERVICE:

        s = bio_get_string16(msg, &len);

        ptr = bio_get_ref(msg);

        if (do_add_service(bs, s, len, ptr, txn->sender_euid))

            return -1;

        break;

 

그래서 service_manager는 하나의 서비스를 s라고 이름 짓고 실행할 것이다. 그리고 bio_get_ref를 통해 object 정보를 얻는다.

 

void *bio_get_ref(struct binder_io *bio)

{

    struct binder_object *obj;

 

    obj = _bio_get_obj(bio);

    if (!obj)

        return 0;

 

    if (obj->type == BINDER_TYPE_HANDLE)

        return obj->pointer;

 

    return 0;

}

 

bio_get_ref flatten_binder의 작업을 예약한다. do_add_service는 마지막에 BC_ACQUIRE를 호출하여 ptr에 의해 나타나는 객체에 대한 강한참조(strong reference)를 얻는다.

 

Summary:

이 섹션은 service manager에 어떻게 서비스가 추가되는지 보였다.

여러분의 IFunnyTest 서비스를 실행하고 싶다면 다음과 같이 해라.

l  service_manager의 허가된 service list에 여러분의 service name을 추가해라.

 

Get IAudioFlinger

 

서비스 인터페이스를 얻는 유일한 방법은 IServiceManager::getSerivce를 통한 것이다. 예를 들면 여기 있는 이 메소드는 AudioSystem이 하나의 IAudioFlinger를 얻기 위한 것이다.

 

// establish binder interface to AudioFlinger service

const sp<IAudioFlinger>& AudioSystem::get_audio_flinger()

{

    Mutex::Autolock _l(gLock);

    if (gAudioFlinger.get() == 0) {

        sp<IServiceManager> sm = defaultServiceManager();

        sp<IBinder> binder;

        do {

            binder = sm->getService(String16("media.audio_flinger"));

            if (binder != 0)

                break;

            LOGW("AudioFlinger not published, waiting...");

            usleep(500000); // 0.5 s

        } while(true);

        gAudioFlinger = interface_cast<IAudioFlinger>(binder);

    }

    LOGE_IF(gAudioFlinger==0, "no AudioFlinger!?");

    return gAudioFlinger;

}

 

IServiceManager::getSerivce BpServiceManger::getService로 호출된다.

 

virtual sp<IBinder> getService(const String16& name) const

    {

        unsigned n;

        for (n = 0; n < 5; n++){

            sp<IBinder> svc = checkService(name);

            if (svc != NULL) return svc;

            LOGI("Waiting for sevice %s...\n", String8(name).string());

            sleep(1);

        }

        return NULL;

}

        virtual sp<IBinder> checkService( const String16& name) const

    {

        Parcel data, reply;

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

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

        return reply.readStrongBinder();

    }

 

분석하기 전에 호출은 마지막으로 바인더커널드라이버를 통해 service_manager 프로세스에서 제어된다.

 

switch(txn->code) {

    case SVC_MGR_GET_SERVICE:

    case SVC_MGR_CHECK_SERVICE:

        s = bio_get_string16(msg, &len);

        ptr = do_find_service(bs, s, len);

        if (!ptr)

            break;

        bio_put_ref(reply, ptr);

        return 0;

 

그러면 service_manager media_server에 의해 설정된 이전 핸들로 응답한다. (이것은 AudioFlinger의 인스턴스 주소이다._ 그러면 BpServiceManger::checkService remote()->tracsact 호출로부터 리턴될 것이다. 그리고 IServiceManager를 위해 분석한 것은 새로운 다른 BpBinder 인스턴스가 해당하는 핸들을 service_manager로부터 리턴할 것이다. interface_case<IAudioFlinger>(binder)는 결국 하나의 BpAudioFlinger 인스턴스를 리턴할 것이다.

 

Summary:

IServiceManager를 얻는 것 처럼, 하지만 이번에 이것은 service_manager로부터 핸들을 얻는 것이 필요하다. While for IServiceManager it always use handle 0.

 

RPC Call IAudioFlinger::SetMode

 

만일 우리가 AAA 프로세스에서 IAudioFlinger::SetMode를 호출하면 사실 BpAudioFlinget::setMode 를 호출한것이다.

 

virtual status_t setMode(int mode)

    {

        Parcel data, reply;

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

        data.writeInt32(mode);

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

        return reply.readInt32();

}

 

IServiceManager::addService의 분석처럼 이 함수는 하나의 패킷을 생성하고 바인더커널드라이버에 이것을 쓴다. 그리고 읽기 응답을 기다린다. 한가지 다른점은 이때 타켓 핸들은 media_server 프로세스의 어떤 주소를 가리킨다는 것이다.

 

Handle IAudioFlinger::SetMode

 

바인더커널드라이버는 결국 media_server 프로세스의 Read 쓰레드를 깨울 것이다. 이것은 IPCThreadState::joinThreadPool에서 실행된다. 코드를 다시 보자.

 

void IPCThreadState::joinThreadPool(bool isMain)

{

    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

    

    status_t result;

    do {

        int32_t cmd;

        result = talkWithDriver();

        if (result >= NO_ERROR) {

            size_t IN = mIn.dataAvail();

            if (IN < sizeof(int32_t)) continue;

            cmd = mIn.readInt32();

            result = executeCommand(cmd);

        }

        

                // Let this thread exit the thread pool if it is no longer

                // needed and it is not the main process thread.

        if(result == TIMED_OUT && !isMain) {

            break;

        }

        } while (result != -ECONNREFUSED && result != -EBADF);

    

    mOut.writeInt32(BC_EXIT_LOOPER);

    talkWithDriver(false);

}

 

이번에 talkWithDriver BpServiceManager::serMode 가 생성한 패킷을 리턴할 것이다. 그리고 excuteCommand는 커맨드를 처리할 것이다. 이 시나리오에서 커맨드는 BR_TRANSACTION 이다.

 

case BR_TRANSACTION:

        {

            binder_transaction_data tr;

            

            Parcel reply;

            if (tr.target.ptr) {

                sp<BBinder> b((BBinder*)tr.cookie);

                const status_t error = b->transact(tr.code, buffer, &reply, 0);

                if (error < NO_ERROR) reply.setError(error);

                

            } else {

                const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0);                 if (error < NO_ERROR) reply.setError(error);

            }

            

            if ((tr.flags & TF_ONE_WAY) == 0) {

                LOG_ONEWAY("Sending reply to %d!", mCallingPid);

                sendReply(reply, 0);

            } else {

                LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);

            }

        }

        break;

 

가장 중요한 두줄은 빨간색으로 표시했다. 여기에서 바인더커널드라이버로부터 하나의 주소를 얻고 BBinder 포인터로 보낸다. (IServiceManager::addSrvice를 호출 했을 때 바인더커널드라이버로 넣은 주소) AudioFlinger BBinder로부터 전달된다는 것을 기억해라. 이 포인터는 사실 우리의 AudioFlinger 인스턴스의 포인터와 같은 것이다. 그래서 다음 transact 호출은 결국 우리의 BnAudioFlinger onTransact 가상 함수를 호출할 것이다.

 

status_t BnAudioFlinger::onTransact(

        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

        case SET_MODE: {

            CHECK_INTERFACE(IAudioFlinger, data, reply);

            int mode = data.readInt32();

            reply->writeInt32( setMode(mode) );

            return NO_ERROR;

        } break;

}

 

그러면 응답은 sendReply를 통해 써질 것이다.

 

status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)

{

    status_t err;

    status_t statusBuffer;

    err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);

        if (err < NO_ERROR) return err;

    

    return waitForResponse(NULL, NULL);

}

 

이것은 결국 바인더커널드라이버에 쓸것이다. 그러면 커널드라이버는 AAA의 읽기 쓰레드를 꺠울꺠울 것이다.

 

이 그림에서 안드로이드 IPC 시스템의 전반적 아키텍쳐를 보여준다. 여기에는 네가지 주요 블록블 있다.

 

l  Binder Driver

이것은 IPC 시스템의 핵심이다. 이것은 service proviser service user 간에 데이터를 전달한다.

l  service provider

이것음 몇가지의 서비스를 제공한다. 이것은 바인더드라이버로부터 받은 RPC 호출 데이터를 파스파스할 것. 그리고 실제 동작을 한다.

l  service_manager

이것은 특별한 service provider이다. 이것은 다른 service provider를 위해 service manager 서비스를 제공한다.

 

다음은 예를 든 시나리오의 주요 제어 흐름 리스트 이다.

1.     처음에는 service_manager를 실행시키고 이것은 special node 0를 바인더 드라이버에 등록할 것이다.

2.     media_server special node 0 를 위해 하나의 IserviceManager proxy 오브젝트를 얻는다.

3.     media_server RPC IServiceManager::AddService를 호출해서 IAudioFlinger 서비스에 추가한다. 이 호출은 node 0으로 발송된다. 이것은 바인더드라이버로 데이터를 보낼 것이다.

4.     바인더드라이버는 node 0을 위한 데이터를 통지하고 이 데이터는 한 오브젝트를 묶은 명령어를 포함한다. 그래서 이것은 IAudioFlinger 서비스를 위한 다른 하나의 노드를 생성하고 데이터를 service_manager로 보낸다.

5.     service_manager는 바인더드라이버로부터 데이터를 읽고 IserviceManager:addService RPC 호출을 처리한다.

6.     다른 프로세스 P special node 0 을 위해 IServiceManager proxy 오브젝트를 얻어온다.

7.     P RPC IServiceManager::getService를 호출하여 IAudioFlinger 서비스를 얻어온다. 이 호출은 node 0 으로 전달된다. 이것은 바인더드라이버로 데이터를 보낼것이다.

8.     바인더드라이버는 node 0 을 위한 데이터를 알려준다 그래서 이것은 데이터를 service_manager로 전달할 것이다.

9.     service_manager는 바인더드라이버로부터 데이터를 읽는다. 그리고 IServiceManager::getService RPC 호출 을 처리하고 IAudioFlinger 서비스를 나타내는 node A를 리턴한다.

10.   P RPC IAudioFlinger::serMode를 호출한다. 지금 이 호출은 node A로 전달될것이다.

11.   바인더드라이버는 데이터는 node A를 위한 것이라고 알려준다. 그래서 데이터를 media_server로 전달할 것이다.

12.   media_server 는 바인터 드라이버로부터 데이터를 읽는다. IAudioFlinger::setMode RPC 호출을 조작하고 바이더드라이버로 응답데이터를 보낸다.

13.   바인더드라이버는 P로 응답을 전달한다.

14.   P는 바인더드라이버로부터 데이터를 읽고 결국 응답을 얻는다.

 

반응형
Posted by Real_G