您的位置:首页 > 移动开发

Android App层通过JNI从驱动获取Input Event

2015-01-25 13:23 781 查看


1 概述

尝试在App层直接读取驱动的Input Event,获取触屏事件(本文获取的是电磁笔触屏事件),而不通过Android的Input Framework.





2 架构




3 实现


3.1 JNI层

共有以下几个文件:





3.1.1 input_pen.h

首先看input_pen.h



[cpp] view
plaincopy





#ifndef _INPUT_PEN_H

#define _INPUT_PEN_H





#include <pthread.h>

#include <linux/input.h>

#include <sys/types.h>

#include <linux/types.h>



#ifdef _cplusplus

extern "C" {

#endif



//获取input_event数据的方法指针,由App层提供

typedef void (*get_event_callback)(__u16 type, __u16 code, __s32 value );



//创建线程的函数指针,通过Java虚拟机创建

typedef pthread_t (*create_thread_callback)(const char* name, void (*start)(void *), void* arg);



//释放线程资源的函数指针

typedef int (*detach_thread_callback)(void);



//回调函数结构体

typedef struct {

get_event_callback get_event_cb;

create_thread_callback create_thread_cb;

detach_thread_callback detach_thread_cb;

} input_callback;



/*******************************************************************/

//public methods



//初始化函数

unsigned char input_pen_init(input_callback *callback);



//退出函数

void input_pen_exit();





/*******************************************************************/





#ifdef _cplusplus

}

#endif



#endif







3.1.2 input_pen.cpp

[cpp] view
plaincopy





#include <pthread.h>

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <string.h>

#include <unistd.h>

#include <fcntl.h>

#include <sys/types.h>

#include <sys/epoll.h>

#include <sys/wait.h>

#include <sys/un.h>

#include <stddef.h>

#include <linux/input.h>



#include "input_pen.h"

#include "debug.h"







//驱动路径

#define DEV_PATH "/dev/input/event1"



//最大侦听

#define MAX_EVENTS 1



//epoll_wait的最大时间

#define EPOLL_SLEEP 200



//线程是否由pthread创建,否则由JVM创建

#define USING_PTHREAD 0





#if USING_PTHREAD

#define LOGD(fmt, arg...) do{printf(fmt"\n", ##arg);}while(0)

#define LOGE LOGD

#endif



/**************************************************************************************/



//static variable



//当前驱动文件指针

static int fd = 0;

//epoll文件指针

static int epoll_fd = 0;

//epoll_event数组

static struct epoll_event events[MAX_EVENTS];

//回调函数

static input_callback *callbacks = NULL;



//标记线程是否已启动

static unsigned char start_flag = 0;

//标记线程是否已退出

static unsigned char exit_flag = 0;

//线程锁

static pthread_mutex_t exit_mutex;



/**************************************************************************************/





//set non-blocking for fd

static int set_non_blocking(int fd) {

int opts;



opts = fcntl(fd, F_GETFL);

if (opts < 0) {

LOGE("fcntl F_GETFL error: %s", strerror(errno));

return -1;

}

opts = (opts | O_NONBLOCK);

if (fcntl(fd, F_SETFL, opts) < 0) {

LOGE("fcntl F_SETFL error: %s", strerror(errno));

return -1;

}



return 0;

}



//register epoll events for fd

static void epoll_register( int epoll_fd, int fd ) {

struct epoll_event ev;

int ret;



ev.events = EPOLLIN;//interested in receiving data

ev.data.fd = fd;



do {

//register events for fd

ret = epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &ev );

} while (ret < 0 && errno == EINTR);



}







//remove epoll events for fd

static void epoll_unregister( int epoll_fd, int fd ) {

int ret;

do {

ret = epoll_ctl( epoll_fd, EPOLL_CTL_DEL, fd, NULL );

} while (ret < 0 && errno == EINTR);



}





//通知退出线程,由其他线程调用

static void thread_cancel() {

LOGD("thread_cancel");

pthread_mutex_lock(&exit_mutex);



exit_flag = 1;



pthread_mutex_unlock(&exit_mutex);

}



//停止线程,由本线程调用

static void thread_exit() {

unsigned char flag ;





pthread_mutex_lock(&exit_mutex);



flag = exit_flag;



pthread_mutex_unlock(&exit_mutex);



if (flag == 1) {

LOGD("thread_exit");

//close devices

close(fd);



//clean variablies

fd = 0;

epoll_fd = 0;

start_flag = 0;

exit_flag = 0;

//release thread resources

if (callbacks != NULL && callbacks->detach_thread_cb != NULL) {

callbacks->detach_thread_cb();

LOGD("callbacks->detach_thread_cb();\n");

}



//exit current thread

pthread_exit(NULL);





}

}





//线程运行函数

