在进行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;
}
//把目标进程中新建的fd和file信息关联起来,实现了进程间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;