HAL/JNI简明笔记(二)——基于stub架构的HAL实例

前面文章一说完HAL的基本架构,下面以实例展现整个添加新模块的过程。

工作中自己编写的一个基于stub结构的HAL程序,包含:hal,jni,java/service三个层次,依次被后一个调用。hal完全是调用内核驱动的接口,jni就是一个让java能调用c的转换接口,java/service就是将硬件接口以java的形式提供给framework。

程序是关于触摸屏相关接口,我将简化只保留一个接口,其实框架函数保留。


(1)hal层:

包含ctp.h和ctp.c两个文件,还有对应的Android.mk

msm8x12\hardware\libhardware\include\hardware\ctp.h

#ifndef ANDROID_CTP_INTERFACE_H
#define ANDROID_CTP_INTERFACE_H

#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>

#include <hardware/hardware.h>

__BEGIN_DECLS

/**
 * The id of this module
 */
#define CTP_HARDWARE_MODULE_ID "ctp"

/**
 * Every hardware module must have a data structure named HAL_MODULE_INFO_SYM
 * and the fields of this data structure must begin with hw_module_t
 * followed by module specific information.
 */
struct ctp_module_t {
    struct hw_module_t common;
};

/**
 * Every device data structure must begin with hw_device_t
 * followed by module specific public methods and attributes.
 */
struct ctp_device_t {
    struct hw_device_t common;

	/** Input device number */
	int input_number;

    /**
     *GetCtpVer
     *
     * Returns: firmware CTP version
     */
	int (*GetCtpVer)(struct ctp_device_t* dev,char *ctp_ver);

};


__END_DECLS

#endif  // ANDROID_CTP_INTERFACE_H

ctp.h其实就是在继承hardware.h的基本结构体来自定义自己模块的结构体,在ctp_device_t中第一个成员必须是hw_device_t,这样便于后边代码进行指针强制转换,实际上他们的首地址确实是一样的,后面的部分对应于增加的方法接口或属性;同样ctp_module_t第一个成员也必须是hw_module_t,后面是自定义方法或属性,但是一般做法让后面为空,即ctp_module_t在内存上就是hw_module_t;另外ctp.h还要定义该模块的module ID,用于hw_get_module以此识别来查找ctp的module。

其中的__BEGIIN_DECLS和__END_DECLS表示让c++代码能用C编译出的代码,在cdefs.h中定义如下:

  1. #if defined(__cplusplus)
  2.        #define __BEGIN_DECLS extern "C" {
  3.        #define __END_DECLS }
  4.        #else
  5.        #define __BEGIN_DECLS
  6.        #define __END_DECLS
  7. #endif


msm8x12\hardware\qcom\ctp\hal\ctp.c

#include <cutils/log.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <hardware/ctp.h>

#ifdef CONFIG_ARCH_MSM8610_D510		//atmel TP
char const* const GLOVE_ATTR_DEV
	= "/sys/class/input/input%d/device/glove_mode";
#else									//synaptics	TP
char const* const GLOVE_ATTR_DEV
	= "/sys/class/input/input%d/rmidev/glove_mode";
#endif

#ifdef CONFIG_ARCH_MSM8610_D510
char const* const CTP_VER_DEV
	= "/sys/class/input/input%d/device/fw_version";
#else
char const* const CTP_VER_DEV
	= "/sys/class/input/input%d/config_id";
#endif

#ifdef CONFIG_ARCH_MSM8610_D510
char const* const CTP_ENABLE_DEV
	= "/sys/class/input/input%d/device/touch_enable";
#else
char const* const CTP_ENABLE_DEV
	= "/sys/class/input/input%d/rmidev/touch_enable";
#endif


char const* const CTP_VKey_DISABLED_DEV
	= "/sys/class/input/input%d/rmidev/virtualKey_disabled";


