• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

android一次完整的 IPC 通信流程是怎样的

武飞扬头像
Lud_
帮助1

需要了解

	1.  binder 整体的架构原理
	2. 了解应用和binder驱动的交互原理(client 端和 Server 端与binder驱动的交互是不一样的)
	3. 了解 IPC 过程中的通信协议

binder 的架构

学新通
上图从参与角色来看分为 Client、Server 和 binder 驱动,从分层来看分为 应用层、(java Framework层、Native 层)驱动层。

Client 端发起 IPC 调用后,就是将 Proxy 给到了 BinderProxy ,然后BinderProxy传入到了 Native层的 BpBinder ,然后通过 IPCThreadState 调用了 transact 把数据丢给驱动,驱动在把数据丢到 Server 进程,在他的binder线程中网上传递。

client 端

  • Client 端是怎么将请求发送到 Server 端的?

以 aidl 为例,客户端和服务端有同一个 aidl 文件,服务端注册好 Service

interface IMyAidlInterface {
    void go(String param);
}

编译完成后,通过连接 service 来获取 binder 对象代码如下:

    private var iMyAidlInterface: IMyAidlInterface? = null

    private val serviceConnection = object : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            Log.e(tag, "onServiceConnected")
            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service)
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            Log.e(tag, "onServiceDisconnected")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
	// 点击绑定
    fun bindService(view: View) {
        val intent = Intent()
        intent.component = ComponentName("com.example.server", "com.example.server.MyService")
        bindService(intent, serviceConnection, BIND_AUTO_CREATE)
    }
	// 点击执行服务端的go() 方法
    fun invokeFun(view: View) {
        iMyAidlInterface?.go("hello aidl....")
    }
学新通

aidl 文件生成.java 文件的 proxy 代理类如下:

private static class Proxy implements com.example.server.IMyAidlInterface
    {
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote)
      {
        mRemote = remote;
      }
      @Override public android.os.IBinder asBinder()
      {
        return mRemote;
      }
      public java.lang.String getInterfaceDescriptor()
      {
        return DESCRIPTOR;
      }
      @Override public void go(java.lang.String param) throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          _data.writeString(param);
          boolean _status = mRemote.transact(Stub.TRANSACTION_go, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().go(param);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      public static com.example.server.IMyAidlInterface sDefaultImpl;
    }
学新通

go() 方法先把数据都写到 Parcel 里面,然后通过 mRemote.transact() 进行底层的交互,如上图所示,走到了 BinderProxy 的 transact() 方法

  • BinderProxy.java 中 transact 方法最后调用到了 transactNative 方法
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
	 return transactNative(code, data, reply, flags);
}
    public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
  • android_util_Binder.cpp 的 android_os_BinderProxy_transact() 方法
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{   
    // dataObj 中封装的是java层传入进来的数据 这个在 aidl 编译后生成的 Proxy 对象中可以看得到下面代码
    // android.os.Parcel _data = android.os.Parcel.obtain();
    //  _data.writeInterfaceToken(DESCRIPTOR); 这个是记录的java层aidl接口地址
    //  _data.writeString(param); // 这是写入的参数
    if (dataObj == NULL) {
        jniThrowNullPointerException(env, NULL);
        return JNI_FALSE;
    }
    // 先通过java对象创建 native 层的 Parcel 
    Parcel* data = parcelForJavaObject(env, dataObj);
    if (data == NULL) {
        return JNI_FALSE;
    }
    Parcel* reply = parcelForJavaObject(env, replyObj);
    if (reply == NULL && replyObj != NULL) {
        return JNI_FALSE;
    }
    // 通过 binderProxy 对象 拿到 native 层的 binder 对象 ,也就是 BPBinder
    IBinder* target = getBPNativeData(env, obj)->mObject.get();
    // ....
    // 获取到 BPBinder 以后,调用它的 transact() 方法
    status_t err = target->transact(code, *data, reply, flags);
    // .....
    return JNI_FALSE;
}
学新通
  • BPBinder.cpp 中的 transact 函数
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() 函数,IPCThreadState::self() 是线程内的单例,每一个线程都有一个 IPCThreadState,传入了 mHandle ,驱动可以根据 mHandle 来确定是哪个binder

  • IPCThreadState 的 transact() 函数
