写给应用开发的 Android Framework 教程是一个系列教程,现在已更新以下内容:

  • Android Framework 学习道路指南

  • 体系开发基础

    • Ubuntu 运用快速入门
    • Make 构建东西入门
    • 了解 Unicode UTF-8 UTF-16 UTF-32
    • Linux Shell 脚本编程入门——核心基础语法
    • SEAndroid 运用极速上手
    • 了解 C++ 的 Memory Order
  • AOSP 上手指南

    • AOSP 极速上手
    • 体系开发东西推荐
    • 增加 Product
    • 增加 C/C++、Java 可履行程序
    • 增加 C/C++、Java 库
    • 增加装备文件与删除已有模块
    • 体系 App 源码增加
    • 运用 Android Studio 开发体系 App
    • 增加开机自发动 Shell 脚本
  • 学穿 Binder 系列

    • Binder 基本原理
    • Binder 程序示例之 C 言语篇
    • Binder 服务注册进程情形剖析之C言语篇
    • Binder 服务获取与运用进程情形剖析之C言语篇
    • Binder C++ 程序示例
    • Binder C++ 程序剖析之主要类解析
    • Binder 服务注册进程情形剖析之 C++ 篇
    • Binder 服务获取与运用进程情形剖析之 C++ 篇
  • HAL 与硬件服务

    • Kernel 下载与编译
    • Linux 驱动开发入门
    • Hal 层增加拜访 linux 驱动的接口 (本文)

本文基于 AOSP Android10 r41 源码环境

1. Android O 后的 HAL —— Treble 方案

在 Android O 曾经,framework 与 hal 是紧耦合的存在于 system.img 中,因此进行版别晋级时需求: OEM 厂商适配 framework ,SoC 厂商适配 hal, 之后将修正打包到system.img,生成 OTA 晋级包,推送到手机进行 OTA 晋级。

​在 Android O 以后,framework 与 hal 进行了解耦, framework 存在于system.img,hal 存在于 vendor.img ,进行版别晋级时,分为两次晋级:

  • framework 晋级: OEM 厂商适配 framework,将修正打包到system.img, 生成OTA 晋级包,推送到手机进行OTA 晋级(framework 产生改动,hal 层未变)。
  • hal 晋级:SoC 厂商适配 hal, 将修正打包到 vendor.img, 生成 OTA 晋级包,推送到手机进行 OTA 晋级(framework产生改动,hal 层产生改动)。

要完成 Android O 以后的晋级方法,需求保证 System 分区与 Vendor 分区之间的接口稳定,Android O 中供给了多种技能来维护这个接口的稳定性,其中最主要的是:

  • VNDK:System 分区中的库,这些库 Vendor 分区或许会运用到,这些库由 google 维护和保持其对外的稳定性。
  • HIDL:Vendor 分区经过 HIDL 方法来对外供给运用接口,需求 SOC 厂商维护其对外接口的稳定性。

接下来咱们就来看看 HIDL 在 HAL 层中的运用。

2. Hal 层的完成

源码能够在 github.com/yuandaimaah… 这儿下载到。

首要咱们需求将 Linux 驱动开发入门 中介绍的 hello 驱动增加到内核中,接着在看下面的操作:

2.1 Hal 协议完成

Hal 层的完成一般放在 vendor 目录下,咱们在 vendor 目录下创立如下的目录:

mkdir -p jelly/hardware/interface/hello_hidl/1.0

接着在 vendor/jelly/hardware/interface/hello_hidl/1.0 目录下创立 Hal 文件;

IHello.hal :

//界说包名,最终跟一个版别号
package jelly.hardware.hello_hidl@1.0;
//界说 hidl 服务对外供给的接口
interface IHello {
    //for test,generates 后边跟的是返回类型
    addition_hidl(uint32_t a,uint32_t b) generates (uint32_t total);
    //写 hello 驱动
    write(string name) generates (uint32_t result);
    //读 hello 驱动
    read() generates (string name);
};

这儿的 IHello.hal 界说了咱们的服务对外供给了哪些函数。能够认为这便是咱们服务的对外协议。协议一般界说好就不会再修正,以保持对外的稳定性。

关于 hal 的写法,能够参阅官方的文档,别的也能够参阅 hardware 目录下体系自带的 hal 的写法。

2.2 hal 文件生成 C++ 源文件

接着咱们运用 hidl-gen 指令将咱们写的 hal 文件转换为 C++ 文件:

source build/envsetup.sh
PACKAGE=jelly.hardware.hello_hidl@1.0
LOC=vendor/jelly/hardware/interfaces/hello_hidl/1.0/default
hidl-gen -o $LOC -Lc++-impl -rjelly.hardware:vendor/jelly/hardware/interfaces $PACKAGE

接着就会生成一些 C++ 代码:

vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下会生成 Hello.cpp 和 Hello.h

out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++/gen/jelly/hardware/hello_hidl/1.0 中生成了如下代码:

写给应用开发的 Android Framework 教程——HAL与硬件服务之 Hal 层添加访问 linux 驱动的接口

out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++_headers/gen/jelly/hardware/hello_hidl/1.0 中生成了如下代码:

写给应用开发的 Android Framework 教程——HAL与硬件服务之 Hal 层添加访问 linux 驱动的接口

接着修正 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下生成的 Hello.cpp:

// FIXME: your file license if you have one
#include "Hello.h"
#include <log/log.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
namespace jelly {
namespace hardware {
namespace hello_hidl {
namespace V1_0 {
namespace implementation {
// Methods from ::jelly::hardware::hello_hidl::V1_0::IHello follow.
Return<uint32_t> Hello::addition_hidl(uint32_t a, uint32_t b) {
    ALOGE("hello_hidl service is init success....a :%d,b:%d",a,b);
    return uint32_t {};
    return uint32_t {};
}
Return<uint32_t> Hello::write(const hidl_string& name) {
    int fd = open("/dev/hello", O_RDWR);
    if (fd == -1)
	{
		printf("can not open file /dev/hello\n");
		return  uint32_t { 0 };
	}
    ::write(fd, name.c_str(), 100);
    close(fd);
    return uint32_t { 1 };
}
//经过 read_cb function 回调函数,回传数据
Return<void> Hello::read(read_cb _hidl_cb) {
    char buf[100];
    int fd = open("/dev/hello", O_RDWR);
    if (fd == -1)
	{
		printf("can not open file /dev/hello\n");
		return Void();
	}
    ::read(fd, buf, 100);
    hidl_string result(buf);
    _hidl_cb(result);
    close(fd);
    return Void();
}
}  // namespace implementation
}  // namespace V1_0
}  // namespace hello_hidl
}  // namespace hardware
}  // namespace jelly

这儿主要是对咱们的协议进行完成,完成了对上一节完成的设备文件 /dev/hello 的读写。至此咱们的 hidl 服务就界说好了

2.3 服务端完成

接着咱们需求写一个 Server 端来向 HwServiceManager 注册咱们的服务。在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下增加 service.cpp:

#include <hidl/HidlTransportSupport.h>
#include <utils/Looper.h>
#include <utils/StrongPointer.h>
#include <log/log.h>
#include "Hello.h"
using android::hardware::configureRpcThreadpool;
using android::hardware::joinRpcThreadpool;
using jelly::hardware::hello_hidl::V1_0::IHello;
using jelly::hardware::hello_hidl::V1_0::implementation::Hello;
int main() {
    ALOGD("hello-hidl is starting...");
    configureRpcThreadpool(4, true /* callerWillJoin */);
    android::sp<IHello> service = new Hello();
    android::status_t ret = service->registerAsService();
    if (ret != android::NO_ERROR) {
    }
    joinRpcThreadpool();
    return 0;
    //Passthrough形式
    //return defaultPassthroughServiceImplementation<IHello>(4);
}

咱们的服务端需求在开机时发动,创立 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default/jelly.hardware.hello_hidl@1.0-service.rc 文件:

service vendor_hello_hidl_service /vendor/bin/hw/jelly.hardware.hello_hidl@1.0-service
	class hal
	user system
	group system

接着咱们需求增加 VINTF 目标,关于注册到 hwservicemanager 的服务都需求增加一个 VINTF 目标。关于编码来说 VINTF 目标便是一个 xml 文件,创立 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default/jelly.hardware.hello_hidl@1.0-service.xml 文件:

<manifest version="1.0" type="device">
  <hal format="hidl">
        <name>jelly.hardware.hello_hidl</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>IHello</name>
            <instance>default</instance>
        </interface>
    </hal>
</manifest>

2.4 生成 Android.bp

接着咱们来生成 Android.bp:

hidl-gen -o $LOC -Landroidbp-impl -rjelly.hardware:vendor/jelly/hardware/interfaces $PACKAGE

