硬件抽象层是google为了满足各个厂商不想公开源码而又不违背GPL规定的一种方式,我们在这里讲解如何添加hal层,当然我们这里为了简化没有实际的访问硬件,我在这里用一个变量(hello_number)当成是我们的硬件,首先hal层的定义有一些标准,这里我们先通过例子说明然后再将原理
首先杂货android源码目录hardware/libhardware/include/hardware下新建文件hello.h,内容如下
//目录:hardware/libhardware/include/hardware/hello.h
#ifndef ANDROID_HELLO_INTERFACE_H
#define ANDROID_HELLO_INTERFACE_H
#include
__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_value)(struct hello_device_t* dev, int val);
int (*get_value)(struct hello_device_t* dev, int* val);
};
__END_DECLS
#endif
接着在hardware/libhardware/modules目录下建立hello目录然后建立hello.c内容如下:
//目录:hardware/libhardware/modules/hello/hello.c
#define LOG_TAG "HelloStub"
#include
#include
#include
#include
#include
#include
#define MODULE_NAME "Hello"
#define MODULE_AUTHOR "jinpeng_he"
/*设备打开和关闭接口*/
static int hello_number;
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_value(struct hello_device_t* dev, int val);
static int hello_get_value(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,
}
};
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_value = hello_set_value;
dev->get_value = hello_get_value;
*device = &(dev->common);
LOGI(" hello hal successfully.");
return 0;
}
static int hello_device_close(struct hw_device_t* device) {
struct hello_device_t* hello_device = (struct hello_device_t*)device;
if(hello_device) {
LOGI(" hello hal close.");
}
return 0;
}
static int hello_set_value(struct hello_device_t* dev, int val) {
LOGI(" set value %d to device hello_number.", val);
hello_number=val;
return 0;
}
static int hello_get_value(struct hello_device_t* dev, int* val) {
if(!val) {
LOGE("Hello : error val pointer");
return -EFAULT;
}
*val=hello_number;
LOGI(" get value %d from device", *val);
return 0;
}
在hello目录底下创建Android.mk内容如下:
//目录:hardware/libhardware/modules/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)
编译:在android源码目录底下执行:
mmm hardware/libhardware/modules/hello
编译成功之后,在out/target/product/generic/system/lib/hw目录下看到hello.default.so。注:有可能这里不是generic各个公司会有自己的目录,这个和makefile环境设置有关
这里主要生成一个动态库,注意hal层接口定义的要求,详细可以查看相关资料不是很复杂
前面我们已经生成了hal层动态库,这里我们在hal层调用这个动态库,将我们的方法注册进java虚拟机
进入到frameworks/base/services/jni目录,新建com_android_server_HelloService.cpp文件,内容如下:
#define LOG_TAG "HelloService"
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include
#include
#include
#include
#include
namespace android
{
/*在硬件抽象层中定义的硬件访问结构体,参考*/
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_value(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_value(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 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是在
namespace android {
。。。。。
int register_android_server_HelloService(JNIEnv *env);
};
extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved)
{
。。。。。
register_android_server_HelloService(env);
}
修改frameworks/base/services/jni/Android.mk,添加如下内容
com_android_server_HelloService.cpp
LOCAL_SRC_FILES:= \
com_android_server_AlarmManagerService.cpp \
com_android_server_BatteryService.cpp \
com_android_server_InputManager.cpp \
。。。。。。
com_android_server_HelloService.cpp /
onload.cpp
编译mmm frameworks/base/services/jni
重新打包system.img:make snod
通过上面这几步我们已经将JNI层的接口定义好,烧写系统之后,我们的JNI层接口就会注册进虚拟机(因为我们在onload.cpp里面注册了这个会在系统启动虚拟机的时候将这些JNI方法注册起来)。到这里我们只是将我们的接口注册好了,但是还没有调用,所以下面就是在systemservice这个进程里面添加我们调用JNI接口的service,并且启动service,之后,我们就可以通过应用程序访问我们的硬件(这里用hello_number模拟)
什么是aidl文件呢?翻译过来是android 接口定义语言,通俗点讲,我的理解是这样的,因为我们aidl通常用于android binder进程间通信,binder进程间通信接口定义有一定的标准,所以既然是标准那么就相对比较固定,这样就可以自动化实现,所以就定义了这个aidl,我们只要在aidl里面定义好我们的接口,那么对应的接口,那么我们用于binder进程间通信的一些接口类就会自动生成这样减少了工程师的工作量。说白了就是代码自动生成功能
下面是我们定义的aidl文件
frameworks/base/core/java/android/os/IHelloService.aidl,内容如下:
package android.os;
interface IHelloService {
void setValue(int val);
int getValue();
}
frameworks/base/Android.mk文件,修改LOCAL_SRC_FILES变量的增加我们这里定义的aidl文件
LOCAL_SRC_FILES += /
。。。。
core/java/android/os/IHelloService.aidl /
。。。。
编译:mmm frameworks/base
就会根据IHelloService.aidl生成相应的IHelloService.Stub接口
新增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();
}
private static native boolean init_native();
private static native void setVal_native(int val);
private static native int getVal_native();
};
由于HelloService继承IHelloService.Stub所以他就天然的有了binder进程间通信的接口(aidl文件编译生成)
到这里我们访问硬件的service就算定义好了,我们只用将这个service在systemserver这个进程里面注册进servicemanager就可以了
在
@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);
}
}
编译HelloService和重新打包system.img
mmm frameworks/base/services/java
make snod
然后烧录我们的HelloService就注册进servicemanager这个进程了,之后我们的应用程序只需要通过binder进程间通信就可以访问我们的HelloService,也就是访问我们的硬件了。
package jinpeng.he.hello;
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 = "jinpeng.he.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);
}
}
}
将该工程文件放在packages/experimental/目录底下并修改该目录底下的Android.mk:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := Hello
include $(BUILD_PACKAGE)
编译:mmm packages/experimental/Hello
打包system.img:make snod
这样system.img里面就内置了Hello.apk
烧录启动系统之后就可以通过这个apk操作硬件(这里是hello_number变量)
1,进程间通信,在我们这里一共牵扯到了zygote,systemserver,servicemanager,还有我们的应用程序这四个进程
2,我在这里模拟一下这几个进程的关系
首先系统启动,内核将各个driver注册起来之后启动第一个进程init进程,在init进程启动之后会启动zygote进程。
zygote是一个比较重要的进程他会注册我们的JNI接口,在这里就是JNI调用hal层操作硬件的接口,然后启动虚拟机,紧接着启动systemserver进程。(其实我们android所有应用程序都是zygote通过系统调用fork出来的)。
systemserver进程启动之后通过ServiceManager.addService(“hello”, new HelloService());将我们的service注册进servicemanager进程。
servicemanager进程是一个native进程用C实现进程,当然在java有binder通信接口。这个servicemanager进程就是后台不断的接收应用的添加service,请求服务等通信。就是一个死循环,不过如果没有请求就休眠。
我们的应用程序通过helloService = IHelloService.Stub.asInterface(
ServiceManager.getService(“hello”)); 实际就是通过binder进程间通信让servicemanager找到我们注册的helloservice,然后我们就可以通过helloService调用我们在service里面定义的接口了。当然也是代理接口