status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err;
    // 把数据写出去
    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    if ((flags & TF_ONE_WAY) == 0) {
        #if 0
        #endif
        if (reply) {
            // 等待回复
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        // ...
    } else {
        err = waitForResponse(NULL, NULL);
    }

    return err;
}
学新通

IPCThreadState::transact 函数主要有 writeTransactionData() 将数据写出去,然后 waitForResponse() 等待返回结果
接下来看一下数据是怎么写出去的

  • writeTransactionData()
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;
    // 接下来把各种数据都赋值给 data
    tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
    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(binder_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 = reinterpret_cast<uintptr_t>(statusBuffer);
        tr.offsets_size = 0;
        tr.data.ptr.offsets = 0;
    } else {
        return (mLastError = err);
    }
    // 写到 mOut 中,IPCThreadState 有 mIn 和 mOut 它们的功能就是和驱动进行数据的读写
    // 把数据写到驱动 先写一个 cmd 命令,
    mOut.writeInt32(cmd);
    // 然后再把数据写进去 这里只是先写到了 mOut 中,还没有写到驱动,后面会往驱动写
    mOut.write(&tr, sizeof(tr));
    return NO_ERROR;
}
学新通

writeTransactionData() 函数是将数据封装成 binder_transaction_data 对象,然后通过 mOut 写到驱动(这里是还未开始写到驱动,只是存储到了 mOut中)具体写到驱动是waitForResponse () 方法执行的
接下来看一下 waitForResponse () 函数

  • waitForResponse()
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;
    // 一个交互需要多次 所以通过  while (1) 一直交互 知道后面调用  goto finish; 才结束循环
    while (1) {
        // talkWithDriver() 是有两种,一种是往驱动里面写,一种是从驱动里面读取,这里是通过 mOut 往驱动里面写
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        // 通过 mIn 把指令读出来 然后根据命令来执行下面的 switch 操作
        cmd = (uint32_t)mIn.readInt32();
        switch (cmd) {
        // BR_TRANSACTION_COMPLETE 表示驱动已经收到了请求
        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;
                // 返回的数据是存储到了 mIn 中,读取到 binder_transaction_data 的结构中
                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) {
                        // 把 binder_transaction_data 里面的数据给到 reply,这样就可以从 reply 中获取到数据
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t),
                            freeBuffer, this);
                    } else {
                        err = *reinterpret_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 binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t), this);
                    }
                } else {
                    freeBuffer(NULL,
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(binder_size_t), this);
                    continue;
                }
            }
            goto finish;
        }
    }
finish:
    return err;
}
学新通
  • 接下来看看 talkWithDriver() 是如何写到驱动的
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
	    binder_write_read bwr;
    // 如果是写到驱动 需要确定好 写的大小 缓冲区等
    // 如果是读则需要确定好读的大小和读的缓冲区
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();

     // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
	#if defined(__ANDROID__)
        // 通过 ioctl 作传输
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
}
学新通

server 端

当Zygote应用启动之后, 会将当前线程设置为 binder 线程,从而为 service AMS 等 binder 跨进程的调用提供服务,
Server 端使用 aidl 是通过注册 Service 来返回 Server 端的 binder 对象,Service 的启动原理可以看这里

	// AndroidRuntime.cpp -> app_main.cpp -> onZygoteInit()
    virtual void onZygoteInit()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    } 
    // ProcessState.cpp
    virtual bool threadLoop()
    {
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }
    
  • IPCThreadState::joinThreadPool()
void IPCThreadState::joinThreadPool(bool isMain)
{
    LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());
    // 先将BC_ENTER_LOOPER : BC_REGISTER_LOOPER 写到 mOut 中,表示把当前线程注册到binder驱动,变为binder线程
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

    status_t result;
    do {
        processPendingDerefs();
        // now get the next command to be processed, waiting if necessary
        // do while 循环中不停的执行了 getAndExecuteCommand() 获取并且执行命令 
        result = getAndExecuteCommand();
		// 	...
    } while (result != -ECONNREFUSED && result != -EBADF);
    mOut.writeInt32(BC_EXIT_LOOPER);
    talkWithDriver(false);
}
学新通
  • getAndExecuteCommand();
