正文开始
下图是一张常见的Binder通信机制流程图:
为便于跟踪,需要创建一个demo程序,在demo程序中添加一个按钮,在按钮的onClick()函数中添加以下代码:
String TAG = "BinderDemo";
PowerManager.WakeLock wakeLock;
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "hello binder");
if (null != wakeLock) {
Log.d(TAG, "before calling wakeLock.acquire()");
wakeLock.acquire(); // 关键代码
Log.d(TAG, "after calling wakeLock.acquire()");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
wakeLock.release();
}然后,编译demo程序并安装到手机中。在上述代码中,首先获取电源管理PowerManager的一个WakeLock实例,然后通过该实例调用WakeLock类的acquire()函数。acquire()函数实际上会构造一个Binder请求,向下传递给Binder驱动设备,Binder驱动设备收到这个请求后,会根据这个请求所附带的Binder引用信息,找到对应的Binder服务端,将这个请求添加到Binder服务端的工作任务列表,此时客户端线程进入等待状态。在本例中,这个服务端就是PowerManagerService。当服务端完成这个请求后,会把结果传回Binder驱动,此时客户端线程收到返回数据,结束等待,一层一层地向上返回,直到退出acquire()函数。
上述过程涉及的代码很长很复杂,跨越了不同的进程,从app上层深入到底层kernel,又从底层kernel传递信号到服务端上层java代码,服务端上层代码处理完成后,返回给kernel层,然后又从kernel层返回到客户端app的上层,这一路峰回路转,机关暗藏,但呈现给开发人员的只有短短一句wakeLock.acquire(),这充分体现了Binder机制的巧妙设计。
接下来我们跟踪acquire()函数,看看它是如何将请求传递给Binder驱动。
在WakeLock类中,acquire()函数定义如下:
public void acquire() {
synchronized (mToken) {
acquireLocked();
}
}
可以看到它实际上调用了WakeLock类的acquireLocked()函数。 在acquireLocked()函数中,把请求向下传递的代码如下:
try {
mService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource, mHistoryTag);
} catch (RemoteException e) {
}
其中,mService是一个IPowerManager对象,我们可以将它理解为电源服务的一个引用(不是实例)。 现在,我们想要继续往下跟,看看这个acquireWakeLock()函数是如何实现的。但是,我们这时候会发现,要找到它的定义,不是一件易事。在IDE里面,我们无法进入这个函数的声明或定义,因为它是在framework层声明的。于是我们只能在安卓源代码里面找。然后,我们发现有很多个类里面都有这个同名函数,只是参数不一样。
最终,我们找到这个函数的声明是在以下路径:
frameworks/base/core/java/android/os/IPowerManager.aidl
void acquireWakeLock(IBinder lock, int flags, String tag, String packageName, in WorkSource ws, String historyTag);
在这个文件里面,它只有声明。在整套安卓源代码中,我们找不到它的定义。
因此,我们有点懵逼。
但是,请不要慌!细心的司机这时候会注意到这个文件并不是一个普通文件,它是一个aidl文件。老司机们都知道,aidl文件对应的java文件会在编译时自动生成。
(注:这时候我们已经从安卓上层进入到Framework层)
于是,我们进入out目录搜索IPowerManager.java:
find out/target/common/ -name 'IPowerManager.java'
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/os/IPowerManager.java
在IPowerManager.java文件中,我们找到了mService.acquireWakeLock()的定义:
@Override public void acquireWakeLock(android.os.IBinder lock, int flags, java.lang.String tag, java.lang.String packageName, android.os.WorkSource ws, java.lang.String historyTag) 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.writeStrongBinder(lock);
_data.writeInt(flags);
_data.writeString(tag);
_data.writeString(packageName);
if ((ws!=null)) {
_data.writeInt(1);
ws.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeString(historyTag);
mRemote.transact(Stub.TRANSACTION_acquireWakeLock, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
这个函数把参数信息打包进一个Parcel对象(_data),然后调用mRemote变量的transact()函数把这个Parcel向下传递,当transact()返回时,Binder机制返回的数据被保存在另一个Parcel对象中(_reply)。
现在,我们的问题是,这个mRemote是什么东西?transact()函数的内容是什么?
我们看下mRemote的定义:
private android.os.IBinder mRemote;
可以看到它所属的包名是android.os。于是我们进入frameworks/base/core/java/android/os/目录找到了对应类名的IBinder.java文件。
我们打开IBinder.java,发现IBinder是一个接口。文件中transact()函数的声明如下:
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;
现在我们需要找到它的实现者。
通过搜索代码库,我们发现frameworks/base/core/java/android/os/Binder.java的内部类BinderProxy继承了IBinder,并且定义了transact()函数:
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
return transactNative(code, data, reply, flags);
}
这时候,其实我们不能100%确认这个transact()函数就是我们调用acquireWakeLock()时会被执行的函数。为了验证上述分析成果是否正确,我们在函数中添加Log信息如下: public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
String TAG = "BinderDemo";
Log.d(TAG, "BinderProxy transact()");
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
return transactNative(code, data, reply, flags);
}
现在我们在安卓源代码根目录下执行“source 配置文件 & lunch 项目名”,然后mmm frameworks/base/,把out目录下生成的system/framework/framework.jar通过adb push到手机的/system/framework/目录下,替换掉原来的framework.jar。然后重启手机,使新的framework.jar生效。重启完成后,先在终端输入“adb logcat |grep BinderDemo”,然后启动demo程序,点击按钮,可以在终端看到以下信息: 在上图中,第一行和第三行是demo程序的打印信息,第二行是我们在上述transact()函数中添加的打印。可以看到,前面三行的pid都是8322(即demo程序的pid),因此,现在我们可以确定BinderProxy类的transact()函数就是我们所要跟踪的函数之一。
第四至第六行则属于其它进程的打印信息。从打印的时间间隔来看,BinderProxy类的transact()函数具有全局性,在安卓系统中会被各个进程频繁调用。这也印证了Binder机制在安卓系统中广泛存在,并且频繁调用。
由于Binder机制相关函数调用太过频繁,在Binder函数中加入的日志,都会被频繁地打印出来。为了过滤掉其它进程的信息,后面我们将考虑根据pid来判断是否打印日志。
(注:现在我们将从java世界,进入c++世界。但是,依然在Framework层。)
Transact()函数通过调用transactNative(),把Binder请求继续往下传递。从函数的名字可以看出,接下来我们将进入c++编写的native层。
在代码库中,我们搜索该函数,发现它的实现是位于frameworks/base/core/jni/android_util_Binder.cpp文件,事实上,它是android_os_BinderProxy_transact()的映射:
{"transactNative","(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z",(void*)android_os_BinderProxy_transact},
在android_os_BinderProxy_transact()函数的几十行代码中,真正把Binder请求向下传递的代码如下:
IBinder* target = (IBinder*)env->GetLongField(obj, gBinderProxyOffsets.mObject);
status_t err = target->transact(code, *data, reply, flags);这里又冒出了一个transact()函数。与我们前面java层遇到的同名函数不一样的是,它是一个c++的函数。为了找到这个c++ transact()函数的实现,我们需要先弄清楚这个“target”是什么东西。
这个“target”是一个IBinder对象的指针。而IBinder是一个接抽象类(定义见frameworks/native/include/binder/IBinder.h),因此我们需要找到是哪个c++类继承了IBinder。
通过进一步搜索,发现是BpBinder(实现文件frameworks/native/libs/binder/BpBinder.cpp)继承了IBinder抽象类,并实现了IBinder的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;
}
为了确定target->transact()与BpBinder::transact()对等,我们现在在BpBinder::transact()代码中加入如下打印信息:
ALOGD("BinderDemo: BpBinder::transact() is called\n");
然后,编译native binder,编译命令为“mmm frameworks/native/libs/binder/”。
编译完成后,把编译生成的out目录下的system/lib64/libbinder.so和system/lib/libbinder.so分别push进手机对应的目录,然后重启系统。
进入桌面后,先在终端输入“adb logcat |grep BinderDemo”,然后启动demo程序,点击按钮,可以在终端看到以下信息:
这说明,target指针指向的正是一个BpBinder对象。
在前面贴出的BpBinder::transact()实现代码中,把请求往下传递的关键代码是:
status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);
在很多介绍Binder机制的书籍和文档中,IPCThreadState通常都是作为一个关键模块被详细剖析。现在,我们可以一睹它的真容实貌。
(下文待续)