千里马android binder深入分析-承上启下的native层(客户端部分)

本文主要是介绍千里马android binder深入分析-承上启下的native层(客户端部分),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

csdn在线学习课程,课程咨询答疑和新课信息:QQ交流群:422901085进行课程讨论

android跨进程通信实战视频课程(加群获取优惠)
image.png
相信大家对上面这一副binder跨进程通信的图都应该比较熟悉,图中的主要有4个角色:
1、客户端(A进程)
2、SerivceManager
3、服务端(B进程)
4、binder驱动
平时一问到binder是怎么跨进程通信的?那么同学们肯定大部分回答:binder最后调到底层还是通过binder驱动来实现跨进程通信的。那么这里就要问一下:请问android中app的java层binder调用,是怎么一步步调用到了底层binder驱动呢?
image.png
这个中间问号部分也就本节要给大家重点分享的部分。
##1.ServiceManager方法调用也是binder跨进程通信
跨进程通信那么必然会出现有两端,即客户端和服务端这样的C/S模型,那么问题是C端怎么知道的S端?那么这就会引出一个重要的角色ServiceManager,ServiceManager本身工作相对简单,其功能:查询和注册服务。客户端通过ServiceManager查询到了注册服务后,才可以进行跨进程通信,这里就是常说的ServiceManager像个DNS服务器。但问题来了,ServiceManager本身自己就是一个独立进程,那么客户端去SerivceManager中查询服务也肯定是跨进程通信,所以本节分析跨进程通信时就拿ServiceManager.getService这个跨进程调用来展开分析。

##2.ServiceManager接口获取分析
平时系统中获取某一个服务,一般都是最后都是ServiceManager.getService这种方式,来看看它的源码:

//ServiceManager.java中public static IBinder getService(String name) {try {//缓存中获取,第一次缓存没有IBinder service = sCache.get(name);if (service != null) {return service;} else {//Binder.allowBlocking仅仅是为了提示是否为阻塞行接口,没有实际干活return Binder.allowBlocking(getIServiceManager().getService(name));}} catch (RemoteException e) {Log.e(TAG, "error in getService", e);}return null;}

这里大家可以看到实际真正干活的是getIServiceManager().getService(name),这里首先来看getIServiceManager()方法:

//ServiceManager.java中private static IServiceManager getIServiceManager() {//这里有一个sServiceManager来缓存,但是第一次肯定还为nullif (sServiceManager != null) {return sServiceManager;}// 这里用调用了ServiceManagerNative类的asInterface方法和BinderInternal.getContextObject()方法sServiceManager = ServiceManagerNative.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));return sServiceManager;}

这里又调用到了ServiceManagerNative类的asInterface方法这方法本身应该在应用写aidl生成的java文件中也经常见到:

//ServiceManagerNative.javastatic public IServiceManager asInterface(IBinder obj){if (obj == null) {return null;}IServiceManager in =(IServiceManager)obj.queryLocalInterface(descriptor);if (in != null) {return in;}//其实就是 new ServiceManagerProxy代理对象,参数是IBinder 类型的对象return new ServiceManagerProxy(obj);}

它的核心就是new ServiceManagerProxy,但是真正干活的还是IBinder参数,参数getIServiceManager方法中可以看出获取其实是核心是BinderInternal.getContextObject()方法,但getContextObject是个native的方法,所以就得看对应的jni方法:

//android_util_Binder.cpp
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{//调用getContextObject构造出一个IBinder类型对象sp<IBinder> b = ProcessState::self()->getContextObject(NULL);//把C++层面的IBinder类型对象转成java成的IBinder对象return javaObjectForIBinder(env, b);
}

这里一共才2行代码,那就一行行深入分析,首先来看ProcessState::self()->getContextObject:

//ProcessState.cpp
//这里直接就没有使用传递来的参数,本身传递来的也是个NULL
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{return getStrongProxyForHandle(0);
}

ProcessState这里又调用了getStrongProxyForHandle方法,而且传递了一个参数0:

