`
836811384
  • 浏览: 545976 次
文章分类
社区版块
存档分类
最新评论

Android 模拟系统事件(二)

 
阅读更多
简介

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

功能

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

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

int ioctl(int fd, ind cmd, …)
void *memset(void *s,int c,size_t n) //总的作用:将已开辟内存空间 s 的首 n 个字节的值设为值 c。
ssize_t write(int fd,const void *buf,size_t nbytes)//将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数.失败时返回-1. 并设置errno变量. 在网络程序中,当我们向套接字文件描述符写时有俩种可能. 


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

代码实现

关键代码如下:

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源码地址

调用代码如下:

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");
	}
}

其它相关代码如下:

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来实现控制的功能。


下载

项目下载

分享到:
评论

相关推荐

    Android代码模拟按键事件

    Android代码模拟按键事件,demo中以按数字键1为例子,响应power按键事件,系统进待机。按数字键1可以替换成接收消息,广播,接口回调,等等

    安卓按键模拟点击相关-android系统模拟点击事件发送QQ消息.rar

    android系统模拟点击事件发送QQ消息.rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。

    Android系统模拟触摸按键

    博文源码《Android基于Socket无线遥控(2)--无线控制篇》 http://blog.csdn.net/zzp16/article/details/7939852 为Android系统添加模拟按键对外接口

    JNI模拟系统事件

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

    android注入代码到驱动模拟系统点击事件demo

    有个问题是,我的实现方式和在activity里面模拟点击事件完全不同,我的目的不是在一个activity里模拟系统点击事件,只是用这个例子来测试下看看能不能把数据写到驱动里,系统自动响应写进去的代码,模拟出我手动点击...

    Android应用程序输入事件处理机制

    触摸屏和键盘事件是统一由系统输入管理器InputManager进行分发的。也就是说,InputManager负责从硬件接收输入事件,然后再将接收到的输入事件分发当前激活的窗口处理。此外,InputManager也能接收模拟的输入事件,...

    基于android模拟考试系统源代码.zip

    安卓考试系统java编写有单选题,多选题,判断题做题改卷处理代码,可选择题库。输入做题范围。题库在assets里面,运用读取txt文件知识、事件处理、基本控件等知识。

    android游戏开发基础模拟粒子系统

    android游戏开发粒子模拟

    安卓按键模拟点击相关-android注入代码到驱动模拟系统点击事件demo.rar

    android注入代码到驱动模拟系统点击事件demo.rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。

    android中模拟焦点移动

    模拟android系统中控件之间焦点移动效果

    android列车查询系统(模拟)

    模拟列车查询系统,模拟车站查询,车次查询,添加功能

    模拟Android系统Zygote启动流程

    模拟Android系统Zygote启动流程

    android模拟内存分配

    操作系统作业,模拟操作系统的内存分配,分区回收。

    android模拟内存分配与回收

    注意!我有用到appcompat_v7依赖包!所以没有的同学可以到我的上传资源列表中下载 ps:我只实现了循环首次适应算法和最佳适应算法

    android系统模拟点击事件发送QQ消息

    这个demo的最终效果就是,打开这个app以后,点击启动服务,然后程序会自动请求root权限,如果你的手机root过,或者有root权限管理工具的话,就选择授权,然后,手机会自动回到主页,然后打开QQ,然后搜索联系人,...

    基于android模拟考试系统源代码

    安卓考试系统java编写有单选题,多选题,判断题做题改卷处理代码,可选择题库。输入做题范围。题库在assets里面,运用读取txt文件知识、事件处理、基本控件等知识。

    Android模拟系统截屏.zip

    操作系统:LInux、IOS、树莓派、安卓开发、微机操作系统、网络操作系统、分布式操作系统等。此外,还有嵌入式操作系统、智能操作系统等。 网络与通信:数据传输、信号处理、网络协议、网络与通信硬件、网络安全网络...

    Android模拟教务系统登录的设计与实现.pdf

    Android模拟教务系统登录的设计与实现.pdf

    android系统 Gps模拟.zip

    Android 项目是使用 Android 操作系统和相关开发工具开发的一款移动应用程序。Android 平台提供了丰富的功能和接口,开发人员可以使用 Java 或 Kotlin 等编程语言编写 Android 应用程序。Android 项目也可以是针对...

    android手机上模拟的gbt28181设备端app

    在android手机上模拟的gbt28181设备端的app软件,兼容主流版本的android手机系统,可以支持gb28181设备端的所有协议,从此调试不用买gb28181的摄像头了。

Global site tag (gtag.js) - Google Analytics