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] viewplaincopy
#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] viewplaincopy
#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] viewplaincopy
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] viewplaincopy
#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] viewplaincopy
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] viewplaincopy
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
- Android App层通过JNI从驱动获取Input Event
- Android App层通过JNI从驱动获取Input Event
- Android模拟器学framework和driver之传感器篇6(Android 通过JNI连接驱动层和framework)
- Android 通过 JNI 获取 Wi-Fi 网络接口名
- Android-NDK开发示例--通过JNI获取MD5码
- Android模拟器学framework和driver之传感器篇6(Android 通过JNI连接驱动层和framework)
- [android] 调试linux input子系统驱动的用户空间命令 getevent/sendevent
- Android驱动(一)硬件访问服务学习之(一)Android通过JNI访问硬件
- Android模拟器学framework和driver之传感器篇6(Android 通过JNI连接驱动层和framework)
- Android中有没有办法通过jni在Linux内核获取摄像头yuv数据??
- Android中APK直接通过JNI访问驱动
- android 通过jni实现framework(app)层调用android驱动
- 通过JNI调用android驱动
- Android中有没有办法通过jni在Linux内核获取摄像头yuv数据??
- 通过JNI调用android驱动
- 在 Android 通过 get_event 获得 input 设备 上报event
- Android模拟器学framework和driver之传感器篇6(Android 通过JNI连接驱动层和framework)
- Android 直接通过JNI访问驱动
- Android中APK直接通过JNI访问驱动