/*return input_index*/
static int GetCtpInputNo(const char *name )
{
	int  i;
	char input_path[128] = { 0 };
	char name_value[64] = { 0 };
	int	 fd;
	const int INPUT_MAX = 10;

	for(i = 0;i < INPUT_MAX ;i++){
		sprintf(input_path,"/sys/class/input/input%d/name",i);
		int fd = open(input_path,O_RDONLY);
		if(fd < 0){
			continue;
		}
		if(read(fd,name_value,sizeof(name_value)) < 0)
			continue;
		if(strstr(name_value,name))	// find synaptics CTP
		{
			return i;
		}
		
	}
	return -1;
}

/*return 0 get OK,or -1 get fail
 *pass ctp_ver out
 */
int GetCtpVer(struct ctp_device_t* dev, char *ctp_ver)
{
  	int fd_ctp;
	int cnt = 0;
	char filename[128];

	sprintf(filename, CTP_VER_DEV, dev->input_number);
	if ((fd_ctp = open(filename,O_RDONLY)) < 0)
	{
		return -1;
	}
	if((cnt = read(fd_ctp,ctp_ver,15))  < 0)
	{
		close(fd_ctp);
		return -1;
	}
	
	ctp_ver[strlen(ctp_ver)-1]='\0';
	close(fd_ctp);

	return 0;
}

/** Close the ctp device */
static int close_ctp(struct ctp_device_t *dev)
{
    if (dev) {
        free(dev);
    }
    return 0;
}
/******************************************************************************/

/**
 * module methods
 */

/** Open a new instance of a ctp device using name */
static int open_ctp(const struct hw_module_t* module, const char* name,
        struct hw_device_t** device)
{
	int input_number = 0;

	if (!name)
        return -EINVAL;

	input_number = GetCtpInputNo(name);
	if (input_number < 0)
		return -ENODEV;

struct ctp_device_t *dev = malloc(sizeof(struct ctp_device_t));
    memset(dev, 0, sizeof(*dev));

dev->common.tag = HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = (struct hw_module_t*)module;
dev->common.close = (int (*)(struct hw_device_t*))close_ctp;
dev->input_number = input_number;
dev->GetCtpVer	  = GetCtpVer;
*device = (struct hw_device_t*)dev;
return 0;
}

static struct hw_module_methods_t ctp_module_methods = {
    .open =  open_ctp,
};

/*
 * The ctp Module :stub
 */
struct ctp_module_t HAL_MODULE_INFO_SYM = {
	common : {
		tag				: HARDWARE_MODULE_TAG,		//必须填写HARDWARE_MODULE_TAG,
		version_major	: 1,
		version_minor	: 0,						//当前必须为0
		id				: CTP_HARDWARE_MODULE_ID,	//ctp.h中定义的module ID
		name			: "CTP Module",
		author			: "Seuic, Inc.",
		methods			: &ctp_module_methods,
	}
};
ctp.c实际上就是Android系统中众多stub中的一员,代码中最后定义的module变量名必须为HAL_MODULE_INFO_SYM,其他就是根据ctp.h中增加的方法和属性进行实现,方法open_ctp就是当hw_get_module获取module/stub后,再调用module中的open函数指针,就会获得hw_device_t,也就获得了所有的方法和属性。
ctp.c对应的Android.mk如下
LOCAL_PATH:= $(call my-dir)
# HAL module implemenation stored in
# hw/<COPYPIX_HARDWARE_MODULE_ID>.<ro.board.platform>.so
include $(CLEAR_VARS)

LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw

LOCAL_SHARED_LIBRARIES := liblog

# NEED TO MODIFY BY USER
LOCAL_CFLAGS := $(common_flags) -DLOG_TAG=\"CTP\"

ifeq ($(strip $(SEUIC_PRODUCT_D510)),true)
$(warning ATMEL CTP HAL for D510)
LOCAL_CFLAGS += -DCONFIG_ARCH_MSM8610_D510
endif

LOCAL_SRC_FILES := ctp.c
LOCAL_MODULE := ctp.$(TARGET_BOARD_PLATFORM)

LOCAL_MODULE_TAGS := optional eng
include $(BUILD_SHARED_LIBRARY)
以我的msm8x10平台编译后为ctp.msm8610.so,放在安卓系统的/system/lib/hw目录下。
虽然此处stub以so的形式编译,但它不是动态库。

