千里马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

相关文章

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl

native和static native区别

本文基于Hello JNI  如有疑惑,请看之前几篇文章。 native 与 static native java中 public native String helloJni();public native static String helloJniStatic();1212 JNI中 JNIEXPORT jstring JNICALL Java_com_test_g

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Android 10.0 mtk平板camera2横屏预览旋转90度横屏拍照图片旋转90度功能实现

1.前言 在10.0的系统rom定制化开发中,在进行一些平板等默认横屏的设备开发的过程中,需要在进入camera2的 时候,默认预览图像也是需要横屏显示的,在上一篇已经实现了横屏预览功能,然后发现横屏预览后,拍照保存的图片 依然是竖屏的,所以说同样需要将图片也保存为横屏图标了,所以就需要看下mtk的camera2的相关横屏保存图片功能, 如何实现实现横屏保存图片功能 如图所示: 2.mtk

android应用中res目录说明

Android应用的res目录是一个特殊的项目,该项目里存放了Android应用所用的全部资源,包括图片、字符串、颜色、尺寸、样式等,类似于web开发中的public目录,js、css、image、style。。。。 Android按照约定,将不同的资源放在不同的文件夹中,这样可以方便的让AAPT(即Android Asset Packaging Tool , 在SDK的build-tools目

Android fill_parent、match_parent、wrap_content三者的作用及区别

这三个属性都是用来适应视图的水平或者垂直大小,以视图的内容或尺寸为基础的布局,比精确的指定视图的范围更加方便。 1、fill_parent 设置一个视图的布局为fill_parent将强制性的使视图扩展至它父元素的大小 2、match_parent 和fill_parent一样,从字面上的意思match_parent更贴切一些,于是从2.2开始,两个属性都可以使用,但2.3版本以后的建议使

Android Environment 获取的路径问题

1. 以获取 /System 路径为例 /*** Return root of the "system" partition holding the core Android OS.* Always present and mounted read-only.*/public static @NonNull File getRootDirectory() {return DIR_ANDR