Android framework开发实战一
开机动画BootAnimation

learn check 点
基础
- 熟悉aosp项目结构+构建系统(mk、bp、bazel)+产物 src->target path
- 编译aosp14源码+emulator模拟器跑起来(cf模拟器可选)
- 熟悉调试工具+性能等工具的使用
- 自定义系统APP编译进AOSP里
- 带so的app和不到so的app
- AS里源码开发构建测试app 打进AOSP里
- 直接在AOSP里定义好JAVA文件构建APP
- 别人的二进制APP编入AOSP里
- 自定义C++库/可执行文件打入AOSP系统内
- 定制开机动画
linux 内核源码下载
当我们打开AOSP源码的/kernel目录下的时候,我们可以看到AOSP并不包含对应使用的Linux源码,只有预先编译好的各个版本的Linux,这意味着如果我们替换AOSP编译时使用的Linux Kernel版本,或者希望修改Linux Kernel代码在设备在启动Android之前做一些工作的话,我们就必须下载对应版本的Linux源码进行修改和编译。
作者:贾艺驰
链接:https://juejin.cn/post/7343509431714742313
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
下面我们就要想办找到我们设备所使用的Android Linux Kernel版本,首先打开模拟器,进入Setting->About->Android Version,我们就可以看到我们的Linux Kernel版本了:

android-14.0.0_r28对应的linux内核版本是6.1.23-android14-4-00257-g7e35917775b8-ab9964412
下载linux kernel源码在kernel目录下
git clone https://android.googlesource.com/kernel/common
git checkout 7e35917775b8
Init进程
https://juejin.cn/post/7343889290432839691
Init进程是什么
当我们在adb shel中输入ps -A列出所有进程时,我们可以看到有一个名字为init,pid为1的进程,

再往下看,可以看到很多ppid为1的进程,这意味着这些进程都是从init进程中fork出来的,包括我们最熟悉的zygote进程:

可以说,init进程是我们整个Android世界的起始点。
谁启动了Init进程
- Android系统启动,先bootloader,然后启动linux kernel,kernel里的init/main.c里的rest_int方法会启动Init进程
Init进程做了什么 三个stage
- Init代码主要在/system/core/init,入口是main.cpp 的main方法。init里有好几个步骤,步骤详细介绍可以看 /system/core/init/README.md;这个
main方法会被传入不同的参数反复执行,执行的顺序大概是- first stage
- selinux stage
- second stage
解析RC文件并启动服务
- first stage工作和逻辑较为繁杂,可以查看
/system/core/init/first_stage_init.cpp - selinux stage,则是为Android加载selinux,关于Android对SELinux的支持,可以看这里:
source.android.com/docs/securi…
- 最后是second stage了,在second stage,我们就会开始解析rc文件并根据rc文件启动一系列进程,打开
/system/core/init/init.cpp,就可以看到我们的second stage的主要处理方法了:

在这个方法中查找,我们可以看到LoadBootScripts方法被调用,进入此方法,我们就可以看到,在这里,Android完成了对rc文件的读取以及解析工作:

最后,就是开始启动rc文件中的early-init和init了:

下面是/system/core/rootdir/init.rc文件中early-init和init两个trigger的定义:

如果不知道如何阅读rc文件,可以看这里:
juejin.cn/post/734166…
至此,可以说Android已经开始基于rc文件要启动起来了。
Zygote进程
在阅读本篇建议先阅读:
Android Init Language
juejin.cn/post/734166…
Init进程:
juejin.cn/post/734388…
Zygote是什么
Zygote翻译过来就是受精卵的意思,这个名字其实就已经说明了它的作用,是为了诞生"生命"用的。
那么对于Android来说,什么是最重要的"生命"? 那当然是我们的一个又一个App了。
打开模拟器,输入adb shell,然后列出所有进程ps -A,找到我们的zygote64的pid:

然后输出ps -A | grep 284,就可以看到好多ppid为284,也就是说由我们的Zygote孵化出来的进程了:

我们可以看到,这么多的进程,都是由Zygote孵化的,如果说Init是Android世界的起始点,那么Zygote可以说是Android 应用层的起始点了。
为啥需要Zygote
我们应该都基本清楚字节码,虚拟机的这些概念,也能理解,我们的App里面的dex文件,最终是需要一个Dalvik虚拟机来运行的,还有每个App和他们的虚拟机,默认情况下应该就在单独一个进程里。
那么问题来了,如果我们每次启动一个应用,然后每次都从头开始一个进程,然后这个进程去做虚拟机的初始化工作并启动虚拟机,那手机就慢死了,因为每个应用开启前都要进行大量的重复工作。
所以,最好能有一个进程,让他在系统启动的时候去完成这些繁杂的重复工作,把运行应用程序之前所需的一些系统资源加载到内存里,然后常驻在Android系统中,如果有新的应用要启动,那么直接从这个进程fork出来就好了,而且所有的这些预加载资源,会变成应用间共享的。
Zygote就是起这样一个作用的。
那么现在,我们知道了Zygote的作用和行为,接下来就要看看,Zygote是怎么完成这一系列工作得了。
谁启动了Zygote
Zygote是由Init进程启动的,打开/system/core/rootdir/init.rc,可以看到如下的代码,在late-init中,trigger了zygote-start:

然后在zygote-start中,启动了zygote和zygote_secondary:

这里就会有一个疑问了,怎么出来了2个zygote,还有上面的进程图里面,也出现了zygote和zygote64,这其实是因为我们两个版本的zygote一个是对32位的,一个是对64位的,我们可以打开 /system/core/rootdir/init.zygote64_32.rc,就可以看到zygote_secondary,就是32位版本的zygote:

根据最上面我们的进程图可以看到,基本上目前的进程主要是由zygote64进程fork来的,这个zygote,也就是我们的primary zygote,即主要的zygote,32版本的zygote,即是secondary zygote,即次要的zygote。
那么接下来我们就要找到late-init是在哪里被trigger的,其实打开/system/core/init/init.cpp的SecondStageMain方法,就可以找到了:

Zygote Native起始工作流程
首先打开/frameworks/base/cmds/app_process/app_main.cpp,这个类便是Zygote Native的起始点,找到main方法:

然后直接看main方法的最后一行,可以看到这三行代码:

可以看到,这里进行了一个判断,我们先看自己最关心的,第一行里面,好像是和Zygote最相关的,如果希望运行到这里,需要zygote为true,那么这个变量,到底什么情况下会为true?
再往上翻,就可以看到,zygote是在什么情况下设置为true了:

再上面的代码逻辑,我就不贴出来了,主要是处理一些传进来的参数,我们从这里面的代码逻辑,就能看到,当我们的main函数在有--zygote这个参数的时候,zygote就会被设置为true,那么这个参数什么时候会被传进来?
其实在Init进程启动Zygote的时候,就会传进来了,打开/system/core/rootdir/init.zygote64.rc,我们可以看到被传递进来的参数里,就有--zygote:

那么我们就可以得出一个结论,在刚开始的时候

这段代码,就会被执行。接下来的问题就是,这段代码代表着什么?
首先,我们来看runtime的定义,在main函数的开始部分,我们就可以看到:

会发现这个类总共就80多行,看起来也没什么,下面我们再看看他的父类AndroidRuntime,具体在/frameworks/base/core/jni/AndroidRuntime.cpp,这个类里面,我们就可以看到大量虚拟机相关的代码了,比如startVm:

好了,我们再回到刚才的代码,进入start方法,进入之后可以看到最后跳转的是AndroidRuntime的start方法:

其它代码暂时不用关心,往下翻,看到这里:

从这里就可以看到,我们进入了上面提到过的startVm方法,那么这个方法,其实就是我们启动虚拟机的地方了,进入这个方法里面,可以看到Android硬编码了一大堆虚拟机参数:

这个方法很长,中间我们都不需要关心,我们只需要知道,这个方法是用来启动虚拟机的就可以了,那么当虚拟机启动好了以后,我们还要做后续的工作。
我们再看最开始的这段代码:

可以看到他传了一个参数,这好像是个Java类,而且从名称上判断,这应该和Zygote初始化有关系。
那么我们的虚拟机是什么时候处理这个参数的?
在AndroidRuntime的start方法中,在startVm再往下翻,就可以看到这样一段代码:

这个className,从start开头的地方可以知道,就是我们传进来的com.android.internal.os.ZygoteInit,再往下翻,我们就可以看到处理的逻辑了:

这段代码的意思,就是执行我们传进来的那个Java类的main方法了,接下来,我们要进入Java的世界了。
ZygoteInit预加载资源
打开/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java,并找到它的main方法:

找到这部分代码:

可以看到这个判断是,如果不启用lazy preload,那么就会开始预加载资源,启用了则跳过,然后再打开/system/core/rootdir/init.zygote64.rc,可以看到我们的primary zygote是没有启用lazy preload的,但是打开/system/core/rootdir/init.zygote64_32.rc我们就会看到我们的secondary zygote,启用了lazy preload,所以我们的primary zygote,也就是64位版本的zygote,是一定会执行preload的:

进入preload,可以看到Android预先加载了各种资源:

首先我们进入preloadClasses,可以看到PRELOAD_CLASSES这个常量:

他的值为:

从代码中,我们可以得知,会一行行的读取这个preloadClasses文件,并且将对应的类加载进来:

那么这个文件在哪呢? 这个文件其实在/frameworks/base/config/preloaded-classes,打开这个文件,就可以看到如下内容:

这个文件有17000多行,这些就是我们要预先加载的类了。
再回到preload,进入preloadResources方法:

可以看到这部分就是根据R.array.preloaded_drawable,去加载一些Drawable文件,这个array的定义是在/frameworks/base/core/res/res/values/arrays.xml下面:

可以看到注释里也提到了这是给zygote预加载资源用的。
至于preload里面的nativePreloadAppProcessHALs,maybePreloadGraphicsDriver,preloadSharedLibraries,preloadTextResources这些就不展开说了,有兴趣的可以查看源码。
至此,Zygote就完成资源的预加载。
ZygoteInit启动Zygote Server并开始监听
从上面我们讲过,Zygote除了预加载这些虚拟机和资源文件,还需要做好准备,随时等待被fork成一个应用进程,那么Zygote是怎么做到的?
其实Zygote是通过LocalSocket来完成这一点的。再次打开/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java,并找到它的main方法,可以看到在方法的开头定义的zygoteServer:

然后下面可以看到zygoteServer的初始化:

进入构造方法,我们就可以看到使用LocalSocket相关的代码了:

打开我们模拟器的adb shell,输入:
netstat -nlp | grep LISTEN | grep zygote

我们可以看到,怎么还有usap的监听,这是干什么的?
其实这是Android新提供的一种新的应用启动方式,是为了加速我们的应用启动的,相关的介绍可以看这里:
juejin.cn/post/692270…
在main方法后面的位置,我们可以看到zygoteServer的启动:

从注释我们也可以知道,到这,Zygote就已经进入等待的状态了,剩下的工作就交给ZygoteServer去处理了,至于ZygoteServer和ZygoteConnection是如何处理请求并fork处新的应用进程的,我后续会找机会再写一篇。
至此,我们已经梳理了整个Zygote初始化的过程,由于我只贴了关键代码,所以还是建议跟着AOSP源码去看这篇文章,才能理解整个的处理逻辑。
在Zygote的Init过程中,其实可以看到很多SystemServer相关的代码,这是因为Zygote最重要的需要fork进程之一,就是SystemServer了,下一篇我就会写SystemServer的启动流程分析。
System Server
在阅读本篇建议先阅读:
Zygote
juejin.cn/post/734457…
Linuxfork概念
juejin.cn/post/691261…
本篇依旧会只展示关键代码,所以最好打开AOSP源码一步步的跟着看,才能理解整个的流程。
什么是System Server
在Android的应用层开发中,我们会使用到一系列的service,比如ActivityManagerService,PowerManagerService等,System Server的作用,就是去启动这些Service。
System Server进程启动分析
我们的System Server是从Zygote进程中fork出来的,下面,我就将一步步讲解Zygote启动System Server的整个流程。
首先,我们打开/frameworks/base/core/java/com/android/internal/os/ZygoteInit,找到它的main方法,然后可以找到如下代码:

这里,就是我们System Server启动的关键部分,从代码中我们可以得知,只有startSystemServer这个变量为true的时候,执行我们启动System Server的逻辑,在往上,我们可以看到:

这个变量的值,是由外部参数决定的,这个参数其实在我们的Zygote启动的时候,就已经传进来了,可以打开/system/core/rootdir/init.zygote64.rc,可以看到对应的参数:

那么forkSystemServer这个方法究竟是做什么的,其实从名字上就可以判断出来,这个方法就是从Zygote去fork出我们的System Server的进程的。
首先进入这个方法,其它的先不管,找到这段代码:

再进入Zygote类的forkSystemServer方法:

就可以看到实际上我们是通过Native层去fork我们的进程的,打开/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp,找到com_android_internal_os_Zygote_nativeForkSystemServer,这就是我们的Native层的实际调用。
然后在这个方法里面,我们可以看到它调用了zygote的ForkCommon方法:

再进入这个方法,在中间我们就可以找到fork的调用了:

到这一步,其实我们的System Server进程就已经fork出来了,但问题是目前这个进程还没有我们System Server相关的内容,那么接下来,我们就要开始真正启动我们的System Server了。
启动System Server
打开/frameworks/base/core/java/com/android/internal/os/ZygoteInit,继续看它的forkSystemServer方法,首先我们看这一部分:

从注释里面我们也能看出来,这是System Server的启动参数,比如--nice-name=system_server,这个其实就是进程名,com.android.server.SystemServer,其实就是我们的启动类了。
再往下看,可以看到这部分代码:

从刚才我们就已经知道,Zygote.forkSystemServer,是帮我们fork进程的,那么下面这段代码,只有子进程,也就是我们刚刚fork出来的System Server才会执行,如果不明白这部分逻辑的,可以看下开头的fork相关概念。
那我们看下这段代码,首先,System Server进程做了一个判断,如果有32位的Secondary Zygote,那么就等待,等待Secondary Zygote启动之后再进行下一步,接着,就关闭了本进程的zygoteServer,因为被fork出来的System Server子进程不需要通过LocalSocket来fork应用进程,再然后,会调用handleSystemServerProcess这个方法:

然后我们直接看这段代码,然后再依次进入ZygoteInit.zygoteInit,RuntimeInit.applicationInit,findStaticMain:


最后,在findStaticMain中,找到这部分代码:

可以看到,这就是我们最终返回的Runnable了,那么这个类主要是做什么的呢? 其实就是执行我们传给他的startClass的main方法:

最后这个Runnable的执行,可以在/frameworks/base/core/java/com/android/internal/os/ZygoteInit的main方法里面,forkSystemServer调用的后面找到:

在注释里我们也可以看到,这部分代码是由system_server,也就是Zygotefork出来的System Server进程,去执行的。
那么接下来,我们就要看看我们的com.android.server.SystemServer的main方法,看看它到底做了什么。run就会调用SystemServer.main()方法
System Server启动其它服务
打开/frameworks/base/services/java/com/android/server/SystemServer.java,找到它的main方法,可以看到他就是调用了自己这个类的run:

我们再进入run方法:

找到如下的代码块:

这里,就可以看到我们启动服务的地方了,可以看到,服务被分为了4类:BootstrapServices,CoreServices,OtherServices和ApexServices,比如我们熟悉的ActivityManagerService,他就被放在了startBootstrapServices中启动。
然后我们回到刚才run方法的最后,可以看到在这里,System Server进入了loop状态,而所有的这些Service就都会运行在system_server这个进程里面了。

打开adb shell,输入ps -A | grep system_server,就可以看到我们的System Server进程了:

到这里,我们就完成了整个System Server的启动分析,和它启动其它Service的分析,那么接下来,我们就要去看看,我们的Launcher是如何启动的了。
Launcher启动
什么是Launcher
Launcher的概念,应该做应用层开发的都不会陌生,它可以说对用户来讲最重要的一个东西了,就好像Windows的桌面一样,也是我们其它应用主要的入口。
Launcher本质上来讲,也只是我们的一个Android应用,既然是应用,就肯定要被启动,那么问题来了,谁启动了Launcher?
谁启动了Launcher
我想这个问题肯定立刻就有人抢答了,要么说是System Server,要么说是ActivityManagerService,那么真的是这样吗? 就让我们接下来一探究竟。
启动源码分析
打开/frameworks/base/services/java/com/android/server/SystemServer.java,如果了解了SystemServer.java这个类的话,就应该知道它的主要工作就是启动一系列的重要Service,找到startOtherServices方法,拉到最后,可以找到如下代码:

可以看到,在这里SystemServer调用了ActivityManagerService的systemReady方法,进入此方法,其它的先不管,找到这段代码:

可以看到这里调用了mAtmInternal.startHomeOnAllDisplays,这个mAtmInternal其实是一个ActvityTaskManagerService实例,我们进入ActvityTaskManagerService的startHomeOnAllDisplays这个方法,然后可以看到它依次调用了:

最终,我们进入RootWindowContainer的startHomeOnTaskDisplayArea方法:

可以看到,这个方法的主要内容就是构造了一个homeIntent,最后调用ActivityStartController的startHomeActivity方法,将Intent发送了出去:

我们再看里面的getHomeIntent方法,可以看到确实加了Home的Category:

接下来,应该就是启动Luancher了吧。
Log分析
好,我们接下来验证一下。
首先打开我们的模拟器,等待它进入到Launcher之后,接着进入adb shell,输入logcat | grep "ActivityTaskManager: START",然后可以看到如下的日志:
03-13 17:51:41.529 509 509 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.settings/.FallbackHome (has extras)} with LAUNCH_MULTIPLE from uid 0 (BAL_ALLOW_ALLOWLISTED_UID) result code=0
03-13 17:51:35.896 509 937 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.launcher3/.uioverrides.QuickstepLauncher (has extras)} with LAUNCH_SINGLE_TASK from uid 0 (BAL_ALLOW_ALLOWLISTED_UID) result code=0
可以看到,我们启动的第一个Activity,并不是Luancher而是com.android.settings.FallbackHome,那么问题就来了,FallbackHome是什么?
FallbackHome是什么
我们打开/packages/apps/Settings/src/com/android/settings/FallbackHome.java,先看看它的布局文件:

好像也没什么特别内容,再看看它的代码,尤其注意以下三块:

可以看到多处在尝试调用这个maybeFinish,那么我们再看下这个方法:

这里的核心逻辑,其实就是调用isUserUnlocked这个方法,然后只要返回true,那么就再发送一个Home Intent,从上面的Activity启动log我们可以得知,这里的Home Intent,就会被Launcher拦截了。
那么isUserUnlocked到底代表什么? 其实这代表着设备的解密,这也是为什么我们需要FallbackHome的原因。
为什么需要FallbackHome
如果想要知道这个,首先我们就要了解什么是文件级加密和直接启动,这里是官方文档:
文件级加密
source.android.com/docs/securi…
直接启动
developer.android.com/privacy-and…
简单来说,在Android 7以后,Android开始支持文件级加密,Android 10以后,更是开始强制使用文件级加密,文件级加密将存储分成了两部分,凭据加密部分和设备加密部分,以下是两者区别:

对于我们的设备来讲,刚开始开机的时候,系统属于未解密状态,因为Launcher可能与其它的很多应用会产生数据的交互,需要读取凭据加密存储空间,所以默认情况下无法支持直接启动,那么我们就需要一个loading页面作为中间页,让他来显示等待页面,等待解密完成后,由它来启动Launcher。
我们可以打开/packages/apps/Settings/AndroidManifest.xml,可以看到整个FallBackHome所属于的整个Setting应用都增加了对直接启动模式的支持:

至此,我们就完成了对Launcher启动的整个源码分析。
AIDL
什么是AIDL
AIDL,全称 Android Interface Definition Language,是一种接口定义语言,这里是关于AIDL的官方文档:
developer.android.com/develop/bac…
为什么我们需要AIDL
要完整理解Stub,必须和它的孪生兄弟 Proxy 一起看。它们是AIDL自动生成的一对类,分别位于通信的两端:
Proxy(存在于客户端进程):它是客户端本地的一个“替身”。当你在客户端调用 aidlInterface.someMethod()时,你实际上是在调用 Proxy 对象的方法。Proxy 的工作是把这次本地调用打包成跨进程请求,通过Binder发送出去,然后等待并解包来自服务端的返回结果。Stub(存在于服务端进程):它是服务端真正的“本体骨架”。它接收来自Proxy的请求包,解析并执行真正的业务逻辑,然后将结果打包返回。
Stub 是 AIDL 在服务端的核心枢纽,它继承自 Binder,将跨进程通信的细节(onTransact)与你的具体业务实现(你重写的抽象方法)连接起来。 你通过实现 Stub 的抽象方法提供了“做什么”,而 Stub 基类则为你处理了“如何跨进程地去做”。要回答这个问题,要先看看AIDL替我们做了什么,我们先创建一个IRemoteService.aidl,内容如下:
interface IRemoteService {
void Wow(inout int[] test);
}然后创建一个Service,实现所生成的Stub:
class RemoteService : Service() {
override fun onCreate() {
super.onCreate()
bindService(Intent(this, RemoteService::class.java), object : ServiceConnection{
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}, BIND_AUTO_CREATE)
}
override fun onBind(intent: Intent): IBinder {
// Return the interface.
return binder
}
private val binder = object : IRemoteService.Stub() {
override fun Wow(test: IntArray) {
}
}
}然后进入IRemoteService,可以看到基于我们所提供的AIDL文件,为我们生成的代码:
public interface IRemoteService extends android.os.IInterface
{
/** Default implementation for IRemoteService. */
public static class Default implements com.example.aidlapplication.ui.theme.IRemoteService
{
@Override public void Wow(int[] test) throws android.os.RemoteException
{
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.aidlapplication.ui.theme.IRemoteService
{
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.aidlapplication.ui.theme.IRemoteService interface,
* generating a proxy if needed.
*/
public static com.example.aidlapplication.ui.theme.IRemoteService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.aidlapplication.ui.theme.IRemoteService))) {
return ((com.example.aidlapplication.ui.theme.IRemoteService)iin);
}
return new com.example.aidlapplication.ui.theme.IRemoteService.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;
if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
data.enforceInterface(descriptor);
}
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
}
switch (code)
{
case TRANSACTION_Wow:
{
int[] _arg0;
_arg0 = data.createIntArray();
this.Wow(_arg0);
reply.writeNoException();
reply.writeIntArray(_arg0);
break;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
return true;
}
private static class Proxy implements com.example.aidlapplication.ui.theme.IRemoteService
{
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 Wow(int[] 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);
_data.writeIntArray(test);
boolean _status = mRemote.transact(Stub.TRANSACTION_Wow, _data, _reply, 0);
_reply.readException();
_reply.readIntArray(test);
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_Wow = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public static final java.lang.String DESCRIPTOR = "com.example.aidlapplication.ui.theme.IRemoteService";
public void Wow(int[] test) throws android.os.RemoteException;
}这些代码,理论上来讲,我们都可以自己写,即完全抛弃AIDL,无论客户端或者服务端,我们都自己实现所有的传输逻辑,但是你可以看到,这么简单的一个方法就需要我们写这么多的代码,尤其传输数据的部分,我们需要使用Parcel去顺序的进行读写,如果手写这部分代码,就十分容易弄错,而且还要服务端和客户端进行协同,如果我们有很多的Service,这些Service需要暴露更多的方法给客户端,那么我们需要手写的代码将会增加多少可想而知,而且还要通知客户端及时更新对应的逻辑,这样就十分复杂。
但是通过AIDL,我们可以简化这些工作,在Android源码中,我们可以看到大量的AIDL文件,可以说AIDL大大降低了应用层使用Binder通讯的重复和复杂性。
在Gradle编译过程中,实际上是使用了Android SDK提供的aidl工具去根据aidl文件去生成模板代码的,打开/Sdk/build-tools/34.0.0/,可以看到aidl程序:

运行此程序,可以看到,aidl不光可以生成java代码,C++甚至Rust代码都可以生成:

in, out, inout和oneway关键字
在AIDL中,我们可以使用oneway,in,out,inout等关键字来修饰我们的方法或参数,下面我将讲解这些参数代表着什么。
in,out,inout
当我们编写AIDL文件时,如果方法的参数为非基础类型参数,那么就会提示我们要标记参数的类型,否则会报错,比如下面的AIDL:
interface IRemoteService {
void Wow(int[] test);
}编译的时候会提示错误:

当我们把参数用in,out或者inout标记以后,就不会报错了:
interface IRemoteService {
void Wow(inout int[] test);
}
那么in,out,inout实际指代什么呢? 它其实代表着我们的数据流向。
in表示数据只能由客户端流向服务端,用in标识的参数,当服务端修改此参数时,客户端读取到的参数仍然是原来的值,不会被修改。
out表示数据只能由服务端流向客户端,用out标识的参数,服务端接到的参数会是空对象,而服务端修改此参数的时候,客户端能读取到修改后的值。
inout则表示数据会双向流动,即客户端和服务端对参数的修改,彼方都可以接收到。
oneway
我们可以使用oneway来修饰我们的Wow方法,注意,由oneway修饰的方法,非基础类型参数必须标记为in:
interface IRemoteService {
oneway void Wow(in int[] test);
}然后当我们的客户端在调用Wow时,就不会等待服务端的处理,会立刻进行下一步,不再等待服务端的处理。
为什么需要这些关键字
现在,我们理解了这些关键字的作用,那么问题来了,为什么需要这些关键字? 比如方法为什么需要设置为oneway,为什么参数不全设置成inout。
要理解这个问题,首先就要理解,Binder通讯的本质是什么,Binder通讯的本质,就是基于Binder驱动的一种进程间通讯,也就是说,我们最终数据的传递是需要Native层控制Binder驱动替我们去处理的,那么这些关键字最终影响的,其实就是我们调用Binder驱动传递数据的方式和逻辑,比如打开/frameworks/native/libs/binder/IPCThreadState.cpp,找到transact方法,就可以看到在调用写入数据writeTransactionData之后,如果被标记为oneway,那么就会向waitForResponse传入空的参数,在waitForResponse中其实就可以看到,在数据传输之后就会直接退出循环,在Java层的表现就会变成调用被oneway标识的方法之后,没有阻塞立刻执行下一步:

我们该什么时候使用这些关键字
使用这些关键字的自由度,其实是比较高的,但是秉承的原则应该是非必须原则,比如客户端如果想控制一些服务端的状态,比如setProperty1(boolean),那么这种方法就可以标记为oneway,因为这类方法一般不需要服务端的返回,同样的,有一些参数是客户端希望服务端去设置并读取的,那么就可以标记为out,总之在应用层,应该考虑尽量减少数据传输的负担。
Binder Native端的工作方式
相信做过Android开发的,一定对Binder这个词不陌生,我看过很多关于Binder的面试题和面试答案,总感觉是在隔靴搔痒,死记硬背一些概念,所以本文打算系统的讲解一下,到底什么是Binder。
什么是Binder
Binder,其实本质上来讲就是Android为我们提供的一种基于Binder驱动的跨进程通讯方式,如果说要问Android区别于Linux最大的特色是什么,那我可能会说Binder。
那么问题来了,为什么我们需要Binder?
为什么需要Binder
Linux本身有很多跨进程通讯手段:
机制 | 通信方向 | 亲缘关系要求 | 主要用途 | 性能 | 复杂度 |
|---|---|---|---|---|---|
匿名管道 | 单向 | 需要(通常父子) | 数据流 | 高 | 低 |
命名管道 | 单向/双向 | 不需要 | 简单数据流 | 中 | 低 |
消息队列 | 单向 | 不需要 | 结构化消息 | 中 | 中 |
共享内存 | 双向 | 不需要 | 大数据量、高性能 | 最高 | 高(需同步) |
信号量 | - | 不需要 | 同步与互斥 | 高 | 中 |
信号 | 单向 | 不需要 | 事件通知 | 高 | 低 |
套接字 | 双向 | 不需要 | 网络/通用通信 | 中(网络)/ 高(Unix域) | 高 |
内存映射 | 双向 | 不需要 | 大数据/文件共享 | 高 | 中 |
比如Socket,比如管道,Android 系统中的 Binder 是 Linux IPC 的一种深度定制和优化实现,它基于内存映射和共享内核驱动,在性能和安全(身份标识)上做了大量改进,成为Android生态中最核心的IPC机制。
为什么Android要多提供Binder这样一种新的跨进程通讯方式呢? 这个问题其实由Brian Swetland回答过了,他是2004年到2012年期间Android系统及内核的技术负责人,而他在邮件中是这么说的:

Android使用Binder的核心理由,其实就两条:
- avoiding copies by having the kernel copy from the writer into a
ring buffer in the reader's address space directly (allocating space
if necessary)
- managing the lifespan of proxied remoted userspace objects that can
be shared and passed between processes (upon which the userspace
binder library builds its remote reference counting model)第一条,其实是说的Binder在驱动层使用了Linux提供的mmap内存映射机制,第二条,应该是指的Binder驱动层对"对象"的生命周期的管理,总之,Android的技术负责人认为这种新的IPC更高效和便捷。
好了,现在我们知道了Android为什么要使用Binder了,那我们应该看看,Binder到底是怎么工作的。
Binder驱动
Binder 通信必须调用 Binder 驱动,这是其设计的核心。驱动位于 Linux 内核中,是 Binder IPC 机制的唯一枢纽和仲裁者。没有驱动,Binder 就无法工作。
为什么必须通过内核驱动?
Binder 的目标是在 Android 的沙盒环境下,实现高效、安全、可控的跨进程通信。直接让用户态进程互相访问内存是危险且不可控的。驱动提供了以下关键能力:
- 内存管理:实现“一次拷贝”
- 核心优势:Binder 性能高的关键。驱动在内核空间开辟一块共享内存区域,发送方(客户端)将数据一次性拷贝到这块内核缓冲区,接收方(服务端)通过内存映射直接读取。这避免了传统 IPC(如管道、Socket)需要在内核和用户空间之间进行两次数据拷贝的开销。
- 驱动的作用:管理这块共享内存,执行这次关键的数据拷贝。
- 进程/线程调度与管理
- 线程池管理:驱动维护了每个服务进程的 Binder 线程池。当请求到来时,驱动会唤醒或分配一个空闲的服务端线程来处理,避免了服务端自己维护复杂线程逻辑。
- 同步调用:Binder 默认是同步的。当客户端发起调用后,驱动会挂起(阻塞)当前客户端线程,直到服务端处理完毕并返回结果。这个阻塞和唤醒的调度由驱动完成。
- 安全与权限控制(最重要的原因之一)
- 身份标识:驱动知道每个发起调用的进程的 PID(进程ID)和 UID(用户ID)。服务端可以验证调用者的身份,决定是否允许其操作。
- 权限仲裁:Android 的权限模型(Permission)依赖于此。系统服务(如联系人、位置)在驱动层面验证调用者是否声明了相应权限,这是 Android 安全架构的基石。
- 引用计数与对象生命周期管理
- Binder 驱动跟踪所有 Binder 对象的引用。当一个 Binder 对象(代表远程服务)被跨进程传递时,驱动会为它在目标进程中创建一个“引用”,并增加引用计数。
- 当进程终止或不再需要该对象时,驱动负责清理和释放资源,防止内存泄漏。这是驱动作为“中央管理器”的职责。
- 命令解析与路由
- 驱动解析
ioctl系统调用传来的命令(如BINDER_WRITE_READ),判断是发送数据、返回数据,还是其他控制命令。 - 它根据数据包中的 Binder 句柄(handle),找到目标服务进程,并将请求放入该进程的待处理队列,完成精确的路由。
- 驱动解析
- 效率:一次拷贝的内存模型。
- 安全:基于进程身份和权限的访问控制。
- 稳定:统一的线程和生命周期管理。
- 透明:对上层的 Java/Kotlin 开发者隐藏了底层复杂性。
首先,我们来看看Binder驱动,Binder驱动层的代码,并不在AOSP中,而是在Android Linux Kernel中,如何下载Android Linux Kernel源码,可以看这里:
juejin.cn/post/734350…
下载好之后,查看drivers/android/,在此目录下可以看到如下文件:

打开binder.c,这里,就是驱动层的核心代码所在了:

下面,我将详细介绍整个Binder的工作流程。
Binder的工作流程
第一步 注册Binder设备
首先,我们已经知道了我们的Binder通信是基于Binder驱动来进行的,那么最开始的工作,就是Binder驱动层的初始化,打开drivers/android/binder.c,找到binder_init方法:

这里,便是我们的Binder驱动初始化方法,其它的我们暂时不关心,主要看调用的init_binder_device方法,这里面最核心的,就是调用了misc_register去注册了我们的Binder驱动:


下面是我们的binder_device结构体:

hlist: 这个变量主要是用来存储binder_device的全局链表,目前binder_device,会有三种,分别是/dev/binder,/dev/hwbinder和/dev/vndbinder,他们的区别可以看这里
www.jianshu.com/p/a79ccbf84…
miscdevice: 这是杂项设备信息,如果不了解什么是杂项设备,可以看这里:
developer.aliyun.com/article/842…
context: 这个变量存储的即是一个特殊的binder_node,即ServiceManager,后面会讲到。
这是我们的Binder驱动设备文件操作方法定义:

如果不了解misc_register是怎么注册设备文件方法的,可以看这里:
www.cnblogs.com/snowdrop/ar…
简单来说,当我们注册好设备文件方法,当Native层调用mmap的时候,最终会调用到binder_mmap这个方法中来。
第二步 启动ServiceManager
什么是ServiceManager呢? ServiceManager你可以理解为它是一个Binder驱动层的DNS服务,当一个客户端想要与服务端进行通讯的时候,只需要通过一个"网址",就可以通过ServiceManager找到真正的服务端,它可以说是理解Binder通信的核心概念之一,下面我们来看看,它是怎么被启动的。
首先打开AOSP/system/core/rootdir/init.rc,找到如下三行:

我们可以看到,它也是被Init进程通过解析RC文件被启动的,我们再打开AOSP/frameworks/native/cmds/servicemanager/main.cpp,找到main方法,这里,便是ServiceManager的实际启动入口:

我们可以看到,整个方法并不复杂,但是我首先要简单介绍两个类的概念,以方便我们对Native层Binder通信的理解。
ProcessState
打开AOSP/frameworks/native/libs/binder/ProcessState.cpp,我们就可以看见ProcessState的具体实现。
这个类的主要作用是控制Binder驱动,比如打开,关闭。
每个进程,都会只有一个ProcessState实例,如果有兴趣的可以仔细看看它的init方法,看看它是怎么保持每个进程只有单个实例的。
IPCThreadState
打开AOSP/frameworks/native/libs/binder/IPCThreadState.cpp,我们就可以看到IPCThreadState的具体实现。
这个类的主要作用,就是实际的通过Binder驱动发送和接收数据了。
每个线程,都会只有一个IPCThreadState实例。
好,接下来我们重新回到ServiceManager的初始化部分,首先我们来看这段代码:
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
最先开始,是进行了ProcessState的初始化,进入此方法,可以发现调用的是init方法:

进入init方法,可以看到最终调用了ProcessState的构造方法:

接下来再进入构造方法,我们可以看到如下内容:

这里,我们就可以看到,最开始,调用了open_driver方法,这个方法,就是用来打开Binder驱动的,我们进入此方法:

前面知道,我们已经注册了Binder驱动层设备文件方法了,所以最开始的open,会进入Binder驱动的binder_open方法,我们先看看驱动层在这个方法做了什么:

其实binder_open方法,最主要的作用就是在Binder驱动层创建并初始化了一个binder_proc,接下来我再介绍下,什么是binder_proc。
binder_proc
在AOSPLinuxKernel/drivers/android/binder_internal.h中,可以找到这样一个结构体:

这个结构体的主要作用,就是存储我们的进程信息,当Native层每次打开Binder驱动的时候,Binder驱动层都会创建一个binder_proc,可以说每一个Native层的ProcessState,在Binder进程中,都会有一个对应的binder_proc。
好,接下来我们再回到Native层ProcessState的open_driver,可以看到调用open之后,获取了Binder驱动的fd,下面就是执行这样一条:
status_t result = ioctl(fd, BINDER_VERSION, &vers);
这里,又会再次回到Binder驱动层的binder_ioctl:

这个方法,主要是执行我们的一些控制命令,从上面我们可以知道,我们传进去的命令是BINDER_VERSION,那么就会执行到下面这个部分:

这里主要是获取内核Binder版本,然后与用户层的Binder版本进行对比,防止错误。
接下来我们再回到open_driver,可以看到接下来执行到了这一步:
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
同样的,我们再回到驱动层,可以看到执行的是这里:

它的主要作用,是设置了驱动层当前进程的对应binder_proc的线程上限。
再回到open_driver,接下来到了这一步:
result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);
然后驱动层就会执行这里:

这里就是设置是否开启OneWay Spam的检测,那么什么是OneWay Spam呢?众所周知,我们的方法是可以设置为one way的,也就是客户端不等待服务端的返回,直接继续执行自己的逻辑,那么这个时候,客户端如果疯狂的调用oneway方法,就会挤爆Binder的缓冲区,这个行为就叫OneWay Spam(垃圾信息),当开启此项,如果进程有OneWay Spam的嫌疑,那么驱动层就会返回一个BR_ONEWAY_SPAM_SUSPECT,在IPCThreadSate.cpp里,也可以看到对此返回的处理:

到这里,open_driver方法已经执行完毕了,在最后,返回了驱动的fd,接下来我们再回到ProcessState的构造方法。
接下来,会对open_driver的返回进行一个判断,如果打开正常,则会执行如下:
mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, opened.value(), 0);首先,我们来看BINDER_VM_SIZE的定义:
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
这就是我们Binder缓冲区的大小,默认情况下是1024kb - 8kb,也就是1016kb。
这里的mmap,最终会调用驱动层的binder_mmap:

可以看到,最后实际执行的binder_alloc_mmap_handler去进行内存的分配,这里就不展开讲了,下面说明一下,Binder驱动层所谓的一次拷贝到底是怎么实现的。
Binder单次拷贝
Binder的单次拷贝,其原理就是Binder驱动会申请一块实际的物理内存,并且通过mmap内存映射机制,将服务端进程的用户空间的虚拟内存地址和内核空间的虚拟内存地址都同时映射在这一块物理内存上,这样当客户端进程向Binder驱动层传输数据的时候,Binder驱动层会把数据写入服务端进程内核空间的对应虚拟内存地址,那么对于服务端的用户空间来说,就可以直接读取到客户端传输过来的数据,因为虚拟内存地址所指向的是同一块物理内存。
到这一步,其实Binder驱动层只是分配了内核的连续内存和实际的物理内存,当驱动层调用binder_transaction并且在里面调用binder_alloc_new_buf,才会进行实际的内存映射。
至此ProcessState的构造方法便执行完了,接下来,我们再回到AOSP/frameworks/native/cmds/servicemanager/main.cpp,可以看到ProcessState初始化完成以后,会调用:
ps->setThreadPoolMaxThreadCount(0);

这里,最终调用的也是刚才驱动层的方法,这个方法主要是设置的Native层ProcessState的最大线程上限为0,并且让驱动层对应的binder_proc的最大线程上限也为0。
好,我们再回到刚才,接着往下看这段代码:
sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
LOG(ERROR) << "Could not self register servicemanager";
}可以看到,这里初始化了ServiceManager,并且调用了addService,并且把自己的实例传了进去,这里就是开始注册服务端了,而最先开始被注册成服务端的,第一个服务,就是我们的ServiceManager,也可以理解为Binder通讯的"DNS服务"。
我们进入此方法,直接找到这一段:

我们可以看到,其实ServiceManager是通过mNameToService来保存我们的Binder服务端实例的,我们再看下它的类型:
struct Service {
sp<IBinder> binder; // not null
bool allowIsolated;
int32_t dumpPriority;
bool hasClients = false; // notifications sent on true -> false.
bool guaranteeClient = false; // forces the client check to true
Access::CallingContext ctx; // process that originally registers this
// the number of clients of the service, including servicemanager itself
ssize_t getNodeStrongRefCount();
~Service();
};
using ServiceMap = std::map<std::string, Service>;
ServiceMap mNameToService;可以看到它就是一个HashMap,key为服务的名字,而value则是Service实例,而在Service中,则保存了binder实例,但是这并不是一个实际上其它进程的服务实例,而是一个基于其句柄构造BPBinder实例。
我们再回到前面的main方法,看这两条语句:
IPCThreadState::self()->setTheContextObject(manager);
ps->becomeContextManager();第一条,是获取或创建当前线程的IPCThreadState实例,并且调用它的setTheContextObject方法并且将ServiceManager实例赋值给它,可以看到里面的实现很简单:

第二条,则是调用了刚才创建的ProcessState的becomeContextManager,我们进入此方法:

可以看到这里调用了驱动层,并且命令为BINDER_SET_CONTEXT_MGR_EXT,我们进入驱动层的此方法:

可以看到最终调用了驱动层的binder_ioctl_set_ctx_mgr,这里,其实就是根据当前binder_proc创建一个特殊的binder_node,并且将它作为特殊的binder_node保存下来,这里再说下binder_node。
binder_node
binder_node可以理解为一个实体的节点,这个节点就是指一个Binder服务端,也就是说,每一个Binder服务端,在驱动层都会有对应的binder_node,下面是binder_node的结构体:

总之becomeContextManager这个方法的作用,你可以理解为ServiceManager作为一个特殊的Binder服务端,不光是在Native层特殊,在驱动层,Binder也为ServiceManager留下了一个特殊的位置,即一个特殊的binder_node,可以让驱动层随时能联系上它。
好,我们再回到main方法,可以看到接下来,创建了一个Native层的Looper,并且进入等待状态:
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
BinderCallback::setupTo(looper);
ClientCallbackCallback::setupTo(looper, manager);
#ifndef VENDORSERVICEMANAGER
if (!SetProperty("servicemanager.ready", "true")) {
LOG(ERROR) << "Failed to set servicemanager ready property";
}
#endif
while(true) {
looper->pollAll(-1);
}
// should not be reached
return EXIT_FAILURE;至此,ServiceManager,也就是我们的"DNS服务"已经准备完成了。
第三步 注册成为服务端
那么现在,我们的Binder驱动也准备好了,"DNS服务"也注册好了,现在要做的是什么呢? 就是开启一个又一个的Binder服务了。
我们来找个Binder服务注册的例子,打开AOSP/frameworks/av/media/audioserver/main_audioserver.cpp,找到main方法,划到这部分代码:
sp<IServiceManager> sm = defaultServiceManager();
sm->addService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME), afAdapter,
false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);可以看到,首先调用defaultServiceManager,获取到了IServiceManager,那我们先来看看这个方法的实现:

我们可以看到这里实际调用了ProcessState的getContextObject,再进入此方法:

可以看到这里调用了getStrongProxyForHandle,并且传入了0,下面我不再展开,而是大致说一下这里的实现逻辑。
首先,handle是一个驱动层使用的概念,你可以理解为它是一个句柄,驱动层通过这个句柄,可以找到对应的binder_node和binder_proc,而我们的ServiceManager因为其特殊的身份,所以它的句柄是0。
因为我们目前与ServiceManager不在同一个进程,所以我们的defaultServiceManager获取的并不是ServiceManager实例,而是一个可以与ServiceManager进行Binder通信的BpServiceManager,而这个BpServiceManager,是由AOSP/frameworks/native/libs/binder/aidl/android/os/IServiceManager.aidl生成出来的,而当我们调用IServiceManager的方法的时候,你可以理解为最终是通过BpServiceManager通过Binder通讯的方式,最终调用到了我们在单独进程的那个ServiceManager,这中间的逻辑根我们在Java层使用aidl文件的逻辑,其实没有什么差别,具体中间是怎样实现的,有兴趣的可以详细看一下相关类的具体实现,这里不再赘述。
接下来,我们再回到刚才的main方法,这里的addService,最终会调用我们ServiceManager的addService,这里的逻辑其实和ServiceManager初始化后调用自己的addService一样,就是把当前的服务名和Binder对象添加进了mNameToService里。
到这里,服务可以说是已经注册完成了,接下来就是开始启动我们的Binder服务。
第四步 启动Binder服务
启动Binder服务端,只需要调用这两条语句就可以:
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
首先,我们先分析startThreadPool,ProcessState的初始化,上面已经讲过,这里不再赘述:

可以看到,这里调用了spawnPooledThread,再看看这个方法:

可以看到,这里面最核心的其实是创建了一个PoolThread,并且运行它的run方法,那我们再看看PoolThread的定义:

可以看到,里面很简单,就是调用了IPCThreadState::self()->joinThreadPool()。
那么问题来了,可以看到上面还会执行一次IPCThreadState::self()->joinThreadPool(),为什么同样的方法要执行两次?
这个问题,只要进入joinThreadPool就可以知道了:

这个方法,其实就是我们最终与Binder驱动层交互的主要逻辑了,可以看到这里的主要执行逻辑,就是不断地等待客户端的请求,并且处理命令,主要result不出问题,那么就会一直循环执行。我们接下来再进入getAndExecuteCommand:

可以看到先调用了talkWithDriver,进入此方法:

可以看到前面的代码,主要是为了构造一个binder_write_read,在Native层,它的结构体如下:

然后我们再进入驱动层,也可以找到一个binder_write_read,结构如下:

可以看到Native层和驱动层的结构基本是一致的,这个binder_write_read,代表的就是驱动层的单次读写。我们再回到talkWithDriver,找到这部分代码:

这里就是将我们构造好的binder_write_read传输给驱动层了,我们再进入驱动层的处理:

再进入binder_ioctl_write_read:

可以看到里面调用了binder_thread_write和binder_thread_read去处理写和读:

因为我们给mOut复制了值,这个值可能是BC_ENTER_LOOPER或BC_REGISTER_LOOPER,可以看到对应的处理,主要是给thread的looper进行赋值:

要注意,当调用ProcessState::self()->startThreadPool()的时候,会创建一个新的线程,并设置那个新的线程为主线程,当调用IPCThreadState::self()->joinThreadPool()的时候,指的是当前线程,所以ProcessState::self()->startThreadPool()最终创建出来的线程同样会去调用IPCThreadState::self()->joinThreadPool(),但是那是主线程的joinThreadPool(),你可以理解当执行完ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool();的时候,目前已经有两条,即一条主线程,一条子线程可以与Binder通信的线程了
回到刚才的binder_ioctl_write_read,我们执行完binder_thread_write之后,就要执行binder_thread_read了,因为在IPCThreadState的talkWithDriver中,我们的doReceive和needRead都为true,所以read_size的值是大于0的:

我们进入binder_thread_read,看到这里调用了binder_wait_for_work:

可以看到从这个方法开始,线程进入等待状态,并且将当前的thread加入对应binder_proc的waiting_threads里面:

到这里,我们的Binder服务就启动起来了,线程会进入等待状态,当有客户端想要进行Binder通讯时,对应的线程才会被唤醒,接下来,我们就来看看当有客户端进行Binder通信时,整个的处理流程是怎么样的。
第五步 Binder客户端通信
比如当我们看到如下代码的时候:
sp<IBinder> binder =
defaultServiceManager()->getService(String16("media.player"));
sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
CHECK(service.get() != NULL);
service->addBatteryData(params);
前三句,我们都已经知道具体流程了,那么当我们调用service->addBatteryData(params),实际调用的是什么呢? 这里实际的调用,是在AOSP/frameworks/av/media/libmedia/IMediaPlayerService.cpp中,可以看到BpMediaPlayerService的实际实现:

可以看到,这里面实际的传输,是通过remote()->transact()来进行传输的,remote()返回的,其实就是一个BpBinder实例,transact方法的具体实现,可以在AOSP/frameworks/native/libs/binder/BpBinder.cpp看到:

可以看到,最终实际的传输还是调用IPCThreadState::self()->transact来进行的,我们再进入此方法,可以看到,这里调用了writeTransactionData,并且传入的cmd参数为BC_TRANSACTION,再进入此方法:

可以看到,这个方法主要是组装了一个binder_transaction_data,并将其写入mOut中。
我们再次回到IPCThreadState::self()->transact中,可以看到下面这段代码:

这段代码的主要含义,就是判断当前的调用是否是ONE_WAY,如果不是的话,就向waitForResponse传入reply,如果是的话,就传入nullptr。
我们再进入waitForResponse方法:

可以看到,从这里开始,我们又进入了talkWithDriver方法了,也就意味着,我们开始要往驱动层写入数据了,我们直接进入驱动层:

可以看到,无论是BC_TRANSACTION还是BC_REPLY,驱动层的逻辑都是相同的,就是先把传入的binder_transaction_data从用户空间拷贝到内核空间,然后调用binder_transaction方法,我们再进入此方法:

可以看到,这里面的方法很长,一共900多行,所以我大致讲一下这里面做的事情,首先通过传入的binder_transaction_data的handle找到对应的服务端binder_node和binder_proc,然后将传入的数据写入到服务端进程的缓冲区内,然后从目标进程的waiting_thread中找一条线程来进行唤醒。
至于里面是如何通过binder_thread的todo列表,binder_transaction和binder_work来实现的,有兴趣可以自己看看。
接下来,就是服务端进程的工作了,我们再回到第三步所提到过的binder_thread_read,直接看这部分:

这里,就是服务端的线程开始被唤醒,并且开始处理客户端的请求了,接下来主要是一系列的数据的处理和赋值,我们先不关心,主需要知道,从这里开始,服务端线程会把回传给Native层的数据的cmd设置为BR_TRANSACTION,就可以了,然后也需要知道执行完这里,我们的服务端就可以获取到客户端传过来的数据了,以上面的代码举例也就是params这个参数。
接下来我们再看服务端Native层的处理:

这个时候talkWithDriver已经不会再阻塞,服务端下面要执行的就是executeCommand,进入此方法,找到这部分代码:

可以看到,这里调用的就是根据tr.cookie转换成的BBinder指针,通过MediaPlayerService的初始化方法我们可以知道,这其实指向的就是一个MediaPlayerService实例:

我们再进入BBinder的transact方法,可以看到最终调用的是自身的onTransact,那么我们看下MediaPlayerService的头文件,可以看到他继承了BnMediaPlayerService,然后再看BnMediaPlayerService,可以看到它重写了onTransact方法:

可以看到它的实现,最终是调用了MediaPlayerService的addBatteryData方法:

然后我们再回到executeCommand的BR_TRANSACTION部分的处理,可以看到接下来下做了判断,如果不是OneWay调用的话,就会调用sendReply方法:

进入此方法,可以看到又调用了writeTransactionData和waitForResponse,将服务端的返回写入到驱动层:

这里就不再赘述了,这里其实还是一样的逻辑,将回复的数据写入客户端的内存缓冲区中,然后将cmd设置成BR_REPLY,然后再让客户端对应的请求去处理服务端的返回。
客户端的Native层,会将驱动层的数据先读取到binder_transaction_data中,然后再将数据赋值给reply:

最后,上层再从reply中读取到服务端的返回值,至此,就完成了一次Binder通信。