//ProcessState.cpp
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{sp<IBinder> result;AutoMutex _l(mLock);//根据handle查询handle_entry对象,handle没有初始化过则构造一个handle_entry空壳出来handle_entry* e = lookupHandleLocked(handle);//因为handle_entry已经有了,只不过是一个空壳里面内容都为空if (e != NULL) {IBinder* b = e->binder;//这里e->binder第一次肯定为空if (b == NULL || !e->refs->attemptIncWeak(this)) {//如果传入handle为0if (handle == 0) {Parcel data;//这里会调用 IPCThreadState::self()->transact方法来传递一个PING_TRANSACTION,主要目的//是为了试探ServiceManager是否还存活status_t status = IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0);if (status == DEAD_OBJECT)return NULL;}//利用handle值构造出一个BpBinder对象,最后返回也是这个BpBinder对象b = new BpBinder(handle); e->binder = b;if (b) e->refs = b->getWeakRefs();result = b;} else {result.force_set(b);e->refs->decWeak(this);}}return result;
}

到这里我们就看到了最后其实就是new BpBinder(handle);,这里handle就是我们经常说 “引用”,即对远程对象的引用,这里因为ServiceManager的的特殊性,所以android系统中默认把任何进程对ServiceManager的引用handle值都设置为固定的0。那么到这里就分析完了ProcessState::self()->getContextObject(NULL)了,返回的其实就是个new BpBinder(0),但是这里还是C++层的BpBinder对象,而我们是要获取Java层的IBinder类型的对象,那接下来就要分析上面剩下的 javaObjectForIBinder(env, b):

//android_util_Binder.cpp
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{//省略部分//这里构造出了gBinderProxyOffsets的java对象object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);if (object != NULL) {// The proxy holds a reference to the native object.//这里吧val这个c++的对象指针设置到gBinderProxyOffsets.mObject这个java属性中env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());val->incStrong((void*)javaObjectForIBinder);// The native object needs to hold a weak reference back to the// proxy, so we can retrieve the same proxy if it is still active.jobject refObject = env->NewGlobalRef(env->GetObjectField(object, gBinderProxyOffsets.mSelf));//前面只是基于val建立对应的java对象,及java对象可以直接拿到val指针//但是我们进程也需要val指针可以获取到java对象val->attachObject(&gBinderProxyOffsets, refObject,jnienv_to_javavm(env), proxy_cleanup);//省略部分}return object;
}

这里面gBinderProxyOffsets相关的是什么呢?这里再来看它的对应初始化代码:

//android_util_Binder.cpp
const char* const kBinderProxyPathName = "android/os/BinderProxy";static int int_register_android_os_BinderProxy(JNIEnv* env)
{jclass clazz = FindClassOrDie(env, "java/lang/Error");gErrorOffsets.mClass = MakeGlobalRefOrDie(env, clazz);//通过jni找出android/os/BinderProxy这个java类clazz = FindClassOrDie(env, kBinderProxyPathName);gBinderProxyOffsets.mClass = MakeGlobalRefOrDie(env, clazz);//通过jni找出android/os/BinderProxy这个java类的构造方法gBinderProxyOffsets.mConstructor = GetMethodIDOrDie(env, clazz, "<init>", "()V");gBinderProxyOffsets.mSendDeathNotice = GetStaticMethodIDOrDie(env, clazz, "sendDeathNotice","(Landroid/os/IBinder$DeathRecipient;)V");
///通过jni找出android/os/BinderProxy这个java类的mObject 属性gBinderProxyOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
//省略部分
}

这里面可以看出其实都是通过jni相关方法,让native的相关变量与java层的BinderProxy相绑定一起,到这里就可以看出在BinderProxy是在native层面被构造出来的,设置了它的mObject值就是c++层的BpBinder指针,那么到这里就已经分析完了开始的getIServiceManager方法,这个时候就有了ServiceManager的Proxy代理,总结以下几点核心部分:
1、ServiceManager特殊,它的handle固定为0,native层直接进行new BpBinder(0)
2、native层面的BpBinder需要转换成Java层的BinderProxy,这个需要native通过jni构造出java层对应的BinderProxy,且BinderProxy的mObject值就是BpBinder(0)的指针,mObject也是Java层面与native层面进行互通的最关键纽带

image.png

##3.getService获取远程服务代理的源码分析
首先来看ServiceManagerNative.java中的ServiceManagerProxy类中的getService

//ServiceManagerNative.javapublic IBinder getService(String name) throws RemoteException {//准备输入和返回Parcel对象Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeInterfaceToken(IServiceManager.descriptor);data.writeString(name);//这里已经知道mRemote其实就是前面分析构造出来的BinderProxy对象mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);//读取返回的IBinder数据IBinder binder = reply.readStrongBinder();reply.recycle();data.recycle();return binder;}

这里其实核心就是 mRemote.transact和 reply.readStrongBinder两个部分,这里先来分析transact部分,
这个mRemote对象是BinderProxy对象,所以来看BinderProxy类的transact方法,注意这里BinderProxy没有单独java文件,它在Binder.java文件中:

//Binder.java
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//省略try {return transactNative(code, data, reply, flags);} finally {//省略}}

这里其实又是调用了一个native方法transactNative,注意这里transactNative到android_os_BinderProxy_transact是有一个转换关系的,这个在分析android系统源码时候很常见:
image.png

//android_util_Binder.cpp
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{//省略部分
//这里从BinderProxy类型的java对象中获取mObject属性,前面分析过它的值就是BpBinder对象指针IBinder* target = (IBinder*)env->GetLongField(obj, gBinderProxyOffsets.mObject);//省略部分
//调用BpBinder对象的transact方法status_t err = target->transact(code, *data, reply, flags);
//省略部分
}

这里就可以看出前面讲的mObject为啥是“重要纽带”,那么接下来看看BpBinder的transact方法:

