您的位置:首页 > 产品设计 > UI/UE

Binder客户端和驱动端通信流程实例分析----以acquireWakeLock()函数为例 (一)

2017-08-21 09:36 615 查看
(本文适合对Binder框架具有基本了解,但缺乏感性认识的读者。为流畅阅读本文,读者还需要对Android Framework、Linux Kernel有一定的理解,并且手上最好有整套可编译的安卓6.0源代码,以便复现分析步骤。如有转载,请注明出自本博客。)

正文开始

下图是一张常见的Binder通信机制流程图:



现在我们从细节入手,以PowerManager内部类WakeLock提供的获取亮屏锁函数acquire()为例,分析客户端的上层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层返
4000
回到客户端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通常都是作为一个关键模块被详细剖析。现在,我们可以一睹它的真容实貌。

(下文待续)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: