android 按键精灵。C++编写。
可以记录屏幕键盘等传感器对系统的输入。
上一篇文章做的那个稍微有点复杂了,还需要把板子的输出拿回电脑处理再倒回去。这个就简单多了用法如下
usage:
event record /path/file1
event replay /path/file1
给我女友写的程序直接搬过来了,所以注释有些冗余。
"stdafx.h"
#include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <fcntl.h> #include <sys/ioctl.h> #include <errno.h> #include <asm/types.h> #include <unistd.h> #include <iostream> #include <vector> struct input_event { timeval time; __u16 type; __u16 code; __s32 value; }; struct myinputevent:public input_event //继承了16字节的数据结构,添加了一个deviceID { int deviceID; };
main.cpp
#include "stdafx.h" void record(char* filename); void replay(char* filename); int main(int argc, char *argv[]) { if(argc!=3 || (strcmp(argv[1],"record") & strcmp(argv[1],"replay"))) { std::cout<< "usage:"<<std::endl<< "event record /path/file1"<<std::endl<< "event replay /path/file1"<<std::endl; return 0; } if(!strcmp(argv[1],"record")) record(argv[2]); else replay(argv[2]); return 0; }
replay.cpp
#include "stdafx.h" //#define DEBUG int timedif(timeval& a,timeval& b) //这是个计算时间差的函数,对于一个timeval的数据结构,前面4字节秒,后面4字节微秒 //这个函数接受两个这样的结构,返回一个微秒单位的时间差 { return (b.tv_sec-a.tv_sec)*1000000+(b.tv_usec-a.tv_usec); } void replay(char* filename) { int fi = open(filename,O_RDONLY); if(fi==-1) { std::cout<<filename<<" cannot be opened"<<std::endl; exit(0); } char dev[40]="/dev/input/eventX"; int DeviceFile[10]; for(char i='0';i<='9';++i) { dev[16]=i; int f=open(dev,O_RDWR); //把所有的event都打开准备写入 DeviceFile[i-'0']=f; //把句柄保存下来,如果打不开的话f值是-1 #ifdef DEBUG if(f!=-1) std::cout<<"Device opened:"<<i<<std::endl; #endif } myinputevent event0,event1; read(fi,&event0,sizeof(myinputevent)); //先读取第一个event到event0中 timeval timetemp=event0.time; //临时存储event中的时间,因为往设备写的时候,时间要清空 memset(&event0.time, 0, sizeof(struct timeval)); //清空时间 write(DeviceFile[event0.deviceID], &event0.time, sizeof(input_event)); //写回设备 event0.time=timetemp; //把刚刚保存的时间拿回来 while(read(fi,&event1,sizeof(myinputevent))) //只要文件还能读,就读取下一个event到event1中 { int sleeptime=timedif(event0.time,event1.time); //计算event0和event1的时间差 #ifdef DEBUG std::cout<<"sleep"<<sleeptime<<std::endl; #endif usleep(sleeptime); //进行时间间隔 event0.time=event1.time; //把event1的时间赋值给event0,这样下次循环的时候继续对比下一个时间差 memset(&event1.time, 0, sizeof(struct timeval)); //清空event1的时间 write(DeviceFile[event1.deviceID], &event1.time, sizeof(input_event)); //把event1写回设备 } for(int i=0;i<=9;++i) { if(DeviceFile[i]!=-1) close(DeviceFile[i]); //关闭所有设备 } std::cout<<"Replay completed"<<std::endl; }
record.cpp
#include "stdafx.h" #define DEBUG inline bool eventcmp(const myinputevent& a,const myinputevent& b) //这个比较函数为了之后排序用,因为两个自定义的数据结构,你用标准排序函数,排序肯定不知道你这俩结构应该怎么比大小 //定义了我的数据结构之间如果做小于比较时,时间小就算小。 //这样就可以采用标准排序函数对事件进行排序了。 { if(a.time.tv_sec<b.time.tv_sec) return 1; if(a.time.tv_sec>b.time.tv_sec) return 0; if(a.time.tv_usec<b.time.tv_usec) return 1; else return 0; } std::vector<myinputevent> AllRecordedEvent; //这里有个数组,用来添加所有录制到的event char fn[200]; void afterstop(int x) //按下ctrl+c和回车之后,录制停止,做数据整理和保存工作 //主要是按时间顺序对事件排序,然后保存 { #ifdef DEBUG std::cout<<"Recorded events:"<<AllRecordedEvent.size()<<std::endl; #endif std::cout<<"Sorting & saving..."<<std::endl; std::stable_sort(AllRecordedEvent.begin(),AllRecordedEvent.end(),eventcmp); std::cout<<"Sort completed"<<std::endl; //这是排序语句,对所有的录制的event排序,排序时大小比较用eventcmp函数,也就是根据时间先后排序啦 //因为有的时候,设备的输出并不按照时间顺序,比如你按开机键,它涉及的不止一个event,有时会有微微的时间上的错序 //任何错序都会严重影响replay,所以这里必须排序啦,这里一定要stable_sort,也就是排序必须是稳定的 //对于不稳定的排序,有时两个事件的时间完全相等,可能会使原来在后面的跑前面去 int f=open(fn,O_WRONLY | O_CREAT | O_TRUNC); if(f==-1) { std::cout<<fn<<" cannot be opened!"; exit(0); } //打开一开始event record file中那个file文件进行写入 for(unsigned int i=0;i<AllRecordedEvent.size();++i) { write(f,&AllRecordedEvent[i],sizeof(myinputevent)); } close(f); std::cout<<"Record completed. Filename:"<<fn<<std::endl; exit(0); } void record(char* filename) { std::cout<<"If using windows adb, you have to \"busybox stty intr ^X\" to change the INTR from Ctrl-C to Ctrl-X"<<std::endl; std::cout<<"And the stop recording command will be ctrl-X."<<std::endl <<"If using linux adb, you are fine."<<std::endl; //注意如果用windows中的adb,按下ctrl+c之后windows的shell也会收到命令从而终止adb shell,这样达不到终止录制的功能 //所以如果非要在windows中使用adb,就需要用这个命令busybox stty intr ^X,把板子的终止组合键注册到ctrl-X上去 //这样按ctrl-X再回车,就可以终止录制 strcpy(fn,filename); int f=open(fn,O_WRONLY | O_CREAT); //先尝试打开一下参数中的文件,若失败就不继续了 if(f==-1) { std::cout<<fn<<" cannot be opened!"; exit(0); } close(f); struct sigaction act; act.sa_handler=afterstop; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGINT,&act, NULL); //这里注册了ctrl+c事件,这样录制过程中按下ctrl+c然后回车,可以执行afterstop函数。 char dev[40]="/dev/input/eventX"; int DeviceFile[10]; for(char i='0';i<='9';++i) { dev[16]=i; int f=open(dev,O_RDONLY | O_NONBLOCK); //把所有的event都打开,这样保证能包括键盘和屏幕 //这里O_RDONLY是只读,O_NONBLOCK是非阻塞。非阻塞的意思就是读取的时候,如果设备里没有,就返回-1。 //如果是阻塞的话,设备里没有,read函数是不返回的,要等到有了才返回。 //所以阻塞肯定不行,因为我有一堆设备要循环读取的,如果第一个设备没数据就会卡住,后面的设备都读不到。 DeviceFile[i-'0']=f; //把句柄保存下来,如果打不开的话f值是-1 #ifdef DEBUG if(f!=-1) std::cout<<"Device opened:"<<i<<std::endl; #endif } char buffer[2048]; std::cout<<"Recording started, press Ctrl+C then press ENTER to stop recording"<<std::endl; while(1) { for(int i=0;i<10;++i) { if(DeviceFile[i]==-1) continue; int ReadSize=read(DeviceFile[i],buffer,2048); //不断的循环,把打开了的文件反复读取,这里缓存区必须是16的倍数,因为生成的数据是16字节的数据结构,这个基本结构不能破坏了 //16字节的数据结构中,前4字节是秒,然后4字节微秒,最后8字节是实际的设备发送的信息 //这里read会返回读取到的字节数量,如果啥也没读到会返回-1 if(ReadSize!=-1) { #ifdef DEBUG std::cout<<"device"<<i<<"read"<<ReadSize<<std::endl; #endif for(input_event *p=(input_event*)buffer;(char*)p<buffer+ReadSize;++p) { myinputevent my; my.code=p->code; my.type=p->type; my.value=p->value; my.time=p->time; my.deviceID=i; AllRecordedEvent.push_back(my); //这里可能难理解,buffer里存储了大量16字节的event,所以让指针p是数据结构指针,然后16字节16字节的挪动 //我有一个自己的数据结构叫myinputevent,比这个16字节来说,尾部添加了一个4字节的int,存储设备的event号 //你可以在stdafx.h中看到这些数据结构 //把那16字节的原封不动的放到我的数据结构中,再把我自己的设备号赋值,就完成了这一个事件的录制。 //然后就存储到AllRecordedEvent里面去。 } } } } }