//BpBinder.cpp
status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{if (mAlive) {//本质是调用了IPCThreadState的transact方法status_t status = IPCThreadState::self()->transact(mHandle, code, data, reply, flags);if (status == DEAD_OBJECT) mAlive = 0;return status;}return DEAD_OBJECT;
}

那么接下来看看IPCThreadState的transact方法:

//IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle,uint32_t code, const Parcel& data,Parcel* reply, uint32_t flags)
{//省略部分if (err == NO_ERROR) {//准备好与驱动通信的数据格式结构体err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);}
//如果不是TF_ONE_WAY即oneway类型的调用if ((flags & TF_ONE_WAY) == 0) {if (reply) {//这里需要等待答复err = waitForResponse(reply);} else {Parcel fakeReply;err = waitForResponse(&fakeReply);}} else {
//即oneway类型的调用err = waitForResponse(NULL, NULL);}return err;
}

这里面分为两个大块writeTransactionData和waitForResponse,writeTransactionData主要目的就是把数据组装成和驱动一致的binder_transaction_data结构体,然后在是waitForResponse:

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{uint32_t cmd;int32_t err;while (1) {//与驱动进行通信,这里主要通过相关ioctl相关方法if ((err=talkWithDriver()) < NO_ERROR) break;if (mIn.dataAvail() == 0) continue;//得到驱动返回cmdcmd = (uint32_t)mIn.readInt32();switch (cmd) {case BR_REPLY:{binder_transaction_data tr;//得到驱动返回数据err = mIn.read(&tr, sizeof(tr));//省略部分}
//省略部分
}

可以看出主要就是talkWithDriver与驱动进行通信并且一直等待返回驱动相应结果,这里涉及驱动部分就不给大家在这里讲解,大家把驱动暂时当作黑盒既可以。
这个地方简单看看ServiceManager这个服务端,面对客户端的getService做了什么呢?
在servicemanager代码中如果接受到客户端请求,最后都会调用到svcmgr_handler来负责处理:

int svcmgr_handler(struct binder_state *bs,struct binder_transaction_data *txn,struct binder_io *msg,struct binder_io *reply)
{struct svcinfo *si;uint16_t *s;size_t len;uint32_t handle;uint32_t strict_policy;int allow_isolated;
//..省略部分switch(txn->code) {case SVC_MGR_GET_SERVICE:case SVC_MGR_CHECK_SERVICE://根据获取到service的names = bio_get_string16(msg, &len);if (s == NULL) {return -1;}//根据name从已经存在集合中找出对应的handle即索引handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);if (!handle)break;//把handle获取到了后写入replybio_put_ref(reply, handle);return 0;
//..省略部分default:ALOGE("unknown code %d\n", txn->code);return -1;}bio_put_uint32(reply, 0);return 0;
}

这里面根据name从集合中寻找出了服务的handle,注意这里只是找出了一个int类型的handle,然后在调用bio_put_ref进行写入reply,再通过binder驱动传递给客户端,这里看看bio_put_ref方法:

void bio_put_ref(struct binder_io *bio, uint32_t handle)
{
//构造出一个binder结构体struct flat_binder_object *obj;if (handle)obj = bio_alloc_obj(bio);elseobj = bio_alloc(bio, sizeof(*obj));if (!obj)return;
//给flat_binder_object 结构体对应属性赋值obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;//设置类型为BINDER_TYPE_HANDLEobj->type = BINDER_TYPE_HANDLE;//把handle赋值给flat_binder_object 的handle属性obj->handle = handle;obj->cookie = 0;
}

这里大概我们就清除了原来servicemanager这一边做的事情就是根据name查找到这个name对应的handle,再把这个handle值包装到flat_binder_object 结构体中,再把结构体传递到驱动,驱动再传递到客户端,具体传递过程就不在这一节详细讲述,分析这里相当于客户端调用transact方法后,服务端servicemanager的数据经过binder驱动进行了返回。但是返回后的数据是怎么一步步又变成IBinder对象的呢?这里我们接下来看 IBinder getService(String name)的 IBinder binder = reply.readStrongBinder()方法了,这里看着就是从Parcel类型的reply中调用了readStrongBinder方法就返回了IBinder对象。注意啦Parcel这里是java层的,但是最后调用它又会调用到Parcel.cpp中,先看Parcel.java:

//Parcel.javapublic final IBinder readStrongBinder() {return nativeReadStrongBinder(mNativePtr);}

这里其实就只调用了nativeReadStrongBinder方法,它又是一个jni方法,所以这个方法后就会调用到native层,具体看一下这个方法在native层映射成了哪个方法:

//android_os_Parcel.cpp{"nativeReadStrongBinder",    "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},

这里被映射成了android_os_Parcel_readStrongBinder方法:

//android_os_Parcel.cpp
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if (parcel != NULL) {return javaObjectForIBinder(env, parcel->readStrongBinder());}return NULL;
}

这里又会先调用parcel->readStrongBinder()获取native层的BpBinder对象作为参数,然后再调用javaObjectForIBinder转为java对象,这个前面已经讲过了哦。

//Parcel.cpp
sp<IBinder> Parcel::readStrongBinder() const
{sp<IBinder> val;
//这里又调用了readNullableStrongBinderreadNullableStrongBinder(&val);return val;
}

接下来看看readNullableStrongBinder:

status_t Parcel::readNullableStrongBinder(sp<IBinder>* val) const
{return unflatten_binder(ProcessState::self(), *this, val);
}

这里又调用到了unflatten_binder:

status_t unflatten_binder(const sp<ProcessState>& proc,const Parcel& in, sp<IBinder>* out)
{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://这里核心又是获取了handle后调用getStrongProxyForHandle*out = proc->getStrongProxyForHandle(flat->handle);return finish_unflatten_binder(static_cast<BpBinder*>(out->get()), *flat, in);}}return BAD_TYPE;
}

这里我们前面分析servicemanager时候就知道type类型为BINDER_TYPE_HANDLE,所以根据flat->handle的handle值调用getStrongProxyForHandle,这个getStrongProxyForHandle方法前面已经分析过他就是new BpBinder(handle),所以这里就明白了这个getService方法最后也是根据servicemanager返回的handle(这里大家要注意这个客户端的handle值和servicemanager中写入的handle值不一定相等,而是各种进程独立,但是在binder驱动可以关联上,具体区别会binder驱动中进行剖析)。
image.png
getService部分的总结:
1、获取远端Serivce整体依然是new BpBinder(handle) --> javaObjectForIBinder变成BinderProxy的过程
2、但这里的handle不再像ServiceManager那么简单的直接写死为0,而是需要把name传递到SeriveManager查询对应的服务,ServiceManager把对应服务flat_binder_object对象通过binder驱动传递到客户端,客户端再从flat_binder_object获取handle

至此我们就分析完了客户端发起一个调用到获取驱动数据返回的整个过程,那么接下要分析的是作为一个服务端即Binder实现端。

这篇关于千里马android binder深入分析-承上启下的native层(客户端部分)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/300664

相关文章

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开

android 免费短信验证功能

没有太复杂的使用的话,功能实现比较简单粗暴。 在www.mob.com网站中可以申请使用免费短信验证功能。 步骤: 1.注册登录。 2.选择“短信验证码SDK” 3.下载对应的sdk包,我这是选studio的。 4.从头像那进入后台并创建短信验证应用,获取到key跟secret 5.根据技术文档操作(initSDK方法写在setContentView上面) 6.关键:在有用到的Mo

android一键分享功能部分实现

为什么叫做部分实现呢,其实是我只实现一部分的分享。如新浪微博,那还有没去实现的是微信分享。还有一部分奇怪的问题:我QQ分享跟QQ空间的分享功能,我都没配置key那些都是原本集成就有的key也可以实现分享,谁清楚的麻烦详解下。 实现分享功能我们可以去www.mob.com这个网站集成。免费的,而且还有短信验证功能。等这分享研究完后就研究下短信验证功能。 开始实现步骤(新浪分享,以下是本人自己实现

Android我的二维码扫描功能发展史(完整)

最近在研究下二维码扫描功能,跟据从网上查阅的资料到自己勉强已实现扫描功能来一一介绍我的二维码扫描功能实现的发展历程: 首页通过网络搜索发现做android二维码扫描功能看去都是基于google的ZXing项目开发。 2、搜索怎么使用ZXing实现自己的二维码扫描:从网上下载ZXing-2.2.zip以及core-2.2-source.jar文件,分别解压两个文件。然后把.jar解压出来的整个c

android 带与不带logo的二维码生成

该代码基于ZXing项目,这个网上能下载得到。 定义的控件以及属性: public static final int SCAN_CODE = 1;private ImageView iv;private EditText et;private Button qr_btn,add_logo;private Bitmap logo,bitmap,bmp; //logo图标private st

Android多线程下载见解

通过for循环开启N个线程,这是多线程,但每次循环都new一个线程肯定很耗内存的。那可以改用线程池来。 就以我个人对多线程下载的理解是开启一个线程后: 1.通过HttpUrlConnection对象获取要下载文件的总长度 2.通过RandomAccessFile流对象在本地创建一个跟远程文件长度一样大小的空文件。 3.通过文件总长度/线程个数=得到每个线程大概要下载的量(线程块大小)。

时间服务器中,适用于国内的 NTP 服务器地址,可用于时间同步或 Android 加速 GPS 定位

NTP 是什么?   NTP 是网络时间协议(Network Time Protocol),它用来同步网络设备【如计算机、手机】的时间的协议。 NTP 实现什么目的?   目的很简单,就是为了提供准确时间。因为我们的手表、设备等,经常会时间跑着跑着就有误差,或快或慢的少几秒,时间长了甚至误差过分钟。 NTP 服务器列表 最常见、熟知的就是 www.pool.ntp.org/zo

高仿精仿愤怒的小鸟android版游戏源码

这是一款很完美的高仿精仿愤怒的小鸟android版游戏源码,大家可以研究一下吧、 为了报复偷走鸟蛋的肥猪们,鸟儿以自己的身体为武器,仿佛炮弹一样去攻击肥猪们的堡垒。游戏是十分卡通的2D画面,看着愤怒的红色小鸟,奋不顾身的往绿色的肥猪的堡垒砸去,那种奇妙的感觉还真是令人感到很欢乐。而游戏的配乐同样充满了欢乐的感觉,轻松的节奏,欢快的风格。 源码下载

Android SurfaceFlinger——图形内存分配器(十一)

前面的文章中的图层合成器(HWC),这里我们接着看一下 SurfaceFlinger 中的另一个重要服务——图形内存分配器。 一、简介         android.hardware.graphics.allocator@2.0 是 Android 系统中硬件抽象层(HAL)的一个组件,专门用于图形内存的分配和管理。它是 SurfaceFlinger 在处理图形数据时所依赖的

【zabbix】zabbix客户端配置

1、部署zabbix客户端 #zabbix 5.0 版本采用 golang 语言开发的新版本客户端 agent2 。#zabbix 服务端 zabbix_server 默认使用 10051 端口,客户端 zabbix_agent2 默认使用 10050 端口。systemctl disable --now firewalldsetenforce 0hostnamectl set-host