首页 > 其他分享 >AndroidQ 打通应用层到HAL层---(HIDL服务实现)

AndroidQ 打通应用层到HAL层---(HIDL服务实现)

时间:2024-07-17 15:21:36浏览次数:15  
标签:HAL HIDL AndroidQ hidl hardware 1.0 android hello

什么是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域不一样

整体设计



可以看到最左侧在源码进行编译的时候,上下层其实是编译成两个不同的镜像的:上层为system.img,底层为vendor.img。 如果直接在两个镜像的“域”中进行传参和调用无疑会出现无数的权限问题和空指针。HIDL就是为了解决上层去调用HAL层的一种技术思想。他对HAL层中对于底层device 的调用方法封装成调用接口,并把调用的部分作为系统服务进行启动。从上层的HIDLClient去访问server进行调用底层,这样的过程耦合度非常之低,并且方便上下层开发人员进行接口对接,并且在日后的演进过程中会极大地减少调试的时间成本和可以避免的bug量。

为什么需要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系统升级的影响与难度

HIDL的使用

HIDL可以分为:HIDL C++(C++实现)、HIDL Java(Java 实现),并且还主要分为直通式和绑定式,本篇文章使用的C++和直通式的HIDL,HIDL用起来非常简单,AOSP的hardware/interfaces/目录下有很多的HIDL,我们仿照其他HIDL创建自己的HIDL目录:hardware/interfaces/hello_hidl/1.0

并在此目录下创建一个IHello.hal文件:

package [email protected];

interface IHello {

    addition_hidl(uint32_t a,uint32_t b) generates (uint32_t total);

};

这个文件定义了一个addition_hidl函数,这个函数用来调用HAL的加法函数

然后就可以使用Android提供的工具hidl-gen来生成HIDL框架,执行如下命令:

 [email protected]
 LOC=hardware/interfaces/hello_hidl/1.0/default/
 hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE
 hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces -randroid.hidl:system/libhidl/transport $PACKAGE

执行命令成功之后我们会发现hardware/interfaces/hello_hidl/1.0下多了一个default目录,进入default目录,里面有三个文件Android.bp,Hello.cpp,Hello.h



之后再在执行./hardware/interfaces/update-makefiles.sh这个命令,update-makefiles.sh这个脚本目的是为HIDL生成对应Android.bp文件

最后目录结构为:



接着我们还需要在default目录下增加一个空文件service.cpp,用作注册HIDL服务,我们采用直通式的HIDL,所以service.cpp的内容为:

#include <android/hardware/hello_hidl/1.0/IHello.h>
#include <hidl/LegacySupport.h>
// Generated HIDL files
using android::hardware::hello_hidl::V1_0::IHello;
using android::hardware::defaultPassthroughServiceImplementation;

int main() {
    return defaultPassthroughServiceImplementation<IHello>();
}

defaultPassthroughServiceImplementation函数最终会向HwServiceManager注册HIDL服务

接着我们来看看之前生成的文件,首先看Hello.h

// FIXME: your file license if you have one

#pragma once

#include <android/hardware/hello_hidl/1.0/IHello.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>

namespace android {
namespace hardware {
namespace hello_hidl {
namespace V1_0 {
namespace implementation {
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;

struct Hello : public IHello {
    // Methods from ::android::hardware::hello_hidl::V1_0::IHello follow.
    Return<uint32_t> addition_hidl(uint32_t a, uint32_t b) override;

    // Methods from ::android::hidl::base::V1_0::IBase follow.

};

// FIXME: most likely delete, this is only for passthrough implementations
//去掉注释
extern "C" IHello* HIDL_FETCH_IHello(const char* name);

}  // namespace implementation
}  // namespace V1_0
}  // namespace hello_hidl
}  // namespace hardware
}  // namespace android

系统自动生成了Hello结构体(当然也可以自己改为class),继承IHello接口,addition_hidl函数就需要在Hello.cpp中去实现了,因为我们采用直通式HIDL,所以需要将// extern “C” IHello* HIDL_FETCH_IHello(const char* name);的注释去掉

// FIXME: your file license if you have one

#include "Hello.h"
#include <utils/Log.h>
namespace android {
namespace hardware {
namespace hello_hidl {
namespace V1_0 {
namespace implementation {

// Methods from ::android::hardware::hello_hidl::V1_0::IHello follow.
Return<uint32_t> Hello::addition_hidl(uint32_t a, uint32_t b) {
    // TODO implement
    ALOGE("hello_hidl service is init success....a :%d,b:%d",a,b);
    return uint32_t {};
}
// Methods from ::android::hidl::base::V1_0::IBase follow.

IHello* HIDL_FETCH_IHello(const char* /* name */) {
    return new Hello();
}
}  // namespace implementation
}  // namespace V1_0
}  // namespace hello_hidl
}  // namespace hardware
}  // namespace android

同样需要去掉HIDL_FETCH_IHello函数的注释,采用直通式HIDL时,通过前面service.cpp中的defaultPassthroughServiceImplementation函数注册HIDL服务时,内部原理就是通过“HIDL_FETCH_”字串拼接defaultPassthroughServiceImplementation传递的IHello,找到HIDL_FETCH_IHello函数并获取IHello对象,我们可以看到HIDL_FETCH_IHello初始代码就是创建了一个Hello对象

在接着看default目录下的Android.bp:

cc_library_shared {
    name: "[email protected]",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "Hello.cpp",
    ],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "[email protected]",
        "liblog",
        "libutils",
    ],
}

这个Android.bp会将Hello这个HIDL服务编译成一个[email protected],它还依赖一个[email protected],这个so哪来的呢?

再接着看1.0目录下的Android.bp:

// This file is autogenerated by hidl-gen -Landroidbp.
hidl_interface {
    name: "[email protected]",
    root: "android.hardware",
    vndk: {
        enabled: true,
    },
    srcs: [
        "IHello.hal",
    ],
    interfaces: [
        "[email protected]",
    ],
    gen_java: true,
}

这个Android.bp会将hardware/interfaces/hello_hidl/1.0这个HIDL编译成一个[email protected],到这里我们发现service.cpp没有用到,所以我们还需要修改default目录下的Android.bp:

// FIXME: your file license if you have one

cc_library_shared {
    name: "[email protected]",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "Hello.cpp",
    ],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",
        "libhardware",
        "[email protected]",
        "liblog",
        "libutils",
    ],
}
cc_binary {
    name: "[email protected]",
    defaults: ["hidl_defaults"],
    relative_install_path: "hw",
    vendor: true,
    srcs: ["service.cpp"],
    shared_libs: [
        "[email protected]",
        "libhardware",
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",
    ],

}

新增加对service.cpp的编译,我们将service.cpp编译成一个二进制可执行文件[email protected],用来启动HIDL服务,好了,最终我们这个HIDL会编译出来如下三个so:

[email protected][email protected][email protected]

还有一点需要注意的是,这个HIDL想要被Framework获取使用还需要在manifest.xml中注册,

manifest.xml在手机vendor/etc/vintf/manifest.xml下,我们将这个文件pull出来然后添加如下代码:

   <hal format="hidl">
        <name>android.hardware.hello_hidl</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>IHello</name>
            <instance>default</instance>
        </interface>
        <fqname>@1.0::IHello/default</fqname>
    </hal>

然后在Hello.cpp中添加一行log,之后进行编译

IHello* HIDL_FETCH_IHello(const char* /* name */) {

    ALOGE("hello_hidl service is init success....");

    return new Hello();
}

执行mmm hardware/interfaces/hello_hidl/1.0/



编译成功后我们将生成的三个so分别push到手机vendor/lib64/hw/,vendor/lib64/,vendor/bin/hw/目录下