(2)jni层:

msm8x12\hardware\qcom\ctp\jni\com_seuic_touch_TouchService.cpp

#define LOG_TAG "CTPService JNI"

#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/ctp.h>

#include <stdio.h>
#include <string.h>

#ifdef CONFIG_ARCH_MSM8610_D510
#define CTP_NAME "atmel_mxt_ts"  	//contain "atmel_mxt_ts" device name
#else
#define CTP_NAME "synaptics"  		//contain "synaptics" device name
#endif
namespace android
{
// These values must correspond with the Mode constants in
// CTPService.java
enum {
    MODE_GLOVE_OFF = 0,
	MODE_GLOVE_ON = 1,
};

static ctp_device_t* get_device(hw_module_t* module, const char* name)
{
    int err;
    hw_device_t* device;
    err = module->methods->open(module, name, &device);
    if (err == 0) {
        return (ctp_device_t*)device;
    } else {
        return NULL;
    }
}

/*return !NULL when OK,or NULL*/
static jint open(JNIEnv *env, jobject clazz)
{
    int err;
    hw_module_t* module;
	ctp_device_t* dev = NULL;

    err = hw_get_module(CTP_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
    if (err == 0) {
        dev = get_device(module, CTP_NAME);
    }

    return (jint)dev;
}

static void close(JNIEnv *env, jobject clazz, int ptr)
{
	ctp_device_t* dev = (ctp_device_t*)ptr;

    if (dev) {
		dev->common.close((struct hw_device_t*)dev);
    }
}

static jstring GetCtpVer(JNIEnv *env, jobject clazz, int ptr)
{
	char ver[15];
	memset(ver, 0 , sizeof(char) * 15);
    ctp_device_t* dev = (ctp_device_t*)ptr;
    if (!dev) {
        return NULL;
    }

	dev->GetCtpVer(dev,(char *)ver);
	return env->NewStringUTF(ver);
}



static JNINativeMethod method_table[] ={
    { "open", "()I", (void*)open },
    { "close", "(I)V", (void*)close },
	{ "GetCtpVer", "(I)Ljava/lang/String;", (void*)GetCtpVer},
};
int register_CTPService(JNIEnv *env)
{
	return jniRegisterNativeMethods(env, "com/seuic/touch/TouchService",
		method_table, NELEM(method_table));
}

extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
	JNIEnv* env = NULL;
	jint result = -1;

	if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
		ALOGE("GetEnv failed!");
		return result;
	}
	ALOG_ASSERT(env, "Could not retrieve the env!");

	register_CTPService(env);

	return JNI_VERSION_1_4;
}


}//end of namespace android

此JNI就是获取stub/module并获取对应设备的方法指针,然后进一步封装ctp.h中增加的方法或属性。用jniRegisterNativeMethods来注册提供给java框架的接口,第二个参数表示这些接口导出给com.seuic.touch.TouchService这个类用。至于java和C代码的参数传递(基本数据类型和java应用类型如string class等)会在以后开辟文章记录下。

com_seuic_touch_TouchService.cpp对应的Android.mk:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
    com_seuic_touch_TouchService.cpp \

LOCAL_C_INCLUDES += \
    $(JNI_H_INCLUDE) \
    libcore/include \
    libcore/include/libsuspend \
	$(call include-path-for, libhardware)/hardware \
	$(call include-path-for, libhardware_legacy)/hardware_legacy \

LOCAL_SHARED_LIBRARIES := \
    libandroid_runtime \
    libandroidfw \
    libcutils \
    liblog \
    libhardware \
    libhardware_legacy \
    libnativehelper \
    libsystem_server \
    libutils \
    libui \
    libinput \
    libskia \
    libgui \
    libusbhost \
    libsuspend

ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
    LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
endif

ifeq ($(strip $(SEUIC_PRODUCT_D510)),true)
$(warning ATMEL CTP JNI for D510)
LOCAL_CFLAGS += -DCONFIG_ARCH_MSM8610_D510
endif

