您的位置:首页 > 移动开发 > Android开发

Android 模拟系统事件(二)

2014-09-06 22:41 573 查看
简介

JNI(Java Native Interface)是本地编程接口,它允许Java代码和其他语言写的代码进行交互,它可以在
Java 虚拟机 (VM) 内部运行的 Java 代码与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行交互操作。
功能

通过jni实现向Android系统注入事件,从而实现模拟按键、模拟触屏等操作!用它直接跳过上面的Android平台权限的问题!

原理是在jni中通过Linux内核的ioctl函数和c语言函数(memset、write)来实现对设备的I/O通道进行管理的。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。它的调用个数如下:

[java] view
plaincopy

int ioctl(int fd, ind cmd, …)

[java] view
plaincopy

void *memset(void *s,int c,size_t n) //总的作用:将已开辟内存空间 s 的首 n 个字节的值设为值 c。

[java] view
plaincopy

ssize_t write(int fd,const void *buf,size_t nbytes) //将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数.失败时返回-1. 并设置errno变量. 在网络程序中,当我们向套接字文件描述符写时有俩种可能.

其中fd就是用户程序打开设备时使用open函数返回的文件标示符,cmd就是用户程序对设备的控制命令,至于后面的省略号,那是一些补充参数,一般最多一个,有或没有是和cmd的意义相关的。

代码实现

关键代码如下:

[java] view
plaincopy

jint Java_net_pocketmagic_keyinjector_NativeInput_intEnableDebug( JNIEnv* env,jobject thiz, jint enable ) {

g_debug = enable;

if (enable == 1) debug("Debug enabled.");

return g_debug;

}

jint Java_net_pocketmagic_keyinjector_NativeInput_intCreate( JNIEnv* env,jobject thiz, jstring inputdev, jint keyboard, jint mouse)

{

jboolean iscopy;

char szDev[255] = "";

const char *pszDev = (*env)->GetStringUTFChars(env, inputdev, &iscopy);

if (pszDev) strncpy(szDev, pszDev, 255);

(*env)->ReleaseStringUTFChars(env, inputdev, pszDev);

debug("intCreate call (%s)", szDev);

struct uinput_dev dev;

int fd_kb, aux;

fd_kb = open(szDev, O_RDWR);

if (fd_kb < 0) {

debug("Can't open input device:%s ", szDev);

return -1;

}

memset(&dev, 0, sizeof(dev));

strcpy(dev.name, "AndroidKeyInjector Input");

dev.id.bustype = 0x0003;// BUS_USB;

dev.id.vendor = 0x0000;

dev.id.product = 0x0000;

dev.id.version = 0x0000;

if (write(fd_kb, &dev, sizeof(dev)) < 0) {

debug("Can't write device information");

close(fd_kb);

return -1;

}

if (mouse) {

ioctl(fd_kb, UI_SET_EVBIT, EV_REL);

for (aux = REL_X; aux <= REL_MISC; aux++)

ioctl(fd_kb, UI_SET_RELBIT, aux);

}

if (keyboard) {

ioctl(fd_kb, UI_SET_EVBIT, EV_KEY);

ioctl(fd_kb, UI_SET_EVBIT, EV_LED);

ioctl(fd_kb, UI_SET_EVBIT, EV_REP);

for (aux = KEY_RESERVED; aux <= KEY_UNKNOWN; aux++)

ioctl(fd_kb, UI_SET_KEYBIT, aux);

//for (aux = LED_NUML; aux <= LED_MISC; aux++)

// ioctl(fd_kb, UI_SET_LEDBIT, aux);

}

if (mouse) {

ioctl(fd_kb, UI_SET_EVBIT, EV_KEY);

for (aux = BTN_LEFT; aux <= BTN_BACK; aux++)

ioctl(fd_kb, UI_SET_KEYBIT, aux);

}

ioctl(fd_kb, UI_DEV_CREATE);

debug("intCreate success: %d", fd_kb);

return fd_kb;

}

void Java_net_pocketmagic_keyinjector_NativeInput_intClose( JNIEnv* env,jobject thiz, jint fd_kb)

