AndroidO之后,使用hidl来实现Framework和 HAL层之间的解耦。这篇文章就简要的介绍一下HIDL的使用方式。
HIDL,全称HAL interface definition language。关于它的详细介绍可以参考Android官网。
一般情况下,都是在
hardware/interfaces中创建hal文件,但是在system/hardware/interfaces/和frameworks/hardware/interfaces/目录下也有定义。
以万能的 Hello World为例,首先在下创建helloworld文件夹,然后在helloworld文件夹下创建一个表示版本的文件夹,这里我们创建的是1.0,最后在该文件夹下创建一个IHelloWorld.hal文件。
目录结构如下:
// hardware/interfaces
helloworld
│   └── 1.0
│       └── IHelloWorld.hal
//IHelloWorld.hal
package android.hardware.helloworld@1.0;
interface IHelloWorld{
    helloworld() generates(string retStr);
};
对于hal文件,不同目录下的文件,对应的package也不同,具体参照下表:
| Package Prefix | Location | Interface Types | 
|---|---|---|
android.hardware.* | 
hardware/interfaces/* | 
HAL | 
android.frameworks.* | 
frameworks/hardware/interfaces/* | 
frameworks/ related | 
android.system.* | 
system/hardware/interfaces/* | 
system/ related | 
android.hidl.* | 
system/libhidl/transport/* | 
core | 
此外,目录到版本目录前,/转换成.,版本信息转换为@major.minor。比如我们的helloword,其hal文件目录为hardware/interfaces/helloworld/1.0/,转换为package就是android.hardware.helloword@1.0。
这些文件格式可以参考AOSP中已经存在的模块。
接下来就是编译hal文件了。
在shell中输入如下指令:
PACKAGE=android.hardware.helloworld@1.0	# 对应hal文件中的package
LOC=hardware/interfaces/helloworld/1.0/default/	#指定 编译后的成果物输出路径
./out/host/linux-x86/bin/hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces     -randroid.hidl:system/libhidl/transport $PACKAGE		#生成 C++文件。
./out/host/linux-x86/bin/hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces     -randroid.hidl:system/libhidl/transport $PACKAGE #生成 编译脚本
此时的目录结构如下:
helloworld/
└── 1.0
    ├── IHelloWorld.hal
    └── default
        ├── Android.bp
        ├── HelloWorld.cpp
        └── HelloWorld.h
先看一下生成的Android.bp:
cc_library_shared {
    name: "android.hardware.helloworld@1.0-impl",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "HelloWorld.cpp",
    ],  
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "android.hardware.helloworld@1.0",
    ],  
}

对于Passthrough mode和Binderized Mode,这两个实现都依赖于android.hardware.*@*-impl.so。
接下来执行 ./hardware/interfaces/update-makefiles.sh,该命令执行后,回生成几个bp文件,具体编译规则大家自己去看生成的文件。
此时的目录结构如下:
hardware/interfaces/helloworld/
├── 1.0
│   ├── Android.bp
│   ├── Android.mk
│   ├── default
│   │   ├── Android.bp
│   │   ├── HelloWorld.cpp
│   │   ├── HelloWorld.h
│   │   └── main.cpp
│   └── IHelloWorld.hal
└── Android.bp
最后,执行mmm hardware/interfaces/helloworld/1.0/编译Helloworld。
编译后的成果物位于out/soong/.intermediates/hardware/interfaces/helloworld/1.0.
目录结构如下:
.
├── android.hardware.helloworld@1.0
│   ├── android_arm64_armv8-a_cortex-a53_shared_core
│   │   ├── android.hardware.helloworld@1.0.so
│   ├── android_arm64_armv8-a_cortex-a53_static_core
│   │   ├── android.hardware.helloworld@1.0.a
│   ├── android_arm_armv7-a-neon_cortex-a15_shared_core
│   │   ├── android.hardware.helloworld@1.0.so
│   └── android_arm_armv7-a-neon_cortex-a15_static_core
│       ├── android.hardware.helloworld@1.0.a
├── android.hardware.helloworld@1.0_genc++
│   └── gen
│       └── android
│           └── hardware
│               └── helloworld
│                   └── 1.0
│                       └── HelloWorldAll.cpp
├── android.hardware.helloworld@1.0_genc++_headers
│   └── gen
│       └── android
│           └── hardware
│               └── helloworld
│                   └── 1.0
│                       ├── BnHwHelloWorld.h
│                       ├── BpHwHelloWorld.h
│                       ├── BsHelloWorld.h
│                       ├── IHelloWorld.h
│                       └── IHwHelloWorld.h
└── default
    └── android.hardware.helloworld@1.0-impl
        ├── android_arm64_armv8-a_cortex-a53_shared_core
        │   ├── android.hardware.helloworld@1.0-impl.so
        │   └── obj
        │       └── hardware
        │           └── interfaces
        │               └── helloworld
        │                   └── 1.0
        │                       └── default
        │                           ├── HelloWorld.o
        │                           └── HelloWorld.o.d
        └── android_arm_armv7-a-neon_cortex-a15_shared_core
            ├── android.hardware.helloworld@1.0-impl.so
对于这些生成的文件的作用,感兴趣的可以自己去看,这里,我们只要知道我们实现hardware/interfaces/helloworld/1.0/default/HelloWorld.cpp中的接口,然后调用其IHelloWorld::registerAsSevice注册到HwServiceManager即可。
按照前面提到的,我们不需要生成android.hardware.helloworld@1.0-impl.so,所以我们清空hardware/interfaces/helloworld/1.0/default/Android.bp中的内容,并写入如下内容:
cc_binary {
    name: "android.hardware.helloworld@1.0-service",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "main.cpp",
        "HelloWorld.cpp",
    ],
    init_rc: ["android.hardware.helloworld@1.0-service.rc"],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "android.hardware.helloworld@1.0",
    ],
}
创建android.hardware.helloworld@1.0-service.rc文件
//hardware/interfaces/helloworld/1.0/default/android.hardware.helloworld@1.0-service.rc
service helloworld-1-0 /vendor/bin/hw/android.hardware.helloworld@1.0-service
    class hal
    user system
创建main.cpp
#include <hwbinder/ProcessState.h>
#include <hidl/HidlTransportSupport.h>
#include "HelloWorld.h"
using namespace android::hardware;
using namespace android::hardware::helloworld::V1_0;
using namespace android::hardware::helloworld::V1_0::implementation;
int main() {
    /* Configures the threadpool used for handling incoming RPC calls in this process.
     *
     * This method MUST be called before interacting with any HIDL interfaces,
     * including the IFoo::getService and IFoo::registerAsService methods.
     *
     * @param maxThreads maximum number of threads in this process
     * @param callerWillJoin whether the caller will join the threadpool later.
     *
     * Note that maxThreads must include the caller thread if callerWillJoin is true;
     *
     * If you want to create a threadpool of 5 threads, without the caller ever joining:
     *   configureRpcThreadPool(5, false);
     * If you want to create a threadpool of 1 thread, with the caller joining:
     *   configureRpcThreadPool(1, true); // transport won‘t launch any threads by itself
     *
     */
    configureRpcThreadpool(1, true /*callerWillJoin*/);
    sp<IHelloWorld> service(new HelloWorld());
    service->registerAsService();
    //blcok
    joinRpcThreadpool();
    return 0;
}
configureRpcThreadpool(1,true),表示内核不会主动请求创建binder线程,简单的说,整个service中的binder线程只有一个,就是主线程。
我们创建一个HelloWorld实例,然后调用其registerAsService,注册到HwServiceManager中。该函数由hidl-gen自动生成,其实现如下:
::android::status_t IHelloWorld::registerAsService(const std::string &serviceName) {
    ::android::hardware::details::onRegistration("android.hardware.helloworld@1.0", "IHelloWorld", serviceName);
    const ::android::sp<::android::hidl::manager::V1_0::IServiceManager> sm
            = ::android::hardware::defaultServiceManager();
    if (sm == nullptr) {
        return ::android::INVALID_OPERATION;
    }
    
	//注册到 HwServiceManager
    ::android::hardware::Return<bool> ret = sm->add(serviceName.c_str(), this);
    return ret.isOk() && ret ? ::android::OK : ::android::UNKNOWN_ERROR;
}
这里,我们简单的返回Hello Android HAL.
//hardware/interfaces/helloworld/1.0/default/HelloWorld.cpp
Return<void> HelloWorld::helloworld(helloworld_cb _hidl_cb) {
    // TODO implement
    _hidl_cb("Hello Android HAL");
    return Void();
}
执行mmm hardware/interfaces/helloworld/1.0/编译hellworld模块,编译完成后,会在out/target/product/*/vendor/bin/hw/目录下生成我们的可执行程序。然后我们的android.hardware.helloworld@1.0-service.rc也会拷贝到out/target/product/rk3399/vendor/etc/init目录下。
如果要配置为开机启动,还需要导入selinux规则,这里可以参考Android官网关于SELinux的部分。
还有一点就是要在manifest.xml中加入如下信息:
<hal format="hidl">                           
	<name>android.hardware.helloworld</name>     
	<transport>hwbinder</transport>             
	<version>1.0</version>                         
	<interface>                     
		<name>IHelloWorld</name>       
		<instance>default</instance> 
    </interface>
</hal>
该信息在HwServiceManager使用,如果我们没有填写这些信息,当我们获取服务时,就会失败。
该文件的路径通常是:device/*/common/manifest.xml。
我们可以重新编译打包Vendor.img、System.img并将其刷入设备,或者手动将helloworld相关的执行程序和so库push到系统中。
将
android.hardware.helloworld@1.0.so放入system/lib目录,
然后我们手动执行./android.hardware.helloworld@1.0-service.
前面执行 ./hardware/interfaces/update-makefiles.sh时,还会多生成一个Android.mk,其作用就是生成客户端的java库。
默认情况下生成物如下:
out/target/common/obj/JAVA_LIBRARIES/android.hardware.helloworld-V1.0-java-static_intermediates$ ls                      
anno  classes    classes.jack    jack-rsc  jack-rsc.java-source-list  link_type
这里是没有生成jar文件的,为了我们的Client App能够使用,我们需要更改一下Android.mk文件。
LOCAL_MODULE := android.hardware.helloworld-V1.0-java-static
# 在 static 部分新增这条语句,禁用jack。。。
LOCAL_JACK_ENABLED := disabled
重新编译:
out/target/common/obj/JAVA_LIBRARIES/android.hardware.helloworld-V1.0-java-static_intermediates$ ls                       
anno  classes  classes-full-debug.jar  classes.jack  classes.jar  jack-rsc  jack-rsc.java-source-list  link_type
我们把生成的classes.jar导入AS中,同时讲frameworks.jar也导入AS中。

此时,安装运行,会报如下错误:

我的解决方法是删除IHelloWorld.jar(这是我自己重命名的)中重复的部分删除,没有研究能不能通过修改Makefile过滤掉重复部分的classes文件。
最后,运行结果如图:

原文:https://www.cnblogs.com/liutimo/p/14265059.html