LOCAL_MODULE:= libctp_jni
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)


此处 libctp_jni.so是动态库,并且由java代码使用System.loadLibrary("ctp_jni");进行加载

(3)java层:

3个文件TouchService.java,对应的Android.mk,还有权限com.seuic.touch.xml文件(不过不开放当然也可以不要xml)

java层实际上就是一个硬件service,这个概念不同于android中的标准service类。

msm8x12\hardware\qcom\ctp\java\com\seuic\touch\TouchService.java

package com.seuic.touch;

import android.content.Context;
import android.content.Intent;

public class TouchService {
    private static final String TAG = "TouchService";
    private static final boolean DEBUG = false;

	private static TouchService touchService;

	public static TouchService getInstance() {

		if (touchService == null)
			touchService = new TouchService();

		return touchService;
	}

    public String getTouchVersion() {
		return GetCtpVer(mNativeDevPtr);
    }

    private TouchService() {
        mNativeDevPtr = open();
    }

    protected void finalize() throws Throwable {
        close(mNativeDevPtr);
        super.finalize();
    }

    private static native int open();//JNI方法用native声明
    private static native void close(int ptr);

	private static native String GetCtpVer(int ptr);



    static {
		System.loadLibrary("ctp_jni");//加载库
    }

    private int mNativeDevPtr;
}

这个硬件服务,需要加载动态库libctp_jni.so,并且JNI注册到这个TouchService的方法需要在此用native关键字声明。其实当时我写的这个代码不好,层次分的不好,此处的mNativeDevPtr就是ctp_device_t设备指针,其实应该将其在JNI中设一个静态变量保存,此Java层就应该省掉这个参数。

对应的Android.mk

LOCAL_PATH:= $(call my-dir)

# the library
# ============================================================
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
            $(call all-subdir-java-files) \

LOCAL_MODULE:= touch
LOCAL_MODULE_TAGS:= optional

include $(BUILD_JAVA_LIBRARY)

include $(BUILD_DROIDDOC)

# the xml
# ============================================================

include $(CLEAR_VARS)
LOCAL_MODULE:= com.seuic.touch.xml
LOCAL_MODULE_TAGS:= optional
LOCAL_MODULE_CLASS:= ETC
LOCAL_MODULE_PATH:= $(PRODUCT_OUT)/system/etc/permissions
LOCAL_SRC_FILES:= $(LOCAL_MODULE)

include $(BUILD_PREBUILT)




以下可有可无
权限是否开放的xml,实际上此处原理我也不是很明白。

如果要对全Android系统开放,还要在init.rc中的 export BOOTCLASSPATH增加/system/framework/touch.jar

msm8x12\hardware\qcom\ctp\java\com.seuic.touch.xml

<?xml version="1.0" encoding="UTF-8"?>
<permissions>
	<library
		name="com.seuic.touch"
		file="/system/framework/touch.jar" />
</permissions>


总结:

硬件抽象层三层次:hal ,jni, java.

hal中根据hardware.h基本结构定义xx_device_t和xx_module_t,并定义当前模块的module ID,这样形成xx.h。对应的实现为xx.c,那么你需要在xx.c中实现硬件的操作方法,还有stub的定义struct xx_module_t HAL_MODULE_INFO_SYM,最终编译假设为xx.xx2.so,为借尸so的stub。

jni中就是hal和java的转接口。hw_get_module根据module ID获取stub/module,然后调用module中的methods->open方法,即可将xx_device_t的指针通过参数传出,这样jni就得到了xx模块的所有方法或属性。最终编译假设为xx_jni.so

java一般就将jni中导出的接口封装为java格式。java/service中利用System.loadLibrary(xx_jni)来加载jni导出函数,并且函数要声明native。


这个java格式的硬件service可以被java应用直接调用,也可以加入到systemserver用Manager来调用。


JNI和java格式的service参数传递(包括普通类型如int,还有类),相互访问属性和方法此处不提,后面有空会单独说明。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值