adb push vendor/lib64/hw/[email protected] vendor/lib64/hw/

adb push system/lib64/[email protected] vendor/lib64/

adb push vendor/bin/hw/[email protected] vendor/bin/hw/

接着我们到手机vendor/bin/hw/目录下去执行[email protected]这个二进制可执行文件,这个文件就会执行service.cpp的代码,调用defaultPassthroughServiceImplementation注册我们的HIDL服务

再看看log输出:



在执行[email protected]时就会输入这句log,代表我们这个HIDL服务已经实现,其实通常的HIDL服务都是通过rc文件来开机启动的,我这里为了方便演示就没有写

再执行adb shell ps -A|grep -i --color "hello_hidl"命令看下这个服务状态



HIDL这个服务已经能够正常启动了,接着写一个测试程序看能否获取这个服务,并且调用该服务的函数,我在Hello.cpp的addition_hidl函数中添加了一句log:

Return<uint32_t> Hello::addition_hidl(uint32_t a, uint32_t b) {
    // TODO implement
    ALOGD("dongjiao...Hello::addition_hidl a = %d,b = %d",a,b);
    return uint32_t {};
}

测试程序写在hardware/interfaces/hello_hidl/1.0/default目录下:



Hello_hidl_test.cpp:

#include <android/hardware/hello_hidl/1.0/IHello.h>
#include <hidl/LegacySupport.h> 
#include <log/log.h>

using android::sp;
using android::hardware::hello_hidl::V1_0::IHello;
using android::hardware::Return;
int main(){
    android::sp<IHello> hw_device = IHello::getService();
    if (hw_device == nullptr) {
              ALOGD("dongjiao...failed to get hello-hidl");
              return -1;
        }
    ALOGD("dongjiao...success to get hello-hidl....");
    Return<uint32_t> total = hw_device->addition_hidl(3,4);
    return 0;
}

测试程序代码也比较简单,获取IHello的服务,然后调用addition_hidl函数

看一下Android.bp:

cc_binary {
    name: "Hello_hidl_test",
    srcs: ["Hello_hidl_test.cpp"],
    shared_libs: [
        "liblog",
        "[email protected]",
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "libutils",
    ],
}

我们再编译这个测试程序,它会被编译成一个可执行二进制文件Hello_hidl_test编译命令:mmm hardware/interfaces/hello_hidl/1.0/default/test/



编译成功了,将这个可执行文件push到手机/system/bin/目录下

在执行Hello_hidl_test之前别忘了把HIDL服务启动起来



接着执行Hello_hidl_test



然后看log输出



可以看到成功了,成功获取到了HIDL服务并且调用了该服务的addition_hidl函数,将我们的参数传递了过去,实际开发中就可以在获取HIDL服务时打开HAL模块,然后可以直接调用HAL的函数,上一篇文章其实也写了测试程序测自定义的HAL模块,我们只需要将上一篇文章中的测试程序中的代码copy到HIDL初始化代码中就能够调用HAL的那个加法函数了,具体就不测试了

到这里HIDL服务已经成功实现并且能够正常使用,HIDL这个框架用起来还是比较简单的,大部分代码都是通过工具生成的

标签:HAL,HIDL,AndroidQ,hidl,hardware,1.0,android,hello
From: https://www.cnblogs.com/linhaostudy/p/18307441