#if USING_PTHREAD

static void *run(void *args) {

#else

static void run(void *args) {



#endif

int n = 0;

int i = 0;

int res;

struct input_event event;



LOGD("run...");

while (1) {



thread_exit();//每次检测是否要退出运行





n = epoll_wait(epoll_fd, events, MAX_EVENTS, EPOLL_SLEEP);//检测是否有事件发生

if (n == -1) {

LOGE("epoll_wait error:%s", strerror(errno));

continue;

}



for (i = 0; i < n; i++) {

if (events[i].data.fd == fd) { //有读事件发生

res = read(fd, &event, sizeof(event));

if (res < (int)sizeof(event)) {

LOGE("could not get event\n");

continue;

}



#if (!USING_PTHREAD)

//把input_event的数据回调到java层

if (callbacks != NULL && callbacks->get_event_cb != NULL) {

callbacks->get_event_cb(event.type, event.code, event.value);

}

#else

//printf("[%8ld.%06ld] ", event.time.tv_sec, event.time.tv_usec);

if (event.type == EV_ABS) {

printf("%04x %04x %08x\n", event.type, event.code, event.value);

}

#endif

}

}

}

#if USING_PTHREAD

return NULL;

#else

return ;

#endif



}





//初始化函数

unsigned char input_pen_init(input_callback *cb) {



pthread_t thread;



LOGD("input_pen_init");

if (start_flag) {

return 1;

}



//callbacks

callbacks = cb;



//open device

fd = open(DEV_PATH, O_RDWR);

if (fd < 0) {

LOGE("open device failed!\n");

return 0;

}



//create epoll

epoll_fd = epoll_create(MAX_EVENTS);

if (epoll_fd == -1) {

LOGE("epoll_create failed!\n");

return 0;

}

//set non-blocking

set_non_blocking(fd);



//epoll register

epoll_register(epoll_fd, fd);



//mutex

if (pthread_mutex_init(&exit_mutex, NULL) != 0) {

LOGE("pthread_mutex_initn failed!");

return 0;

}



//create thread

#if USING_PTHREAD

if (pthread_create(&thread, NULL, run, (void *)NULL) != 0) {

LOGE("pthread_create failed!\n");

return 0;

}

#else

if (callbacks != NULL && callbacks->create_thread_cb != NULL) {

thread = callbacks->create_thread_cb("input_pen_thread", run, NULL);

if (thread == 0) {

LOGE("create thread failed!\n");

return 0;

}



start_flag = 1;

LOGD("input_pen_init success!");

return 1;



}

#endif







return 0;



}



//退出函数

void input_pen_exit() {

thread_cancel();

}



#if USING_PTHREAD

int main() {

int count = 0;

input_pen_init(NULL);



while (1) {

sleep(1);

count ++;

if (count == 20) {

thread_cancel();

sleep(1);

break;

}

}

return 0;

}

#endif









以上的关键流程为:



1、open驱动文件,初始化epoll,创建线程。

2、在线程中通过epoll_wait检测事件,有事件发生则通过read函数读取驱动的input_event数据,并通过get_event_cb回调到Jav层。



3.1.3 com_jiagutech_input_InputPen.cpp

[cpp] view
plaincopy





#include <stdlib.h>

#include <malloc.h>

#include <jni.h>

#include <JNIHelp.h>

#include <utils/Log.h>

#include "android_runtime/AndroidRuntime.h"



#include "input_pen.h"

#include "debug.h"



//Java类名

#define CLASS_NAME "com/jiagutech/input/InputPen"



using namespace android;





static jobject mCallbacksObj = NULL;

static jmethodID method_get_event;





static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {

if (env->ExceptionCheck()) {

LOGE("An exception was thrown by callback '%s'.", methodName);

LOGE_EX(env);

env->ExceptionClear();

}

}



//获得input_event数据的回调函数

static void GetEventCallback(__u16 type, __u16 code, __s32 value) {

JNIEnv* env = AndroidRuntime::getJNIEnv();

//invoke java callback method

env->CallVoidMethod(mCallbacksObj, method_get_event, type, code, value);



checkAndClearExceptionFromCallback(env, __FUNCTION__);

}



//创建线程的回调函数

static pthread_t CreateThreadCallback(const char* name, void (*start)(void *), void* arg) {

return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);

}



//释放线程资源的回调函数

static int DetachThreadCallback(void) {

JavaVM* vm;

jint result;



vm = AndroidRuntime::getJavaVM();

if (vm == NULL) {

LOGE("detach_thread_callback :getJavaVM failed\n");

return -1;

}



result = vm->DetachCurrentThread();

if (result != JNI_OK)

LOGE("ERROR: thread detach failed\n");

return result;

}





//回调函数结构体变量