这个指令会在 vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下生成一个 Android.bp,咱们在生成的基础上稍作修正如下:

// FIXME: your file license if you have one
cc_library_shared {
    name: "jelly.hardware.hello_hidl@1.0-impl",
    relative_install_path: "hw",
    proprietary: true,
    srcs: [
        "Hello.cpp",
    ],
    shared_libs: [
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "jelly.hardware.hello_hidl@1.0",
        "liblog",
    ],
}
cc_binary {
    name: "jelly.hardware.hello_hidl@1.0-service",
    init_rc: ["jelly.hardware.hello_hidl@1.0-service.rc"],
    vintf_fragments: ["jelly.hardware.hello_hidl@1.0-service.xml"],
    defaults: ["hidl_defaults"],
    relative_install_path: "hw",
    vendor: true,
    srcs: ["service.cpp", "Hello.cpp"],
    shared_libs: [
        "jelly.hardware.hello_hidl@1.0",
        "libhardware",
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",
    ],
}

生成的库里面有一个依靠 jelly.hardware.hello_hidl@1.0,接着咱们来生成这个库对应的 Android.bp:

hardware/interfaces 目录下将 update-makefiles.sh 拷贝到 vendor/jelly/hardware/interfaces/ 目录下,并修正如下:

#!/bin/bash
source $ANDROID_BUILD_TOP/system/tools/hidl/update-makefiles-helper.sh
do_makefiles_update \
  "jelly.hardware:vendor/jelly/hardware/interfaces"

接着履行:

./vendor/jelly/hardware/interfaces/update-makefiles.sh

就会生成 vendor/jelly/hardware/interfaces/Android.bp 文件:

hidl_interface {
    name: "jelly.hardware.hello_hidl@1.0",
    root: "jelly.hardware",
    srcs: [
        "IHello.hal",
    ],
    interfaces: [
        "android.hidl.base@1.0",
    ],
    gen_java: false,
}

其中的 hidl_interface 是 hidl 独有的,当编译源码时,它会将 out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++/gen/jelly/hardware/hello_hidl/1.0out/soong/.intermediates/vendor/jelly/hardware/interfaces/hello_hidl/1.0/jelly.hardware.hello_hidl@1.0_genc++_headers/gen/jelly/hardware/hello_hidl/1.0 目录下的源码编译为 jelly.hardware.hello_hidl@1.0.so 文件,并预制到手机的 /vendor/lib/vendor/lib64/ 目录下。

为了使编译经过,新建 vendor/jelly/hardware/interfaces/Android.bp 文件:

hidl_package_root {
    name: "jelly.hardware",
    path: "vendor/jelly/hardware/interfaces",
}

这个 Android.bp 的作用是告知编译体系包名与路径的映射关系。

接着新建 vendor/jelly/hardware/interfaces/current.txt 文件,current.txt 记录了所有 hal 接口的 hash 值,接口有变化时,一起需求更新 current.txt 中的 hash 值,这是咱们先随意设置一个 hash 值:

123456 jelly.hardware.hello_hidl@1.0::IHello

再履行一遍 update-makefiles.sh,这个时分就会发现提示 hash 值不正确了,一起会给出正确的 hash 值,咱们把正确的 hash 值替换到 current.txt 即可。

2.5 客户端编写

vendor/jelly/hardware/interfaces/hello_hidl/1.0/default 目录下创立如下的文件和文件夹:

写给应用开发的 Android Framework 教程——HAL与硬件服务之 Hal 层添加访问 linux 驱动的接口

其中 hello_hidl_test.cpp:

#include <jelly/hardware/hello_hidl/1.0/IHello.h>
#include <hidl/LegacySupport.h>
#define LOG_TAG "hello_hidl"
#include <log/log.h>
using android::sp;
using jelly::hardware::hello_hidl::V1_0::IHello;
using android::hardware::Return;
using android::hardware::hidl_string;
int main(){
    android::sp<IHello> hw_device = IHello::getService();
    if (hw_device == nullptr) {
              ALOGD("failed to get hello-hidl");
              return -1;
        }
    ALOGD("success to get hello-hidl....");
    Return<uint32_t> total = hw_device->addition_hidl(3,4);
    hw_device->write("hello");
    hw_device->read([&](hidl_string result){
        ALOGD("%s\n", result.c_str());
    });
    return 0;
} 

Android.bp:

