前两篇Binder的文章,分析了service manager的启动和获取service manager的代理对象,这篇文章开始讲怎么把一个服务注册到service manager里面,也就是addService方法的流程。在开始讲addService之前,我们先简单讨论下,所谓的服务是个什么?大家都开发过跨进程service吗,android中一般要自定义个跨进程的程序,我们常用的是aidl。aidl是android定义的一个接口,其本身并不是什么模块,而仅仅是定义进程间通信的方法借口,类似如下:
```java
interface IMyAidl {
void test();
}
```
比如我要定义一个进程间通信的方法教test,然后就按照如上写就可以了,android系统在编译后会把这个文件自动生成一个java接口,类似如下:
```java
public interface IMyAidl extends android.os.IInterface
{
...........
public static abstract class Stub extends android.os.Binder implements com.xx.IMyAidl
{
private static final java.lang.String DESCRIPTOR = "com.xx.IMyAidl";
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
public static com.xx.IMyAidl asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.xx.IMyAidl))) {
return ((com.xx.IMyAidl)iin);
}
return new com.xx.IMyAidl.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@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_test:
{
data.enforceInterface(descriptor);
this.test();
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.xx.IMyAidl
{
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 test() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().test();
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.xx.IMyAidl sDefaultImpl;
}
.............................
}
}
```
上面这一串代码是自动生成的,具体不用看细节,只要看到在这个接口内部有个Stub类,继承了Binder类,而Binder类的父类是IBinder,只要知道这点就可以了,比如我们实例化一个服务。
```java
private IBinder iBinder = new IMyAidl.Stub() {
@Override
public void test() throws RemoteException {
..............
}
};
```
我们注册到service manager中的其实就是这个Stub类,这个stub在服务端重写了他的test方法,也就是最终服务端要执行的方法。而在客户端通过stub可以获得一个代理类,代理类通过service manager就可以寻找到服务端这里的执行方法。
说到这里其实还是没有把stub这个类说透,我们在讲注册服务前,还是需要先把服务弄明白了,在去看注册的过程就比较容易了。前面说的stub的父类是Binder类,那么我们在启动这个服务的时候,比如也会执行Binder类的构造函数,我们去看看Binder的构造函数
## Binder探秘
先上代码:
```java
private native final void init();
public Binder() {
init();
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Binder> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
}
```
这里面主要是调用了init()函数,他也是一个native方法,我们跟进去:
```c++
//简单说就是创建一个 JavaBBinderHolder,它里面有(service,即stub,即binder)和JavaBBinder,同时service也保存了JavaBBinderHolder
static void android_os_Binder_init(JNIEnv* env, jobject clazz) // java的Binder类构造方法会调用这里,class是service,即stub,即binder
{
JavaBBinderHolder* jbh = new JavaBBinderHolder(env, clazz); //创建一个JavaBBinderHolder对象
if (jbh == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
return;
}
LOGV("Java Binder %p: acquiring first ref on holder %p", clazz, jbh);
jbh->incStrong(clazz); //增加引用计数
env->SetIntField(clazz, gBinderOffsets.mObject, (int)jbh); // 把JavaBBinderHolder设置到BInder的mObject中
}
```
可以看到这里主要是new了个JavaBBinderHolder,我们看下JavaBBinderHolder是什么:
```c++
class JavaBBinderHolder : public RefBase
{
public:
JavaBBinderHolder(JNIEnv* env, jobject object)
: mObject(object) // 这里是具体的service,也即stub,也即Binder
{
LOGV("Creating JavaBBinderHolder for Object %p\n", object);
}
~JavaBBinderHolder()
{
LOGV("Destroying JavaBBinderHolder for Object %p\n", mObject);
}
sp<JavaBBinder> get(JNIEnv* env) // JavaBBinderHolder的mBinder是JavaBBinder,获得之
{
AutoMutex _l(mLock);
sp<JavaBBinder> b = mBinder.promote(); // 先将这个弱引用提升为强引用
if (b == NULL) { // 如果空
b = new JavaBBinder(env, mObject); // 新建一个本地对象
mBinder = b;
LOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%d\n",
b.get(), b->getWeakRefs(), mObject, b->getWeakRefs()->getWeakCount());
}
return b;
}
sp<JavaBBinder> getExisting()
{
AutoMutex _l(mLock);
return mBinder.promote();
}
private:
Mutex mLock;
jobject mObject; //这里是具体的service,也即stub,也即Binder
wp<JavaBBinder> mBinder; // 这个是类型为JavaBBinder的本地对象
};
```
这个类的构造方法初始化了mObject这个字段,传进来的object,即为调用这个jni方法的类,这个我们讨论的是service启动的时候调用的构造方法,所以这里的object就是我们的服务。所以到这里我们知道了JavaBBinderHolder的mObject字段持有了java层的服务,另外有个字段mBinder也是比较重要的,他指向JavaBBinder对象,这个就是java层的本地对象,后面会有说到。
这里我们已经把一个服务设置到了JavaBBinderHolder里面,回到android_os_Binder_init方法,我们看到最后还调用了env->SetIntField(clazz, gBinderOffsets.mObject, (int)jbh)这句话,这里的clazz是我们的服务Binder类,这里把刚创建的JavaBBinderHolder也设置给了Binder,这样他们之间是相互的持有了,java层的Binder可以直接调用native层的JavaBBinderHolder了。
服务Binder类的初始化说完了,接着我们在说一下有关Parcel的初始化,因为马上我们讲的addService方法传递数据就是用的parcel。
## Parcel的初始化
由于后面讲的数据之间传递的载体都是用的parcel,所以稍微了解下parcel现在是怎么初始化的。记得之前说BinderProxy的时候,有个记录BinderProxy字段的结构体吗,那是在系统启动时候初始化的:
```c++
int register_android_os_Binder(JNIEnv* env)
{
if (int_register_android_os_Binder(env) < 0)
return -1;
if (int_register_android_os_BinderInternal(env) < 0)
return -1;
if (int_register_android_os_BinderProxy(env) < 0)
return -1;
if (int_register_android_os_Parcel(env) < 0)
return -1;
return 0;
}
```
还记得这个方法吗,系统启动的时候会调用,然后初始化一些数据,我们现在看最后一个和parcel有关的:
```c++
const char* const kParcelPathName = "android/os/Parcel";
static int int_register_android_os_Parcel(JNIEnv* env)
{
jclass clazz;
................
clazz = env->FindClass(kParcelPathName);
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Parcel");
gParcelOffsets.mObject
= env->GetFieldID(clazz, "mObject", "I");
gParcelOffsets.mOwnObject
= env->GetFieldID(clazz, "mOwnObject", "I");
...................
return AndroidRuntime::registerNativeMethods(
env, kParcelPathName,
gParcelMethods, NELEM(gParcelMethods));
}
static struct parcel_offsets_t
{
jfieldID mObject; // 指向java层parcel的mObject,他表示一个c++层的parcel实例
jfieldID mOwnObject; // 指向java层parcel的mOwnObject
} gParcelOffsets;
```
去掉一些和目前分析场景无关的,我们看到这里的结构体parcel_offsets_t在初始化的时候,关联着jave层的2个字段,mObject和mOwnObject,mObject关联着一个c++层的parcel,我们数据的存储注意就是在c++层的这个parcel保存的。mOwnObject表示了这个c++层parcel在初始化时候是传入的,还是已经存在的parcel。我们可以看下初始化方法:
```c++
static void android_os_Parcel_init(JNIEnv* env, jobject clazz, jint parcelInt)
{
Parcel* parcel = (Parcel*)parcelInt;
int own = 0;
if (!parcel) {
//LOGI("Initializing obj %p: creating new Parcel\n", clazz);
own = 1;
parcel = new Parcel;
} else {
//LOGI("Initializing obj %p: given existing Parcel %p\n", clazz, parcel);
}
if (parcel == NULL) {
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
return;
}
//LOGI("Initializing obj %p from C++ Parcel %p, own=%d\n", clazz, parcel, own);
env->SetIntField(clazz, gParcelOffsets.mOwnObject, own);
env->SetIntField(clazz, gParcelOffsets.mObject, (int)parcel);
}
```
可以看到最后把上面说的2个字段值,设置到了clazz的mOwnObject和mObject字段中了,clazz就是一个java层的parcel实例。这个方法参数parcelInt就是一个c++层的parcel,如果有传入的话own设置为1,否则为0,然后把这个c++的parcel设置到mObject自动。至于调用这个方法是在一个java层被初始化的时候,比如我们可以看到下面这些代码:
```java
private native void init(int obj);
private Parcel(int obj) {
if (DEBUG_RECYCLE) {
mStack = new RuntimeException();
}
//Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
init(obj);
}
static protected final Parcel obtain(int obj) {
final Parcel[] pool = sHolderPool;
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
if (p != null) {
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
p.init(obj);
return p;
}
}
}
return new Parcel(obj);
}
public static Parcel obtain() {
final Parcel[] pool = sOwnedPool;
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
if (p != null) {
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
return p;
}
}
}
return new Parcel(0);
}
```
可以看到在获取一个java层parcel,或者一个java层parcel初始化等情况下,都是会有被执行的可能的,所以一旦我们获取的一个可能的parcel,那么他在c++层也就有了一个parcel,我们就可以获取他来进行读写了,在下面我们马上就会看到有这个例子。好了,parcel初始化就说到这里,下面开始讲addService。
## addService开始
直接上代码:
```java
public void addService(String name, IBinder service)
throws RemoteException {
Parcel data = Parcel.obtain(); // 向binder驱动请求的数据
Parcel reply = Parcel.obtain(); // binder返回的数据
data.writeInterfaceToken(IServiceManager.descriptor); // 写入请求头
data.writeString(name); // 写入service名字
data.writeStrongBinder(service); // 写入service,这里service实际上是JavaBBinder或者BpBInder
mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0); // 发送请求给binder驱动
reply.recycle();
data.recycle();
}
```
这个方法首先获取2个缓冲区,一个写缓冲区data,一个读缓冲区reply,写缓冲区用来给binder驱动发送命令,读缓冲区用来接受binder驱动返回的命令。看这就调用了前面说的parcel的obtain方法来获取parcel实例了吧。紧接着调用writeInterfaceToken方法写入一个请求头,这里请求头代表了你要请求服务的一个字符标识,这里我们需要向service manager请求注册服务,所以这个字符串是service manager独有的,当service manager获得了这个数据后会进行验证,我们看下这个方法:
```java
static void android_os_Parcel_writeInterfaceToken(JNIEnv* env, jobject clazz, jstring name)
{
Parcel* parcel = parcelForJavaObject(env, clazz);
if (parcel != NULL) {
// In the current implementation, the token is just the serialized interface name that
// the caller expects to be invoking
const jchar* str = env->GetStringCritical(name, 0);
if (str != NULL) {
parcel->writeInterfaceToken(String16(str, env->GetStringLength(name)));
env->ReleaseStringCritical(name, str);
}
}
}
```
这里由于进入native层了,首先获取一个parcel,我们看下parcelForJavaObject方法:
```c++
// 把java的Parcel对象obj转化为C++的Parcel对象
Parcel* parcelForJavaObject(JNIEnv* env, jobject obj)
{
if (obj) {
// 这里的obj是java层的,在java层的obj有个mObject字段,在java层Pracel.obtain的时候
// mObject会指向一个c++层new出来的Parcel,实际的数据存储会在c++的这个parcel中进行
//这里即是从这个字段里获取c++层的Parcel
Parcel* p = (Parcel*)env->GetIntField(obj, gParcelOffsets.mObject);
if (p != NULL) {
return p;
}
jniThrowException(env, "java/lang/IllegalStateException", "Parcel has been finalized!");
}
return NULL;
}
```
这个方法是从jave层的parcel的mObject变量中获取c++层的parcel,前面parcel初始化的时候刚有说过,这里就不多说了。返回c++层的parcel,然后调用它的writeInterfaceToken方法写入请求头:
```c++
// 写入请求头,由严格模式策略字符, 以及后面的描述中字符串组成
// 这个方法写入先是32位头,然后32位字符串长度,在是字符串
status_t Parcel::writeInterfaceToken(const String16& interface)
{
writeInt32(IPCThreadState::self()->getStrictModePolicy() |
STRICT_MODE_PENALTY_GATHER);
// currently the interface identification token is just its name as a string
return writeString16(interface);
}
```
这个方法里面首先写入一个32位的数字,这里数字后面其实也没用到,所以具体也不用管,知道是个32位的数字就行了,然后就写入一个16位的字符串:
```c++
status_t Parcel::writeString16(const String16& str) // 写入字符串,实际会写入字符串长度+字符串
{
return writeString16(str.string(), str.size());
}
status_t Parcel::writeString16(const char16_t* str, size_t len) // 写入字符串,先写32位的长度,再赋值字符串
{
if (str == NULL) return writeInt32(-1);
status_t err = writeInt32(len);
if (err == NO_ERROR) {
len *= sizeof(char16_t); // 字符串个数* 每个16位 = 总字节
uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); // 可以理解成开辟空间,这里多加一个sizeof(char16_t)是最后的结束符'\0'?
if (data) {
memcpy(data, str, len); // 复制字符串
*reinterpret_cast<char16_t*>(data+len) = 0;
return NO_ERROR;
}
err = mError;
}
return err;
}
```
这里最后写入的开始是32位的字符串长度,然后在写入字符串,这里写入的细节可以看注释,不多做解释了。我们回到addService方法,接着执行data.writeString16(name)写入service的名字,这个也不用多说。后面一句data.writeStrongBinder(service)这个是要写入service了,我们看下方法:
```c++
// Parcel的jni方法,写入一个对象,这里object就是一个服务sevice
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jobject clazz, jobject object)
{
Parcel* parcel = parcelForJavaObject(env, clazz); // 获取一个Paecel在c++端对应的对象
if (parcel != NULL) {
// ibinderForJavaObject方法是,如果是FregService,先获取JavaBBinderHolder,在获取JavaBBinder。如果是BInderProxy类,返回的就是一个BPBInder
const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object)); // 获得JavaBBinder或者BpBInder写入writeStrongBinder
if (err != NO_ERROR) {
jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
}
}
}
这里首先获得一个native层的parcel,然后后面会执行parcel的writeStrongBinder。在这个方法之前我们先看下他的参数,即ibinderForJavaObject方法:
```c++
// 获得java层obj对应的C++层的本地对象。这里obj要么是一个Service,要么是一个BinderProxy
// 从这个方法可以看到,JavaBBinderHolder的mObject是一个service,mbinder是一个JavaBBinder,而JavaBBinder的mobject也是service
// 这样service就存在avaBBinderHolder和JavaBBinder中。(service是一个BBinder)
// 如果是Service,先获取JavaBBinderHolder,在获取JavaBBinder。如果是BInderProxy类,返回的就是一个BPBInder
sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
if (obj == NULL) return NULL;
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) { // 如果是Service,先获取JavaBBinderHolder,在获取JavaBBinder
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetIntField(obj, gBinderOffsets.mObject); // 获取 JavaBBinderHolder
return jbh != NULL ? jbh->get(env) : NULL; // 返回本地对象JavaBBinder
}
if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) { // 如果是BInderProxy类
return (IBinder*)
env->GetIntField(obj, gBinderProxyOffsets.mObject); // 返回的就是一个BPBInder
}
LOGW("ibinderForJavaObject: %p is not a Binder object", obj);
return NULL;
}
```
这里IsInstanceOf方法判断这个传进来的参数是不是一个binder,gBinderOffsets是个结构体,如下:
```c++
static struct bindernative_offsets_t //java的Binder,stub的父类
{
// Class state.
jclass mClass; // 指向binder类
jmethodID mExecTransact; // 指向binder类的execTransact方法
// Object state.
jfieldID mObject; // 指向binder类的mObject变量,保存着javaBBinderHolder
} gBinderOffsets;
```
他的初始化也是在系统启动后执行的:
```c++
const char* const kBinderPathName = "android/os/Binder";
static int int_register_android_os_Binder(JNIEnv* env) // 初始化gBinderOffsets结构体
{
jclass clazz;
clazz = env->FindClass(kBinderPathName);
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Binder");
gBinderOffsets.mClass = (jclass) env->NewGlobalRef(clazz); // 指向binder类
gBinderOffsets.mExecTransact
= env->GetMethodID(clazz, "execTransact", "(IIII)Z"); // 指向execTransact方法
assert(gBinderOffsets.mExecTransact);
gBinderOffsets.mObject
= env->GetFieldID(clazz, "mObject", "I"); // 指向binder类的mObject变量
assert(gBinderOffsets.mObject);
return AndroidRuntime::registerNativeMethods(
env, kBinderPathName,
gBinderMethods, NELEM(gBinderMethods));
}
```
由于我们这里传入的确实是个Binder类,所以会接着从Binder类的mObject字段中获取JavaBBinderHolder对象,然后调用它的get方法:
```c++
sp<JavaBBinder> get(JNIEnv* env) // JavaBBinderHolder的mBinder是JavaBBinder,获得之
{
AutoMutex _l(mLock);
sp<JavaBBinder> b = mBinder.promote(); // 先将这个弱引用提升为强引用
if (b == NULL) { // 如果空
b = new JavaBBinder(env, mObject); // 新建一个本地对象
mBinder = b;
LOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%d\n",
b.get(), b->getWeakRefs(), mObject, b->getWeakRefs()->getWeakCount());
}
return b;
}
```
记得在前面介绍Binder初始化的时候说到JavaBBinderHolder的mBinder字段是指向一个JavaBBinder对象的吗,当时这个字段也没有赋值过,所以现在当然是空,接着会new一个JavaBBinder对象,然后赋值给mBinder变量。我们看下JavaBBinder的构造方法:
```c++
JavaBBinder(JNIEnv* env, jobject object)
: mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)) // mObject指向一个service
{
LOGV("Creating JavaBBinder %p\n", this);
android_atomic_inc(&gNumLocalRefs);
incRefsCreated(env);
}
```
可以看到这里初始化了他的mObject变量,这个变量指向一个object,这个object就是我们最开始的服务。知道我们梳理一下这里的3个类,Binder,JavaBBinderHolder,JavaBBinder.
Binder的mObject变量持有JavaBBinderHolder
JavaBBinderHold的mObject变量持有Binder,mBinder变量持有JavaBBinder
JavaBBinder的mObject持有Binder
到这里其实返回回去的就是JavaBBinder对象,最终传给service manager的也就是JavaBBinder对象了。上面这3个类是不是感觉有点复杂,我也是这么觉得,但是谷歌为什么要这么做呢,JavaBBinder就是注册在service manager中的对象,所以他持有Binder是很正常的,否则他就不知道要调用谁了。而Binder如果在客户端的是返回给用户的是一个proxy,他需要通过binder驱动来找到对应的JavaBBinder才能够执行服务端的方法,所以他必须要持有找到JavaBBinder的一个路径,这么我们看到JavaBBinderHold这层其实完全可以不要直接让Binder和JavaBBinder相互联系就好,但是这样的话耦合就比较严重了,万一有什么原因不能让Binder直接调用JavaBBinder的话,改动起来就比较麻烦了,需要修改可能会影响比较大,所以我理解中间加了一层JavaBBinderHold也是为了后续的扩展更灵活些。好了我们回到android_os_Parcel_writeStrongBinder方法,接下去执行的代码是:
```c++
parcel->writeStrongBinder(ibinderForJavaObject(env, object));
```
现在需要把获得的JavaBBinder写入parcel:
```c++
status_t Parcel::writeStrongBinder(const sp<IBinder>& val) // 将一个JavaBBinder组件封装成一个结构体
{
return flatten_binder(ProcessState::self(), val, this);
}
```
继续跟进:
```c++
status_t flatten_binder(const sp<ProcessState>& proc,
const sp<IBinder>& binder, Parcel* out) // 将一个JavaBBinder组件封装成一个结构体
{
flat_binder_object obj;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; // 0x7f表示server线程优先级不能低于0x7f, FLAT_BINDER_FLAG_ACCEPTS_FDS 表示可以将包含文件描述符的数据传给service处理
if (binder != NULL) {
IBinder *local = binder->localBinder(); // 获取BBbinder,即服务端的service,本地对象
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; // 设置这个binder对象类型,表示是要注册到service manager中的.之后再binder_thread_write -> binder_transaction 中会把他修改成BINDER_TYPE_HANDLE类型
obj.binder = local->getWeakRefs(); // 设置弱引用计数
obj.cookie = local;
}
} else { // 没有本地对象
obj.type = BINDER_TYPE_BINDER;
obj.binder = NULL;
obj.cookie = NULL;
}
return finish_flatten_binder(binder, obj, out);
}
struct flat_binder_object { // 描述在进程通信中数据缓冲区中的数据有binder对象,描述binder对象的类,可能是binder实体对象,也可能是binder引用对象
/* 8 bytes for large_flat_header. */
unsigned long type; // 类型代表,具体值看枚举类中BINDER_TYPE_BINDER等值的意义
unsigned long flags; // 只有是实体对象的时候才有意义,0-7位表示线性应该具有的最小优先级,第八位是1表示binder实体对象是否允许将一个包含文件描述符的缓冲区传给目标进程
/* 8 bytes of data. */
union {
void *binder; /* local object */ // 如果该类是binder实体,bingder指向binder实体内部serviceweakref_impl
signed long handle; /* remote object */ // 如果该类是binder引用,那么就是引用对象的句柄值
};
/* extra data associated with local object */
void *cookie; // 如果该类是binder实体,就是指向service本地对地址
};
```
这个方法开始有个结构体flat_binder_object,这个结构体是用来在进程间通信的时候,保存binder对象的,我们知道binder对象是专门用来进行进场交互的对象,所以无论你需要传递什么数据都比如通过binder对象才能找得对应的方法,所以虽然binder对象也可以看做是数据,但是它属于特殊的数据,所以专门用这个结构体来描述它,由于我们现在是要注册到service manager中,所以当service manager获取它后会从这个结构体中取出需要的信息保存在自己里面,所以现在我们需要把一个JavaBBinder先转化为flat_binder_object结构体。
obj.flags这个字段做一些线程优先级等的处理。接着调用binder->localBinder()方法,我们知道binder现在是一个JavaBBinder类,他的localBinder方法是:
```c++
BBinder* BBinder::localBinder()
{
return this;
}
```
返回的是他本身,即非空。后面设置flat_binder_object对象的type为BINDER_TYPE_BINDER,binder是他的弱引用计数,cookie就是JavaBBinder。之后再调用finish_flatten_binder方法:
```c++
inline static status_t finish_flatten_binder( // 把一个扩展的本地对象写入parcel中
const sp<IBinder>& binder, const flat_binder_object& flat, Parcel* out)
{
return out->writeObject(flat, false);
}
```
这里是要把上面的flat_binder_object对象写入parcel中,我们继续跟进去:
```c++
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData) // 写入object
{
const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity; // mDataPos是当前写入的数据偏移位置,这句表示是否还有足够空间写入数据
const bool enoughObjects = mObjectsSize < mObjectsCapacity; // mObjectsSize是偏移数组下标,偏移数组是指数据区中,binder对象在哪些偏移位置,这里判断偏移数组是否足够写入数据
if (enoughData && enoughObjects) {
restart_write:
// 把flat_binder_object对象的对应的结构体地址写入parcel数据区中
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
// Need to write meta-data?
if (nullMetaData || val.binder != NULL) { // 如果是个binder,需要把这个binder结构体,即上面写入的位置,写到偏移数组中,从而可以知道数据区中哪些是binder对象
mObjects[mObjectsSize] = mDataPos; // binder对象写入数据区的偏移位置
acquire_object(ProcessState::self(), val, this);
mObjectsSize++;
}
// remember if it's a file descriptor
if (val.type == BINDER_TYPE_FD) { // 是否包含文件描述符?
mHasFds = mFdsKnown = true;
}
return finishWrite(sizeof(flat_binder_object)); // 调整下一个写入点mDataPos
}
if (!enoughData) { // 如果数据缓冲区大小不够
const status_t err = growData(sizeof(val)); // 扩容缓冲区大小
if (err != NO_ERROR) return err; // 如果不能扩容了,报错
}
if (!enoughObjects) { // 如果偏移数组缓冲区大小不够,扩容
size_t newSize = ((mObjectsSize+2)*3)/2;
size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t));
if (objects == NULL) return NO_MEMORY;
mObjects = objects;
mObjectsCapacity = newSize;
}
goto restart_write; // 如果扩容后,重新回到前面写入object
}
```
在parcel中mData和mObjects分别是用来保存数据和flat_binder_object对象相关的2个缓冲区。该方法会先判断缓冲区是否足够空间来保存这些数据,如果空间不够,在方法最后一部分代码就是用来开辟内存空间的。如果空间足够的话,会把flat_binder_object对象的地址值val写入mData,由于mData这个缓冲区中除了保存binder对象外,其他所有的对象也都会保存在这里,所以为了让对方知道哪些些数据是flat_binder_object的对象,所以使用另外一个缓冲区mObjects来保存所有flat_binder_object对象在mData中的偏移位置,即mDataPos,这样在解析mData中数据的时候就可以正确的解析出flat_binder_object对象了,这点我们在后面讲到service manager解析数据的时候会讲到,这里就先提一下。
数据保存到缓冲区后,最后分别更新两个缓冲区的偏移位置mObjectsSize和mDataPos,mDataPos位置的更新是执行下面的方法:
```c++
status_t Parcel::finishWrite(size_t len) //调整parcel下一个写入点
{
//printf("Finish write of %d\n", len);
mDataPos += len; // 写入点前移
LOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos);
if (mDataPos > mDataSize) { // 更新缓冲区全部数据大小
mDataSize = mDataPos;
LOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize);
}
//printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
return NO_ERROR;
}
```
方法比较简单就是偏移位置mDataPos前移,然后根据是否超过缓冲区最大容量更新。
到这里我们把一个服务,即JavaBBinder对象写入了缓冲区,我们回到addService方法,继续下面的执行:
```java
mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);
```
接着会执行这行代码。根据前面binder系列文章第二篇的分析,我们获取service manager后,这里的mRemote是BinderProxy对象,现在我们看下他的transact方法:
```java
public native boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
```
可以看到他是一个native方法,我们继续到native那边去看:
```c++
// BinderProxy向binder驱动发请求. code是命令,dataObj是传给binder驱动的数据,replyObj是返回的数据
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj,
jobject replyObj, jint flags)
{
if (dataObj == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
return JNI_FALSE;
}
Parcel* data = parcelForJavaObject(env, dataObj); // 把java层的parcel转为c++
if (data == NULL) {
return JNI_FALSE;
}
Parcel* reply = parcelForJavaObject(env, replyObj); // 把java层的parcel转为c++
if (reply == NULL && replyObj != NULL) {
return JNI_FALSE;
}
// 获取代理比如service manager的BPBInder
// obj是BinderProxy,他的mObject变量就是BpBinder
IBinder* target = (IBinder*)
env->GetIntField(obj, gBinderProxyOffsets.mObject); // 这里obj是BinderProxy,target是BpBinder
if (target == NULL) {
jniThrowException(env, "java/lang/IllegalStateException", "Binder has been finalized!");
return JNI_FALSE;
}
........................
status_t err = target->transact(code, *data, reply, flags); // 真正发起了binder请求
//if (reply) printf("Transact from Java code to %p received: ", target); reply->print();
if (time_binder_calls) {
conditionally_log_binder_call(start_millis, target, code);
}
if (err == NO_ERROR) {
return JNI_TRUE;
} else if (err == UNKNOWN_TRANSACTION) {
return JNI_FALSE;
}
signalExceptionForError(env, obj, err);
return JNI_FALSE;
}
```
由于这里是从jave层转到native层了,所以方法开头先把2个parcel缓冲区转为c++的。接着我们从传过来的参数obj,即一个BinderProxy对象的mObject变量获取对象,从前面binder文章的第二篇的分析中我们知道mObject是一个BpBinder对象,所以接下去我们就看到调用了BpBinder的transact方法,传入参数也都是从java层那边传过来的。这里特意提醒下,code的ADD_SERVICE_TRANSACTION,这个代表目前是个注册service的事务。
## BpBinder::transact
现在进入BpBinder的transact方法:
```c++
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); // mHandle是句柄值。IPCThreadState是调用当前线程的类,继续调用transact方法 code就是命令,flag是同步还是异步,data是数据,mHandle是代理对象句柄,reply保存返回结果的缓冲区
if (status == DEAD_OBJECT) mAlive = 0; // 如果本地对象死了, mAlive = 0
return status;
}
return DEAD_OBJECT;
}
```
这里的BpBinder就是service manager的BpBinder,这点不要忘记了,所以他的句柄值mHandle是0,后面的参数都是前面传过来的,不用多说。他继续调用IPCThreadState的transact方法:
```c++
// 向binder驱动发命令
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");
//这里写入的命令是BC_TRANSACTION
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); //把数据写入binder_transation_data结构体中,然后把命令和binder_transation_data结构体保存在一个缓冲区中
}
if (err != NO_ERROR) { // 出错了
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) { // 等于0表示同步的进程通信
...............
if (reply) { // 如果有返回结果缓冲区
err = waitForResponse(reply);// 等待请求的返回
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
.......................
} else { // 异步通信
err = waitForResponse(NULL, NULL);
}
return err;
}
```
先从整体上分析这个方法,开始先做一些数据检查,然后调用writeTransactionData方法来把请求的数据写入一个binder_transation_data结构体,然后回调waitForResponse方法继续执行,如果是同步请求,会在传入返回的缓冲区reply,异步的话都不用传了。好了,我们先看下writeTransactionData这个方法,特别注意下这个第一个参数命令是BC_TRANSACTION,这个先记下,后续执行到的时候再说:
```c++
enum transaction_flags {
TF_ONE_WAY = 0x01, /* this is a one-way call: async, no return */ // 表示异步通信
TF_ROOT_OBJECT = 0x04, /* contents are the component's root object */ // 不允许目标进程返回结果包含文件描述符
TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */ // 成员变量data是4字节的状态码
TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */ // 是否运行携带文件描述符
};
struct binder_transaction_data { // 描述进程间通信的数据
union { // 可能是一个binder实体对象,也可能是一个binder引用对象
size_t handle; // 如果是binder引用对象,就是引用对象的句柄值
void *ptr; // 如果是一个binder实体对象,ptr是service的weakref_impl
} target;
void *cookie; // BR_TRANSACTION是向service请求任务,这个变量就是service地址
unsigned int code; // 请求的命令
/* General information about the transaction. */
unsigned int flags; // 见transaction_flags类
pid_t sender_pid; // 发起请求经常的pid
uid_t sender_euid; // 发起请求进程的euid
size_t data_size; // 下面data中,ptr的buffer的大小
size_t offsets_size; // 偏移数组的大小
union {
// 下面buffer如果包含有binder对象,那么offsets就包含了所有binder对象在buffer中的偏移值,而上面offsets_size表示有几个binder对象,即offsets数组有几个值
struct {
const void *buffer; // 如果数据量大,动态开辟空间
const void *offsets; // 如果上面buffer中有保存binder实体对象,这里就会保存实体对象的偏移位置,有几个,这个就保存几个数字。
} ptr;
uint8_t buf[8]; // 如果数据量小,就用它够了
} data;
};
// 准备向binder驱动发送命令前,这个方法把命令和数据结构体(binder_transaction_data)写入缓冲区
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; // transaction_flags类的定义,同步异步之类的
const status_t err = data.errorCheck();
if (err == NO_ERROR) { // 数据没有错,赋值给binder_transaction_data
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); // 写入binder驱动需要的命令
mOut.write(&tr, sizeof(tr)); // 写入binder驱动需要的数据
return NO_ERROR;
}
```
这个方法主要就是把之前parcel中的数据,封装为一个binder_transaction_data结构体,这个结构体就是进程间传输时候的格式了。可以简单看下这个结构体,主要的就是对方服务的句柄,比如我们现在向service manager请求,那么就是0。 请求的命令,比如现在是注册服务就是ADD_SERVICE_TRANSACTION。然后当前是同步还是异步以及具体的数据。
关于数据,前面在分析写入finish_flatten_binder结构体的时候是否记得,数据存储的时候,单独把binder相关的信息也写入了一个数组中。这里我们写入binder_transaction_data结构体的时候也需要这样来写。这个方法如果对前面数据写入过程已经比较了解的话,看起来也很简答,上面注释也比较详细了,就不多说了,最后把命令cmd,现在也就是BC_TRANSACTION,以及binder_transaction_data结构体写入IPCThreadState的缓冲区。我们返回前面IPCThreadState::transact方法,继续看后面的流程。
## 进程间通信开始
前面writeTransactionData方法把数据写入缓冲区了,后面执行waitForResponse方法继续发请求,这个方法里面就是正式开始了进程间通信了:
```c++
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) // 向binder驱动发送命令,等待回复的结果
{
int32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break; // talkWithDriver向binder发送命令,正常返回0
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue; // 如果没有返回数据继续等待
cmd = mIn.readInt32();
............
switch (cmd) {
case BR_TRANSACTION_COMPLETE: // 如果是一个binder驱动给的正常的回复,会重新执行这个while继续talkWithDriver执行
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:
{
LOG_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: // 到这里client就收到了server发过来的回复了,剩下的主要任务就是根据返回数据处理
{
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr)); // 获取server回复给client的数据
LOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
if (err != NO_ERROR) goto finish;
if (reply) { // 如果Parcel非空
if ((tr.flags & TF_STATUS_CODE) == 0) { // 0表示请求被处理成功,把返回的缓冲区保存在parcel中
reply->ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t),
freeBuffer, this); // 析构reply时会调用freeBuffer这个方法,该方法中会调用BC_FREE_BUFFER来释放缓冲区
} else { // 处理失败了,直接是否缓冲区
err = *static_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 size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
}
} else { // 如果Parcel为空,直接调用freeBuffer释放内核中的缓冲区
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(size_t), this);
continue;
}
}
goto finish;
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
if (err != NO_ERROR) {
if (acquireResult) *acquireResult = err;
if (reply) reply->setError(err);
mLastError = err;
}
return err;
}
```
这个方法开始我们看到一个while(1)的循环,然后调用了talkWithDriver方法,talkWithDriver方法里面就是进入binder驱动中进行任务的处理,处理完后返回,根据返回的情况,以及这里cmd命令,会进行不同的处理,有可能返回到调用端,有可能继续等待下一次命令。我们这里还是按照注册一个服务到service manager的流程来说,如果正常返回的话cmd就是BC_TRANSACTION,这个处理我们后面再说,现在先进入talkWithDriver方法:
```c++
struct binder_write_read {
signed long write_size; /* bytes to write */ // 缓冲区大小
signed long write_consumed; /* bytes consumed by driver */ // binder驱动已经处理了多少
unsigned long write_buffer; // 传给binder的缓冲区,在用户空间
signed long read_size; /* bytes to read */ // 缓冲区大小
signed long read_consumed; /* bytes consumed by driver */ // 已经读取了多少
unsigned long read_buffer; // binder返回给用户的数据缓冲区,在用户空间
};
// 这个方法注意,和binder驱动交互分为给binder命令和从binder读取返回数据,提供的缓冲区分别是mout和min,方法开始一段会根据不同情况,设置是否交互的时候读或者写都会起效。比如min中有之前没处理完
// 的数据的时候,就会把bwr.read_size = 0,表示不需要binder驱动返回数据。同理,doReceive为true表示只需要接受数据,不要给binder驱动发数据,所以doReceive为true,或者min有之前未处理完的数据的时候,就不会给binder驱动发命令
status_t IPCThreadState::talkWithDriver(bool doReceive) // 和binder驱动进行交互。 doReceive为true表示只希望接受binder返回的数据,不给binder传命令和数据。默认为true
{
LOG_ASSERT(mProcess->mDriverFD >= 0, "Binder driver is not opened");
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize(); // 当前读的位置>=数据长度了,说明读取完了
// 如果希望给binder驱动传数据,或者之前返回的数据已经处理完了,那么就把传给binder的数据的缓冲区大小设置为mOut.dataSize()
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail; // 设置输出缓冲区大小
bwr.write_buffer = (long unsigned int)mOut.data(); // 设置输出缓冲区地址
// This is what we'll read.
if (doReceive && needRead) { // 如果希望binder驱动返回数据,并且之前所以得返回数据已经处理完毕了
bwr.read_size = mIn.dataCapacity(); // 设置输入缓冲区大小
bwr.read_buffer = (long unsigned int)mIn.data(); // 设置输入缓冲区
} else {
bwr.read_size = 0; // 否则就不给输入缓冲区
}
// Return immediately if there is nothing to do.
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR; // 读写缓冲区都没数据,说明不需要进行binder驱动交互,return
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
IF_LOG_COMMANDS() {
alog << "About to read/write, write size = " << mOut.dataSize() << endl;
}
#if defined(HAVE_ANDROID_OS)
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) // 正式和binder交互
err = NO_ERROR;
else
err = -errno;
#else
err = INVALID_OPERATION;
#endif
IF_LOG_COMMANDS() {
alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
}
} while (err == -EINTR);
...................................
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) { // 大于0表示binder驱动已经处理了这么多命令了,但是可能还没有处理完
if (bwr.write_consumed < (ssize_t)mOut.dataSize()) // 如果处理的位置小于数据总数
mOut.remove(0, bwr.write_consumed); // 那么就把从0开始的已经处理的删除
else
mOut.setDataSize(0); // 如果全处理完了,就设置为0
}
if (bwr.read_consumed > 0) { //这个是binder驱动返回给用户的数据
mIn.setDataSize(bwr.read_consumed); // 设置数据的大小
mIn.setDataPosition(0); // 设置数据起始读的位置
}
....................
return NO_ERROR;
}
return err;
}
```
这个方法首先声明了一个结构体binder_write_read,这个结构体保存的就是传输的数据,包括数据的大小,读写偏移位置都有在这个结构体里面。开始一部分代码就是把当前需要请求的数据写入这个结构体中,并且会做一些校验,比如如果没有数据的话也不会做请求了。等数据都准备好了就会调用ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)这个方法来正式请求任务了,关于ioctl这个方法,不知道看过binder第一篇文章的同学是否还记得在讲binder驱动初始化的时候,有个驱动操作的列表:
```c
static struct file_operations binder_fops = {
.owner = THIS_MODULE,
.poll = binder_poll,
.unlocked_ioctl = binder_ioctl, // IO控制
.mmap = binder_mmap, // 内存映射
.open = binder_open, // 打开设备
.flush = binder_flush,
.release = binder_release, // 释放设备
};
```
上面这个结构体就是驱动操作的列表,左边的操作调用的方法名字,右边是具体的方法。这里我们调用的是ioctl,虽然左边没有ioctl这个名字,但是有unlocked_ioctl这个名字,这个是linux内核的操作一个知识了,早起对于进入内核都会加锁,所以用的名字是ioctl,后来为了增加并发性,已经修改为unlocked_ioctl这个方法了,所以现在调用ioctl的话会直接调用这里unlocked_ioctl的方法,即binder_ioctl。这个方法就是进入binder驱动中执行了,由于这篇文章已经比较长了,这个方法我们下一篇文章再说,我们回到talkWithDriver这个方法,先把整体看完。
在talkWithDriver这个方法执行完后,如果有需要的话,会把返回的数据写入binder_write_read这个结构体中read相关的字段中,然后调用者取出这些数据写入自己IPCThreadState类中的缓冲区中,然后在一层层返上去,最后拿到请求返回的数据做处理。这样一个完整的进程间通信就执行完毕了。
这篇文章把进入内核binder驱动前的准备工作都分析了一遍,接下去就真正进入到内核的binder驱动中了,还是有点期待。好了,最后还是把这篇文章设计的时序图画一下,加深理解。

Android进程间通信Binder(三)