Android App层通过JNI从驱动获取Input Event
转自 http://blog.csdn.net/xnwyd/article/details/42235807
1 概述
尝试在App层直接读取驱动的Input Event,获取触屏事件(本文获取的是电磁笔触屏事件),而不通过Android的Input Framework.
2 架构
3 实现
3.1 JNI层
共有以下几个文件:
3.1.1 input_pen.h
首先看input_pen.h
- #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
- #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
- #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
- 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
- #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
- 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
- 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();
- }