Android Monkey原理探讨

0x0 概述

本文不涉及如何使用monkey,官网说得够详细了,网上资料也一大堆。本文着重探讨monkey的实现原理,以及基于这些原理,我们可以做些什么?本文涉及的Monkey的源码位于AOSP的development项目的cmds目录下。

0x1 Monkey事件触发原理

Monkey的主要作用是,发送一些随机交互事件模拟人的随机操作。它是如何触发各种事件的呢?

1.1 触摸事件

触摸事件包括屏幕以及物理键的触摸,滑动,点击事件。我们可以通过发送一些adb命令模拟这些事件。不过Monkey实现时,直接调用hiden API实现。

InputManager.getInstance().injectInputEvent(keyEvent, int)

构造需要的事件,然后调用该接口就能触发了。

2. Activity事件

Activity事件是指我们调用Android系统组件的事件。一般测试中,我们也是通过adb shell am命令来实现的。Monkey在实现是,是直接通过IActivityManager实例:

//同样通过hiden API获得IActivityManager实例
IActivityManager am = ActivityManagerNative.getDefault();
//利用IActivityManager接口打开Activity
am.startActivity();

3. Window事件

Window事件是指操作Window的事件,例如转屏。这里直接用IWindowManager实现。

//获得IWindowManager实例
IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
//调用IWindowManager接口转屏
wm..thawRotation();

此外还会调用其他的一些hiden API获取崩溃信息,ANR信息等等。这些直接从底层hiden接口拿,比我们上层获取更加方便,准确。

0x2 Monkey的框架设计

上一节分析到Monkey的功能主要分成两块:
1. 产生事件;
2. 触发事件。

因此它的框架设计非常简洁,核心类是Monkey.java, MonkeyEventSource.java, MonkeyEvent.java。

核心类 说明
Monkey 程序的入口,同时也是调度中心,根据参数选择合适的MonkeyEventSource,并适时触发MonkeyEvent。
MonkeyEventSource MonkeyEvent的工厂,是一个接口。它有各种实现,例如随机生成MonkeyEvent,根据配置文件生成MonkeyEvent,根据网络数据生成MonkeyEvent等等。
MonkeyEvent 各种事件的具体实现,是一个抽象类,不同事件有不同实现。在Monkey中各种活动都是事件,除了基本的触摸事件,Activity事件外,事件之间的停顿也是通过一个MonkeyThrottleEvent来实现。这样概念的扩展,将各种活动一视同仁的对待,使设计变得简单。

如果我们需要扩展Monkey的功能,只需要增加自己实现的MonkeyEventSource和MonkeyEvent即可。

0x3 Monkey的运行原理

我们在terminal中执行adb shell monkey,实际上是执行手机中/system/bin/monkey这个脚本(在源码中也能看到),该脚本具体内容是

# Script to start "monkey" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/monkey.jar
trap "" HUP
exec app_process $base/bin com.android.commands.monkey.Monkey $*

所以他是通过/system/bin/app_process运行/system/framework/monkey.jar。
app_process的核心源码是app_main.cpp
它会新建一个native进程,初始化虚拟机,从CLASSPATH找到我们定义的Main Class,并把参数传给它。可以参考Android系统进程Zygote启动过程的源代码分析

0x4 自定义可执行jar包

根据上一节的分析,可以通过exec app_process直接执行一个jar包。所以理论上我们可以自己写一个jar包,丢给它执行。可以参考一下解析android framework下利用app_process来调用java写的命令及示例

这里需要注意,JVM中的jar文件是一对class文件的zip包。而在Android中,可执行文件是dex,所以Android里面的jar本质上里面也是dex,直接把eclipse导出的jar包放进去是无法执行的。

这里也有一个小例子(可以在附件中下载完整代码):

/**
 * Android可执行jar测试。
 * 测试程序会每隔三秒钟写一次文件,报告自己还活着,可以测试该程序的运行时长。
 *
 * 使用方式:
 * 1. 利用android_jar.sh一键生成一个dex文件test.dex;
 * 2. 将testJar放到/data/local/tmp/目录下,并授予执行权限;
 * 3. 运行testJar文件。
 * Created by shangjie on 2016/11/9.
 */
public class Monkey {
    public static void main(String[] args) {
        Process.setArgV0("io.light.monkey");
        System.out.println("Process start");
        Monkey main = new Monkey();
        main.run();
        System.exit(0);
    }

    public void run() {
                File file = Environment.getExternalStorageDirectory();
        file = new File(file, "ali_jar_process.txt");
        if (file.exists()) {
            boolean isDelete = file.delete();
            println("Delete file: " + isDelete);
        }

        try {
            boolean result = file.createNewFile();
            println("Create file: " + result);
        } catch (IOException e) {
            e.printStackTrace();
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HH:mm:ss");
        int i  = 0;
        while (i < 900) {
            String time = sdf.format(new Date());
            String content = time + " I am still live.";
            println(content);
            writeFile(file, content);

            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            i ++;
        }
    }

    void writeFile(File file, String mValue) {
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(file, true))) {
            bw.newLine();
            bw.write(mValue);
            bw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void println(String msg) {
        System.out.println(msg);
    }
}

我可以看到它成功运行了:

进一步分析该进程,通过ps命令,我们看到该进程是属于shell用户组的,因此它没有root,system那样高的权限,但是比一般的用户进程权限也要高很多,可以多做很多事情,例如静默地安装,卸载。

通过查看该进程的oom_adj文件,我们知道他的ADJ值是-17,根据源码ProcessList-17是NATIVE_ADJ是不受系统控制的,理论上不会被系统杀掉,可以一直运行,不过我自己的测试中,发现还是会die,还在分析原因。从我们直观经验中,monkey,UiAutomator的确可以运行很久。

0x5 总结

上述Monkey的分析同样适用于UiAutomator等Android内部的测试工具,更重要的是,我们可以基于这些原理实现我们自己的功能强大,且稳定可靠的测试工具。

0x6 引用