static input_callback mCallbacks = {

GetEventCallback,

CreateThreadCallback,

DetachThreadCallback,

};



//初始化Java的回调函数

static void jni_class_init_native

(JNIEnv* env, jclass clazz) {

LOGD("jni_class_init_native");



method_get_event = env->GetMethodID(clazz, "getEvent", "(III)V");





}



//初始化

static jboolean jni_input_pen_init

(JNIEnv *env, jobject obj) {

LOGD("jni_input_pen_init");



if (!mCallbacksObj)

mCallbacksObj = env->NewGlobalRef(obj);



return input_pen_init(&mCallbacks);

}





//退出

static void jni_input_pen_exit

(JNIEnv *env, jobject obj) {

LOGD("jni_input_pen_exit");

input_pen_exit();

}



static const JNINativeMethod gMethods[] = {

{ "class_init_native","()V", (void *)jni_class_init_native },

{ "native_input_pen_init","()Z", (void *)jni_input_pen_init },

{ "native_input_pen_exit","()V", (void *)jni_input_pen_exit },

};







static int registerMethods(JNIEnv* env) {





const char* const kClassName = CLASS_NAME;

jclass clazz;

/* look up the class */

clazz = env->FindClass(kClassName);

if (clazz == NULL) {

LOGE("Can't find class %s/n", 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/n", kClassName);

return -1;

}

/* fill out the rest of the ID cache */

return 0;

}





jint JNI_OnLoad(JavaVM* vm, void* reserved) {

JNIEnv* env = NULL;

jint result = -1;

LOGI("InputPen JNI_OnLoad");

if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {

LOGE("ERROR: GetEnv failed/n");

goto fail;

}



if (env == NULL) {

goto fail;

}

if (registerMethods(env) != 0) {

LOGE("ERROR: PlatformLibrary native registration failed/n");

goto fail;

}

/* success -- return valid version number */

result = JNI_VERSION_1_4;

fail:

return result;

}








3.1.4 Android.mk

[python] view
plaincopy





LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)



LOCAL_SRC_FILES:= \

input_pen.cpp \

com_jiagutech_input_InputPen.cpp



LOCAL_MODULE_TAGS := optional



LOCAL_SHARED_LIBRARIES := liblog libcutils libandroid_runtime libnativehelper

LOCAL_PRELINK_MODULE := false



LOCAL_MODULE:= libinput_pen

include $(BUILD_SHARED_LIBRARY)





#LOCAL_MODULE:=inputpen

#include $(BUILD_EXECUTABLE)


1.1.1 Debug.h

[cpp] view
plaincopy





#ifndef _DEBUG_H

#define _DEBUG_H



#include <utils/Log.h>



#ifdef ALOGD

#define LOGD ALOGD

#endif

#ifdef ALOGV

#define LOGV ALOGV

#endif

#ifdef ALOGE

#define LOGE ALOGE

#endif

#ifdef ALOGI

#define LOGI ALOGI

#endif



#define LOG_TAG "InputPen"



#endif






3.2 App层

共有两个文件:

PenEvent.java

InputPen.java



3.2.1 PenEvent.java

[java] view
plaincopy





package com.jiagutech.input;



public class PenEvent {





public PenEvent() {



}



public final static int ACTION_DOWN = 0x0;

public final static int ACTION_UP = 0x1;

public final static int ACTION_MOVE = 0x2;



//表示事件类型,down/up/move

private int action;

//x轴坐标

private float x;

//y轴坐标

private float y;

//压力数据

private float pressure;



public void setAction(int action) {

this.action = action;

}



public int getAction() {

return action;

}





public void setX(float x) {

this.x = x;

}



public float getX() {

return x;

}





public void setY(float y) {

this.y = y;

}



public float getY() {

return y;

}



public void setPressure(float p) {

this.pressure = p;

}



public float getPressure() {

return pressure;

}







}






3.2.2 InputPen.java

[java] view
plaincopy





package com.jiagutech.input;



import java.util.LinkedList;



import android.os.Handler;