cc_binary {
    name: "hello_hidl_test",
    srcs: ["hello_hidl_test.cpp"],
    vendor: true,
    shared_libs: [
        "liblog",
        "jelly.hardware.hello_hidl@1.0",
        "libhidlbase",
        "libhidltransport",
        "libhwbinder",
        "libutils",
    ],
}

2.6 selinux 装备

device/Jelly/Rice14/sepolicy 目录下增加:

device.te 中增加如下内容:

type hello_hidl_dev_t, dev_type;

hwservice.te:

type hello_hidl_hwservice, hwservice_manager_type;

hello_hidl.te:

type hello_hidl, domain;
type hello_hidl_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hello_hidl);
add_hwservice(hello_hidl, hello_hidl_hwservice)
hwbinder_use(hello_hidl)
allow hello_hidl hidl_base_hwservice:hwservice_manager { add };
allow hello_hidl hello_hidl_dev_t:chr_file { open read write };
binder_call(hello_hidl,hwservicemanager)
get_prop(hello_hidl,hwservicemanager_prop)

hwservice_contexts:

jelly.hardware.hello_hidl::IHello      u:object_r:hello_hidl_hwservice:s0

hello_hidl_test.te

type  hello_hidl_test, domain;
type  hello_hidl_test_exec, exec_type, vendor_file_type, file_type;
domain_auto_trans(shell, hello_hidl_test_exec, hello_hidl_test);
get_prop(hello_hidl_test, hwservicemanager_prop)
allow hello_hidl_test hello_hidl_hwservice:hwservice_manager find;
hwbinder_use(hello_hidl_test);

在 file_contexts 中增加:

/dev/hello                      u:object_r:hello_hidl_dev_t:s0
/vendor/bin/hw/jelly\.hardware\.hello_hidl@1\.0-service    u:object_r:hello_hidl_exec:s0

注意,file_contexts 最终必须留一行空行,不然会编译失败

2.7 编译履行

接着在 device/Jelly/Rice14/Rice14.mk 中增加如下内容:


BOARD_SEPOLICY_DIRS += \
    device/Jelly/Rice14/sepolicy
PRODUCT_PACKAGES += \
    jelly.hardware.hello_hidl@1.0-service \
    hello_hidl_test \
    jelly.hardware.hello_hidl@1.0-impl \

然后整编体系:

source
lunch 1
make -j16

最终测试

# 履行客户端程序
hello_hidl_test &
# 检查 log
# logcat | grep hello 
05-03 14:20:19.816  1555  1555 D         : hello-hidl is starting...
05-03 14:20:19.816  1555  1555 I ServiceManagement: Registered jelly.hardware.hello_hidl@1.0::IHello/default (start delay of 36ms)
05-03 14:20:19.816  1555  1555 I ServiceManagement: Removing namespace from process name jelly.hardware.hello_hidl@1.0-service to hello_hidl@1.0-service.
05-03 14:20:38.843  3469  3469 D hello_hidl: success to get hello-hidl....
05-03 14:20:38.843  1555  1563 E hello_hidl: hello_hidl service is init success....a :3,b:4
05-03 14:20:38.844  1555  1563 D hello_hidl: service is writing 
05-03 14:20:40.846  1555  1563 D hello_hidl: service is reading 
05-03 14:20:40.847  3469  3469 D hello_hidl: hello

能够看到咱们的客户端已经拜访到服务端了。

后续

以上是经过 hwbinder 跨进程通讯的方法,来完成的。关于效率要求较高的模块,咱们仍是希望经过 dlopen 直接加载的方法来运用 hal 层。Android 的 Hidl 框架也是供给了支撑的。这种方法称为直通形式(passthrough),这部分内容会在下一篇教程中讲解。

参阅资料

  • Android体系开发入门-11.增加hidl服务
  • Android体系开发入门-12.hidl服务回调
  • Android HIDL模型下HAL Service增加SELinux规矩实战
  • ROC-RK3399-PC Pro Android10 源码
  • 一个简略的HIDL开发笔记
  • 简略HIDL HAL的完成
  • hidl 服务发动流程
  • SElinux权限装备
  • AndroidO Treble架构下Hal进程发动及HIDL服务注册进程
  • Android10.0 Binder通讯原理(一)Binder、HwBinder、VndBinder概要
  • 体系角度解读Android P新特性
  • Android : 供应商原生开发套件 (VNDK)
  • android 自界说驱动(第三篇:HIDL服务端)