Android HAL 介绍
2010年11月18日
HAL 介绍 Android 的 HAL (硬件抽像层)是 Google 因应厂商「希望不公开源码」的要求下,所推出的新观念,其架构如下图。虽然 HAL 现在的「抽象程度」还不足,现阶段实作还不是全面符合 HAL 的架构规划,不过也确实给了我们很好的思考空间。
图 1 : Android HAL 架构规划
这是 Patrick Brady (Google) 在 2008 Google I/O 所发表的演讲「 Anatomy & Physiology of an Android 」中,所提出的 Android HAL 架构图。从这张架构图我们知道, HAL 的目的是为了把 Android framework 与 Linux kernel 完整「隔开」。让 Android 不至过度依赖 Linux kernel ,有点像是「 kernel independent 」的意思,让 Android framework 的开发能在不考虑驱动程序的前提下进行发展。
在 Android 原始码里, HAL 主要的实作储存于以下目录:
1. libhardware_legacy/ - 过去的实作、采取链接库模块的观念进行
2. libhardware/ - 新版的实作、调整为 HAL stub 的观念
3. ril/ - Radio Interface Layer
在 HAL 的架构实作成熟前(即图 1 的规划),我们先就目前 HAL 现况做一个简单的分析。另外,目前 Android 的 HAL 实作,仍旧散布在不同的地方,例如 Camera 、 WiFi 等,因此上述的目录并不包含所有的 HAL 程序代码。
HAL 的过去
图 2 : Android HAL / libhardware_legacy 过去的 libhardware_legacy 作法,比较是传统的「 module 」方式,也就是将 *.so 档案当做「 shared library 」来使用,在 runtime ( JNI 部份)以 direct function call 使用 HAL module 。透过直接函数呼叫的方式,来操作驱动程序。
当然,应用程序也可以不需要透过 JNI 的方式进行,直接以加载 *.so ?( dlopen )的做法呼叫 *.so 里的符号( symbol )也是一种方式。
HAL 的现实状况
图 3 : Android HAL / libhardware 现在的 libhardware 作法,就有「 stub 」的味道了。 HAL stub 是一种代理人( proxy )的概念, stub 虽然仍是以 *.so ?的形式存在,但 HAL 已经将 *.so 档隐藏起来了。 Stub 向 HAL 「提供」操作函数( operations ),而 runtime 则是向 HAL 取得特定模块( stub )的 operations ,再 callback 这些操作函数。这种以 indirect function call 的实作架构,让 HAL stub 变成是一种「包含」关系,即 HAL 里包含了许许多多的 stub (代理人)。 Runtime 只要说明「类型」,即 module ID ,就可以取得操作函数。 HAL 的实现主要在 hardware.c 和 hardware.h 文件中。实质也是通过加载 *.so 档( dlopen )从而呼叫 *.so 里的符号( symbol )实现。这里所谓的代理,我感觉不过是 Android 统一定义了三个结构体,然后通过几个"必须"从而统一了调用接口
HAL 的未来发展?
新的 HAL 做法,倾向全面采用 JNI 的方式进行。也就是,在 Android 的架构中,修改 Android runtime 实作(即「 Core Library 」),在取得 HAL 模块的 operations 后再做 callback 操作。将 HAL 模块完全放在 HAL 里面。以上我想应该是针对 framework 开发来说的。如果仅是使用 hal 访问硬件,完全可以不修改 core library 。
一、 HAL 使用步骤:
(1)Java AP 初始化一个 java service , 然后根据需求组合调用 java service 提供的接口。
(2)Java Service 设置 Native Interface 声明, 并在初始化时加载 Native Service 所在的库 .Native Service 实际上是一个动态链接库, 通过 JNI 和 Java Service 交互。
(3) 通过 OnLoad 方法注册与 Java Service 的 Native Function 之间的对应 JNI table 。
(4) 通过 HAL Module ID 获得当前实际板上对应的硬件设备的 Module , 并通过此 Module 的 HAL 接口 Open 获得硬件设备的 device 实例。 通过 device 提供的接口组合本地函数的实现。
(5) 编写 HAL stub, 对具体的硬件设备初始化对应 Module 和 Device 实例, 并实现对硬件驱动的 API 封装。
(6)HAL 模块要以 MODULE_ID.platform.so 的名字存放在文件系统的 /system/lib/hw/ 下面。
Android HAL 层主要在 hardware 目录下,其中 hardwarelibhardware 下是同一用模块的概念来加载 HAL 的 .so 库。 这里以一个简单的 led 小例子(假设备,不涉及硬件操作)来说明具体实现步骤。
二、 HAL Stub 实现步骤 (Implementation)
1. 设计自已的 wrapper data structure
* 编写头文件 led.h
* 定义 struct led_module_t
* 框架提供的 struct hw_module_t 、 struct hw_device_t 必须 放在第一个 field ,并取名为 common 。
* 可参考 hardware/hardware.h
struct led_module_t {
struct hw_module_t common;
/* support API for LEDServices constructor */
};
2. led_module_t 的意义
声明初始化时期 (new object) 的 supporting API 、在 constructor 里会使用到。
3. 定义 led_control_device_t
声明控制时期的 supporting API 、在 Manager API 里会使用到。
struct led_control_device_t {
struct hw_device_t common;
/* supporting control APIs go here */
int (*getcount_led)(struct led_control_device_t *dev);
int (*set_on)(struct led_control_device_t *dev);
int (*set_off)(struct led_control_device_t *dev);
};
4. 每? HAL stub 都要声明 module ID
#define LED_HARDWARE_MODULE_ID "led"
5. 声明 Stub operations 并实现 callback functions
Stub 的 module 结构体必须 取名为 HAL_MODULE_INFO_SYM ,此名不可更改。
const struct led_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: LED_HARDWARE_MODULE_ID,
name: "led HAL module",
author: "gggggg",
methods: &led_module_methods,
},
/* supporting APIs go here */
};
以下是具体的文件内容。
led.h 文件:
#include
#include
#include
#include
#include
#define LED_HARDWARE_MODULE_ID "led"
struct led_module_t {
struct hw_module_t common;
/* support API for LEDServices constructor */
};
struct led_control_device_t { struct hw_device_t common; /* supporting control APIs go here */ int (*getcount_led)(struct led_control_device_t *dev); int (*set_on)(struct led_control_device_t *dev); int (*set_off)(struct led_control_device_t *dev); }; struct led_control_context_t { struct led_control_device_t device; }; led.c 文件:
#define LOG_TAG "LedStub"
#include
#include
#include
#include
#include
#include "../include/led.h"
static int led_device_close(struct hw_device_t* device)
{
struct led_control_context_t* ctx = (struct led_control_context_t*)device;
if (ctx) {
free(ctx);
}
return 0;
}
static int led_getcount(struct led_control_device_t *dev)
{
LOGI("led_getcount");
return 4;
}
static int led_set_on(struct led_control_device_t *dev)
{
//FIXME: do system call to control gpio led
LOGI("led_set_on");
return 0;
}
static int led_set_off(struct led_control_device_t *dev)
{
//FIXME: do system call to control gpio led
LOGI("led_set_off");
return 0;
}
static int led_device_open(const struct hw_module_t* module, const char* name,
struct hw_device_t** device)
{
struct led_control_context_t *context;
LOGD("led_device_open");
context = (struct led_control_context_t *)malloc(sizeof(*context));
memset(context, 0, sizeof(*context));
//HAL must init property
context->device.common.tag= HARDWARE_DEVICE_TAG;
context->device.common.version = 0;
context->device.common.module= module;
context->device.common.close = led_device_close;
// 初始化控制 API
context->device.set_on= led_set_on;
context->device.set_off= led_set_off;
context->device.getcount_led = led_getcount;
*device= (struct hw_device_t *)&(context->device);
return 0;
}
static struct hw_module_methods_t led_module_methods = {
open: led_device_open
};
const struct led_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: LED_HARDWARE_MODULE_ID, name: "led HAL module", author: "gggggg", methods: &led_module_methods, }, /* supporting APIs go here */ }; Android.mk 文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_PRELINK_MODULE := false
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := led.c
LOCAL_MODULE := led.goldfish
include $(BUILD_SHARED_LIBRARY)
三、在 Eclipse 中建一个工程使用我们编写的 led stub 。
1. 定义两个类,源码如下:
Myhal.java 文件:
package com.hello.MyHal;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.hello.LedService.LedService;;
public class Myhal extends Activity implements View.onClickListener {
static LedService led_srv;
static Button btn;
static boolean iflag = false;
/** Called when the activity is first created. */
[email protected]
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.i("Java App", "OnCreate");
led_srv = new LedService();
Log.i("Java App", "Load Java Serivce");
btn = (Button)this.findViewById(R.id.mybtn);
btn.setonClickListener(this);
}
public void onClick(View v) {
Log.i("Java App", "btnonClicked");
String title = new String();
if (iflag) {
title = led_srv.set_off();
btn.setText("Turn On");
setTitle(title);
iflag = false;
} else {
title = led_srv.set_on();
btn.setText("Turn Off");
setTitle(title);
iflag = true;
}
}
}
LedService.java 文件:
package com.hello.LedService;
import android.util.Log;
public final class LedService {
/*
* load native service.
*/
static {
Log.i ( "Java Service" , "Load Native Serivce LIB" );
System.loadLibrary ( "led_runtime" );
}
public LedService() { int icount ; Log.i ( "Java Service" , "do init Native Call" ); _init (); icount = _get_count (); Log.d ( "Java Service" , "Init OK " ); } /* * LED native methods. */ public String set_on() { Log.i ( "com.hello.LedService" , "LED On" ); _set_on (); return "led on" ; } public String set_off() { Log.i ( "com.hello.LedService" , "LED Off" ); _set_off (); return "led off" ; } /* * declare all the native interface. */ private static native boolean _init(); private static native int _set_on(); private static native int _set_off(); private static native int _get_count(); } 其中 LedService 类通过 native 函数使用 led stub 提供的功能。
2. 接下来我们实现通过 jni 接口实现 native 方法。
这里无需使用 javah 生成相应的头文件。
com_hello_LedService.cpp 文件:
#define LOG_TAG "LedService"
#include "utils/Log.h"
#include
#include
#include
#include
#include
#include "../../led_stub/include/led.h"
static led_control_device_t *sLedDevice = 0;
static led_module_t* sLedModule=0;
static int get_count(void)
{
return 0;
}
static jint led_setOn(JNIEnv* env, jobject thiz) {
//if (sLedDevice) {
LOGI("led_set_on");
sLedDevice->set_on(sLedDevice);
//}
return 0;
}
static jint led_setOff(JNIEnv* env, jobject thiz) {
//if (sLedDevice) {
LOGI("led_set_off");
sLedDevice->set_off(sLedDevice);
//}
return 0;
}
/** helper APIs */
static inline int led_control_open(const struct hw_module_t* module,
struct led_control_device_t** device) {
LOGI("led_control_ope");
return module->methods->open(module,
LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}
static jint led_init(JNIEnv *env, jclass clazz)
{
led_module_t const * module;
LOGI("led_init");
if (hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {
LOGI("get Module OK");
sLedModule = (led_module_t *) module;
if (led_control_open(&module->common, &sLedDevice) != 0) {
LOGI("led_init error");
return -1;
}
}
LOGI("led_init success");
return 0;
}
/*
* Array of methods.
*
* Each entry has three fields: the name of the method, the method
* signature, and a pointer to the native implementation.
*/
static const JNINativeMethod gMethods[] = { {"_init", "()Z", (void*)led_init}, { "_set_on", "()I", (void*)led_setOn }, { "_set_off", "()I", (void*)led_setOff }, { "_get_count", "()I", (void*)get_count }, }; static int registerMethods(JNIEnv* env) { static const char* const kClassName = "com/hello/LedService/LedService"; jclass clazz; /* look up the class */ clazz = env->FindClass(kClassName); if (clazz == NULL) { LOGE("Can't find class %s ", kClassName); return -1; } /* register all the methods */ if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK) { LOGE("Failed registering methods for %s ", kClassName); return -1; } /* fill out the rest of the ID cache */ return 0; } /* * This is called by the VM when the shared library is first loaded. */ jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; LOGI("JNI_OnLoad"); if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { LOGE("ERROR: GetEnv failed "); goto fail; } assert(env != NULL); if (registerMethods(env) != 0) { LOGE("ERROR: PlatformLibrary native registration failed "); goto fail; } /* success -- return valid version number */ result = JNI_VERSION_1_4; fail: return result; } Android.mk 文件:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
#LOCAL_MODULE_TAGS := eng
# This is the target being built.
LOCAL_MODULE:= libled_runtime
# All of the source files that we will compile.
LOCAL_SRC_FILES:=
com_hello_LedService.cpp
# All of the shared libraries we link against.
LOCAL_SHARED_LIBRARIES :=
libandroid_runtime
libnativehelper
libcutils
libutils
libhardware
# No static libraries.
LOCAL_STATIC_LIBRARIES :=
# Also need the JNI headers.
LOCAL_C_INCLUDES +=
$(JNI_H_INCLUDE)
# No specia compiler flags.
LOCAL_CFLAGS +=
# Don't prelink this library. For more efficient code, you may want
# to add this library to the prelink map and set this to true.
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
将 Led stub , LedService 相关文件放到 development/my_module 分别 make 即可生成相应的 so 文件。使用 adb push 命令安装到虚拟机上运行即可。
文件组织机构如下:
[email protected]:~/work/android/source_android/development /my_module$ pwd
/home/a/work/android/source_android/development/my _module
[email protected]:~/work/android/source_android/development /my_module$ tree hal
hal
|-- LedService
| `-- jni
| |-- Android.mk
| `-- com_hello_LedService.cpp
`-- led_stub
|-- include
| `-- led.h
`-- module
|-- Android.mk
`-- led.c
6 directories, 7 files
四、编译运行中遇到的问题及解决方法:
1.usr/bin/ld: cannot find -lz
collect2: ld 返回 1
只是库命名的问题,简单的做了个软链接,一切搞定
ln -svf /lib/libz.so.1 /lib/libz.so
这个库文件与软连接的命名只差了一个 .1
2.frameworks/base/tools/aidl/AST.cpp:10: error: 'fprintf' was not declared in this scope 的错误
下载 gcc-4.3 和 g++-4.3
apt-get install gcc-4.3 g++-4.3
大约十多兆,然后
进入 /usr/bin
cd /usr/bin
建个软连接
ln -s gcc-4.3 gcc
ln -s g++-4.3 g++
33./bin/bash: flex :找不到命令
make: *** [out/host/linux-x86/obj/EXECUTABLES/aidl_intermedi ates/aidl_language_l.cpp] 错误 127 [email protected]:~/work/android/source_android$ sudo apt-get install flex 4.JNI 的 .so 文件放到 /system/lib 下,而 hal moudule 需要放到 /system/lib/hw 下,且命名需符合约定, eg : led.goldfish.so 。