如何跨app对其他应用进行虚拟点击

可能很多人在Android开发中会有这样的想法,如何模拟屏幕点击,向另外的app发送点击事件,来达到某种目的。 就像我们平时用 adb shell sendevent命令一样,模拟用户的一组输入操作,来实现自动化测试。

但是如果不通过 adb,是否也有办法做到呢? 当然是可以的。sendevent工具的原理是通过向设备节点 dev/input/eventX 写入事件,我们也可以用这个原理做同样的事情。

准备工作

root设备。。。

是的,如果没有root的话,没有办法打开 dev/input/下的设备节点。 sendevent可以,是因为它本身就是系统应用,拥有系统权限。

然后还需要关闭SELinux的安全校验

否则会看到

avc: denied { 操作权限 } for pid=7201 comm=“进程名” scontext=u:r:源类型:s0 tcontext=u:r:目标类型:s0 tclass=访问类别 permissive=0

这种错误,"操作权限"可能是 open/read/write 或者其他,但只要看到 avc denied,基本可以确定是 SElinux的问题了。 请在root下面执行这个命令

getenforce

如果显示的是 enforcing,那么说明SELinux是启用的,可以用 setenforce 0 来关闭它。

开始写硬件设备节点

定位硬件设备节点

在 dev/input/下面有很多设备节点,以我手上的机器来举例

root@hydrogen:/ # getevent -l add device 1: /dev/input/event8 name: "uinput-fpc" add device 2: /dev/input/event7 name: "msm8976-tashalite-snd-card Headset Jack" add device 3: /dev/input/event6 name: "msm8976-tashalite-snd-card Button Jack" add device 4: /dev/input/event2 name: "qpnp_pon" could not get driver version for /dev/input/mouse0, Not a typewriter add device 5: /dev/input/event1 name: "hbtp_vm" add device 6: /dev/input/event0 name: "input_mt_wrapper" could not get driver version for /dev/input/mice, Not a typewriter add device 7: /dev/input/event3 name: "gpio-keys" add device 8: /dev/input/event5 name: "pwm-ir" add device 9: /dev/input/event4 name: "ft5x46"

可以看到有各种不同的设备节点,它们对应不同的输入设备。如果在 getevent -l执行后触摸屏幕的话会看到有一串输入事件,

/dev/input/event4: EV_ABS ABS_MT_TRACKING_ID 00000062 /dev/input/event4: EV_ABS ABS_MT_POSITION_X 0000016a /dev/input/event4: EV_ABS ABS_MT_POSITION_Y 000005e3 /dev/input/event4: EV_KEY BTN_TOUCH DOWN

这里的 event4就是我们的目标节点了。 当然不同设备节点名字也不同,有些是 event0,有些是event4,可以自己写个规则来获取设备的触摸节点

打开设备节点

这个只能在jni层来操作,我已经封装了一个so库,只需要对目标设备节点执行 open操作就可以

/**
 * function Open : opens an input event node
 * @param forceOpen will try to set permissions and then reopen if first open attempt fails
 * @return true if input event node has been opened
 */
public boolean Open(boolean forceOpen) {
  int res = OpenDev(m_nId);
    // if opening fails, we might not have the correct permissions, try changing 660 to 666
    if (res != 0) {
      // possible only if we have root
      if(forceOpen && Shell.isSuAvailable()) {
        // set new permissions
        Shell.runCommand("chmod 666 "+ m_szPath);
        // reopen
          res = OpenDev(m_nId);
      }
    }
    m_szName = getDevName(m_nId);
    m_bOpen = (res == 0);
    // debug
    Log.d(LT,  "Open:"+m_szPath+" Name:"+m_szName+" Result:"+m_bOpen);
    // done, return
    return m_bOpen;
}

m_nId 是指定的设备节点的序列号,序列号是通过jni扫描设备节点后得到的。

写入事件

对于设备的事件,用 getevent -c 20 命令可以看到是一堆十六进制的数据,在分析之后他们对应的事件类型如下

final int SYNC_REPORT = 0x00;
final int EV_SYNC = 0x00,
        EV_KEY = 0x01,
        EV_REL = 0x02,
        EV_ABS = 0x03,
        REL_X = 0x00,
        REL_Y = 0x01,
        REL_Z = 0x02,
        BTN_TOUCH = 0x14a;// 330
final int DOWN = 0x01,
        UP = 0x00;
final int ABS_X = 0x35,
          ABS_Y = 0x36;

在已经打开了设备节点之后,就可以直接调jni的方法 injectEvent来写入了。

