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相关推荐
- android jni framework,Android Framework层的JNI机制(二)
Java框架层中有很多地方使用JNI机制,每一个部分的框架层代码,都可能有与之对应的JNI库.先了解Java框架层的组成,继续看一下JNI在框架层中的使用. Java框架层的组成 Java框架层的实体 ...
- 【Android App】利用腾讯地图获取地点信息和规划导航线路讲解及实战(附源码和演示视频 超详细必看)
需要源码请点赞关注收藏后评论区留言~~~ 一.获取地点信息 至于如何集成腾讯地图和调用腾讯地图接口此处不再赘述,有需要请参见我之前的博客 腾讯地图用来搜索POI地点的工具是TencentSearch, ...
- Android App层 单独使用SystemProperties
单独拷贝成 SystemProperties.java 即可享用. import java.lang.reflect.Method;public class SystemProperties {pub ...
- Android APP层 ShellUtils
拷贝成 ShellUtils.java 即可享用. import java.io.BufferedReader; import java.io.DataOutputStream; import jav ...
- Android 底层驱动开发步骤——linux内核层、HAL层、JNI层
1.Linux驱动实现 2.Linux内核驱动测试 3.Android HAL层实现 4.Aidl实现 5.Service java实现 6.Service jni 实现 7.注册service和jn ...
- android l camera no panorama,Android Camera从App层到framework层到HAL层的初始化过程
Android camera 从上到下能够分为四个部分: Application层. framework层. HAL(hardware abstract layer)层. Kernel层 通常面向开发 ...
- 编译FFmpeg4.1.3并移植到Android app中使用(最详细的FFmpeg-Android编译教程)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/bobcat_kay/article/d ...
- android 动态库获取路径问题,一种Android App在Native层动态加载so库的方案
这篇文章通过实战案例,介绍了一种有条理的组织Native层代码层级结构的方法.并且,在良好的代码层级.作用分工的基础上,实现了动态的按需加载.卸载so库.文章的最后,还介绍了实践过程中遇到的困难以及对 ...
- android系统底层驱动多个物理按键上报同一个键值给app层,app层如何区分
如果设备有多个按键上报同一个键值给app层,app通过getScanCode()可以区分是哪个物理按键,得到的值就是linux驱动层的扫描码
最新文章
- Android NDK JNI C++ 13 pthread多线程
- 一文了解 Apache Flink 核心技术
- 抓包工具Charles使用技巧
- python保存变量_Python变量存储
- 人脸对齐(二十)--PRN
- Linux内存管理:Linux 可加载内核模块剖析:2.6 内核透视图
- ACL2021 | 一种巧妙解决NER覆盖和不连续问题的方法
- 幂的后三位相同 详解(C++)
- Maya: Motion Graphics Workflow with MASH Maya教程:运动图形工作流程与MASH Lynda课程中文字幕
- 真无线蓝牙耳机,享受高品质杜比音效
- java连接点菜基站_基于JAVA的电信基站接口调用代码实例
- Fluent Mybatis 牛逼!
- 分布式计算原理之分布式协调与同步(1)——分布式选举
- linux 有线链接树莓派,linux-通过公共互联网连接到树莓派
- 智能穿戴设备的“最强心脏”
- 社交返利APP正在被返利机器人,普通返利APP集体围攻绞杀
- 微信JSAPI支付对接流程及支付接口设计
- 【大学生项目与竞赛】2021年全国大学生电子设计大赛 (二)模块储备
- 两车相撞的力怎么计算_两车正面碰撞事故车辆行驶速度计算方法
- 机器学习4. 贝叶斯
热门文章
- 存储过程传递参数时出现类型转换错误!如:varchar转换为int时出错
- Shader 学习笔记 ---Depth of Field 介绍
- CodeMirror 多功能在线代码编辑器
- mfc 如何将cstring转byte_如何将PDF转成JPG?PDF转图片的技巧
- linux虚拟化技术 教程,Linux上实现虚拟化技术的优势
- flink 异步io mysql 缓存_Flink用于外部数据访问的异步I/O
- [数据结构]P2.1 二叉搜索树
- laravel 运用
- 基于最大堆实现最大优先队列【代码】
- Java 中的array数组总结之一