eg:hello
一:Hardware
//作用,向系统申请设备号,设备节点实体化file_operations,cdv
struct cdev{
dev_t dev;
};
static struct file_operations hello_ops={
.owner = THIS_MODULE,
.ioctl = hello_ioctl,
.read = hello_read,
.write = hello_write,
.open = hello_open,
......
};
static __init xxx_init() 模块加载函数
{
}
static __exit xxx_exit() 模块卸载函数
{
}
module_init(hello_init);
module_exit(hello_exit);
二:HAL
/*
Android硬件抽象层规范的要求,
分别定义模块ID、模块结构体以及硬件接口结构体。
在硬件接口结构体中,fd表示设备文件描述符,对应我们将要处理的设备文件"/dev/hello",
set_val和get_val为该HAL对上提供的函数接口。
__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);
int (*get_val)(struct hello_device_t* dev, int* val);
};
/*设备打开和关闭接口*/
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,
}
};
//这里,实例变量名必须为HAL_MODULE_INFO_SYM,tag也必须为HARDWARE_MODULE_TAG,这是Android硬件抽象层规范规定的
//定义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;
}
//DEVICE_NAME定义为"/dev/hello"。由于设备文件是在内核驱动里面通过device_create创建的,
//而device_create创建的设备文件默认只有root用户可读写,而hello_device_open一般是由上层APP来调用的,
//这些APP一般不具有root权限,这时候就导致打开设备文件失败
//解决办法是类似于Linux的udev规则,打开Android源代码工程目录下,进入到system/core/rootdir目录,
//里面有一个名为ueventd.rc文件,往里面添加一行:
/dev/hello 0666 root root
//定义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;
}
三:JNI
/*
Android的Application Frameworks层提供硬件服务
Java应用程序通过JNI来调用硬件抽象层接口
准备好硬件抽象层模块,确保Android系统镜像文件system.img已经包含hello.default模块。
二. 进入到frameworks/base/services/jni目录,新建com_android_server_HelloService.cpp文件:
在com_android_server_HelloService.cpp文件中,实现JNI方法。注意文件的命令方法,com_android_server前缀表示的是包名,表示硬件服务HelloService是放在frameworks/base/services/java目录下的com/android/server目录的,即存在一个命令为com.android.server.HelloService的类。这里,我们暂时略去HelloService类的描述,在下一篇文章中,我们将回到HelloService类来。简单地说,HelloService是一个提供Java接口的硬件访问服务类。
接着定义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);
}
/*通过硬件抽象层定义的硬件访问接口读取硬件寄存器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......");
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));
}
};
注意,在hello_init函数中,通过Android硬件抽象层提供的hw_get_module方法来加载模块ID为HELLO_HARDWARE_MODULE_ID的硬件抽象层模块,其中,HELLO_HARDWARE_MODULE_ID是在<hardware/hello.h>中定义的。Android硬件抽象层会根据HELLO_HARDWARE_MODULE_ID的值在Android系统的/system/lib/hw目录中找到相应的模块,然后加载起来,并且返回hw_module_t接口给调用者使用。在jniRegisterNativeMethods函数中,第二个参数的值必须对应HelloService所在的包的路径,即com.android.server.HelloService。
三. 修改同目录下的onload.cpp文件,首先在namespace android增加register_android_server_HelloService函数声明:
namespace android {
..............................................................................................
int register_android_server_HelloService(JNIEnv *env);
};
在JNI_onLoad增加register_android_server_HelloService函数调用:
extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
{
.................................................................................................
register_android_server_HelloService(env);
.................................................................................................
}
这样,在Android系统初始化时,就会自动加载该JNI方法调用表。
四. 修改同目录下的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
五. 编译和重新找亿system.img:
USER-NAME@MACHINE-NAME:~/Android$ mmm frameworks/base/services/jni
USER-NAME@MACHINE-NAME:~/Android$ make snod
这样,重新打包的system.img镜像文件就包含我们刚才编写的JNI方法了,也就是我们可以通过Android系统的Application Frameworks层提供的硬件服务HelloService来调用这些JNI方法,进而调用低层的硬件抽象层接口去访问硬件了。前面提到,在这篇文章中,我们暂时忽略了HelloService类的实现,在下一篇文章中,我们将描述如何实现硬件服务HelloService,敬请关注。
四:java Frameworks
/*
一:
我们要先定义好通信接口。进入到frameworks/base/core/java/android/os目录
新增IHelloService.aidl接口定义文件:
IHelloService接口主要提供了设备和获取硬件寄存器val的值的功能,分别通过setVal和getVal两个函数来实现。
eg
*/
package android.os;
interface IHelloService {
void setVal(int val);
int getVal();
}
/*
二
返回到frameworks/base目录,打开Android.mk文件,修改LOCAL_SRC_FILES变量的值,增加IHelloService.aidl源文件:
eg
*/
core/java/android/os/IHelloService.aidl /
/*
三
编译IHelloService.aidl接口:
USER-NAME@MACHINE-NAME:~/Android$ mmm frameworks/base
这样,就会根据IHelloService.aidl生成相应的IHelloService.Stub接口。
*/
/*
四
.进入到frameworks/base/services/java/com/android/server目录,新增HelloService.java文件:
HelloService主要是通过调用JNI方法init_native、setVal_native和getVal_native
(见在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供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();
}
private static native boolean init_native();
private static native void setVal_native(int val);
private static native int getVal_native();
};
/*
五. 修改同目录的SystemServer.java文件,在ServerThread::run函数中增加加载HelloService的代码:
*/
try {
Slog.i(TAG, "Hello Service");
ServiceManager.addService("hello", new HelloService());
} catch (Throwable e) {
Slog.e(TAG, "Failure starting Hello Service", e);
}
/*
七. 编译HelloService和重新打包system.img:
USER-NAME@MACHINE-NAME:~/Android$ mmm frameworks/base/services/java
USER-NAME@MACHINE-NAME:~/Android$ make snod
这样,重新打包后的system.img系统镜像文件就在Application Frameworks层中包含了我们自定义的硬件服务HelloService了,并且会在系统启动的时候,自动加载HelloService。这时,应用程序就可以通过Java接口来访问Hello硬件服务了。我们将在下一篇文章中描述如何编写一个Java应用程序来调用这个HelloService接口来访问硬件,敬请期待。
*/
五:Application
/*
我们将在Android系统的应用层增加一个内置的应用程序,
这个内置的应用程序通过ServiceManager接口获取指定的服务,然后通过这个服务来获得硬件服务。
*/
package shy.luo.hello;
import shy.luo.hello.R;
import android.app.Activity;
import android.os.ServiceManager;
import android.os.Bundle;
import android.os.IHelloService;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class Hello extends Activity implements 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);
helloService = IHelloService.Stub.asInterface(
ServiceManager.getService("hello"));
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, "Remote Exception 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, "Remote Exception while writing value to device.");
}
}
else if(v.equals(clearButton)) {
String text = "";
valueText.setText(text);
}
}
}
/*
程序通过ServiceManager.getService("hello")来获得HelloService,
接着通过IHelloService.Stub.asInterface函数转换为IHelloService接口。
其中,服务名字“hello”是系统启动时加载HelloService时指定的,
而IHelloService接口定义在android.os.IHelloService中,
具体可以参考在Ubuntu上为Android系统的Application Frameworks层增加硬件访问服务一文。
这个程序提供了简单的读定自定义硬件有寄存器val的值的功能,
通过IHelloService.getVal和IHelloService.setVal两个接口实现。
*/
ServiceManager.addService("hello", new HelloService());
ServiceManager.getService("hello")
public class HelloService extends IHelloService.Stub {}
helloService = IHelloService.Stub.asInterface(
ServiceManager.getService("hello"));