Skip to content

BnInterface与BpInterface模板

追踪 libbinder 接口模板如何连接本地 Stub、远程 Proxy、descriptor、asInterface 和 asBinder。

基于android-17.0.0_r1
AndroidBinderBnInterfaceBpInterface源码阅读

BnInterface与BpInterface模板 ​

BnInterface 和 BpInterface 把同一个接口分别接到本地 BBinder 与远程 BpBinder。asInterface() 先问 Binder 是否能返回匹配 descriptor 的本地接口;不能时才构造 Proxy。因此同一份客户端代码可能拿到本地实现或远程代理。

本文面向已经读过 BBinder本地对象、BpBinder代理对象 和 transact发送 的读者。本文追踪模板继承、接口转换、descriptor 检查和 Stub/Proxy 调用路径,不展开每个 AIDL 业务方法的 Parcel 字段。

1. 两类实现 ​

1.1 Bn本地端 ​

源码文件:frameworks/native/libs/binder/include/binder/IInterface.h

cpp
template <typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder {
public:
    virtual sp<IInterface> queryLocalInterface(
            const String16& descriptor);
    virtual const String16& getInterfaceDescriptor() const;
protected:
    virtual IBinder* onAsBinder();
};

BnInterface<I> 同时是接口和 BBinder。本地实现覆写接口业务方法或 onTransact();onAsBinder() 返回自身。

1.2 Bp远程端 ​

cpp
template <typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase {
public:
    explicit BpInterface(const sp<IBinder>& remote);
protected:
    virtual IBinder* onAsBinder();
};

BpInterface<I> 持有远程 IBinder,接口方法通常构造 Parcel 后调用 remote()->transact()。

模板Binder基类onAsBinder()执行位置
BnInterface<I>BBinder本地对象服务进程
BpInterface<I>BpRefBase远程代理对端服务

2. 接口转换 ​

2.1 Cast入口 ​

源码文件:frameworks/native/libs/binder/include/binder/IInterface.h

cpp
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(
        const sp<IBinder>& obj) {
    return INTERFACE::asInterface(obj);
}

template<typename INTERFACE>
inline sp<INTERFACE> checked_interface_cast(
        const sp<IBinder>& obj) {
    if (obj->getInterfaceDescriptor() !=
            INTERFACE::descriptor) return nullptr;
    return interface_cast<INTERFACE>(obj);
}

普通 cast 不先验证 descriptor;checked cast 在转换点立即拒绝不匹配对象。两者都不改变远程 Binder 的 handle 或引用。

2.2 asInterface ​

cpp
::android::sp<ITYPE> ITYPE::asInterface(
        const ::android::sp<::android::IBinder>& obj) {
    ::android::sp<ITYPE> intr;
    if (obj != nullptr) {
        intr = ::android::sp<ITYPE>::cast(
                obj->queryLocalInterface(ITYPE::descriptor));
        if (intr == nullptr) {
            intr = ::android::sp<BPTYPE>::make(obj);
        }
    }
    return intr;
}

宏生成代码先尝试本地接口,失败后创建 Proxy。queryLocalInterface() 返回空并不等于 Binder 无效,只表示当前对象没有暴露匹配的本地实现。

2.3 asBinder ​

源码文件:frameworks/native/libs/binder/IInterface.cpp

cpp
sp<IBinder> IInterface::asBinder(
        const sp<IInterface>& iface) {
    if (iface == nullptr) return nullptr;
    return sp<IBinder>::fromExisting(
            iface->onAsBinder());
}

BnInterface 返回自身,BpInterface 返回 remote();这是多态取回现有 Binder,不会重新创建对象。

3. Descriptor分层 ​

3.1 本地查询 ​

cpp
template<typename INTERFACE>
inline sp<IInterface> BnInterface<INTERFACE>::queryLocalInterface(
        const String16& descriptor) {
    if (descriptor == INTERFACE::descriptor) {
        return sp<IInterface>::fromExisting(this);
    }
    return nullptr;
}

descriptor 匹配时返回本地接口;不匹配时 asInterface() 退回 Proxy,而不是强制转换错误对象。

3.2 事务令牌 ​

源码文件:frameworks/native/libs/binder/include/binder/IInterface.h

cpp
#define CHECK_INTERFACE(interface, data, reply) \
    do { \
        if (!(data).checkInterface(this)) { \
            return PERMISSION_DENIED; \
        } \
    } while (false)

descriptor 选择接口对象;CHECK_INTERFACE 检查本次 Parcel 的 interface token。两者分别发生在对象转换和事务分发阶段。

3.3 AIDL边界 ​

Android 17 仍支持手写接口宏,但源码提示优先使用 .aidl 自动生成。AIDL Stub/Proxy 仍遵循相同 local-interface/remote-proxy 模型,业务 Parcel 读写和 transaction code 由生成器提供。

4. 调用路径 ​

4.1 本地直连 ​

若 queryLocalInterface() 返回 BnInterface,接口方法直接调用本地实现,不经过驱动;asBinder() 仍可把它传给其他 Binder API。

4.2 远程代理 ​

若返回空,BpInterface 保存远程 BpBinder,接口方法调用 transact();驱动交付后由 Stub 的 onTransact() 解包并调用实现。

5. 测试边界 ​

5.1 本地对象 ​

源码文件:frameworks/native/libs/binder/tests/binderLibTest.cpp

PromoteLocal、LocalGetExtension 和 AddManagerToManager 分别验证本地引用、扩展对象和 IInterface::asBinder() 的多态入口。

5.2 远程分发 ​

测试 callback 继承 BBinder 并覆写 onTransact();已知 code 返回结果,未知 code 返回 UNKNOWN_TRANSACTION。这证明 Stub/Proxy 分发边界,不覆盖所有 AIDL 语言后端。

5.3 可执行阅读 ​

bash
rg -n "BnInterface|BpInterface|interface_cast|checked_interface_cast|DECLARE_META_INTERFACE" \
  frameworks/native/libs/binder/include/binder/IInterface.h

rg -n "asBinder|onAsBinder|queryLocalInterface" \
  frameworks/native/libs/binder/include/binder/IInterface.h \
  frameworks/native/libs/binder/IInterface.cpp

rg -n "PromoteLocal|LocalGetExtension|AddManagerToManager|UNKNOWN_TRANSACTION" \
  frameworks/native/libs/binder/tests/binderLibTest.cpp

遇到调用没有进入预期远程进程时,先检查是否命中了本地 queryLocalInterface();descriptor 错误要区分普通 cast 的延迟失败和 checked cast 的立即空值;远程路径再检查 Proxy code 与 Stub 的 onTransact()。