这里封装了几个简单的JNI接口,比如发送一个点击事件,x和y是坐标。 简单的获取坐标的方法可以打开调试模式里的显示坐标选项,手动触摸一个地方然后记下坐标位置。你也可以自己写个逻辑来计算需要的位置。

public int SendTouchDownAbs(int x, int y ) {
  intSendEvent(m_nId, EV_ABS, ABS_X, x); //set x coord
  intSendEvent(m_nId, EV_ABS, ABS_Y, y); //set y coord
  intSendEvent(m_nId, EV_KEY, BTN_TOUCH, DOWN); // touch down
  intSendEvent(m_nId, EV_SYNC, SYNC_REPORT, SYNC_REPORT);
  intSendEvent(m_nId, EV_ABS, ABS_X,x);
  intSendEvent(m_nId, EV_ABS, ABS_Y,y);
  intSendEvent(m_nId, EV_SYNC, SYNC_REPORT, SYNC_REPORT);
  intSendEvent(m_nId, EV_KEY, BTN_TOUCH,UP); //touch up
  intSendEvent(m_nId, EV_SYNC, SYNC_REPORT, SYNC_REPORT);
  return 1;
}

注意点击事件的模拟其实是有顺序的,每个具体的操作之后都需要跟一个 SYNC_REPORT,这样才能把事件写入。

总结

关于上面的代码和具体的demo,可以后台回复"虚拟点击"获取。 虽然说这个方案不需要连接PC,但是缺点也是很明显的。 它需要手机有 root权限,而且需要手动关闭 SELinux。因此不能做到完全自动侵入,还是需要你对手机有控制权。 然而就跨app模拟点击的需求来说,除非有途径可以用系统签名编译一个带 INJECT_EVENT权限的app,并且编入系统镜像, 不然作为一个第三方app来说,这应该是唯一的方案了。

本文分享自微信公众号 - Android每日一讲(gh_f053f29083b9)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-04-25

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏乐沙弥的世界

Oracle RAC OCR 的管理与维护

   OCR相当于Windows的注册表。对于Windows而言,所有的软件信息,用户,配置,安全等等统统都放到注册表里边。而集群呢,同样如此,所有和集群相关的...

12940
来自专栏PPV课数据科学社区

R语言18讲(三)

? 我们在做数据分析工作的前提,当然是得有数据,巧妇难为无米之炊,所以数据的获取和产生是非常重要和基础的,然而,在当前互联网时代,信息非常的膨胀,我们获取...

39260
来自专栏蜉蝣禅修之道

fs学习笔记之输出格式

21130
来自专栏Python攻城狮

itchat库初探--微信好友全头像的拼接

如果安装python的时候pip安装选项没打√ ,就先安装pip。 Python和pip的安装

9320
来自专栏AI研习社

在 Mac OS X 装不上 TensorFlow?看了这篇就会装

这个文档说明了如何在 Mac OS X 上安装 TensorFlow。(从 1.2 版本开始,在 Mac OS X 上 TensorFlow 不再支持 GPU。...

75160
来自专栏进击的程序猿

raft 系列解读(4) 之 etcd-raft学习

大多数Raft的实现都是整体设计,包括存储处理,消息序列化和网络传输,但是本raft库在实现的时候只实现了最核心的算法,换来了灵活性和性能,网络和disk IO...

15540
来自专栏MixLab科技+设计实验室

写给设计师的人工智能指南:Tensorflow快速入门

以下为正文 Tensorflow的环境,我采用的是Docker搭建的。 Docker通常用于如下场景: web应用的自动化打包和发布; 自动化测试和持续集成...

35060
来自专栏开发与安全

linux网络编程之socket(五):tcp流协议产生的粘包问题和解决方案

我们在前面曾经说过,发送端可以是一K一K地发送数据,而接收端的应用程序可以两K两K地提走数据,当然也有可能一次提走3K或6K数据,或者一次只提走几个字节的数据,...

35500
来自专栏AI研习社

AI-Blocks:可以让任何人创建机器学习模型的所见即所得交互界面 | Github 项目推荐

AI-Blocks 是一个强大且直观的所见即所得交互界面,可以让任何人都创建机器学习模型。 ? AI-Block 通过可拖动的对象来创建简单的场景,该模型可以直...

398160
来自专栏Java帮帮-微信公众号-技术文章全总结

Mysql批量插入分析【面试+工作】

最近发现几个项目中都有批次插入数据库的功能,每个项目中批次插入的写法有一些差别,所以本文打算对Mysql的批次插入做一个详细的分析。

39620

扫码关注云+社区

领取腾讯云代金券