Binder源码分析
目录
- 1.简介
- 2.Binder与AIDL
- 2.1 AIDL客户端
- 2.2 AIDL服务端
- 2.3 远程服务的获取与使用
- 3.BInder框架及Native层
- 3.1 Binder Native的入口
- 3.2 Binder 本地层的整个函数/方法调用过程
- 3.3 Binder设备文件的打开和读写
- 4.Binder驱动
- 4.1 Binder设备的创建
- 4.2 Binder协议和数据结构
- 4.3 Binder驱动文件操作
- 5.Binder与系统服务
- 5.1 Context getSystemServie()
- 5.2 Context getSystemService()源码分析
- 6.结论
- 7.参考
1. 简介
Binder是一种Android进程间通信机制,提供远程过程调用(Remote Procedure Call)功能.我们最直接的使用是调用Context.getSystemService()
来获取系统服务,或直接使用AIDl来实现多个程序(APP)间的数据交互.
Binder是非常重要的Android基础组件,几乎所有的进程间通信都是使用Binder机制实现的.本文将结合源码展开讲述Binder,同时对一些重要的知识点提供扩展阅读的参考.
不管是Android系统服务(System Service)还是用户的应用进程(User apps),最终都会通过binder来实现进程间通信.上层应用首先通过IBinder的transcate方法发送命令给libbinder,libbinder再通过系统调用(ioctl)发送命令到内核中的binder驱动,之后再由驱动完成进程间数据的交互.
我们经常使用的Intent,Messager数据传递也是对Binder更高层次的抽象和封装,最终还是会由内核中的binder驱动完成数据的传递.
2. Binder与AIDL
AIDL(Android Interface definition language)是接口描述语言,用于生成在两个进程间进行通信的代码.先看AIDL概念图.
- Stub.Proxy和Stub代码由Android Sdk自动生成,客户端通过Stub.Proxy与远程服务交互.
- Stub包含对IBinder对象操作的封装,需要远程服务实现具体功能.
接下来再看看具体实现,完整代码见
AidlExample.在这个工程中,我们新建了两个应用,app
是客户端代码,remoteService
则是服务端代码.
2.1 AIDL 客户端
在Android Studio项目上右键,New
->AIDL
->AIDL File
输入文件名后可以快速创建一个AIDL的代码结构.例如我们新建一个IRemoteService.aidl文件
|
|
从生成的示例代码可以看出来,AIDL的语法类似Java,basicTypes()
方法传递的参数只是基本类型.
如果要传递自定义类型如User
,则需要实现Parcelable
接口.Parcelable
是一个与Java Serializable
类似的序列化接口.这样User
的实例就可以存储到Parcel
中,而Parcel
则是一个可以通过IBinde发送数据或对象引用的容器.
|
|
再向IRemoteService.aidl
中添加一个addUser()
方法,同时新建一个 User.aidl
文件。
|
|
运行编译后,会在 generated
文件夹中生成一个 IRemoteService.java
接口文件。这个接口中有两个内部类 Stub
和 Stub.Proxy
。注意客户端生成的IRemoteService.java
文件和在后文服务端生成的文件内容是相同的。
客户端会从 Stub.asInterface()
得到 IRemoteService (Stub.Proxy)
的实例,这个实例就是一个通过 Binder 传递回来的 远程对象
的包装。而服务端则需要实现 IRemoteService.addUser()
方法。
|
|
2.2 AIDL服务端
为了演示进程间通信,我们新建一个模块(应用) RemoteService
来实现功能,并在客户端绑定服务。
按客户端的结构新建 IRemoteService.aidl User.aidl User.java
文件,并拷贝内容,注意如果需要请修改包名。
新建服务 RemoteService
,覆盖(Override) onBind()
方法并返回 IRemoteService.Stub
实例 mBinder
:
|
|
这样服务端就实现了 addUser()
方法,当客户端通过远程对象调用 IRemoteService.Stub.Proxy.addUser()
时,远程对象 mRemote 就会通过transact()
发送命令给服务端,服务端收到命令后在 Stub.onTransact()
中读取数据并执行 addUser()
方法。更多细节我们将在3. Binder 框架及 Native 层
小节讲述。
|
|
2.3 远程服务的获取与使用
客户端要使用远程服务,需要绑定服务 (bindService
) 并建立服务连接 (ServiceConnection
)。
|
|
我们可以看出,客户端通过 binderService()
方法,获取远程服务并在服务连接 ServiceConnection
中 onServiceConnected()
回调中得到了 IBinder service
实例, 最后通过上文提到的 IRemoteService.Stub.asInterface(service)
方法得到远程服务 IRemoteService
的实例。通过 IRemoteService.addUser()
方法我们可以像调用本地方法一样调用远程方法。在来看 IRemoteService.addUser()
的实现:
|
|
可以看到客户端调用 remoteService.addUser(new User(1, "neo"))
方法实际上是通过 IBinder service
实例的 transact()
方法,发送了与服务端约定好的命令 Stub.TRANSACTION_addUser
,并将参数按格式打包进 Parcel
对象。
服务端则在 onTransact()
方法中收到命令后会对命令和参数重新解析:
|
|
可以看到在 onTransact()
中,最终 this.addUser(_arg0
) 调用了上文提到的服务端的实现 IRemoteService.Stub.addUser()
。
远程 Binder 对象 mRemote
是由客户端绑定服务时 onServiceConnected()
返回的。继续追踪 bindService()
|
|
可以看到最后是通过 ActivityManagerNative.getDefault().bindService()
来绑定服务
|
|
追踪到 ActivityManagerNative.getDefault().bindService()
,可以发现ActivityManager
和 IServiceConnection
也是一个 AIDL 实现。通过它的 ActivityManagerProxy.bindService()
将绑定请求发送给本地层。
再从 onServiceConnected()
回调追踪, onServiceConnected()
是由 LoadedApk.ServiceDispatcher.doConnected()
回调的。
关于更多的 bindService()
远程服务创建及 ServiceConnection
回调, 请参考 Android应用程序绑定服务(bindService)的过程源代码分析
- 利用进程间通信,我们可以实现简单的应用插件功能。关于 AIDL 在实际项目中的应用,可以参考
CallerInfo Plugin
的实现
从上面分析可以看出, AIDL 的本质是对 Binder 的又一次抽象和封装,实际的进程间通信仍是由 Binder 完成的。
3. Binder框架及Native层
Binder机制使本地对象可以像操作当前对象一样调用远程对象,可以使不同的进程间互相通信。Binder 使用 Client/Server 架构,客户端通过服务端代理,经过 Binder 驱动与服务端交互。
Binder 机制实现进程间通信的奥秘在于 kernel 中的 Binder 驱动,将在 4. Binder
驱动 小节详细讲述。
JNI 的代码位于 frameworks/base/core/jni
目录下,主要是 android_util_Binder.cpp
文件和头文件 android_util_Binder.h
Binder JNI 代码是 Binder Java 层操作到 Binder Native 层的接口封装,最后会被编译进 libandroid_runtime.so
系统库。
Binder 本地层的代码在 frameworks/native/libs/binder
目录下, 此目录在 Android 系统编译后会生成 libbinder.so
文件,供 JNI 调用。libbinder
封装了所有对 binder 驱动的操作,是上层应用与驱动交互的桥梁。头文件则在 frameworks/native/include/binder
目录下。
3.1 Binder Native 的入口
IInterface.cpp
是 Binder 本地层入口,与 java 层的 android.os.IInterface
对应,提供 asBinder()
的实现,返回 IBinder
对象。
在头文件中有两个类 BnInterface (Binder Native Interface)
和 BpInterface (Binder Proxy Interface)
, 对应于 java 层的 Stub
和 Proxy
|
|
|
|
其中 BnInterface
是实现Stub功能的模板,扩展BBinder的onTransact()方法实现Binder命令的解析和执行。BpInterface
是实现Proxy功能的模板,BpRefBase里有个mRemote对象指向一个BpBinder对象。
3.2 Binder 本地层的整个函数/方法调用过程
Java 层
IRemoteService.Stub.Proxy
调用android.os.IBinder (实现在 android.os.Binder.BinderProxy)
的transact()
发送Stub.TRANSACTION_addUser
命令。由
BinderProxy.transact()
进入 native 层。由
jni
转到android_os_BinderProxy_transact()
函数。调用
IBinder->transac
t 函数。
|
|
而 gBinderProxyOffsets.mObject
则是在 java 层调用 IBinder.getContextObject()
时在 javaObjectForIBinder
函数中设置的
|
|
经过 ProcessState::getContextObject()
和 ProcessState::getStrongProxyForHandle()
|
|
可见 android_os_BinderProxy_transact()
函数实际上调用的是 BpBinder::transact()
函数。
5.BpBinder::transact()
则又调用了 IPCThreadState::self()->transact()
函数。
|
|
由函数内容可以看出, 数据再一次通过 writeTransactionData()
传递给 mOut
进行写入操作。 mOut
是一个 Parcel 对象, 声明在 IPCThreadState.h
文件中。之后则调用waitForResponse()
函数。
6.IPCThreadState::waitForResponse()
在一个 while
循环里不断的调用 talkWithDriver()
并检查是否有数据返回。
|
|
7.IPCThreadState::talkWithDriver()
函数是真正与 binder 驱动交互的实现。ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
就是使用系统调用函数 ioctl
向 binder 设备文件 /dev/binder
发送 BINDER_WRITE_READ
命令。
|
|
经过 IPCThreadState::talkWithDriver()
,就将数据发送给了 Binder 驱动。
继续追踪 IPCThreadState::waitForResponse()
,可以从 第6步 发现 IPCThreadState
不断的循环读取 Binder 驱动返回,获取到返回命令后执行了 executeCommand(cmd)
函数。
8.IPCThreadState::executeCommand()
处理 Binder 驱动返回命令
|
|
9.可以看出其调用了 BBinder::transact()
函数,将数据返回给上层。
|
|
10.而这里的b->transact(tr.code, buffer, &reply, tr.flags)
中的 b (BBinder)
是 JavaBBinder 的实例,所以会调用 JavaBBinder::onTransact()
函数
|
|
11.可见 JNI 通过 gBinderOffsets.mExecTransact
最后执行了 android.os.Binder
的 execTransact()
方法。
execTransact()
方法是 jni 回调的入口。
|
|
12.而我们则在服务端 IRemoteService.Stub
重载了onTransact()
方法,所以数据最后会回到我们的服务端并执行服务端实现的 addUser()
方法.
|
|
上述过程就是所有的 Native
层客户端到服务端的调用过程,总结下来就是 客户端进程发送 BC_TRANSACTION
到 Binder 驱动,服务端进程监听返回的 BR_TRANSACTION
命令并处理。如果是服务端向客户端返回数据,类似的是服务端发送 BC_REPLY
命令, 客户端监听 BR_REPLY
命令。
3.3 Binder设备文件的打开和读写
1. 设备的打开
在上一小节中我们看到 JNI 过程中调用了 ProcessState::getContextObject() 函数, 在 ProcessState 初始化时会打开 binder 设备
open_driver()
函数内容如下
|
|
2.设备的读写
打开设备文件后,文件描述符被保存在 mDriverFD
, 通过系统调用 ioctl
函数操作 mDriverFD
就可以实现和 binder
驱动的交互。
对 Binder 设备文件的所有读写及关闭操作则都在 IPCThreadState
中,如上一小节提及到的 IPCThreadState::talkWithDriver
函数
talkWithDriver()
函数封装了 BINDER_WRITE_READ
命令,会从 binder 驱动读取或写入封装在 binder_write_read
结构体中的本地或远程对象。
|
|
可以看出,本地层是对应用与 binder 驱动交互的直接封装与实现,最终的数据传输仍是由驱动来完成的。本地层对底层驱动进行了完整的封装,上层应用只关心 transact() 和 onTransact() 回调,察觉不到 binder 驱动的存在,减轻了上层应用进程间通信开发的复杂度。
4.Binder 驱动
关于 binder 驱动建议参考另一篇文章 深入分析Android Binder
驱动 [原文](Android Binder
,本小节仍需要完善。
Binder 驱动是 Binder 的最终实现, ServiceManager 和 Client/Service 进程间通信最终都是由 Binder 驱动投递的。
Binder 驱动的代码位于 kernel 代码的 drivers/staging/android
目录下。主文件是 binder.h
和 binder.c
进程间传输的数据被称为 Binder 对象,它是一个 flat_binder_object
,结构如下
|
|
其中 类型 type
描述了 Binder 对象的类型,包含 BINDER
(本地对象)、HANDLE
(远程对象)、 FD
三大类(五种)
|
|
flags
则表述了传输方式
,如异步、无返回等
|
|
而 flat_binder_object
中的 union 联合体
就是要传输的数据,当类型为 BINDER
时, 数据就是一个本地对象 *binder
,而类型为 HANDLE
时,数据则是一个远程对象 handle
。
当 flat_binder_object
在进程间传递时, Binder 驱动会修改它的类型和数据,交换的代码参考 binder_transaction
的实现。
该如何理解本地 BINDER
对象和远程 HANDLE
对象呢?其实它们都代表同一个对象,不过是从不同的角度来看。举例来说,假如进程 RemoteService
有个对象 mBinder
,对于 RemoteService
来说,mBinder
就是一个本地的 BINDER
对象;如果进程 app
通过 Binder
驱动访问 RemoteService
的 mBinder
对象,对于 app
来说, mBinder
就是一个 HANDLE
。因此,从根本上来说 handle
和 binder
都指向 RemoteService
的 mBinder
。本地对象还可以带有额外的数据,保存在 cookie
中。
Binder 驱动直接操作的最外层数据结构是 binder_transaction_data
, Binder 对象 flat_binder_object
被封装在 binder_transaction_data
结构体中。
binder_transaction_data
数据结构才是真正传输的数据,其定义如下
|
|
flat_binder_object
就被封装在 *buffer
中,其中的 unsigned int code
; 则是传输命令,描述了 Binder 对象执行的操作。
4.1 binder 设备的创建
device_initcall()
函数是内核加载驱动的入口函数,我们先来看这个函数的调用过程。
|
|
可以看出 binder_init()
使用 misc_register()
函数创建了 binder 设备。从 misc_register(&binder_miscdev)
; 及 .name = "binder"
可以看出, binder 向 kernel 注册了一个 /dev/binder
的字符设备,而文件操作都在 binder_fops
结构体中定义。
|
|
从上面 binder_fops
结构体可以看出,主要的操作是 binder_ioctl()
binder_mmap()
binder_open()
等函数实现的。
4.2 binder 协议和数据结构
binder.h
文件中定义了 binder 协议和重要的数据结构。
首先在 enum
中定义了 binder 处理的类型,引用或是句柄
|
|
下面这段宏定义则是在 ioctl
函数调用时可用的具体命令。
|
|
在 BinderDriverReturnProtocol
和 BinderDriverCommandProtocol
中 则分别定义了 客户端调用 和 服务端 返回的命令。
4.3 binder 驱动文件操作
上文已经提到,所有的操作定义在 binder_fops
结构体中,下面讲述这些操作。
设备的打开 - binder_open() 函数
用户空间在打开/dev/binder
设备时,驱动会出发 binder_open()
函数的响应。
|
|
驱动文件释放 - binder_release() 函数
在用户空间关闭驱动设备文件时,会调用 binder_release()
函数,清理 binder_proc 对象,释放占用的内存。
|
|
内存映射 - binder_mmap() 函数
binder_mmap()
函数把设备内存映射到用户进程地址空间中,这样就可以像操作用户内存那样操作设备内存。
|
|
驱动命令接口 - binder_ioctl() 函数
用户态程序调用 ioctl
系统函数向 /dev/binder
设备发送数据时,会触发 binder_ioctl()
函数响应。
上文数据结构中已经提到了 binder_ioctl
可以处理的 命令
|
|
|
|
|
|
BINDER_WRITE_READ 处理过程
在 binder 本地层中,我们看到在 IPCThreadState::talkWithDriver()
函数中, binder 本地层通过 ioctl()(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
命令的形式,与 binder 驱动交互。
可以看出ioctl()
的第三个参数是一个 binder_write_read
结构体
binder.h 头文件中定义了两个数据类型, 一个是 binder_write_read
|
|
其中 write_size
和 read_size
表示需要被读写的字节数, write_consumed
和 read_consumed
表示已经被 binder 驱动读写的字节数, write_buffer
和 read_buffer
则是指向被读写数据的指针。
具体的读写操作被 binder_thread_write
和 binder_thread_read
实现。
数据写入 - binder_thread_write() 函数
将用户空间数据写入到 binder 驱动,从驱动角度来看是读取的操作。
|
|
binder_transaction() 函数
在上文处理 BC_TRANSACTION
和 BC_REPLY
时,调用了 binder_transaction()
函数。我们继续追踪
|
|
从上述代码可以看出 binder 驱动的具体实现,以及是如何发送和接收数据的。
5. Binder 与系统服务
5.1 Context.getSystemService()
Android 系统在启动后会在后台运行很多系统服务提供给应用使用,这些 服务
主要有 WindowManager
, LayoutInflater
, ActivityManager
, PowerManager
, AlarmManager
, NotificationManager
, KeyguardManager
, LocationManager
, SearchManager
, Vibrator
, ConnectivityManager
, WifiManager
, AudioManager
, MediaRouter
, TelephonyManager
, SubscriptionManager
, InputMethodManager
, UiModeManager
, DownloadManager
, BatteryManager
, JobScheduler
, NetworkStatsManager
我们可以通过 Context.getSystemService(String name)
来获取 服务
。
例如 可以通过如下方法从 xml 中插入新的视图
|
|
5.2 Context.getSystemService() 源码分析
追踪 ContextImpl
getSystemService()
源代码
|
|
继续追踪 SystemServiceRegistry
源代码
|
|
追踪 SYSTEM_SERVICE_FETCHERS
可以发现在 SystemServiceRegistry
静态区中注册了几乎所有的系统服务
|
|
上面代码片断中,PhoneLayoutInflater
最终回到了 LayoutInflater
。而 LocationManager
则是对 ILocationManager
的封装。可以发现,在 frameworks/base/location/java/android/location
包下含有大量的 AIDL 文件。
继续追踪 ServiceManager.getService(Context.LOCATION_SERVICE)
|
|
从上面代码片断可以看出,ServiceManager
会从 sCache
缓存或 IServiceManager
中查找服务并返回一个 IBinder
对象。这个 IBinder
就是一个远程对象,可以通过它与其他进程交互。
继续深入 getIServiceManager().getService(name)
, 进入 ServiceManagerNative
|
|
从上边代码片断可以看到,ServiceManager.getIServiceManager()
返回的是一个 ServiceManagerProxy
, 而 ServiceManager.getService()
则是在 ServiceManagerProxy
中通过 ServiceManager
的远程 Binder 对象 mRemote
,操作 Parcel 数据,调用 IBinder.transact(int code, Parcel data, Parcel reply, int flags)
方法来发送请求,并通过 reply.readStrongBinder()
返回了要查找的服务的远程对象。
可以看到,系统服务的获取方式也是通过 AIDL 的方式实现的。
6. 结论
Binder 的实现涉及到 kernel 驱动,本地层,JNI 和应用层,贯穿了整个 Android 系统。系统服务获取、Activity/Service 启动、Intent的传递等都离不开 binder,要掌握 binder 的原理需要深入到系统的每一层代码。
上层的
android.os.Binder
只是对 binder 的又一次抽象封装,我们在应用中一般也不会直接使用。AIDL 本质上是一个用于封装 Binder 操作的工具,最终的进程间通信由 Binder 的
transact
和onTransact
完成。我们在应用中实现 AIDL 接口,可以快速实现进程间通信.
7.参考
Android Binder机制
Android进程间通信(IPC)机制Binder
Android Binder
Android Architecture Binder
AIDL与Binder框架浅谈
Binder框架解析
Deep Dive into Android IPC/Binder Framework at Android Builders Summit 2013
Android Builders Summit 2013 - Deep Dive into Android IPC/Binder Framework (video)
Binder源码分析之驱动层(原)
深入分析Android Binder 驱动
构造IOCTL命令的学习心得—–_IO, _IOR, _IOW, _IOWR 幻数的理解
Service与Android系统设计(7)— Binder驱动
Android Binder
Binder机制,从Java到C (7. Native Service)