  1. 解析android framework下利用app_process来调用java写的命令及示例
  2. Android系统进程Zygote启动过程的源代码分析
  3. 有关Monkey命令的两个隐藏选项
时间: 2024-10-25 16:25:48

Android Monkey原理探讨的相关文章

Oracle Freelist和HWM原理探讨及相关性能优化

oracle|性能|优化 Oracle Freelist和HWM原理探讨及相关性能优化 中兴通讯重庆研究所 游波   关键词:Freelist,HWM,存储参数,段,块,dump,优化 文章摘要:    近期来,FreeList的重要作用逐渐为Oracle DBA所认识,网上也出现一些相关的讨论.本文以FreeList为线索对Oracle的存储管理的原理进行较深入的探讨,涉及Oracle段区块管理的原理,FreeList算法等.而与FreeList密切相关的一个重用特性HWM,与sql性能密切相

深度理解Android InstantRun原理以及源码分析

深度理解Android InstantRun原理以及源码分析 @Author 莫川 Instant Run官方介绍 简单介绍一下Instant Run,它是Android Studio2.0以后新增的一个运行机制,能够显著减少你第二次及以后的构建和部署时间.简单通俗的解释就是,当你在Android Studio中改了你的代码,Instant Run可以很快的让你看到你修改的效果.而在没有Instant Run之前,你的一个小小的修改,都肯能需要几十秒甚至更长的等待才能看到修改后的效果. 传统的代

android lbs-老师,您好:请教Android定位原理

问题描述 老师,您好:请教Android定位原理 我现在在做的是个Android定位系统,不能用第三方的定位API,像高德.百度等都不能用,我现在除了找到GPS定位能用之外,到目前还没找到其他的合适的定位方法,劳烦老师给我解答一下,谢谢您了! 解决方案 可以通过基站定位,你手机和基站有交互,通过对信号强弱得分析和基站得分布对应的位置,获取你所在得位置.

Android 断点续传原理以及实现_Android

Android 断点续传原理以及实现 0.  前言 在Android开发中,断点续传听起来挺容易,在下载一个文件时点击暂停任务暂停,点击开始会继续下载文件.但是真正实现起来知识点还是蛮多的,因此今天有时间实现了一下,并进行记录. 1.  断点续传原理 在本地下载过程中要使用数据库实时存储到底存储到文件的哪个位置了,这样点击开始继续传递时,才能通过HTTP的GET请求中的setRequestProperty()方法可以告诉服务器,数据从哪里开始,到哪里结束.同时在本地的文件写入时,RandomAc

Android Monkey压力测试详细介绍_Android

Monkey 是Android SDK提供的一个命令行工具, 可以简单,方便地运行在任何版本的Android模拟器和实体设备上. Monkey会发送伪随机的用户事件流,适合对app做压力测试 阅读目录 环境搭建 什么是Monkey Monkey 用来做什么 Monkey程序介绍 Monkey 架构 Monkey弱点 Monkey 参数大全 Monkey 命令 基本参数介绍 Monkey 实例 Monkey 查看包名 Monkey  日志分析 必须重视Crash 环境搭建 安装Android SD

Socket通信原理探讨(C++为例)

Socket通信原理探讨(C++为例)   一.网络中进程之间如何通信?      本地的进程间通信(IPC)有很多种方式,但可以总结为下面4类: 1.消息传递(管道.FIFO.消息队列) 2.同步(互斥量.条件变量.读写锁.文件和写记录锁.信号量) 3.共享内存(匿名的和具名的) 4.远程过程调用(Solaris门和Sun RPC)      但这些都不是本文的主题!我们要讨论的是网络中进程之间如何通信?首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!在本地可以通过进程PID来唯一标识

Android Monkey压力测试详细介绍

Monkey 是Android SDK提供的一个命令行工具, 可以简单,方便地运行在任何版本的Android模拟器和实体设备上. Monkey会发送伪随机的用户事件流,适合对app做压力测试 阅读目录 环境搭建 什么是Monkey Monkey 用来做什么 Monkey程序介绍 Monkey 架构 Monkey弱点 Monkey 参数大全 Monkey 命令 基本参数介绍 Monkey 实例 Monkey 查看包名 Monkey  日志分析 必须重视Crash 环境搭建 安装Android SD

Android Handler 原理分析及实例代码

Android Handler 原理分析 Handler一个让无数android开发者头疼的东西,希望我今天这边文章能为您彻底根治这个问题 今天就为大家详细剖析下Handler的原理 Handler使用的原因 1.多线程更新Ui会导致UI界面错乱 2.如果加锁会导致性能下降 3.只在主线程去更新UI,轮询处理 Handler使用简介 其实关键方法就2个一个sendMessage,用来接收消息 另一个是handleMessage,用来处理接收到的消息 下面是我参考疯狂android讲义,写的一个子

Android 断点续传原理以及实现

Android 断点续传原理以及实现 0.  前言 在Android开发中,断点续传听起来挺容易,在下载一个文件时点击暂停任务暂停,点击开始会继续下载文件.但是真正实现起来知识点还是蛮多的,因此今天有时间实现了一下,并进行记录. 1.  断点续传原理 在本地下载过程中要使用数据库实时存储到底存储到文件的哪个位置了,这样点击开始继续传递时,才能通过HTTP的GET请求中的setRequestProperty()方法可以告诉服务器,数据从哪里开始,到哪里结束.同时在本地的文件写入时,RandomAc