public class InputPen {



/**********************************************************************************/

private static InputPen instance = null;



private boolean mStarted = false;



private final static String TAG = "InputPen";



//主线程Handler

private Handler mHandler = null;

//PenEvent列表

private LinkedList<PenEvent> mEventList = new LinkedList<PenEvent>();

//锁

private Object mListLock = new Object();



/*******************************************************************/

//以下定义请参考input_event.h文件

private final static int EV_SYN = 0x0;

private final static int EV_KEY = 0x01;



private final static int EV_ABS = 0x03;





private final static int ABS_X = 0x00;

private final static int ABS_Y = 0x01;

private final static int ABS_PRESSURE = 0x18;

private final static int BTN_TOUCH = 0x14a;

private final static int BTN_TOOL_PEN = 0x140;

private final static int BTN_STYLUS = 0x14b;

/*******************************************************************/

//原始的x最大分辨率

private static final float MAX_X = 15360.0f;

//屏幕x最大分辨率

private static final float MAX_X_STANDARD = 1280.0f;



//原始的y最大分辨率

private static final float MAX_Y = 9600.0f;

//屏幕y最大分辨率

private static final float MAX_Y_STANDARD = 800.0f;



//原始的最大压力数据

private static final float MAX_PRESSURE = 1023.0f;

//Android标准最大压力数据

private static final float MAX_PRESSURE_STANDARD= 1.0f;



private int _x=-1,_y=-1,_pressure=-1;

private int _bintouch = 0, _lastBinTouch = 0;

//x轴转换系数

private float xScale = MAX_X_STANDARD / MAX_X;

//y轴转换系数

private float yScale = MAX_Y_STANDARD / MAX_Y;

//压力值转换系统

private float pScale = MAX_PRESSURE_STANDARD / MAX_PRESSURE;



//y轴便宜

private float yOffset = 73.0f;



/**

* 加载libinput_pen.so,并初始化回调函数

*/

static {

try {

System.loadLibrary("input_pen");

class_init_native();





} catch (UnsatisfiedLinkError e) {

e.printStackTrace();

}



}



private InputPen() {

}



/**

* 单例模式

* @return

*/

public static synchronized InputPen getInstance() {

if (instance == null) {

instance = new InputPen();

}

return instance;

}



/**

* 通知主线程获取PenEvent进行处理

*

*/

private void onPenTouch() {

if (mHandler != null) {

mHandler.sendEmptyMessage(0);

}



}



/**

* 设置主线程handler

* @param handler

*/

public void setHandler(Handler handler) {

mHandler = handler;

}



/**

* 添加PenEvent到list

* @param event

*/

private void addPenEvent(PenEvent event) {

synchronized (mListLock) {

mEventList.add(event);

}

}



/**

* 从list获取最旧的PenEvent

* @return

*/

public PenEvent getPenEvent() {

PenEvent event = null;

synchronized (mListLock) {

if (mEventList.size() > 0) {

event = mEventList.removeFirst();

}

}



return event;

}





/*******************************************************************/

//public method



/**

* 坐标转换,并生成PenEvent数据

* @param event

*/

protected void transform(PenEvent event) {

float x = MAX_Y_STANDARD - ((float)_y) * yScale;

float y = (float)_x * xScale - yOffset;

float p = (float)_pressure * pScale;

event.setX(x);

event.setY(y);

event.setPressure(p);

}



/**

* 处理input_event数据

*/

protected void processEvent() {



if (_bintouch != _lastBinTouch ) {

_lastBinTouch = _bintouch;

//Log.d(TAG, String.format("x=%d,y=%d,pressure=%d,bintouch=%d", _x,_y,_pressure,_bintouch));

if (_bintouch == 1) { //down事件

PenEvent event = new PenEvent();

event.setAction(PenEvent.ACTION_DOWN);

transform(event);

addPenEvent(event);

onPenTouch();



} else { //up事件

PenEvent event = new PenEvent();

event.setAction(PenEvent.ACTION_UP);

transform(event);

addPenEvent(event);

onPenTouch();

}

} else if (_bintouch == 1) { //move事件

PenEvent event = new PenEvent();

event.setAction(PenEvent.ACTION_MOVE);

transform(event);

addPenEvent(event);

onPenTouch();



}









}



/**

* 获取input_event数据,由jni层调用此函数

* @param type

* @param code

* @param value

*/

protected void getEvent(int type, int code, int value) {

switch (type) {



case EV_SYN:

processEvent();

break;



case EV_KEY:

if (code == BTN_TOUCH) {

_bintouch = value;

}

break;

case EV_ABS:

if (code == ABS_X) {

_x = value;

} else if (code == ABS_Y) {

_y = value;

} else if (code == ABS_PRESSURE) {

_pressure = value;

}

break;

default:

break;

}

}



/**

* 启动线程

*/

protected void start() {

if (!mStarted) {

if (native_input_pen_init()) {

mStarted = true;

}



}





}



/**

* 停止线程

*/

protected void stop() {

if (mStarted) {

native_input_pen_exit();

mStarted = false;

}



}



public void dispose() {

stop();

}



@Override

protected void finalize() throws Throwable {



stop();

// TODO Auto-generated method stub

super.finalize();

}





/*******************************************************************/

//native method

protected static native void class_init_native();



protected native boolean native_input_pen_init();



protected native void native_input_pen_exit();

}








3.2.3 Activity

Activity可以通过调用InputPen的start函数,启动读取线程,再调用setHandler设置Handler,从而就可在Handler中处理PenEvent数据了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: