Android Framework 源码阅读:Binder 调用链

63 阅读13分钟

Android Framework 源码阅读:Binder 调用链

本文以旧版 AOSP Framework 实现为例,重点跟踪一次 Binder 调用从客户端到服务端、再返回客户端的完整路径。文中的 ActivityManagerNative、ActivityStack 等类属于历史实现,不能直接等同于新版本 Android 的 ActivityTaskManagerService、ActivityStarter。

摘要

Binder 是 Android Framework 中最常用的进程间通信机制。一次普通的 Java 服务调用,经过接口代理和 Parcel 编码后进入 native Binder,再由 Binder 驱动交给服务端线程池;服务端完成分派后写回结果,客户端把结果还原成 Java 返回值。

本文只围绕这条主线展开:

客户端 Java 接口
  → Stub / Proxy 编解码
  → native BpBinder / IPCThreadState
  → Binder 驱动
  → 服务端 BBinder / onTransact
  → reply Parcel 返回客户端

1. 阅读范围与对象

本文的源码入口主要位于:

  • frameworks/base/core/java/android/os/Binder.java
  • frameworks/base/core/java/android/os/Parcel.java
  • frameworks/native/libs/binder/BpBinder.cpp
  • frameworks/native/libs/binder/BBinder.cpp
  • frameworks/native/libs/binder/IPCThreadState.cpp
  • drivers/android/binder.c

如果要复现,应该先确定 AOSP 分支或 commit。旧版 Framework 的类名和调用顺序会随 Android 版本变化;本文不把旧版实现描述成当前系统的唯一实现。

2. Java 层:Stub、Proxy 与 Parcel

以 AIDL 生成的接口为例,客户端拿到的通常不是服务端对象,而是一个 Proxy。asInterface() 会先尝试查询本进程的本地实现;跨进程时则返回代理对象。

public interface IExample extends IInterface {
    int add(int left, int right) throws RemoteException;

    abstract class Stub extends Binder implements IExample {
        static final int TRANSACTION_add = FIRST_CALL_TRANSACTION;

        public static IExample asInterface(IBinder binder) {
            if (binder == null) return null;
            IInterface local = binder.queryLocalInterface(DESCRIPTOR);
            return local instanceof IExample ? (IExample) local : new Proxy(binder);
        }
    }
}

代理方法的核心工作是把参数写入请求 Parcel,调用 transact(),再从返回 Parcel 中读取结果:

public int add(int left, int right) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    try {
        data.writeInterfaceToken(DESCRIPTOR);
        data.writeInt(left);
        data.writeInt(right);
        mRemote.transact(TRANSACTION_add, data, reply, 0);
        reply.readException();
        return reply.readInt();
    } finally {
        reply.recycle();
        data.recycle();
    }
}

本节结论:Java 层提供类型化接口和参数编解码;跨进程时,Proxy 并不执行服务端实现,只负责发起事务。

3. native 客户端:BpBinder 与 IPCThreadState

BinderProxy.transact() 最终进入 native Binder。native 层可以分成两步理解:

  1. BpBinder::transact() 代表远端 Binder 对象接收请求;
  2. IPCThreadState::transact() 把事务写入线程状态,并通过 ioctl 与 Binder 驱动交互。
status_t BpBinder::transact(uint32_t code,
                            const Parcel& data,
                            Parcel* reply,
                            uint32_t flags) {
    return IPCThreadState::self()->transact(
        handle(), code, data, reply, flags);
}

同步事务会等待服务端结果;oneway 事务只提交请求,不等待 reply。这两种模式共享 Binder 通道,但等待和错误处理路径不同。

Binder 客户端调用路径

本节结论:BpBinder 是客户端代理,真正的事务发送和同步等待由 IPCThreadState 完成。

4. Binder 驱动:事务如何跨进程

每个参与 Binder 通信的进程都会打开 Binder 设备,并通过 mmap 建立驱动可使用的缓冲区。事务提交时,native 层通过 ioctl 把命令和数据交给内核驱动;驱动根据目标句柄找到目标进程,把事务排入目标进程的待处理队列。

这里有两个容易混淆的点:

  • Parcel 是跨进程传输时的序列化容器,不是持久化格式;
  • “一次拷贝”只是对典型缓冲区路径的教学化概括,文件描述符、大对象和不同版本实现可能有特殊路径,不能泛化成所有事务都严格一次拷贝。

驱动为进程建立的映射区域大小由内核和版本实现决定,不能把某个旧分支中的固定数值当作通用规则。

5. 服务端:线程池、BBinder 与 onTransact

