Bootstrap

android机制系列之五 再探Binder机制(Java C++对应融会贯通)

系列目录 https://blog.csdn.net/jzlhll123/article/category/7671581

上一篇文章https://blog.csdn.net/jzlhll123/article/details/80516277 写的不够成熟,很多东西还不够深入。C++也遗留下来了。今日再探。断断续续也花了1-2周业余时间。


LINUX IPC机制

管道,消息队列,共享内存,信号量,Socket

Binder内存拷贝一次,性能好;安全性好check UID/PID。

查看第一部分


Java Binder

接上篇文章android机制系列之二 Binder机制, 如果不了解bindService机制和AIDL,请先展开一些基础学习。

我们实现AIDL或者Binder的时候,几个关键点:

  1. IMyRemote implements IInterface

    添加自定义func()

  2. Stub

    MyStub extends Binder(implments IBinder) implements IMyRemote(IInterface)

    2.1 实现本地服务端func(), 实例对象写具体真实方法;

    2.2 asInterface() 给client用来得到实例对象或者代理对象;

    2.3 onTransact() 从client传递到binder驱动然后回调给server端的代码,通过parcel封装调用本地代码写回结果;

  3. Proxy

    MyProxy implment IMyRemote(IInterface)

    3.1 onServiceConnected返回的是什么?

    附录1。我们拿到的是BinderProxy。

    3.2 onServiceConnected()回到的IBinder,如果同进程queryLocalInterface不为null,就是本地实例;不然,封装成Proxy来使用;即2.2的操作。

    3.3 实现客户端func(),主要通过parcel封装通过transact() (BinderProxy JNI的方法)传递给binder驱动。

  4. Binder

    Stub的父类

    4.1 attachInterface()初始化保存mOwner和mDescriptor,用于Stub初始化;

    4.2 queryLocalInterface() 2.2 asInterface()的返回值;

    4.3 onTransact() Stub实现后,再super调一下;

    4.4 transact() 本地调用则是内部直接调用3.3 onTransact(), 远程客户端是不会使用的

  5. BinderProxy implements IBinder

    这个是真正远程客户端拿到的IBinder对象。

    5.1transact() 对应transactNative() JNI用来3.2;

    5.2queryLocalInterface() 只能return null,因为是远程客户端调用;

  6. Parcel

    parcel在这里面主要2个参数传递,一个传递跨进程数据的任务如writeString8,readCString;

    另外一种,通过JNI writeStrongBinder 和 readStrongBinder()解析获取BinderProxy对象。


C++ Binder

Bp指的是Proxy端,对应的是Client端,调用方;Bn指的是Native端,服务实现的自身。

类图:

这里写图片描述

很多的父类基类管不过来,关注关键的几个类和关键对应java层方法。

  1. IMyRemote : IInterface

    添加func()

    相当于java层的IMyRemote 接口,而且也实现了IInterface;

  2. ProcessState&IPCThreadState

    2.1 ProcessState

    代表使用Binder的进程,进程单例;

    通过IServiceManager::addService在ServiceManager中进行服务的注册

    通过ProcessState::startThreadPool启动线程池

    通过IPCThreadState::joinThreadPool将主线程加入的Binder中。

    2.2 IPCThreadState

    代表了使用Binder的线程,这个类中封装了与Binder驱动通信的逻辑,线程单例;

    BpBinder::transact其实就是调用的这里transact()
    executeCommand回调给onTransact()

  3. BnMyRemote : BnInterface(BBinder), IMyRemote

    也有的代码直接继承BBinder

    onTransact() 从client过来的指令,这边调用真实方法func(),并写入reply;

    相当于java层的Stub的实现。

  4. BpMyRemote:BpInterface, IMyRemote

    4.1 客户端是如何拿到BpMyRemote的呢?

    java层我们知道java binder 第3节中Proxy,是通过bindService等到回调onServiceConnected()的. C++又是如何拿到的?附录2

    4.2 func()实现,通过remote(), 拿到远程Ibinder* mRemote remote()对象,则通过parcel传递参数和返回值参数transact()到binder驱动。

    相当于java层的Proxy。

  5. BpBinder implements Ibinder

    参考附录1的需要牢记的东西。类似java层BinderProxy是通过parcel返回得到的。

    参考附录2我们知道它就是客户端使用的对象。

    内部通过transact()调用Binder驱动再传达到service端。

  6. Parcel

    类似java不多解释。只是注意java层主要是java parcel和JNI。而这里的是frameworks/native/libs/binder/Parcel.cpp


总结

