首页> 资讯 > > 详情

Android Q 打通应用层到 HAL 层 -- ( JNI 服务和 AIDL 服务实现)

2023-03-06 15:52:53 来源:程序员客栈

先回顾一下上一篇关于 HIDL 的内容:


(资料图片仅供参考)

什么是HIDL

HIDL 全称为HAL interface definition language(发音为“hide-l”)是用于指定 HAL 和其用户之间的接口的一种接口描述语言 (IDL),Android O开始引入了HIDL这个概念。

HIDL和应用层AIDL差不多,AIDL常用于连接App和Framework,HIDL则是用来连接Framework和HAL,AIDL使用Binder通信,HIDL则使用HwBinder通信,他们都是通过Binder驱动完成通信,只不过两个Binder域不一样

为什么需要 HIDL

目前Android系统生态是几乎每年google都会出一个Android大版本,而普通手机用户一部手机一般要用两三年,所以你会发现尽管Android系统已经升级到了10,马上11出来了,然后还是有很多用户依然使用的是Android 5,6,7等版本。

对普通用户来说如果不更换手机就很难跟上Android版本,这是因为OEM厂商在同一设备上进行系统升级需要花费时间金钱成本很高,导致他们不愿意升级,成本高的原因是Android O之前Android Framework的升级需要OEM将HAL也进行对应升级,Framework和HAL是一起被编译成system.img,它们存在高耦合。

针对这种情况google在Android O中引入了Treble计划,Treble的目的就是解耦Framework和HAL,就是通过HIDL来实现,Framework不再直接调用HAL,而是通过HIDL来间接使用HAL模块,每个HAL模块都可以对应一个HIDL服务,Framework层通过HwBinder创建HIDL服务。

通过HIDL服务来获取HAL相关模块继而打开HAL下的设备,而最终HAL也从system.img中分离,被编进一个单独的分区vendor.img,从而简化了Android系统升级的影响与难度。

这篇文章要写的是JNI服务和framework层AIDL服务实现,由AIDL服务调用JNI层的服务的函数,为了提供给上层APP使用。

JNI 服务和 AIDL 服务实现

同样我们参照系统其他服务的方式来写,来到frameworks/base/services/core/jni目录下,这下面有许多JNI的服务,创建cpp文件com_android_server_am_HelloService.cpp,为什么要叫这个名字,因为等下我们实现的AIDL服务包名为"com.android.server.am"

#include#include#include#include#includeusingandroid::sp;usingandroid::hardware::hello_hidl::V1_0::IHello;namespaceandroid{sphw_device;staticvoidandroid_server_am_HelloService_nativeInit(JNIEnv*/*env*/,jobject/*clazz*/){ALOGW("hello...android_server_am_HelloService_nativeInit.....");hw_device=IHello::getService();if(hw_device==nullptr){ALOGW("hello...failedtogetIHelloservice");return;}ALOGW("hello...successtogetIHelloservice");}staticjintandroid_server_am_HelloService_nativeAdd(JNIEnv*env,jobject/*clazz*/,jinta,jintb){ALOGW("hello...android_server_am_HelloService_nativeAdd.....");uint32_ttotal=hw_device->addition_hidl(a,b);returnreinterpret_cast(total);}staticconstJNINativeMethodgMethods[]={{"nativeAdd","(II)I",(void*)android_server_am_HelloService_nativeAdd},{"nativeInit","()V",(void*)android_server_am_HelloService_nativeInit},};intregister_android_server_am_HelloService(JNIEnv*env){returnjniRegisterNativeMethods(env,"com/android/server/am/HelloService",gMethods,NELEM(gMethods));}};//namespaceandroid

这个JNI服务中定义两个函数,android_server_am_HelloService_nativeAdd 和android_server_am_HelloService_nativeInit,这两个函数是提供给framework层AIDL服务调用的,添加了一些log方便后面验证,对应等下要实现的AIDL服务中的nativeAdd和nativeInit

android_server_am_HelloService_nativeInit函数作用是获取我们上一篇文章实现的HIDL服务IHello

android_server_am_HelloService_nativeAdd函数作用是调用HIDL服务中定义的addition_hidl函数

JNI服务中的函数想要被framework调用还需要通过register_android_server_am_HelloService函数进行注册,"com/android/server/am/HelloService"这个是等下我们要实现的framework层的AIDL服务

接着需要将这个自定义JNI服务添加到onload.cpp中开机注册,打开frameworks/base/services/core/jni/onload.cpp,添加如下代码:

在这里插入图片描述

接着需要修改Android.bp文件,打开frameworks/base/services/core/jni/Android.bp,添加如下代码:

在这里插入图片描述
主要就是将新增文件添加进编译和添加hello_hidl的依赖库,JNI服务已经创建好了,接着,需要创建framework层AIDL服务

首先到frameworks/base/core/java/android/app/目录下创建IHelloService.aidl文件:

packageandroid.app;interfaceIHelloService{intadd(inta,intb);}

想要编译这个文件还需要修改Android.bp,在frameworks/base/Android.bp中添加如下代码:

在这里插入图片描述
然后就可以编译了,mmm frameworks/base
在这里插入图片描述
编译成功后我们可以去out目录下看看IHelloService.aidl编出来的IHelloService.java文件:

/**Thisfileisauto-generated.DONOTMODIFY.*/packageandroid.app;publicinterfaceIHelloServiceextendsandroid.os.IInterface{/**DefaultimplementationforIHelloService.*/publicstaticclassDefaultimplementsandroid.app.IHelloService{@Overridepublicintadd(inta,intb)throwsandroid.os.RemoteException{return0;}@Overridepublicandroid.os.IBinderasBinder(){returnnull;}}/**Local-sideIPCimplementationstubclass.*/publicstaticabstractclassStubextendsandroid.os.Binderimplementsandroid.app.IHelloService{privatestaticfinaljava.lang.StringDESCRIPTOR="android.app.IHelloService";/**Constructthestubatattachittotheinterface.*/publicStub(){this.attachInterface(this,DESCRIPTOR);}/***CastanIBinderobjectintoanandroid.app.IHelloServiceinterface,*generatingaproxyifneeded.*/publicstaticandroid.app.IHelloServiceasInterface(android.os.IBinderobj){if((obj==null)){returnnull;}android.os.IInterfaceiin=obj.queryLocalInterface(DESCRIPTOR);if(((iin!=null)&&(iininstanceofandroid.app.IHelloService))){return((android.app.IHelloService)iin);}returnnewandroid.app.IHelloService.Stub.Proxy(obj);}@Overridepublicandroid.os.IBinderasBinder(){returnthis;}/**@hide*/publicstaticjava.lang.StringgetDefaultTransactionName(inttransactionCode){switch(transactionCode){caseTRANSACTION_add:{return"add";}default:{returnnull;}}}/**@hide*/publicjava.lang.StringgetTransactionName(inttransactionCode){returnthis.getDefaultTransactionName(transactionCode);}@OverridepublicbooleanonTransact(intcode,android.os.Parceldata,android.os.Parcelreply,intflags)throwsandroid.os.RemoteException{java.lang.Stringdescriptor=DESCRIPTOR;switch(code){caseINTERFACE_TRANSACTION:{reply.writeString(descriptor);returntrue;}caseTRANSACTION_add:{data.enforceInterface(descriptor);int_arg0;_arg0=data.readInt();int_arg1;_arg1=data.readInt();int_result=this.add(_arg0,_arg1);reply.writeNoException();reply.writeInt(_result);returntrue;}default:{returnsuper.onTransact(code,data,reply,flags);}}}privatestaticclassProxyimplementsandroid.app.IHelloService{privateandroid.os.IBindermRemote;Proxy(android.os.IBinderremote){mRemote=remote;}@Overridepublicandroid.os.IBinderasBinder(){returnmRemote;}publicjava.lang.StringgetInterfaceDescriptor(){returnDESCRIPTOR;}@Overridepublicintadd(inta,intb)throwsandroid.os.RemoteException{android.os.Parcel_data=android.os.Parcel.obtain();android.os.Parcel_reply=android.os.Parcel.obtain();int_result;try{_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(a);_data.writeInt(b);boolean_status=mRemote.transact(Stub.TRANSACTION_add,_data,_reply,0);if(!_status&&getDefaultImpl()!=null){returngetDefaultImpl().add(a,b);}_reply.readException();_result=_reply.readInt();}finally{_reply.recycle();_data.recycle();}return_result;}publicstaticandroid.app.IHelloServicesDefaultImpl;}staticfinalintTRANSACTION_add=(android.os.IBinder.FIRST_CALL_TRANSACTION+0);publicstaticbooleansetDefaultImpl(android.app.IHelloServiceimpl){if(Stub.Proxy.sDefaultImpl==null&&impl!=null){Stub.Proxy.sDefaultImpl=impl;returntrue;}returnfalse;}publicstaticandroid.app.IHelloServicegetDefaultImpl(){returnStub.Proxy.sDefaultImpl;}}publicintadd(inta,intb)throwsandroid.os.RemoteException;}

其实这个文件和我们用Android Studio创建AIDL服务生成的中间文件差不多的,都是统一的AIDL框架:有一个Stub抽象类,继承IBinder,实现IHelloService,还有一个代理类Proxy继承IHelloService,通过asInterface方法来获取

了解了IHelloService.aidl生成的一个中间文件,我们再实现HelloService.java的时候就清晰了,在frameworks/base/services/core/java/com/android/server/am/目录下创建HelloService.java文件:

packagecom.android.server.am;importandroid.app.IHelloService;publicclassHelloServiceextendsIHelloService.Stub{publicHelloService(){android.util.Log.d("dongjiao","StartHelloService...");nativeInit();}@Overridepublicintadd(inta,intb){android.util.Log.d("dongjiao","HelloServiceadd()...a=:"+a+",b=:"+b);returnnativeAdd(a,b);}privatestaticnativevoidnativeInit();privatestaticnativeintnativeAdd(inta,intb);}

这个HelloService继承自IHelloService.Stub,它作为Binder的具体实现端,里面定义了两个native方法,这两个方法和之前创建的JNI服务中的那两个函数一一对应,HelloService构造方法中调用nativeInit,add方法提供给外界访问,它里面调用nativeAdd

好了这个AIDL服务已经创建好了,接着我们到SystemServer中去添加开机注册此服务的代码,打开frameworks/base/services/java/com/android/server/SystemServer.java随便在其他某个服务下添加如下代码:

在这里插入图片描述
SystemServer启动时就会将HelloService添加到ServiceManager,名字自定义为”helloService“,代码已经添加完毕,总的修改就是如下部分:
在这里插入图片描述

开始进行编译 mmm frameworks/base/

编译成功后需要将/system/framework/下所有文件push进手机 adb push out/target/product/TOKYO_TF_arm64/system/framework/ /system/

另外定义的JIN服务相关代码会被编译到libandroid_servers.so这个so中,还需push这个so adb push out/target/product/TOKYO_TF_arm64/system/lib64/libandroid_servers.so /system/lib64/

重启手机发现了如下错误:

在这里插入图片描述
这是因为缺少了SELinux权限,实际开发中添加自定义AIDL,HIDL服务都需要SELinux权限,我们这里重点不在SELinux,所以采用规避方案,直接将SELinux关闭adb shell setenforce 0,这需要root权限

我们发现如下log,这是因为我的HIDL服务还没启动

在这里插入图片描述

启动一下前一篇文章实现的HIDL服务:

在这里插入图片描述
我们重新将SystemServer杀掉,为了再看一遍log:
在这里插入图片描述

04-1423:58:58.76092799279E:hello_hidlserviceisinitsuccess…04-1423:58:58.76192799279IServiceManagement:Registeredandroid.hardware.hello_hidl@1.0::IHellodefault(startdelayof52ms)04-1423:58:58.76292799279IServiceManagement:Removingnamespacefromprocessnameandroid.hardware.hello_hidl@1.0-servicetohello_hidl@1.0-service.04-1423:58:58.76292799279Iandroid.hardware.hello_hidl@1.0-service:Registrationcompleteforandroid.hardware.hello_hidl@1.0::IHellodefault.

这一段代表的是HIDL服务的启动注册

04-1423:59:38.881328328IServiceManager:service‘helloService’died04-1423:59:41.5821403714037W:JNI_OnLoad…hello…04-1423:59:41.5941403714037W:register_android_server_am_HelloService…04-1423:59:44.9781403714037Ddongjiao:SystemServer…addService(helloService)…04-1423:59:44.9781403714037Ddongjiao:StartHelloService…04-1423:59:44.9781403714037W:HelloService…nativeInit…04-1423:59:44.9801403714037W:successtogetIHelloservice"

这一段代表AIDL服务的启动注册

可以看到关闭了SELinux权限之后,HIDL和AIDL服务都注册成功了,并且在AIDL服务初始化时也能成功通过JNI服务获取到HIDL服务了,后面如果调用HIDL的addition_hidl函数也应该是很简单了。

其实整个调用逻辑还是比较清晰的,从AIDL到JNI到HIDL,在AIDL服务初始化中调用JNI服务的nativeInit函数,JNI服务的nativeInit函数中获取到HIDL服务,之后就可以随意调用HIDL的函数了。

原文链接: https://blog.csdn.net/qq_34211365/article/details/105642229

关键词: 插入图片 这是因为 中间文件

上一篇:晁盖 热点评
下一篇:最后一页