parent
036a7633e5
commit
884b45508e
@ -0,0 +1,189 @@ |
|||||||
|
--- |
||||||
|
一次完整的 IPC 通信流程 |
||||||
|
--- |
||||||
|
|
||||||
|
1. 了解 binder 的整体架构原理 |
||||||
|
2. 了解应用和 binder 驱动的交互方式 |
||||||
|
3. 了解 IPC 过程中的通信协议 |
||||||
|
|
||||||
|
```c++ |
||||||
|
private static class Proxy implements inuker.com.library.IRemoteCaller{ |
||||||
|
private android.os.IBinder mRemote; |
||||||
|
public void call(int code) throws RemoteException{ |
||||||
|
mRemote.transact(Stub.TRANSACTION_call, _data, _reply, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final class BinderProxy implements IBinder{ |
||||||
|
public boolean transact(int code, Parcel data, Parcel reply, int flags){ |
||||||
|
return transactNative(code, data, reply, flags); |
||||||
|
} |
||||||
|
} |
||||||
|
jboolean android_os_BinderProxy_transact(INIEnv* env, jobject obj, jint code, jobject dataObj, jobject replyObj, jint flags){ |
||||||
|
Parcel* data = parcelForJavaObject(env, dataObj); |
||||||
|
Parcel* reply = parcelForJavaObject(env, replyObj); |
||||||
|
IBinder* target = (IBinder)env->GetLongField(obj, gBinderProxyOffsets.mObject); |
||||||
|
status_t err = target->transact(code, *data, reply, flags); |
||||||
|
} |
||||||
|
|
||||||
|
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){ |
||||||
|
status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags); |
||||||
|
return status; |
||||||
|
} |
||||||
|
status_t IPCThreadState::transact(int32_t handle, ...){ |
||||||
|
writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); |
||||||
|
if((flags&TF_ONE_WAY)==0){ |
||||||
|
if(reply){ |
||||||
|
err = waitForResponse(reply); |
||||||
|
}else{ |
||||||
|
Parcel fakeReply; |
||||||
|
err = waitForResponse(&fakeReply); |
||||||
|
} |
||||||
|
}else{ |
||||||
|
err = waitForResponse(NULL, NULL); |
||||||
|
} |
||||||
|
return err; |
||||||
|
} |
||||||
|
status_t IPCThreadState::writeTransactionData(int32_t cmd, ...){ |
||||||
|
binder_transaction_data tr; |
||||||
|
tr.target.ptr = 0; |
||||||
|
tr.target.handle = handle; |
||||||
|
tr.code = code; |
||||||
|
tr.flags = binderFlags; |
||||||
|
tr.data.size = data.ipcDataSize(); |
||||||
|
tr.data.ptr.buffer = data.ipcData(); |
||||||
|
tr.data.ptr.offsets = data.ipcObjects(); |
||||||
|
|
||||||
|
mOut.writeInt32(cmd); |
||||||
|
mOut.write(&tr, sizeof(tr)); |
||||||
|
return NO_ERROR; |
||||||
|
} |
||||||
|
status_t IPCThreadState::waitForResponse(Parcel *reply, ...){ |
||||||
|
while(1){ |
||||||
|
talkWithDiver(); |
||||||
|
cmd = (uint32_t)mln.readInt32(); |
||||||
|
switch(cmd){ |
||||||
|
case BR_TRANSACTION_COMPLETE: |
||||||
|
break; |
||||||
|
case BR_REPLY:{ |
||||||
|
binder_transaction_data tr; |
||||||
|
mIn.read(&tr, sizeof(tr)); |
||||||
|
reply->ipcSetDataReference(tr.data.ptr.buffer, tr.data_size, ...); |
||||||
|
} |
||||||
|
goto finish; |
||||||
|
} |
||||||
|
} |
||||||
|
return err; |
||||||
|
} |
||||||
|
status_t IPCThreadState::talkWithDriver(bool doReceiver){ |
||||||
|
binder_write_read bwr; |
||||||
|
const bool needRead = mIn.dataPosition()>=mIn.dataSize(); |
||||||
|
const size_t outAvail = (!doReceive||needRead)?mOut.dataSize():0; |
||||||
|
bwr.write_size = outAvail; |
||||||
|
bwr.write_buffer = (uintptr_t)mOut.data(); |
||||||
|
if(doReceive && needRead){ |
||||||
|
bwr.read_size = mIn.dataCapacity(); |
||||||
|
bwr.read_buffer = (uintptr_t)mIn.data(); |
||||||
|
}else{ |
||||||
|
bwr.read_size = 0; |
||||||
|
bwr.read_buffer = 0; |
||||||
|
} |
||||||
|
ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr); |
||||||
|
return err; |
||||||
|
} |
||||||
|
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){ |
||||||
|
switch(cmd){ |
||||||
|
case BINDER_WRITE_READ:{ |
||||||
|
struct binder_write_read bwr; |
||||||
|
if(bwr.write_size>0){ |
||||||
|
ret = binder_thread_write(proc, thread, bwr.write_buffer, ...); |
||||||
|
} |
||||||
|
if(bwr.read.size>0){ |
||||||
|
ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, ...); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
Service 端如何和 binder 交互的? |
||||||
|
|
||||||
|
```c++ |
||||||
|
virtual bool threadLoop(){ |
||||||
|
IPCThreadState::self()->joinThreadPool(mIsMain); |
||||||
|
return false; |
||||||
|
} |
||||||
|
void IPCThreadState::joinThreadPool(bool isMain){ |
||||||
|
mOut.writeInt32(isMain ? BC_ENTER_LOOPER:BC_REGISTER_LOOPER); |
||||||
|
status_t result; |
||||||
|
do{ |
||||||
|
result = getAndExecuteCommand(); |
||||||
|
}while(result != -ECONNREFUSED && result != -EBADF); |
||||||
|
mOut.writeInt32(BC_EXIT_LOOPER); |
||||||
|
talkWithDriver(false); |
||||||
|
} |
||||||
|
status_t IPCThreadState::getAndExecuteCommand(){ |
||||||
|
// 1. 从驱动读取请求 |
||||||
|
talkWithDriver(); |
||||||
|
int32_t cmd = mIn.readInt32(); |
||||||
|
// 2. 处理请求 |
||||||
|
executeCommand(cmd); |
||||||
|
} |
||||||
|
status_t IPCThreadState::executeCommand(int32_t cmd){ |
||||||
|
switch((uint32_t)cmd){ |
||||||
|
case BR_TRANSACTION:{ |
||||||
|
binder_transaction_data tr; |
||||||
|
mIn.read(&tr, sizeof(tr)); |
||||||
|
|
||||||
|
Parcel buffer, reply; |
||||||
|
buffer.ipcSetDataReference(...); |
||||||
|
|
||||||
|
sp<BBinder> b((BBinder*)tr.cookie); |
||||||
|
b->transact(tr.code, buffer, &reply, tr.flags); |
||||||
|
sendReply(reply, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
return result; |
||||||
|
} |
||||||
|
status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags){ |
||||||
|
writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer); |
||||||
|
return waitForResponse(NULL, NULL); |
||||||
|
} |
||||||
|
status_t BBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){ |
||||||
|
switch(code){ |
||||||
|
case PING_TRANNSACTION: |
||||||
|
reply->writeInt32(pingBinder()); |
||||||
|
break; |
||||||
|
default: |
||||||
|
err = onTransact(code, data, reply, flags); |
||||||
|
break; |
||||||
|
} |
||||||
|
return err; |
||||||
|
} |
||||||
|
status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0){ |
||||||
|
JNIEnv* env = javavm_to_jnienv(mVM); |
||||||
|
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact, code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags); |
||||||
|
return res!=JNI_FALSE?NO_ERROE:UNKNOWN_TRANSACTION; |
||||||
|
} |
||||||
|
private boolean execTransact(int code, long dataObj, long replyObj, int flags){ |
||||||
|
Parcel data = Parcel.obtain(dataObj); |
||||||
|
Parcel reply = Parcel.obtain(replyObj); |
||||||
|
boolean res = onTransact(code, data, reply, flags); |
||||||
|
reply.recycle(); |
||||||
|
data.recycle(); |
||||||
|
return res; |
||||||
|
} |
||||||
|
public static abstract class Stub extends android.os.Binder implements IRemoteCaller{ |
||||||
|
boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags){ |
||||||
|
switch(code){ |
||||||
|
data.enforceInterface(descriptor); |
||||||
|
ICallback _arg0 = ICallback.Stub.asInterface(data.readStrongBinder()); |
||||||
|
this.publishBinder(_arg0); |
||||||
|
reply.writeNoException(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
``` |
||||||
|
|
||||||
|
![](https://i.loli.net/2020/03/28/1ZbMj2fUiX8BGc7.png) |
After Width: | Height: | Size: 222 KiB |
Loading…
Reference in new issue