在本文中,将深入学习了解Android HAL的不同方式和架构,以及它们之间的区别和联系。将从最早的Legacy HAL开始,然后从Android 8.0 (Oreo) 开始引入的新的HAL定义方式:HIDL (Hardware Interface Definition Language)。将比较HIDL的两种模式:Passthrough mode 和 Binderized mode,并分析它们各自的优缺点。最后将总结HAL的作用和意义,并展望未来的发展方向 ,代码结构假象的是自定义SystemGpio举例(Legacy HAL)基础之上拓展。
系列文章:
Android HAL深入探索(2): 传统HAL与文件加解密模拟
Android HAL深入探索(3): HIDL Passthrough模式与串口数据回调模拟
0.参考学习
HIDL详解-Android10.0 HwBinder通信原理(二)
Rockchip系列之客制化GPIO接口Hardware部分(3)
1. 传统的HAL (Legacy HAL)
在HIDL之前,Android使用的是传统的HAL,它是用C语言编写的。这种HAL定义了一系列的结构体和函数指针,其中hw_module_t
就是其中的一个结构体,代表一个硬件模块。这种方式的HAL通常位于/hardware/libhardware/include/hardware/
目录下。
交互方式
传统的HAL需要与Android框架进行交互,以提供硬件相关的功能和服务。为了实现这一点,传统的HAL需要经过以下几个层次:
- Java Framework Interface: 这是Android框架中定义的Java类或接口,用于提供给应用或服务使用的API。例如,
android.os.SystemGpio
就是一个Java类,用于控制GPIO (General Purpose Input/Output) 端口。 - AIDL Interface: 这是Android框架中定义的AIDL (Android Interface Definition Language) 文件,用于描述跨进程通信时需要传递或返回的数据类型和方法签名。
android.os.IGpioService.aidl
就是一个AIDL文件,用于定义GPIO服务需要实现的接口。 - Service Implementation in Java: 这是Android框架中实现了AIDL接口的Java类,用于作为一个服务运行在后台,并处理来自其他进程或应用的请求。
com.android.server.GpioService
就是一个Java类,用于实现GPIO服务。 - JNI Layer: 这是Android框架中使用JNI (Java Native Interface) 桥接Java层和C层之间交互的C/C++代码。JNI层负责将Java层传递过来的参数转换为C层可以识别的数据类型,并调用相应的C函数。反之亦然。
com_android_server_GpioService.cpp
就是一个JNI层文件,用于调用GPIO HAL相关函数。 - HAL Header: 这是传统HAL中定义了结构体和函数指针等接口规范的C头文件。这些头文件通常位于
hardware/libhardware/include/hardware/
目录下,并以_hal.h
结尾。gpio_hal.h
就是一个HAL头文件,用于定义GPIO HAL相关接口。 - HAL Implementation: 这是传统HAL中实现了HAL头文件中定义接口规范的C代码。这些代码通常位于
hardware/libhardware/modules/
目录下,并以_hal.c
结尾。gpio_hal.c
就是一个HAL实现文件,用于实现GPIO HAL相关功能。 - Kernel Driver: 这是与硬件直接交互的驱动层代码,通常是操作系统内核的一部分。这些代码通常位于
kernel/drivers/
目录下,并以.c
或.h
结尾。gpio.c
就是一个驱动层文件,用于控制GPIO端口的输入输出状态。
代码架构
传统的HAL是Android系统早期的硬件抽象层设计。它的主要目的是为Android框架提供一个统一的接口,从而使框架能够与底层硬件进行交互,而不需要关心硬件的具体实现。
- Java Framework Interface:
frameworks/base/core/java/android/os/SystemGpio.java
: 这是Android框架中的Java接口,应用程序和系统服务通过这个接口与GPIO硬件进行交互。
- AIDL Interface:
frameworks/base/core/java/android/os/IGpioService.aidl
: AIDL (Android Interface Definition Language) 定义了一个跨进程的接口,使得应用程序可以与在另一个进程中运行的GPIO服务进行通信。
- Service Implementation in Java:
frameworks/base/services/core/java/com/android/server/GpioService.java
: 这是GPIO服务的Java实现,它处理来自应用程序的请求,并通过JNI与底层硬件进行交互。
- JNI Layer:
frameworks/base/services/core/jni/com_android_server_GpioService.cpp
: JNI (Java Native Interface) 层允许Java代码与本地C/C++代码进行交互。这里的JNI代码是桥接Java服务和HAL之间的通信。
- HAL Header:
hardware/libhardware/include/hardware/gpio_hal.h
: 这是HAL的头文件,定义了与硬件交互所需的接口和数据结构。
- HAL Implementation:
hardware/libhardware/modules/gpio/gpio_hal.c
: 这是HAL的C实现,它与底层的硬件驱动进行交互。
- Kernel Driver:
kernel/drivers/misc/gpio/gpio.c
: 这是Linux内核中的GPIO驱动,它直接与硬件进行交互。
架构图
传统的HAL的架构如下图所示:
传统的HAL的优点是简单直接,易于理解和实现。但它也有一些缺点,主要有以下几点:
- 缺乏版本控制和向后兼容性:传统的HAL没有明确的版本号和接口变更机制。如果硬件供应商或Android系统需要修改或扩展HAL接口,那么就需要同时修改所有相关的代码,包括JNI层、框架层和应用层。这会导致代码的维护和升级变得困难和耗时。
- 缺乏安全性和稳定性:传统的HAL作为一个库直接链接到客户端进程中,这意味着客户端和HAL之间的调用是进程内的,没有进程间通信的开销。但这也意味着如果HAL出现了错误或崩溃,那么会影响到客户端进程,甚至导致整个系统崩溃。传统的HAL没有沙盒机制,也没有权限控制,任何进程都可以访问任何HAL模块,这可能带来安全风险。
为了解决传统HAL的缺点,Android从8.0 (Oreo) 开始引入了一种新的HAL定义方式:HIDL。
HIDL (Hardware Interface Definition Language) 是一种语言,用于定义HAL的接口。与传统的HAL相比,HIDL提供了更好的版本控制和向后兼容性。HIDL还支持两种不同的模式:Passthrough mode 和 Binderized mode。这两种模式分别对应了不同的通信方式和架构。
2. HIDL Passthrough mode
交互方式
在Passthrough模式下,HAL服务作为一个库运行在客户端进程中。这意味着客户端和HAL之间的调用是进程内的,没有进程间通信的开销。这种模式与传统的HAL类似,但有以下几点不同:
- 使用C++而不是C:Passthrough模式下的HIDL HAL是使用C++编写的。这使得HAL可以使用C++的特性,如类、继承、多态等。
- 使用.hal而不是.h:Passthrough模式下的HIDL HAL使用
.hal
文件来定义接口规范,而不是.h
文件。.hal
文件是一种特殊的语法,用于描述HAL接口中需要传递或返回的数据类型和方法签名。.hal
文件通常位于hardware/interfaces/
目录下,并以.hal
结尾。例如,IGpio.hal
就是一个.hal
文件,用于定义GPIO HAL相关接口。 - 使用hidl-gen而不是手动编写代码:Passthrough模式下的HIDL HAL使用一个工具:
hidl-gen
来从.hal
文件生成C++和Java代码。这些代码包括了接口实现、客户端代理、服务注册等功能。这样可以避免手动编写重复和繁琐的代码,并保证代码与接口规范一致。 - 使用Android.bp而不是Android.mk:Passthrough模式下的HIDL HAL使用一个新的构建系统:Soong来编译代码。Soong使用
.bp
文件来描述HAL接口中需要传递或返回的数据类型和方法签名。.hal
文件通常位于hardware/interfaces/
目录下,并以.hal
结尾。IGpio.hal
就是一个.hal
文件,用于定义GPIO HAL相关接口。 - 使用Android.bp而不是Android.mk:Passthrough模式下的HIDL HAL使用一个新的构建系统:Soong来编译代码。Soong使用
.bp
文件来描述构建规则和依赖关系,而不是.mk
文件。.bp
文件通常位于HAL实现代码的同一目录下,并以.bp
结尾。Gpio.bp
就是一个.bp
文件,用于编译GPIO HAL相关代码。
Passthrough模式的优点是性能高,没有进程间通信的开销。但它也有一些缺点,主要有以下几点:
- 缺乏安全性和稳定性:Passthrough模式仍然存在传统HAL的安全性和稳定性问题。因为HAL服务作为一个库运行在客户端进程中,如果HAL出现了错误或崩溃,那么会影响到客户端进程,甚至导致整个系统崩溃。Passthrough模式也没有沙盒机制,也没有权限控制,任何进程都可以访问任何HAL模块,这可能带来安全风险。
- 缺乏灵活性和可扩展性:Passthrough模式下的HIDL HAL只能作为一个库链接到客户端进程中,这意味着客户端必须是一个C++的native服务或应用。如果客户端是一个Java层的应用或服务,那么仍然需要JNI层来桥接Java和C++的交互。这会增加代码的复杂度和维护成本。Passthrough模式也不支持多个客户端同时访问同一个HAL服务,这会限制HAL服务的可扩展性。
为了解决Passthrough模式的缺点,Android还提供了另一种HIDL模式:Binderized mode。
代码架构
HIDL (Hardware Interface Definition Language) 是Android Oreo (8.0) 引入的新的硬件抽象机制。Passthrough模式是其中的一种模式,它允许HAL以传统方式运行,但通过HIDL接口与Android框架进行交互。
- Java Framework Interface:
frameworks/base/core/java/android/os/SystemGpio.java
: 这是Android框架中的Java接口,应用程序和系统服务通过这个接口与GPIO硬件进行交互。
- HIDL Interface Definition:
hardware/interfaces/gpio/1.0/IGpio.hal
: 这是HIDL定义的接口,用于描述如何与GPIO硬件进行交互。
- Native Service Implementation:
frameworks/base/services/core/jni/com_android_server_GpioService.cpp
: 如果Java层需要直接访问硬件,那么它会通过这个JNI层与HIDL接口进行交互。
- HAL Implementation:
hardware/interfaces/gpio/1.0/default/Gpio.cpp
: 这是HIDL HAL的实现,它与底层的硬件驱动进行交互。
- Kernel Driver:
kernel/drivers/misc/gpio/gpio.c
: 这是Linux内核中的GPIO驱动,它直接与硬件进行交互。
架构图
Passthrough模式下的HIDL HAL的架构如下图所示:
3. HIDL Binderized mode:
交互方式
在Binderized模式下,HAL服务作为一个独立的进程运行。客户端和HAL之间的通信是通过Binder IPC进行的。这种模式与Passthrough模式不同,但有以下几点相同:
- 使用C++而不是C:Binderized模式下的HIDL HAL也是使用C++编写的。
- 使用.hal而不是.h:Binderized模式下的HIDL HAL也使用
.hal
文件来定义接口规范。 - 使用hidl-gen而不是手动编写代码:Binderized模式下的HIDL HAL也使用
hidl-gen
来从.hal
文件生成C++和Java代码。 - 使用Android.bp而不是Android.mk:Binderized模式下的HIDL HAL也使用Soong来编译代码。
Binderized模式的优点是安全性和稳定性高,因为HAL服务是在其自己的沙盒环境中运行的,并且可以通过SELinux进行权限控制。Binderized模式也支持多个客户端同时访问同一个HAL服务,提高了HAL服务的可扩展性。但它也有一些缺点,主要有以下几点:
- 性能低:Binderized模式下的HIDL HAL需要通过Binder IPC进行跨进程通信,这会带来额外的开销和延迟。特别是对于一些高频率或低延迟要求的硬件服务,如音频、视频、传感器等,Binder IPC可能会影响其性能和体验。
- 复杂度高:Binderized模式下的HIDL HAL需要实现一个独立的服务进程,这会增加代码的复杂度和维护成本。特别是对于一些简单或不常用的硬件服务,如GPIO、LED等,实现一个独立的服务进程可能是不必要的。
代码架构
Binderized模式是HIDL的另一种模式,其中HAL作为一个独立的进程运行,并通过Binder IPC与Android框架进行交互。
- Java Framework Interface:
frameworks/base/core/java/android/os/SystemGpio.java
: 这是Android框架中的Java接口,应用程序和系统服务通过这个接口与GPIO硬件进行交互。
- HIDL Interface Definition:
hardware/interfaces/gpio/1.0/IGpio.hal
: 这是HIDL定义的接口,用于描述如何与GPIO硬件进行交互。
- HAL Service Implementation:
hardware/interfaces/gpio/1.0/service/GpioService.cpp
: 这是Binderized模式下的HAL服务实现,它作为一个独立的进程运行,并通过Binder IPC与Android框架进行交互。
- Kernel Driver:
kernel/drivers/misc/gpio/gpio.c
: 这是Linux内核中的GPIO驱动,它直接与硬件进行交互。
架构图
Binderized模式下的HIDL HAL的架构如下图所示:
总结:HIDL提供了两种不同的模式:Passthrough mode 和 Binderized mode。这两种模式分别对应了不同的通信方式和架构。Passthrough模式的性能高,但安全性和稳定性低。Binderized模式的安全性和稳定性高,但性能低。可以根据具体的硬件服务和需求,选择合适的模式。
HAL的作用和意义
HAL是Android系统与硬件之间的桥梁,它为Android框架提供了一个标准接口,使得Android框架可以与多种硬件进行交互,而不需要知道硬件的具体实现细节。HAL的作用和意义主要有以下几点:
- 提高了硬件供应商和Android系统之间的兼容性:通过HAL,硬件供应商可以为其硬件提供一个满足Android规范的接口,而不必修改其驱动程序或Android系统。这使得硬件供应商可以更容易地将其硬件适配到Android平台上,并且可以更快地更新其驱动程序。
- 提高了Android系统和应用之间的抽象性:通过HAL,Android系统和应用可以使用统一的API来访问硬件服务,而不必关心硬件的具体实现细节。这使得Android系统和应用可以更容易地支持多种硬件平台和设备,并且可以更灵活地扩展和优化其功能。
- 提高了Android系统和应用之间的安全性和稳定性:通过HAL,Android系统和应用可以通过沙盒机制和权限控制来限制对硬件服务的访问,防止恶意或错误的操作对硬件或系统造成损害。通过HAL,Android系统和应用也可以隔离或恢复由于硬件服务出现错误或崩溃而导致的问题,保证系统和应用的正常运行。
未来的发展方向
随着Android系统和硬件平台的不断发展和创新,HAL也会随之变化和进化。可能的发展方向:
- 更多的HIDL化:HIDL是一种新的HAL定义方式,它提供了更好的版本控制和向后兼容性。目前,HIDL已经覆盖了大部分的硬件服务,如音频、视频、传感器、摄像头等。但仍有一些传统的HAL没有被HIDL化,如电源管理、内存管理等。未来,我们可能会看到更多的传统HAL被HIDL化,以提高其兼容性和可维护性。
- 更多的Binderized化:Binderized模式是一种新的HAL通信方式,它提供了更高的安全性和稳定性。目前,Binderized模式已经适用于大部分的HIDL HAL,如音频、视频、传感器、摄像头等。但仍有一些Passthrough模式没有被Binderized化,如GPIO、LED等。可能会看到更多的Passthrough模式被Binderized化,以提高其安全性和可扩展性。
- 更多的VTS化:VTS (Vendor Test Suite) 是一种新的HAL测试工具,它可以自动化地对HAL进行功能、兼容性、安安全性和性能测试。目前,VTS已经支持了大部分的HIDL HAL,如音频、视频、传感器、摄像头等。但仍有一些HAL没有被VTS化,如电源管理、内存管理等。可能会看到更多的HAL被VTS化,以提高其质量和可靠性。
总结
Android HAL是一个重要的组件,它为Android系统与硬件之间提供了一个标准接口。HAL的不同方式和架构反映了Android系统和硬件平台的不断发展和创新。在本文中,我们深入探讨学习了Android HAL的三种方式:Legacy HAL、HIDL Passthrough mode 和 HIDL Binderized mode的基础概念,并分析了它们之间的区别和联系。还总结了HAL的作用和意义,并展望了未来的发展方向。