java & C++ Binder 对应
作用javaC++额外信息
MyRemote接口必须继承此项IIterfaceIIterfaceC++提供asInterface用于转换,java靠stub自行编写
接口IXXXIXXX
XXStub/BnXX必须继承此项BinderBnInterface / BBinder / JavaBBinder类似
真实本地服务代码实现接口的func并实现onTransactXXStubBnXX关注它的onTransact()从驱动层回调
客户端拿到的远程对象BinderProxyBpBinder附录1,附录2 关键字parcel readStrong,onServiceConnected,interface_cast asInterface
客户端使用的时候封装的对象XXProxyBpXXX
MyProxy同时继承MyRemote和此项BpInterface (BpRefBase)java层没有设计BpInterface这层了但是c++的remote()其实类似java Proxy的mRemote都是拿到的BinderProxy/BpBinder
封装参数(包含Ibinder对象)传递ParcelParcel
IPCProcessState / IPCThreadState封装线程操作transact onTransact动作

引用:对应图片几张

这里写图片描述

源码阅读心得

这里我们要牢记,查看源码的一些心得了!

  1. 看到某个func() 内部有mRemote/remote()就是客户端拿到BinderProxy/BpBinder;
  2. func() 都需要将descriptor写入Parcel JNI。如果是获取Ibinder则需要额外写入Binder token, 然后mRemote.transact()/remote->transact()就是调用binder驱动;
  3. 看到onTransact()就代表已经在server端,onTransact() switch case会从binder驱动的返回结果中parcel解析出一般数据,如果是获取IBinder就是BinderProxy/BpBinder对象;这中间就会调用真实的func(),最后写回去;
  4. 一般某个文件中会同时有func()2个同名的,要注意区分他的类作用域,一个是Stub/BnXX实体操作;一个是Proxy/BpXX代理操作。

https://blog.csdn.net/jzlhll123/article/details/80865999
回调机制会加深此处的理解。

至此,结束。


附录

附录1 onServiceConnected()拿到的IBinder是什么


bindService流程搜索onServiceConnected相关流程

client->ContextWrapper bindService

—跨进程让AMS去干活–>

ActivityManagerService bindServiceLocked -> ActiveServices requestServiceBindingLocked

—(跨进程binder调用过来的,这里其实是我们的service了)—> ApplicationThreadNative scheduleBindService->

//Proxy
public final void scheduleBindService(IBinder token, Intent intent, boolean rebind,                                                                         
            int processState) throws RemoteException {
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(IApplicationThread.descriptor); ///**
        data.writeStrongBinder(token); //***
        intent.writeToParcel(data, 0);
        data.writeInt(rebind ? 1 : 0);
        data.writeInt(processState);
        mRemote.transact(SCHEDULE_BIND_SERVICE_TRANSACTION, data, null,
                IBinder.FLAG_ONEWAY); //***
        data.recycle();
    }   

先写入

 //onTransact() //Stub
 case SCHEDULE_BIND_SERVICE_TRANSACTION: {                                                                                                               
            data.enforceInterface(IApplicationThread.descriptor);
            IBinder token = data.readStrongBinder(); //***
            Intent intent = Intent.CREATOR.createFromParcel(data);
            boolean rebind = data.readInt() != 0;
            int processState = data.readInt();
            scheduleBindService(token, intent, rebind, processState);
            return true;
        }

可以继续翻阅相关code,原作者这里讲述不是很详细,readStrongBinder是拿到我们服务的descriptor走的Parcel的JNI android_os_Parcel_readStrongBinder , 然后,javaObjectForIBinder 就是构造一个gBinderProxyOffsets class,就是clazz = env->FindClass(kBinderProxyPathName); kBinderProxyPathName = “android/os/BinderProxy” .

->

IBinder binder = s.onBind(data.intent);

—>AMS publishService publishServiceLocked

—>CLIENT 再2次跨进程回去

可以看到,ActivityManagerService最后(1)处调用service的onBind()方法获取service的Stub对象.Stub是binder通信的本地端.

所以拿到的是BinderProxy对象。


附录2 C++客户端如何拿到BpMyRemote的?


由于所有的服务都addService serviceManager中,并写入descriptor和name,并写入IBinder。

客户端首先要从sp bs = defaultServiceManager()->getService(serviceName); checkService , 类似java 使用JNI Parcel,而这里frameworks/native/libs/binder/Parcel.cpp writeStrongBinder flatten_binder 拿到

IBinder *local = binder->localBinder();
if (!local) {
    BpBinder *proxy = binder->remoteBinder();
    if (proxy == NULL) {
        ALOGE("null proxy");
    }    
    const int32_t handle = proxy ? proxy->handle() : 0; 
    obj.type = BINDER_TYPE_HANDLE;
    obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
    obj.handle = handle;
    obj.cookie = 0; 
} else {
    obj.type = BINDER_TYPE_BINDER;
    obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
    obj.cookie = reinterpret_cast<uintptr_t>(local);
}
//readStrongBinder()
const flat_binder_object* flat = in.readObject(false);

