首页 > 代码库 > Android App层通过JNI从驱动获取Input Event
Android App层通过JNI从驱动获取Input Event
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(); }
3.2.3 Activity
Activity可以通过调用InputPen的start函数,启动读取线程,再调用setHandler设置Handler,从而就可在Handler中处理PenEvent数据了。
Android App层通过JNI从驱动获取Input Event
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。