服务端进程通常会启动 Binder 线程池并进入 joinThreadPool()。当驱动返回 BR_TRANSACTION 后,服务端线程从事务中取出目标对象和 Parcel,再交给 BBinder::transact()。

status_t BBinder::transact(uint32_t code,
                           const Parcel& data,
                           Parcel* reply,
                           uint32_t flags) {
    data.setDataPosition(0);
    status_t err = onTransact(code, data, reply, flags);
    if (reply) reply->setDataPosition(0);
    return err;
}

对于 AIDL Stub,onTransact() 根据 transaction code 读取参数,调用真正的服务实现,再把返回值写入 reply:

@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    if (code == TRANSACTION_add) {
        data.enforceInterface(DESCRIPTOR);
        int result = add(data.readInt(), data.readInt());
        reply.writeNoException();
        reply.writeInt(result);
        return true;
    }
    return super.onTransact(code, data, reply, flags);
}

Binder 服务端分派路径

本节结论:服务端不是被客户端直接调用,而是由 Binder 线程池读取事务,再按 transaction code 分派到 Stub 和具体服务实现。

6. ServiceManager:命名服务,不是传输层

servicemanager 是由 init 启动的独立进程,承担 Binder context manager 的职责:服务进程向它注册名称,客户端按名称查询并获得代理。Binder 驱动负责事务传输,servicemanager 负责命名和查询,两者不能混为一谈。

历史实现中常用特殊 context manager 句柄(通常表现为 handle 0)获取 servicemanager,但这不是普通业务 Binder 对象的固定编号。

服务启动 → addService("example", binder)
客户端   → getService("example")
客户端   → 得到远端代理 → transact(code, data, reply)

7. 返回路径与错误处理

一次同步调用的完整时序如下:

客户端 Proxy
  1. 写入 data Parcel
  2. BpBinder::transact()
  3. IPCThreadState::waitForResponse()
        ↓
      Binder 驱动
        ↓
  4. 服务端读取 BR_TRANSACTION
  5. Stub.onTransact() 调用服务实现
  6. 写入 reply Parcel
        ↓
  7. 客户端收到 BR_REPLY
  8. 读取返回值或异常

服务端返回的 status_t、远端异常和 Binder 对象死亡通知,都在这条事务上下文中完成转换。客户端 Stub 最终把它们还原成 Java 返回值、RemoteException 或其他错误结果。

Binder 请求与返回闭环

8. Framework 服务如何复用这条通道

ActivityManagerService、PackageManagerService 等 Framework 服务,本质上都是把自己的 Stub 注册到 Binder 服务管理器中。客户端拿到代理后,仍然重复前面的流程:

获取服务代理
  → Proxy 写 Parcel
  → Binder 驱动传输
  → 服务端 Stub 分派
  → reply Parcel 返回

旧版源码中的 ActivityManagerNative、ActivityStack、ActivityRecord 只代表特定版本的实现。现代 AOSP 已经引入 ActivityTaskManagerService、ActivityStarter 等新类;阅读旧代码时必须同时记录版本和 commit,不能把类名和调用顺序照搬到新系统。

9. JNI 与 Binder 的边界

JNI 是 Java 与 native 的语言边界,Binder 是进程间通信机制,二者经常同时出现但职责不同:

  • JNI 负责进入 native 方法、访问 JNIEnv 和完成类型转换;
  • Binder 负责把事务发送到另一个进程并返回结果。

历史 JNI 示例中的 javah 只是旧的头文件生成流程;现代 JDK 可以使用 javac -h,或者交给 Gradle/NDK 构建系统生成头文件。动态注册应在 JNI_OnLoad() 中通过 RegisterNatives 把 JNINativeMethod[] 绑定到指定类;JNIEnv 是线程相关的接口指针,不能跨线程直接复用。

10. 复现与核对方法

阅读这类源码时,建议固定以下核对顺序:

  1. 记录 AOSP 分支或 commit,先确认类名是否属于该版本;
  2. 从一个接口的 Proxy 方法进入,而不是从概念定义开始;
  3. 沿 transact → ioctl → BR_TRANSACTION → onTransact 标记每一跳;
  4. 对照 transaction code、请求 Parcel 和 reply Parcel;
  5. 最后再回到 Framework 服务,确认它只是复用了同一条 Binder 通道。

本文的核心结论只有一句话:Framework 服务的 Java 调用,经过 Proxy 编码后由 Binder 驱动传输,服务端 Stub 分派执行,再沿同一事务上下文把结果返回。

未展开的 JNI、Zygote 启动和 Activity 完整生命周期,适合分别拆成后续文章,避免与 Binder 主线混在同一篇中。 看起来是你构建方式不对。# Android Framework 源码阅读:Binder 调用链

本文以旧版 AOSP Framework 实现为例,重点跟踪一次 Binder 调用从客户端到服务端、再返回客户端的完整路径。文中的 ActivityManagerNative、ActivityStack 等类属于历史实现,不能直接等同于新版本 Android 的 ActivityTaskManagerService、ActivityStarter。

摘要

Binder 是 Android Framework 中最常用的进程间通信机制。一次普通的 Java 服务调用,经过接口代理和 Parcel 编码后进入 native Binder,再由 Binder 驱动交给服务端线程池;服务端完成分派后写回结果,客户端把结果还原成 Java 返回值。

本文只围绕这条主线展开:

客户端 Java 接口
  → Stub / Proxy 编解码
  → native BpBinder / IPCThreadState
  → Binder 驱动
  → 服务端 BBinder / onTransact
  → reply Parcel 返回客户端

1. 阅读范围与对象

本文的源码入口主要位于:

  • frameworks/base/core/java/android/os/Binder.java
  • frameworks/base/core/java/android/os/Parcel.java
  • frameworks/native/libs/binder/BpBinder.cpp
  • frameworks/native/libs/binder/BBinder.cpp
  • frameworks/native/libs/binder/IPCThreadState.cpp
  • drivers/android/binder.c

如果要复现,应该先确定 AOSP 分支或 commit。旧版 Framework 的类名和调用顺序会随 Android 版本变化;本文不把旧版实现描述成当前系统的唯一实现。

2. Java 层:Stub、Proxy 与 Parcel

以 AIDL 生成的接口为例,客户端拿到的通常不是服务端对象,而是一个 Proxy。asInterface() 会先尝试查询本进程的本地实现;跨进程时则返回代理对象。

public interface IExample extends IInterface {
    int add(int left, int right) throws RemoteException;

    abstract class Stub extends Binder implements IExample {
        static final int TRANSACTION_add = FIRST_CALL_TRANSACTION;

        public static IExample asInterface(IBinder binder) {
            if (binder == null) return null;
            IInterface local = binder.queryLocalInterface(DESCRIPTOR);
            return local instanceof IExample ? (IExample) local : new Proxy(binder);
        }
    }
}

代理方法的核心工作是把参数写入请求 Parcel,调用 transact(),再从返回 Parcel 中读取结果:

public int add(int left, int right) throws RemoteException {
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    try {
        data.writeInterfaceToken(DESCRIPTOR);
        data.writeInt(left);
        data.writeInt(right);
        mRemote.transact(TRANSACTION_add, data, reply, 0);
        reply.readException();
        return reply.readInt();
    } finally {
        reply.recycle();
        data.recycle();
    }
}

本节结论:Java 层提供类型化接口和参数编解码;跨进程时,Proxy 并不执行服务端实现,只负责发起事务。

3. native 客户端:BpBinder 与 IPCThreadState

BinderProxy.transact() 最终进入 native Binder。native 层可以分成两步理解:

  1. BpBinder::transact() 代表远端 Binder 对象接收请求;
  2. IPCThreadState::transact() 把事务写入线程状态,并通过 ioctl 与 Binder 驱动交互。
status_t BpBinder::transact(uint32_t code,
                            const Parcel& data,
                            Parcel* reply,
                            uint32_t flags) {
    return IPCThreadState::self()->transact(
        handle(), code, data, reply, flags);
}

同步事务会等待服务端结果;oneway 事务只提交请求,不等待 reply。这两种模式共享 Binder 通道,但等待和错误处理路径不同。

Binder 客户端调用路径

本节结论:BpBinder 是客户端代理,真正的事务发送和同步等待由 IPCThreadState 完成。

4. Binder 驱动:事务如何跨进程

每个参与 Binder 通信的进程都会打开 Binder 设备,并通过 mmap 建立驱动可使用的缓冲区。事务提交时,native 层通过 ioctl 把命令和数据交给内核驱动;驱动根据目标句柄找到目标进程,把事务排入目标进程的待处理队列。

这里有两个容易混淆的点:

  • Parcel 是跨进程传输时的序列化容器,不是持久化格式;
  • “一次拷贝”只是对典型缓冲区路径的教学化概括,文件描述符、大对象和不同版本实现可能有特殊路径,不能泛化成所有事务都严格一次拷贝。

驱动为进程建立的映射区域大小由内核和版本实现决定,不能把某个旧分支中的固定数值当作通用规则。

5. 服务端:线程池、BBinder 与 onTransact