{

close(fd_kb);

}

void Java_net_pocketmagic_keyinjector_NativeInput_intSendEvent( JNIEnv* env,jobject thiz, int fd_kb, uint16_t type, uint16_t code, int32_t value)

{

debug("intSendEvent call (%d,%d,%d,%d)", fd_kb, type, code, value);

struct uinput_event event;

int len;

if (fd_kb <= fileno(stderr))

return;

memset(&event, 0, sizeof(event));

event.type = type;

event.code = code;

event.value = value;

len = write(fd_kb, &event, sizeof(event));

debug("intSendEvent done:%d",len);

}

其中的事件id 可以参考Android源码地址

调用代码如下:

[java] view
plaincopy

public class NativeInput {

int m_fd;

final static int EV_KEY = 0x01;

public NativeInput() {

intEnableDebug(1);

for (int i = 0; i < 8; i++) {

m_fd = intCreate("/dev/input/event" + i, 1, 0);

if (m_fd != -1)

break;

}

}

public static int chmod(String path, int mode) throws Exception {

Class fileUtils = Class.forName("android.os.FileUtils");

Method setPermissions = fileUtils.getMethod("setPermissions",

String.class, int.class, int.class, int.class);

return (Integer) setPermissions.invoke(null, path, mode, -1, -1);

}

public int SendKey(int key, boolean state) {

if (state)

return intSendEvent(m_fd, EV_KEY, key, 1); // key down

else

return intSendEvent(m_fd, EV_KEY, key, 0); // key up

}

native int intEnableDebug(int enabled); // 1 will output to logcat, 0 will

// disable

//

native int intCreate(String dev, int kb, int mouse);

native void intClose(int fd);

native int intSendEvent(int fd, int type, int code, int value);

static {

System.loadLibrary("input");

}

}

其它相关代码如下:

[java] view
plaincopy

int EVT_open(struct NATIVE_INFO *info)

{

struct input_absinfo absinfo;

if(initEVT)

return 0;

if(info == NULL)

{

LOGE("info null point.");

goto fail;

}

if(info->FB_width == 0 || info->FB_height == 0)

{

LOGE("error width %d and height %d.", info->FB_width, info->FB_height);

goto fail;

}

memset(&ei, 0, sizeof(ei));

ei.screen_width = info->FB_width;

ei.screen_height = info->FB_height;

scan_dir(DEV_DIR);

if(ioctl(ei.fd_touch, EVIOCGABS(ABS_X), &absinfo)) {

LOGI("Error reading absolute controller ABS_X[%d]: %s", errno, strerror(errno));

return;

}

ei.abs_x_min = absinfo.minimum;

ei.abs_x_max = absinfo.maximum;

if(ioctl(ei.fd_touch, EVIOCGABS(ABS_Y), &absinfo)) {

LOGI("Error reading absolute controller ABS_Y[%d]: %s", errno, strerror(errno));

return;

}

ei.abs_y_min = absinfo.minimum;

ei.abs_y_max = absinfo.maximum;

initEVT = 1;

return 0;

fail:

EVT_close();

return -1;

}

int EVT_close()

{

if(ei.fd_key > 0)

close(ei.fd_key);

if(ei.fd_touch > 0)

close(ei.fd_touch);

initEVT = 0;

return 0;

}

int EVT_touch(int action, float x, float y)

{

int abs_x, abs_y;

if(initEVT == 0)

{

LOGE("event not inital");

return -1;

}

switch(action)

{

case ACTION_DOWN:

calculateXY(x, y, &abs_x, &abs_y);

write_event(ei.fd_touch, 3, 0, abs_x);

write_event(ei.fd_touch, 3, 1, abs_y);

write_event(ei.fd_touch, 1, 330, 1);

write_event(ei.fd_touch, 0, 0, 0);

break;

case ACTION_UP:

write_event(ei.fd_touch, 1, 330, 0);

write_event(ei.fd_touch, 0, 0, 0);

break;

case ACTION_MOVE:

calculateXY(x, y, &abs_x, &abs_y);

write_event(ei.fd_touch, 3, 0, abs_x);

write_event(ei.fd_touch, 3, 1, abs_y);

write_event(ei.fd_touch, 0, 0, 0);

break;

}

return 0;

}

