【Android】Binder传送文件描述符分析

本文详细解析了Android Binder机制中文件描述符(fd)如何跨进程传递,揭示了binder驱动在服务端创建新fd并关联客户端fd对应文件的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在进行dumpsys调用的时候,dump方法的第一个参数是文件描述符

BinderProxy.java

    publicvoid dump(FileDescriptor fd, String[] args) 


通过传送文件描述符来让服务端向给定的文件写数据,

等等,仔细想想,好像有什么不对

是啊,每个进程中的文件描述符是独立无关的,你把C进程中的文件描述符传给S进程,这不是刻舟求剑么?


但是,Android的binder看上去就是传送了文件描述符,这里面暗藏了什么玄机呢?


我们在读写文件描述符的地方加上log

frameworks/native/libs/binder/Parcel.cpp

status_t Parcel::writeDupFileDescriptor(int fd)
{
    int dupFd = dup(fd);
    if (dupFd < 0) {
        return -errno;
    }
//在这里添加log,查看fd的值
ALOGE("writeDupFileDescriptor: fd = %d, dupfd =  %d\n", fd, dupFd);
    status_t err = writeFileDescriptor(dupFd, true /*takeOwnership*/);
    if (err) {
        close(dupFd);
    }
    return err;
}

frameworks/base/core/jni/android_os_Parcel.cpp


static jobject android_os_Parcel_readFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        int fd = parcel->readFileDescriptor();
ALOGD("00 ========== android_os_Parcel_readFileDescriptor, fd=%d", fd);
        if (fd < 0) return NULL;
        fd = dup(fd);
ALOGD("========== android_os_Parcel_readFileDescriptor, fd=%d", fd);
        if (fd < 0) return NULL;
        return jniCreateFileDescriptor(env, fd);
    }
    return NULL;
}

进行一次dump调用,查看log

我们会发现,写入的fd和服务端读出的fd不一样,但是服务端却正确的完成了数据的输出,为什么呢?


肯定有地方进行了处理。

通过查看,我们发现,原来是在数据传输的过程中被做了手脚,binder驱动在传送数据的时候,在服务端上创建了一个新的fd,并且把这个fd和客户端fd所对应的文件进行了关联。


处理流程

 

BinderProxy.java

    public void dump(FileDescriptor fd,String[] args) throws RemoteException {

        Parcel data = Parcel.obtain();

        Parcel reply = Parcel.obtain();

        data.writeFileDescriptor(fd);

        data.writeStringArray(args);

        try {

            transact(DUMP_TRANSACTION, data,reply, 0);

            reply.readException();

        } finally {

            data.recycle();

            reply.recycle();

        }

    }

调用到

Parcel.java

    public final void writeFileDescriptor(FileDescriptor val) {

        updateNativeSize(nativeWriteFileDescriptor(mNativePtr,val));

    }

 

frameworks/base/core/jni/android_util_Binder.cpp

staticjboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,

        jint code, jobject dataObj, jobjectreplyObj, jint flags) // throws RemoteException

{

 

frameworks/native/libs/binder/BpBinder.cpp

status_tBpBinder::transact(

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

{

    // Once a binder has died, it will nevercome back to life.

    if (mAlive) {

        status_t status =IPCThreadState::self()->transact(

            mHandle, code, data, reply, flags);

 

./frameworks/native/libs/binder/IPCThreadState.cpp

status_tIPCThreadState::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(">>>> SENDfrom 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 (reply) {

            err = waitForResponse(reply);

        } else {

            Parcel fakeReply;

            err =waitForResponse(&fakeReply);

        }

 

status_tIPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)

{

    uint32_t cmd;

    int32_t err;

#ifdef_MTK_ENG_BUILD_

    cmd = 0;// initialze it for build error[-Werror=maybe-uninitialized]

#endif

 

    while (1) {

        if ((err=talkWithDriver()) < NO_ERROR) break;

        err = mIn.errorCheck();

        if (err < NO_ERROR) break;

        if (mIn.dataAvail() == 0) continue;

 

        cmd = (uint32_t)mIn.readInt32();

 

 

status_tIPCThreadState::talkWithDriver(bool doReceive)

{

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

 

然后调用到binder驱动里

最终,调用到方法binder_transaction

在里面对fd进行了处理


kernel-3.18/drivers/staging/android/binder.c

static void binder_transaction(structbinder_proc *proc,

                               structbinder_thread *thread,

                               structbinder_transaction_data *tr, int reply)

{

… …

                case BINDER_TYPE_FD:{

                                int target_fd;

                                struct file *file;

 

                                if (reply) {

                                        if(!(in_reply_to->flags & TF_ACCEPT_FDS)) {

                                               binder_user_error

                                                    ("%d:%dgot reply with fd, %d, but target does not allow fds\n",

                                                    proc->pid, thread->pid, fp->handle);

                                               return_error = BR_FAILED_REPLY;

                                               goto err_fd_not_allowed;

                                        }

                                } else if(!target_node->accept_fds) {

                                       binder_user_error

                                           ("%d:%d got transaction with fd, %d, but target does not allowfds\n",

                                            proc->pid, thread->pid, fp->handle);

                                       return_error = BR_FAILED_REPLY;

                                        gotoerr_fd_not_allowed;

                                }

//根据文件描述符获取到其对应的文件信息

//在某些Unix系统中,该方法名是getf

                                file =fget(fp->handle);

                                if (file ==NULL) {

                                       binder_user_error

                                           ("%d:%d got transaction with invalid fd, %d\n",

                                            proc->pid, thread->pid, fp->handle);

                                       return_error = BR_FAILED_REPLY;

                                        gotoerr_fget_failed;

                                }

                                if(security_binder_transfer_file

                                   (proc->tsk, target_proc->tsk, file) < 0) {

                                       fput(file);

                                       return_error = BR_FAILED_REPLY;

                                        goto err_get_unused_fd_failed;

                                }

//在目标进程中获取一个没有使用的文件描述符

                                target_fd =task_get_unused_fd_flags(target_proc, O_CLOEXEC);

                                if (target_fd< 0) {

                                        fput(file);

                                       return_error = BR_FAILED_REPLY;

                                        gotoerr_get_unused_fd_failed;

                                }

//把目标进程中新建的fdfile信息关联起来,实现了进程间fd的复制

                               task_fd_install(target_proc, target_fd, file);

                               trace_binder_transaction_fd(t, fp->handle, target_fd);

                               binder_debug(BINDER_DEBUG_TRANSACTION,

                                             "        fd %d -> %d\n", fp->handle,target_fd);

                                /* TODO: fput?*/

                                fp->binder =0;

                                fp->handle = target_fd;

#ifdefBINDER_MONITOR

                                e->fd =target_fd;

#endif

                        }

                        break;

 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值