服务端进程通常会启动 Binder 线程池并进入 joinThreadPool()。当驱动返回 BR_TRANSACTION 后,服务端线程从事务中取出目标对象和 Parcel,再交给 BBinder::transact()。

status_t BBinder::transact(uint32_t code,
                           const Parcel& data,
                           Parcel* reply,
                           uint32_t flags) {
    data.setDataPosition(0);
    status_t err = onTransact(code, data, reply, flags);
    if (reply) reply->setDataPosition(0);
    return err;
}

对于 AIDL Stub,onTransact() 根据 transaction code 读取参数,调用真正的服务实现,再把返回值写入 reply:

@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
        throws RemoteException {
    if (code == TRANSACTION_add) {
        data.enforceInterface(DESCRIPTOR);
        int result = add(data.readInt(), data.readInt());
        reply.writeNoException();
        reply.writeInt(result);
        return true;
    }
    return super.onTransact(code, data, reply, flags);
}

Binder 服务端分派路径

本节结论:服务端不是被客户端直接调用,而是由 Binder 线程池读取事务,再按 transaction code 分派到 Stub 和具体服务实现。

6. ServiceManager:命名服务,不是传输层

servicemanager 是由 init 启动的独立进程,承担 Binder context manager 的职责:服务进程向它注册名称,客户端按名称查询并获得代理。Binder 驱动负责事务传输,servicemanager 负责命名和查询,两者不能混为一谈。

历史实现中常用特殊 context manager 句柄(通常表现为 handle 0)获取 servicemanager,但这不是普通业务 Binder 对象的固定编号。

服务启动 → addService("example", binder)
客户端   → getService("example")
客户端   → 得到远端代理 → transact(code, data, reply)

7. 返回路径与错误处理

一次同步调用的完整时序如下:

客户端 Proxy
  1. 写入 data Parcel
  2. BpBinder::transact()
  3. IPCThreadState::waitForResponse()
        ↓
      Binder 驱动
        ↓
  4. 服务端读取 BR_TRANSACTION
  5. Stub.onTransact() 调用服务实现
  6. 写入 reply Parcel
        ↓
  7. 客户端收到 BR_REPLY
  8. 读取返回值或异常

服务端返回的 status_t、远端异常和 Binder 对象死亡通知,都在这条事务上下文中完成转换。客户端 Stub 最终把它们还原成 Java 返回值、RemoteException 或其他错误结果。

Binder 请求与返回闭环

8. Framework 服务如何复用这条通道

ActivityManagerService、PackageManagerService 等 Framework 服务,本质上都是把自己的 Stub 注册到 Binder 服务管理器中。客户端拿到代理后,仍然重复前面的流程:

获取服务代理
  → Proxy 写 Parcel
  → Binder 驱动传输
  → 服务端 Stub 分派
  → reply Parcel 返回

旧版源码中的 ActivityManagerNative、ActivityStack、ActivityRecord 只代表特定版本的实现。现代 AOSP 已经引入 ActivityTaskManagerService、ActivityStarter 等新类;阅读旧代码时必须同时记录版本和 commit,不能把类名和调用顺序照搬到新系统。

9. JNI 与 Binder 的边界

JNI 是 Java 与 native 的语言边界,Binder 是进程间通信机制,二者经常同时出现但职责不同:

  • JNI 负责进入 native 方法、访问 JNIEnv 和完成类型转换;
  • Binder 负责把事务发送到另一个进程并返回结果。

历史 JNI 示例中的 javah 只是旧的头文件生成流程;现代 JDK 可以使用 javac -h,或者交给 Gradle/NDK 构建系统生成头文件。动态注册应在 JNI_OnLoad() 中通过 RegisterNatives 把 JNINativeMethod[] 绑定到指定类;JNIEnv 是线程相关的接口指针,不能跨线程直接复用。

10. 复现与核对方法

阅读这类源码时,建议固定以下核对顺序:

  1. 记录 AOSP 分支或 commit,先确认类名是否属于该版本;
  2. 从一个接口的 Proxy 方法进入,而不是从概念定义开始;
  3. 沿 transact → ioctl → BR_TRANSACTION → onTransact 标记每一跳;
  4. 对照 transaction code、请求 Parcel 和 reply Parcel;
  5. 最后再回到 Framework 服务,确认它只是复用了同一条 Binder 通道。

本文的核心结论只有一句话:Framework 服务的 Java 调用,经过 Proxy 编码后由 Binder 驱动传输,服务端 Stub 分派执行,再沿同一事务上下文把结果返回。

未展开的 JNI、Zygote 启动和 Activity 完整生命周期,适合分别拆成后续文章,避免与 Binder 主线混在同一篇中。