status_t IPCThreadState::getAndExecuteCommand()
{
    status_t result;
    int32_t cmd;
    // 从驱动中读取数据
    result = talkWithDriver();
    if (result >= NO_ERROR) {
        size_t IN = mIn.dataAvail();
        if (IN < sizeof(int32_t)) return result;
        // 先从 mIn 中读取 cmd 命令
        cmd = mIn.readInt32();
        // ...
        pthread_mutex_unlock(&mProcess->mThreadCountLock);
        // 执行这个命令
        result = executeCommand(cmd);
		// ...
    }
    return result;
}
学新通

getAndExecuteCommand() 函数从 mIn 中获取数据并且执行命令,接下来看看怎么执行的命令

  • executeCommand()
status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;
    // 根据不同的 cmd 来执行不同的操作
    switch ((uint32_t)cmd) {
    case BR_TRANSACTION: // 这个命令是 Client 端转发给驱动,驱动在转发给 Server 端
        {
            // 先把数据从 mIn 中读取出来,放入到 binder_transaction_data 中
            binder_transaction_data tr;
            result = mIn.read(&tr, sizeof(tr));
            if (result != NO_ERROR) break;
            // buffer 是要传输给 Server 端的数据
            Parcel buffer;
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
			// ...
            //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
            // reply 是要回复给 Client 端
            Parcel reply;
            status_t error;
            if (tr.target.ptr) {
                // We only have a weak reference on the target object, so we must first try to
                // safely acquire a strong reference before doing anything else with it.
                if (reinterpret_cast<RefBase::weakref_type*>(
                        tr.target.ptr)->attemptIncStrong(this)) {
                    // cookie 保存的是 binder 对象 然后调用 BBinder 的 transact 往上层传递 把 buffer 和 reply 传进去
                    error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
                            &reply, tr.flags);
                    reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
                }
            }
            if ((tr.flags & TF_ONE_WAY) == 0) {
                // 传输结束之后 sendReply 返回回去;
                sendReply(reply, 0);
            } 
        }
        break;
学新通

executeCommand() 获取数据并且将传进来的 binder 实体对象转换为 BBinder 并且调用其 transact() 继续上传给 Server 端的 java 层

  • BBinder:: transact()
status_t BBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    status_t err = NO_ERROR;
    switch (code) {
        case PING_TRANSACTION:
            reply->writeInt32(pingBinder());
            break;
        default:
            err = onTransact(code, data, reply, flags);
            break;
    }
    return err;
}

上面函数继续调用了android_util_Binder.cpp 的 onTransact() 函数

  • onTransact(code, data, reply, flags);
   virtual status_t onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
    {
        // 根据javaVM 拿到当前线程的 JNIEnv 拿到 JNIEnv 后可以进行 jni 调用了
        JNIEnv* env = javavm_to_jnienv(mVM);
        IPCThreadState* thread_state = IPCThreadState::self();
        const int32_t strict_policy_before = thread_state->getStrictModePolicy();
        // 调用的是 jni 的 mExecTransact() 函数,mExecTransact 调用到了 binder 对象的 onTransact() 函数
        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
            code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
		// ...
    }

onTransact() 函数实际上是获取 JNIEnv 从而可以调用到 aidl 生成的 Stub 类的 onTransact() 函数,其内部生成对应 Server 端的接口对象,然后调用本地对应的方法,然后一次完成的 IPC 调用就结束了

       @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_go: {
                // 根据 code 
                // 先解析数据,然后转换成接口对象
                    data.enforceInterface(descriptor);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    // 再调用Server端本地的 go() 方法
                    this.go(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }
学新通

通信过程

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgcggaj
系列文章
更多 icon
同类精品
更多 icon
继续加载