if (flat) {
    switch (flat->type) {
        case BINDER_TYPE_BINDER:
            *out = reinterpret_cast<IBinder*>(flat->cookie);
            return finish_unflatten_binder(NULL, *flat, in); 
        case BINDER_TYPE_HANDLE:
            *out = proc->getStrongProxyForHandle(flat->handle);
            return finish_unflatten_binder(
                static_cast<BpBinder*>(out->get()), *flat, in); 
    }    
}    

取出了BpBinder IBinder*。

sp<IServiceManager> sm = defaultServiceManager();
//这里取得就是BpBinder
sp<IBinder> binder = sm->getService(String16("media.camera"));
mCameraService = interface_cast<ICameraService>(binder);

接下来分析下,interface_cast 《T》,就是INTERFACE::asInterface(obj);

    android::sp<I##INTERFACE> I##INTERFACE::asInterface(                \
            const android::sp<android::IBinder>& obj)                   \
    {                                                                   \
        android::sp<I##INTERFACE> intr;                                 \
        if (obj != NULL) {                                              \
            intr = static_cast<I##INTERFACE*>(                          \
                obj->queryLocalInterface(                               \
                        I##INTERFACE::descriptor).get());               \
            if (intr == NULL) {                                         \
                intr = new Bp##INTERFACE(obj);                          \
            }                                                           \
        }                                                               \
        return intr;                                                    \
    }     

采用了模板代码,也是类似java层,queryLocalInterface()是否得到NULL,为NULL,创建一个BpMyRemote出去。所以,这里的remote()拿到的是BpXXX。

这里跟java层类似

java是因为service,client,activityManagerService,肯定service和ams是不同进程的。即使service client是同一个进程,附录1我们知道它肯定必须是跨进程的了所以拿到的是BinderProxy。

然后client拿到以后自己去转换本地对象或者封装Proxy使用。

而C++也是Parcel里面拿到的Ibinder* / sp,然后interface_cast(类似我们aidl文件中的asInterface,而且它的内部实现就叫做asInterface)转成BpBinder或者本地实例。


附录3 基类:RefBase,所有类从此派生; 使用sp,wp引用它的子类*


sp,wp,RefBase基本知识

老罗深入讲解

理解下来,设计RefBase与weakref_impl的相互聚合设计了sp;wp则又多一个weakref_type来当做RefBase引用

1. A:RefBase

RefBase:RefBase()
mRefs(new weakref_impl(this))    
因为设计模式是,weakref_impl跟RefBase是相互有彼此的。因此会传入自己给mRefs

weakref_imp
    mStrong, mWeak 强弱引用计数

析构函数:
1. 没有sp,直接移除mRefs;
2. wp并为0,删除mRefs;
2. sp spA(new A);
sp 构造函数:
A对象指针赋值给m_ptr **直接调用A的incStrong
1. 增加弱引用计数;
2. 增加强引用计数;
3. 第一次onFirstRef

sp析构:
decStrong
1. 减少强引用计数;
2. 是否最后一个,是则释放A对象;
3. 减少弱引用(decWeak)
    3.1 减少,发现还有wp,return
    3.2 根据是否存在sp,否,删除A;是,删除mRefs
    3.3 如果wp,删除A。
wp wpA(new A);
wp 构造函数
A对象指针赋值给m_ptr 比sp多一个m_refs(接口),m_refs是由A createWeak而来
1. 增加弱引用计数;
2. wp的m_Refs得到赋值

wp析构:
就是decWeak上面减少弱引用的操作3.1~3.3
使用层面来讲就简单了。
1. 类A继承RefBase
2. 使用的时候,A* a = new A(); sp<A> spA = a; sp<A> wpA = a; sp<A> spC = new A();
3. 任何一个类,都可以实现它。当他不存在。

2018.07.16补充:
wp提升为sp,就是使用promote()得到强指针。类似java层的weakRefercence,get()以后就是一个实体类对象了。

鸣谢:

  1. Android Binder设计与实现 – 设计篇 https://blog.csdn.net/freshui/article/details/54926111

太提纲挈领,暂时不能深入理解。只提到他对linux IPC的认识。

  1. 理解 Android Binder 机制(二):C++层 https://www.2cto.com/kf/201704/635248.html

本文大部分参考它,结合手中的android6.0 5.1源码学习。

  1. Android深入浅出之Binder机制 http://www.cnblogs.com/innost/archive/2011/01/09/1931456.html

此文通俗,以MediaService为基础学习。少量部分逻辑理不顺结合第二个帖子,和源码分析。

  1. Binder通信——用户空间C/C++层架构与Java层对接点概述 https://blog.csdn.net/a372048518/article/details/75020240

简简单单几张图。但是在学习完本帖回头来看,很有感悟。

  1. bindservice 过程 https://blog.csdn.net/xutao20170209/article/details/72677004

结合源码,辅助一些onServiceConnected流程的理解。

;