Android的硬件抽象层:
简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。
也就是说,把对硬件的支持分成了两层,
一层放在用户空间(User Space),(硬件抽象层)
一层放在内核空间(Kernel Space),(Linux内核驱动程序)
下面这个图阐述了硬件抽象层在Android系统中的位置,以及它和其它层的关系:
二,简单的总结
进入到Android源代码工程的external目录,创建hello目录:
cd external
mkdir hello
在hello目录中新建Android.mk文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := hello
LOCAL_SRC_FILES := $(call all-subdir-c-files)
include $(BUILD_EXECUTABLE)
注意,BUILD_EXECUTABLE表示我们要编译的是可执行程序。
使用mmm命令进行编译:
mmm ./external/hello
编译成功后,就可以在out/target/product/gerneric/system/bin目录下,看到可执行文件hello了。
重新打包Android系统文件system.img:
make snod
这样,重新打包后的system.img文件就包含刚才编译好的hello可执行文件了。
七. 运行模拟器,使用/system/bin/hello可执行程序来访问Linux内核驱动程序。
emulator -kernel ./kernel/common/arch/arm/boot/zImage &
adb shell
cd system/bin
./hello
------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------
三,编写硬件抽象层
进入到在hardware/libhardware/include/hardware目录,新建hello.h文件:
cd hardware/libhardware/include/hardware
vi hello.h
hello.h文件的内容如下:
#ifndef ANDROID_HELLO_INTERFACE_H
#define ANDROID_HELLO_INTERFACE_H
#include <hardware/hardware.h>
__BEGIN_DECLS
/*定义模块ID*/
#define HELLO_HARDWARE_MODULE_ID "hello"
/*硬件模块结构体*/
struct hello_module_t {
struct hw_module_t common;
};
/*硬件接口结构体*/
struct hello_device_t {
struct hw_device_t common;
int fd; // 设备文件描述符
int (*set_val)(struct hello_device_t* dev, int val); // 为该HAL对上提供的函数接口
int (*get_val)(struct hello_device_t* dev, int* val);
};
__END_DECLS
#endif
进入到hardware/libhardware/modules目录,新建hello目录,并添加hello.c文件。 hello.c的内容较多,我们分段来看。
首先是包含相关头文件和定义相关结构:
#define LOG_TAG "HelloStub"
#include <hardware/hardware.h>
#include <hardware/hello.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h>
#define DEVICE_NAME "/dev/hello"
#define MODULE_NAME "Hello"
#define MODULE_AUTHOR "shyluo@gmail.com"
/*设备打开和关闭接口*/
static int hello_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
static int hello_device_close(struct hw_device_t* device);
/*设备访问接口*/
static int hello_set_val(struct hello_device_t* dev, int val);
static int hello_get_val(struct hello_device_t* dev, int* val);
/*模块方法表*/
static struct hw_module_methods_t hello_module_methods = {
open: hello_device_open
};
/*模块实例变量*/
struct hello_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: HELLO_HARDWARE_MODULE_ID,
name: MODULE_NAME,
author: MODULE_AUTHOR,
methods: &hello_module_methods,
}
};
<Android硬件抽象层规范规定>
实例变量名必须为HAL_MODULE_INFO_SYM,
tag也必须为HARDWARE_MODULE_TAG。
定义hello_device_open函数:
static int hello_device_open ( const struct hw_module_t* module, const char* name, struct hw_device_t** device ) {
struct hello_device_t * dev;
dev = (struct hello_device_t*)malloc(sizeof(struct hello_device_t));
if(!dev) {
LOGE("Hello Stub: failed to alloc space");
return -EFAULT;
}
memset(dev, 0, sizeof(struct hello_device_t));
dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (hw_module_t*)module;
dev->common.close = hello_device_close;
dev->set_val = hello_set_val;
dev->get_val = hello_get_val;
if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) {
LOGE("Hello Stub: failed to open /dev/hello -- %s.", strerror(errno));free(dev);
return -EFAULT;
}
*device = &(dev->common);
LOGI("Hello Stub: open /dev/hello successfully.");
return 0;
}
定义hello_device_close、hello_set_val和hello_get_val这三个函数:
static int hello_device_close (struct hw_device_t* device) {
struct hello_device_t * hello_device = (struct hello_device_t*)device;
if(hello_device) {
close(hello_device->fd);
free(hello_device);
}
return 0;
}
static int hello_set_val(struct hello_device_t* dev, int val) {
LOGI("Hello Stub: set value %d to device.", val);
write(dev->fd, &val, sizeof(val));
return 0;
}
static int hello_get_val(struct hello_device_t* dev, int* val) {
if(!val) {
LOGE("Hello Stub: error val pointer");
return -EFAULT;
}
read(dev->fd, val, sizeof(*val));
LOGI("Hello Stub: get value %d from device", *val);
return 0;
}
继续在hello目录下新建Android.mk文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := hello.c
LOCAL_MODULE := hello.default
include $(BUILD_SHARED_LIBRARY)
注意,LOCAL_MODULE的定义规则,hello后面跟有default,hello.default能够保证我们的模块总能被硬象抽象层加载到。
编译:
mmm hardware/libhardware/modules/hello
编译成功后,就可以在out/target/product/generic/system/lib/hw目录下看到hello.default.so文件了。
重新打包Android系统镜像system.img:
make snod
重新打包后,system.img就包含我们定义的硬件抽象层模块hello.default了。
虽然我们在Android系统为我们自己的硬件增加了一个硬件抽象层模块,但是现在Java应用程序还不能访问到我们的硬件。
我们还必须编写JNI方法和在Android的Application Frameworks层增加API接口,才能让上层Application访问我们的硬件。
------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------
四:JNI方法
如何为Android硬件抽象层接口编写JNI方法,以便使得上层的Java应用程序能够使用下层提供的硬件服务。
一. 确保Android系统镜像文件system.img已经包含hello.default模块。
二. 进入到frameworks/base/services/jni目录,新建com_android_server_HelloService.cpp文件:
cd frameworks/base/services/jni
vi com_android_server_HelloService.cpp
com_android_server前缀表示的是包名,表示硬件服务HelloService是放在frameworks/base/services/java目录下的com/android/server目录的,即存在一个命令为com.android.server.HelloService的类。
首先是包含相应的头文件:
#define LOG_TAG "HelloService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <utils/misc.h>
#include <utils/Log.h>
#include <hardware/hardware.h>
#include <hardware/hello.h>
#include <stdio.h>
/* 接着定义hello_init、hello_getVal和hello_setVal三个JNI方法:*/
namespace android
{
/*在硬件抽象层中定义的硬件访问结构体,参考<hardware/hello.h>*/
struct hello_device_t* hello_device = NULL;
/*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/
static void hello_setVal (JNIEnv* env, jobject clazz, jint value) {
int val = value;
LOGI("Hello JNI: set value %d to device.", val);
if(!hello_device) {
LOGI("Hello JNI: device is not open.");
return;
}
hello_device->set_val(hello_device, val); // 在抽象层的open中定义
}
/*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/
static jint hello_getVal(JNIEnv* env, jobject clazz) {
int val = 0;
if(!hello_device) {
LOGI("Hello JNI: device is not open.");
return val;
}
hello_device->get_val(hello_device, &val);
LOGI("Hello JNI: get value %d from device.", val);
return val;
}
/*通过硬件抽象层定义的硬件模块打开接口 打开硬件设备*/
static inline int hello_device_open (const hw_module_t* module, struct hello_device_t** device) {
return module->methods->open(module, HELLO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}
/*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/
static jboolean hello_init (JNIEnv* env, jclass clazz) {
hello_module_t* module;
LOGI("Hello JNI: initializing......");
/* 加载模块ID为HELLO_HARDWARE_MODULE_ID的硬件抽象层模块, Android硬件抽象层会根据HELLO_HARDWARE_MODULE_ID的值
* 在Android系统的/system/lib/hw目录中找到相应的模块,然后加载起来,并且返回hw_module_t接口给调用者使用。
*/
if ( hw_get_module ( HELLO_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module ) == 0 ) {
LOGI("Hello JNI: hello Stub found.");
if(hello_device_open(&(module->common), &hello_device) == 0) {
LOGI("Hello JNI: hello device is open.");
return 0;
}
LOGE("Hello JNI: failed to open hello device.");
return -1;
}
LOGE("Hello JNI: failed to get hello stub module.");
return -1;
}
/*JNI方法表*/
static const JNINativeMethod method_table[ ] = {
{"init_native", "()Z", (void*)hello_init},
{"setVal_native", "(I)V", (void*)hello_setVal},
{"getVal_native", "()I", (void*)hello_getVal},
};
/*注册JNI方法*/
int register_android_server_HelloService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table) );
// 必须对应HelloService所在的包的路径
}
};
在Android系统初始化时,使其自动加载该JNI方法调用表
----------------------------------------------------------------------------
修改同目录下的onload.cpp文件,
(1)在namespace android增加 register_android_server_HelloService函数声明:
namespace android {
..............................................................................................
int register_android_server_HelloService(JNIEnv *env);
};
(2)在JNI_onLoad 增加 register_android_server_HelloService函数调用:
extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
{
.................................................................................................
register_android_server_HelloService(env);
.................................................................................................
}
(3)修改同目录下的Android.mk文件,在 LOCAL_SRC_FILES变量 中增加一行:
LOCAL_SRC_FILES:= \
com_android_server_AlarmManagerService.cpp \
com_android_server_BatteryService.cpp \
com_android_server_InputManager.cpp \
com_android_server_LightsService.cpp \
com_android_server_PowerManagerService.cpp \
com_android_server_SystemServer.cpp \
com_android_server_UsbService.cpp \
com_android_server_VibratorService.cpp \
com_android_server_location_GpsLocationProvider.cpp \
com_android_server_HelloService.cpp /
onload.cpp
最后编译
mmm frameworks/base/services/jni
make snod
这样,重新打包的system.img镜像文件就包含我们刚才编写的JNI方法了,
我们可以通过Android系统的Application Frameworks层提供的硬件服务HelloService来调用这些JNI方法,进而调用低层的硬件抽象层接口去访问硬件了。
五:提供Java访问硬件服务接口
Linux内核层、硬件抽象层和运行时库层提供的自定义硬件服务接口,这些接口都是通过C或者C++语言来实现的。
以下,我们将介绍如何在Android系统的Application Frameworks层提供Java接口的硬件服务。
(为什么用代理?)
在Android系统中,硬件服务一般是运行在一个独立的进程中为各种应用程序提供服务。因此,调用这些硬件服务的应用程序与这些硬件服务之间的通信需要通过代理来进行。为此,我们要先定义好通信接口。
(1)进入到frameworks/base/core/java/android/os目录,新增IHelloService.aidl接口定义文件:
cd frameworks/base/core/java/android/os
vi IHelloService.aidl
IHelloService.aidl定义了IHelloService接口:
package android.os;
interface IHelloService{
void setVal (int val);
int getVal ( );
}
该接口主要提供了设备获取硬件寄存器val的值的功能,分别通过setVal和getVal两个函数来实现。
(2)返回到frameworks/base目录,打开Android.mk文件,修改LOCAL_SRC_FILES变量的值,增加IHelloService.aidl源文件:
core/java/android/os/IHelloService.aidl /
(3)编译IHelloService.aidl接口:
mmm frameworks/base
这样,就会根据IHelloService.aidl生成相应的IHelloService.Stub接口。
(4)进入到frameworks/base/services/java/com/android/server目录,新增HelloService.java文件:
package com.android.server;
import android.content.Context;
import android.os.IHelloService;
import android.util.Slog;
public class HelloService extends IHelloService . Stub {
private static final String TAG = "HelloService";
HelloService() {
init_native();
}
public void setVal(int val) {
setVal_native(val);
}
public int getVal() {
return getVal_native();
}
/* HelloService主要是通过调用JNI方法init_native、setVal_native和getVal_native来提供硬件服务 */
private static native boolean init_native();
private static native void setVal_native(int val);
private static native int getVal_native();
};
(5) 修改同目录的SystemServer.java文件,在ServerThread::run函数中增加加载HelloService的代码:
@Override
public void run() {
....................................................................................
try {
Slog.i(TAG, "Hello Service");
ServiceManager. addService("hello", new HelloService());
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Hello Service", e);
}
......................................................................................
}
(6)编译HelloService和重新打包system.img:
mmm frameworks/base/services/java
make snod
这样,重新打包后的system.img系统镜像文件就在Application Frameworks层中包含了我们自定义的硬件服务HelloService了,并且会在系统启动的时候,自动加载HelloService。
这时,应用程序就可以通过Java接口来访问Hello硬件服务了。
------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------
六:Java调用实例
public class Hello extends Activityimplements OnClickListener {
private final static String LOG_TAG ="shy.luo.renju.Hello";
private IHelloService helloService = null;
private EditText valueText = null;
private Button readButton = null;
private Button writeButton = null;
private Button clearButton = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// IHelloService接口定义在android.os.IHelloService中,
helloService= IHelloService.Stub.asInterface( // 转换为IHelloService接口
ServiceManager.getService("hello")); // 获得HelloService
// 服务名字“hello”是系统启动时加载HelloService时指定
valueText = (EditText)findViewById(R.id.edit_value);
readButton = (Button)findViewById(R.id.button_read);
writeButton = (Button)findViewById(R.id.button_write);
clearButton = (Button)findViewById(R.id.button_clear);
readButton.setOnClickListener(this);
writeButton.setOnClickListener(this);
clearButton.setOnClickListener(this);
Log.i(LOG_TAG, "Hello Activity Created");
}
@Override
public void onClick(View v) {
if(v.equals(readButton)) {
try {
int val =helloService.getVal();
String text = String.valueOf(val);
valueText.setText(text);
} catch (RemoteException e) {
Log.e(LOG_TAG, "RemoteException while reading value from device.");
}
}
else if(v.equals(writeButton)) {
try {
String text =valueText.getText().toString();
int val =Integer.parseInt(text);
helloService.setVal(val);
} catch (RemoteException e) {
Log.e(LOG_TAG, "RemoteException while writing value to device.");
}
}
else if(v.equals(clearButton)) {
String text = "";
valueText.setText(text);
}
}
}
本文转载于: