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数据了。

时间: 2024-08-29 23:49:04

Android App层通过JNI从驱动获取Input Event的相关文章

android app性能测试工具GT源码获取以及部署

GT是TMQ(腾讯移动品质中心)研发的一款app性能测试工具.官方地址:http://gt.qq.com/index.htmlgithub地址:https://github.com/Tencent/GT运行工具:eclipse ,我用的专门集成android环境的版本如下图eclipse下载地址:链接: https://pan.baidu.com/s/1csbzl0 密码: 3633 第一步 去github clone代码在cmd命令行中进入到事先创建好的项目存放文件夹下执行 git clone

Android JNI用于驱动测试

硬件平台:S3C6410 操作系统:Ubuntu.windows 板子系统:Android 开发工具:jdk,ndk,eclipse 本次测试从linux内核模块编译开始,以S3C6410的pwm驱动为例. pwm_6410.c: #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linu

Android开发实践:Java层与Jni层的数组传递

Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的Socket代码发送出去,当然,Jni层也需要把从Socket接收到的数据流返回给Java层.我简单地总结了一下,从Java层到Jni层,从Jni层到JAVA层,各有3种传递方式,下面用代码示例简单地介绍一下. 示例代码的主要文件有两个,一个是Native.java,是Java层的类:另一个是Native.c,是JNI层的文件,关键的地

转:Android开发实践:Java层与Jni层的数组传递

Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的Socket代码发送出去,当然,Jni层也需要把从Socket接收到的数据流返回给Java层.我简单地总结了一下,从Java层到Jni层,从Jni层到JAVA层,各有3种传递方式,下面用代码示例简单地介绍一下. 示例代码的主要文件有两个,一个是Native.java,是Java层的类:另一个是Native.c,是JNI层的文件,关键的地

Android开发实践:JNI层线程回调Java函数示例

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://ticktick.blog.51cto.com/823160/1358558 JNI是Java Native Interface的缩写,是Java平台的重要特性,使得Java代码可以方便地与C/C++代码编译生成的动态链接库进行交互.本文主要给出一份示例代码(工程文件见附件),描述如何在Android的JNI层开启一个线程,并在线程中回调Java层的函数. 代码主要分为Java层

Android framework层JNI的使用浅析

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/42418977 JNI技术对于多java开发的朋友相信并不陌生,即(java native interface),本地调用接口,主要功能有以下两点: 1.java层调用C/C++层代码 2.C/C++层调用java层代码 可能有些人会觉得jni技术破坏了Java语言的跨平台性,有这种想法可能是因为你对java理解得还不够深,如果你看看jdk源码,你会发现在jdk里面大量使用了jni技术,而且

如何获取android app的Activity

如何获取android app的Activity? 我觉得有2中方法比较好用. 第一种 a.启动待测apk b.开启日志输出:adb logcat>D:/log.txt c.关闭日志输出:ctrl+c d.查看日志 找寻: Displayed com.mm.android.hsy/.ui.LoginActivity: +3s859ms appPackage = com.mm.android.hsy appActivity = .ui.LoginActivity

获取 Android app的签名

可以利用Java的JarFile ,JarEntry,Certificate  来获取android app的签名信息 public class Do { private static final char[] wJ = "0123456789abcdef".toCharArray(); /** * 从APK中读取签名 * * @param file * @return * @throws IOException */ public String getSignaturesFromAp

appium简明教程(9)——如何获取android app的Activity

有时候在appium的Desired Capabilities中需要指定被测app的appActivity,下面的方法可能会对你有所帮助. 方法一 如有你有待测项目的源码,那么直接查看源码就好.如果没有,那么请联系有源码的同学,这是推荐方法. 本文版权归乙醇所有,欢迎转载,但请注明作者与出处,严禁用于任何商业用途 方法二 如果你没有代码,那么可以反编译该app. 这里将用到2个工具,分别是dex2jar和jd-gui.你可以在这里下载目前为止的最新版本以及示例apk. 我们以工具包里的Conta