int EVT_key(int action, int key)

{

if(initEVT == 0)

{

LOGE("event not inital");

return -1;

}

switch(action)

{

case ACTION_DOWN:

write_event(ei.fd_key, 1, key, 1);

break;

case ACTION_UP:

write_event(ei.fd_key, 1, key, 0);

break;

}

return 0;

}

int scan_dir(const char *dirname)

{

char devname[PATH_MAX];

char *filename;

DIR *dir;

struct dirent *de;

dir = opendir(dirname);

if(dir == NULL)

return -1;

strcpy(devname, dirname);

filename = devname + strlen(devname);

*filename++ = '/';

while((de = readdir(dir))) {

if(de->d_name[0] == '.' &&

(de->d_name[1] == '\0' ||

(de->d_name[1] == '.' && de->d_name[2] == '\0')))

continue;

strcpy(filename, de->d_name);

open_dev(devname);

}

closedir(dir);

return 0;

}

int open_dev(const char *deviceName)

{

int fd;

int version;

uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];

uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)];

fd = open(deviceName, O_RDWR);

if(fd < 0) {

LOGI("could not open device[%d]: %s", errno, strerror(errno));

return -1;

}

if(ioctl(fd, EVIOCGVERSION, &version)) {

return -1;

}

memset(key_bitmask, 0, sizeof(key_bitmask));

if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask) >= 0) {

if (containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC))

|| containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_GAMEPAD),

sizeof_bit_array(BTN_DIGI))

|| containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK),

sizeof_bit_array(KEY_MAX + 1))) {

ei.fd_key = fd;

LOGI("get key input device: %s", deviceName);

}

}

memset(abs_bitmask, 0, sizeof(abs_bitmask));

if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask) >= 0) {

// Is this a new modern multi-touch driver?

if (test_bit(ABS_MT_POSITION_X, abs_bitmask)

&& test_bit(ABS_MT_POSITION_Y, abs_bitmask)) {

ei.fd_touch = fd;

LOGI("get multi-touch input device: %s", deviceName);

// Is this an old style single-touch driver?

} else if (test_bit(BTN_TOUCH, key_bitmask)

&& test_bit(ABS_X, abs_bitmask) && test_bit(ABS_Y, abs_bitmask)) {

ei.fd_touch = fd;

LOGI("get single-touch input device: %s", deviceName);

}

}

}

int write_event(int fd, int type, int code, int value)

{

struct input_event event;

memset(&event, 0, sizeof(event));

event.type = type;

event.code = code;

event.value = value;

if(write(fd, &event, sizeof(event)) < sizeof(event)) {

LOGI("write event failed[%d]: %s", errno, strerror(errno));

return -1;

}

return 0;

}

void calculateXY(float x, float y, int *abs_x, int *abs_y)

{

*abs_x = ei.abs_x_min +

(int)((x * (float)(ei.abs_x_max - ei.abs_x_min)) / ei.screen_width + 0.5);

*abs_y = ei.abs_y_min +

(int)((y * (float)(ei.abs_y_max - ei.abs_y_min)) / ei.screen_height + 0.5);

}

int containsNonZeroByte(const uint8_t* array, uint32_t startIndex, uint32_t endIndex)

{

const uint8_t* end = array + endIndex;

array += startIndex;

while (array != end) {

if (*(array++) != 0) {

return 1;

}

}

return 0;

}

结论

以上是使用ioctl来实现对设备i/o控制,如果不用ioctl的话,也可以实现对设备I/O通道的控制。例如,我们可以在驱动程序中实现write的时候检查一下是否有特殊约定的数据流通过,如果有的话,那么后面就跟着控制命令。但是如果这样做的话,会导致代码分工不明,程序结构混乱,程序员自己也会头昏眼花的。所以,我们就使用ioctl来实现控制的功能。

下载

项目下载

From: http://blog.csdn.net/banketree/article/details/10474331
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android kernel