相关文章

  • JNI和HAL 的区别
    JNI(JavaNativeInterface)和HAL(HardwareAbstractionLayer)在Android系统中都扮演着与本地代码交互的重要角色,但它们的功能和应用场景有所不同。以下是它们之间的关系和区别:JNI(JavaNativeInterface)用途:JNI是一种框架,允许Java代码调用本地(Native)C/C++代......
  • Datawhale AI 夏令营 task2
    Task1的baseline我们是基于经验模型(使用均值作为结果数据)来解决的问题,Task2版本教程将使用机器学习模型解决本次问题,模型使用简单,数据不需要过多预处理;使用机器学习方法一般主要需要从获取数据&增强、特征提取和模型三个方面下手。一般的使用机器学习模型解决问题的主要步骤......
  • 【Datawhale AI夏令营】 Task1 学习笔记
    目录一、baseline二、NLP模型自然语言处理的主要任务自然语言处理的技术和方法自然语言处理的应用自然语言处理的挑战 三、赛题理解 赛题背景赛事任务术语词典干预术语词典干预的主要特点术语词典干预的实施方法四、实操 步骤体会感想   学习目标:跑......
  • HAL库源码移植与使用之FSMC (例子加思路与理解,万字良心保证你能听懂)
    FMC和FSMC是一样的东西,只是FMC更可控地址更多又可以驱动SDRAM,用法都一样!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!总结:其实fsmc更像是一个有着特定转换时序功能的寄存器,每个fsmc映射在芯片内存里的地址都有一个这样的寄存器,你往这个映射的地址里赋值,这个赋值信息先到达对应fsmc寄存器,他不会像普通寄存器一样直接控制......
  • HAL库源码移植与使用之驱动LCD屏
    LCD屏会有烧屏残影的风险,但因其价格便宜他非常适合用于单片机显示显示屏分为以下几种:他的组成部包含玻璃基板、背光、驱动IC等LCD接口的种类MCU很简单,连51单片机都能驱动,但无法频繁刷新,一般有着20几个引脚引出的就是MCU接口我们常用的是就是MCU,下面讲的也是LCD屏幕MCU驱动......
  • HAL库源码移植与使用之正点原子OLED使用解析
    正点原子的OLED是使用SSD1306来驱动的,并设计了多种通讯方式,通过背后的焊点来选择这里以正点原子开发板最常用的8080并口通讯来讲引脚定义各正点原子开发板对OLED的接线8080时序图发送数据示例代码voidoled_wr_byte(uint8_tdata,uint8_tcmd){ OLED_RS(cmd); /......
  • 电力需求预测挑战赛——Datawhale AI 夏令营第2期【从零入门AI竞赛之机器学习】
    赛事官网https://challenge.xfyun.cn/h5/detail?type=electricity-demand&ch=dw24_uGS8Gs学习者手册https://exn8g66dnwu.feishu.cn/docx/T7WGd7goqowRvFxwoApclo9Pn0bTask1——传统时序模型(2024/7/14)阶段要求根据文档跑通提交并拿下第一个分数;学习相关知识点,理解赛题。......
  • 毕业设计 基于机器视觉的PCB焊接缺陷检测系统(Halcon+C#)
    毕业设计基于机器视觉的PCB焊接缺陷检测系统一、功能需求检测PCB板的焊接缺陷:漏焊、虚焊等二、开发环境1、Halcon2、C#三、运行效果处理图片:运行视频:毕业设计基于机器视觉的PCB焊接缺陷检测系统毕业设计资料(C#软件源码+Halcon算法源码+开题报告+毕业设计+......
  • Datawhale2024年AI夏令营第二期:CV图像--学习笔记
       Deepfake攻防--图像赛道是该夏令营第二期的学习活动(“CV图像”方向),是于蚂蚁集团举办的“外滩大会-全球Deepfake攻防挑战赛”开展的实践学习——适合想入门、了解并实践,关于深度学习和计算机视觉方向的学习者参与。此次学习活动的速通手册如下:从零入门CV图像竞赛(Deepf......
  • Datawhale AI夏令营第二期——机器学习 电力需求预测挑战赛
    #AI夏令营#Datawhale夏令营一种基于LSTM的电力暴力预测1.赛事简介随着全球经济的快速发展和城市化进程的加速,电力系统面临着越来越大的挑战。电力需求的准确预测对于电网的稳定运行、能源的有效管理以及可再生能源的整合至关重要。然而,电力需求受到多种因素的影响,为了提高......