版权声明:本文出自胖喵~的博客,转载必须注明出处。

转载请注明出处:http://www.cnblogs.com/by-dream/p/4713466.html

前言


  根据上一篇我们学会了Monkey的用法,知道了Monkey可以非常容易的模拟伪随机的模拟事件。也许有的时候我们想让他稍微智能化一些,例如只在某个屏幕范围产生伪随机事件,或者说是只对某些指定Activity进行操作,这样就需要我们对Monkey进行改良了。而改良必须去改Monkey的源码,因此本节课们就简单的说说Monkey的源码。

  源码下载地址:https://code.google.com/p/android-source-browsing/source/browse/cmds/monkey/src/com/android/commands/monkey/?repo=platform--development&name=android-cts-4.2_r2  ( 这里只是Monkey的源码,如果要编译Monkey需要下载Android的源码 )

  

概述


  如果你真的打算改造一个属于你的Monkey,那么过程必须要做的是:

  1、下载Android源码

  2、阅读Monkey源码如果需要修改代码

  3、代码编译

  4、运行Monkey

  本节内容主要针对第二部分的 “阅读Monkey源码”,其他的1、3、4 部分会在另外一篇“只允许注册用户访问的”的番外篇里进行介绍,因为这部分有些内容不是本人原创,因此对博客进行了加密处理,以免侵犯到源作者的权利,如需交流这部分内容,请留言给我。

Monkey源码


Monkey的入口在 Monkey.java中:

  1. /**
  2. * Command-line entry point.
  3. *
  4. * @param args The command-line arguments
  5. */
  6. public static void main(String[] args) {
  7. // Set the process name showing in "ps" or "top"
  8. Process.setArgV0("com.android.commands.monkey");
  9.  
  10. int resultCode = (new Monkey()).run(args);
  11. System.exit(resultCode);
  12. }

  第一句的意思就是在 shell 命令行下 使用 ps | grep com.**.monkey 就找到正在运行的monkey进程

  第二句是后续的内容,我们继续看后续干了什么。

  1. /**
  2. * Run the command!
  3. *
  4. * @param args The command-line arguments
  5. * @return Returns a posix-style result code. 0 for no error.
  6. */
  7. private int run(String[] args) {
  8. // Super-early debugger wait
  9. for (String s : args) {
  10. if ("--wait-dbg".equals(s)) {
  11. Debug.waitForDebugger();
  12. }
  13. }
  14.  
  15. // Default values for some command-line options
  16. mVerbose = 0;
  17. mCount = 1000;
  18. mSeed = 0;
  19. mThrottle = 0;
  20.  
  21. // prepare for command-line processing
  22. mArgs = args;
  23. mNextArg = 0;
  24.  
  25. // set a positive value, indicating none of the factors is provided yet
  26. for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
  27. mFactors[i] = 1.0f;
  28. }
  29.  
  30. if (!processOptions()) {
  31. return -1;
  32. }
  33.  
  34. if (!loadPackageLists()) {
  35. return -1;
  36. }
  37.  
  38. // now set up additional data in preparation for launch
  39. if (mMainCategories.size() == 0) {
  40. mMainCategories.add(Intent.CATEGORY_LAUNCHER);
  41. mMainCategories.add(Intent.CATEGORY_MONKEY);
  42. }
  43.  
  44. if (mVerbose > 0) {
  45. System.out.println(":Monkey: seed=" + mSeed + " count=" + mCount);
  46. if (mValidPackages.size() > 0) {
  47. Iterator<String> it = mValidPackages.iterator();
  48. while (it.hasNext()) {
  49. System.out.println(":AllowPackage: " + it.next());
  50. }
  51. }
  52. if (mInvalidPackages.size() > 0) {
  53. Iterator<String> it = mInvalidPackages.iterator();
  54. while (it.hasNext()) {
  55. System.out.println(":DisallowPackage: " + it.next());
  56. }
  57. }
  58. if (mMainCategories.size() != 0) {
  59. Iterator<String> it = mMainCategories.iterator();
  60. while (it.hasNext()) {
  61. System.out.println(":IncludeCategory: " + it.next());
  62. }
  63. }
  64. }
  65.  
  66. if (!checkInternalConfiguration()) {
  67. return -2;
  68. }
  69.  
  70. if (!getSystemInterfaces()) {
  71. return -3;
  72. }
  73.  
  74. if (!getMainApps()) {
  75. return -4;
  76. }
  77.  
  78. mRandom = new SecureRandom();
  79. mRandom.setSeed((mSeed == 0) ? -1 : mSeed);
  80.  
  81. if (mScriptFileNames != null && mScriptFileNames.size() == 1) {
  82. // script mode, ignore other options
  83. mEventSource = new MonkeySourceScript(mRandom, mScriptFileNames.get(0), mThrottle,
  84. mRandomizeThrottle, mProfileWaitTime, mDeviceSleepTime);
  85. mEventSource.setVerbose(mVerbose);
  86.  
  87. mCountEvents = false;
  88. } else if (mScriptFileNames != null && mScriptFileNames.size() > 1) {
  89. if (mSetupFileName != null) {
  90. mEventSource = new MonkeySourceRandomScript(mSetupFileName,
  91. mScriptFileNames, mThrottle, mRandomizeThrottle, mRandom,
  92. mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
  93. mCount++;
  94. } else {
  95. mEventSource = new MonkeySourceRandomScript(mScriptFileNames,
  96. mThrottle, mRandomizeThrottle, mRandom,
  97. mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
  98. }
  99. mEventSource.setVerbose(mVerbose);
  100. mCountEvents = false;
  101. } else if (mServerPort != -1) {
  102. try {
  103. mEventSource = new MonkeySourceNetwork(mServerPort);
  104. } catch (IOException e) {
  105. System.out.println("Error binding to network socket.");
  106. return -5;
  107. }
  108. mCount = Integer.MAX_VALUE;
  109. } else {
  110. // random source by default
  111. if (mVerbose >= 2) { // check seeding performance
  112. System.out.println("// Seeded: " + mSeed);
  113. }
  114. mEventSource = new MonkeySourceRandom(mRandom, mMainApps, mThrottle, mRandomizeThrottle);
  115. mEventSource.setVerbose(mVerbose);
  116. // set any of the factors that has been set
  117. for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
  118. if (mFactors[i] <= 0.0f) {
  119. ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
  120. }
  121. }
  122.  
  123. // in random mode, we start with a random activity
  124. ((MonkeySourceRandom) mEventSource).generateActivity();
  125. }
  126.  
  127. // validate source generator
  128. if (!mEventSource.validate()) {
  129. return -5;
  130. }
  131.  
  132. // If we're profiling, do it immediately before/after the main monkey
  133. // loop
  134. if (mGenerateHprof) {
  135. signalPersistentProcesses();
  136. }
  137.  
  138. mNetworkMonitor.start();
  139. int crashedAtCycle = runMonkeyCycles();
  140. mNetworkMonitor.stop();
  141.  
  142. synchronized (this) {
  143. if (mRequestAnrTraces) {
  144. reportAnrTraces();
  145. mRequestAnrTraces = false;
  146. }
  147. if (mRequestAnrBugreport){
  148. System.out.println("Print the anr report");
  149. getBugreport("anr_" + mReportProcessName + "_");
  150. mRequestAnrBugreport = false;
  151. }
  152. if (mRequestAppCrashBugreport){
  153. getBugreport("app_crash" + mReportProcessName + "_");
  154. mRequestAppCrashBugreport = false;
  155. }
  156. if (mRequestDumpsysMemInfo) {
  157. reportDumpsysMemInfo();
  158. mRequestDumpsysMemInfo = false;
  159. }
  160. if (mRequestPeriodicBugreport){
  161. getBugreport("Bugreport_");
  162. mRequestPeriodicBugreport = false;
  163. }
  164. }
  165.  
  166. if (mGenerateHprof) {
  167. signalPersistentProcesses();
  168. if (mVerbose > 0) {
  169. System.out.println("// Generated profiling reports in /data/misc");
  170. }
  171. }
  172.  
  173. try {
  174. mAm.setActivityController(null);
  175. mNetworkMonitor.unregister(mAm);
  176. } catch (RemoteException e) {
  177. // just in case this was latent (after mCount cycles), make sure
  178. // we report it
  179. if (crashedAtCycle >= mCount) {
  180. crashedAtCycle = mCount - 1;
  181. }
  182. }
  183.  
  184. // report dropped event stats
  185. if (mVerbose > 0) {
  186. System.out.print(":Dropped: keys=");
  187. System.out.print(mDroppedKeyEvents);
  188. System.out.print(" pointers=");
  189. System.out.print(mDroppedPointerEvents);
  190. System.out.print(" trackballs=");
  191. System.out.print(mDroppedTrackballEvents);
  192. System.out.print(" flips=");
  193. System.out.println(mDroppedFlipEvents);
  194. }
  195.  
  196. // report network stats
  197. mNetworkMonitor.dump();
  198.  
  199. if (crashedAtCycle < mCount - 1) {
  200. System.err.println("** System appears to have crashed at event " + crashedAtCycle
  201. + " of " + mCount + " using seed " + mSeed);
  202. return crashedAtCycle;
  203. } else {
  204. if (mVerbose > 0) {
  205. System.out.println("// Monkey finished");
  206. }
  207. return 0;
  208. }
  209. }

run

  这个run中的内容基本就是Monkey运行的流程,主要做了:

  1、处理命令行参数

  1. if (!processOptions()) {
  2. return -1;
  3. }

    没有什么特殊的地方,主要是针对下面这张图里我们用到的参数进行一个统计和预处理。

  

  2、处理要拉起的应用程序的Activity:

    我们在运行Monkey的时候,如果指定了“ -p 包名 ”,那么Monkey一定会拉起这个App的第一个Activity,这个究竟是怎么实现的呢?就是借助Intent这个东西:

  1.     // now set up additional data in preparation for launch
  2. if (mMainCategories.size() == 0) {
  3. mMainCategories.add(Intent.CATEGORY_LAUNCHER);
  4. mMainCategories.add(Intent.CATEGORY_MONKEY);
  5. }

  3、处理Source模块:

    Source模块,以MonkeyEventSource为接口,衍生出三种Source类:MonkeySourceRandom类(随机生成事件)、MonkeySourceScript(从脚本获取事件)、MonkeySourceNetwork(从网络获取事件)。    

  1. if (mScriptFileNames != null && mScriptFileNames.size() == 1) {
  2. // script mode, ignore other options
  3. mEventSource = new MonkeySourceScript(mRandom, mScriptFileNames.get(0), mThrottle,
  4. mRandomizeThrottle, mProfileWaitTime, mDeviceSleepTime);
  5. mEventSource.setVerbose(mVerbose);
  6.  
  7. mCountEvents = false;
  8. } else if (mScriptFileNames != null && mScriptFileNames.size() > 1) {
  9. if (mSetupFileName != null) {
  10. mEventSource = new MonkeySourceRandomScript(mSetupFileName,
  11. mScriptFileNames, mThrottle, mRandomizeThrottle, mRandom,
  12. mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
  13. mCount++;
  14. } else {
  15. mEventSource = new MonkeySourceRandomScript(mScriptFileNames,
  16. mThrottle, mRandomizeThrottle, mRandom,
  17. mProfileWaitTime, mDeviceSleepTime, mRandomizeScript);
  18. }
  19. mEventSource.setVerbose(mVerbose);
  20. mCountEvents = false;
  21. } else if (mServerPort != -1) {
  22. try {
  23. mEventSource = new MonkeySourceNetwork(mServerPort);
  24. } catch (IOException e) {
  25. System.out.println("Error binding to network socket.");
  26. return -5;
  27. }
  28. mCount = Integer.MAX_VALUE;
  29. } else {
  30. // random source by default
  31. if (mVerbose >= 2) { // check seeding performance
  32. System.out.println("// Seeded: " + mSeed);
  33. }
  34. mEventSource = new MonkeySourceRandom(mRandom, mMainApps, mThrottle, mRandomizeThrottle);
  35. mEventSource.setVerbose(mVerbose);
  36. // set any of the factors that has been set
  37. for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
  38. if (mFactors[i] <= 0.0f) {
  39. ((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
  40. }
  41. }
  42.  
  43. // in random mode, we start with a random activity
  44. ((MonkeySourceRandom) mEventSource).generateActivity();
  45. }

    这部分只要是来判断Monkey的事件源来自何方,根据这些事件的来源,由不同的类做处理。MonkeySourceRandom事件的来源就是我们在命令行输入参数后的伪随机压力测试;MonkeySourceScript事件来源于Monkey识别的一种脚本,事实上Monkey也可以做到通过脚本指定位置点击,滑动等操作,但是该脚本的可读性非常的差,编写不易,因此这里我也没有介绍;第三种MonkeySourceNetwork来自于后面我们要讲的Monkeyrunner,Monkeyrunner通过socket将一些要处理的事件发给Monkey,由Monkey来完成最后的处理。

  4、循环处理事件:

  1. mNetworkMonitor.start();
  2. int crashedAtCycle = runMonkeyCycles();
  3. mNetworkMonitor.stop();

    主要看看 runMonkeyCycles() 这个函数主要做了什么:

  1. /**
  2. * Run mCount cycles and see if we hit any crashers.
  3. * <p>
  4. * TODO: Meta state on keys
  5. *
  6. * @return Returns the last cycle which executed. If the value == mCount, no
  7. * errors detected.
  8. */
  9. private int runMonkeyCycles() {
  10. int eventCounter = 0;
  11. int cycleCounter = 0;
  12.  
  13. boolean shouldReportAnrTraces = false;
  14. boolean shouldReportDumpsysMemInfo = false;
  15. boolean shouldAbort = false;
  16. boolean systemCrashed = false;
  17.  
  18. // TO DO : The count should apply to each of the script file.
  19. while (!systemCrashed && cycleCounter < mCount) {
  20. ...
  21. MonkeyEvent ev = mEventSource.getNextEvent();
  22. if (ev != null) {
  23. int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
  24. ...
  25. }
  26. ...
  27. }
  28. ....
  29. }

    这里涉及到了一个重要的东西就是MonkeyEvent。

    以MonkeyEvent为基类,衍生出各种Event类,如Monkey中常见的点击,输入,滑动事件;

    那么一个点击的操作究竟是怎么进行下去的呢?我们可以到上面调用的是injectEvent,这个方法是由基类定义的,每一个子类去实现不同的内容,点击、滑动等这个方法都是通过第一个参数一个iWindowManager的对象而完成的,当然也有不需要这个参数,例如MonkeyThrottleEvent这个类的实现方法,根本没有用到iwm:

  1. @Override
  2. public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
  3.  
  4. if (verbose > 1) {
  5. System.out.println("Sleeping for " + mThrottle + " milliseconds");
  6. }
  7. try {
  8. Thread.sleep(mThrottle);
  9. } catch (InterruptedException e1) {
  10. System.out.println("** Monkey interrupted in sleep.");
  11. return MonkeyEvent.INJECT_FAIL;
  12. }
  13.  
  14. return MonkeyEvent.INJECT_SUCCESS;
  15. }

    那么这个iWindowManager的对象究竟是什么呢?这个事系统隐藏的一个接口,通过这个接口可以注入一些操作事件,那么我们以后是不是也可以用这个接口来进行事件的注入呢?答案是no,为什么呢?我们来看看:

    谷歌为了方便Monkey能够轻松的完成一些点击、滑动事件,因此在使用了这个系统隐藏的接口,Monkey这个应用拥有这个两个独特的权限:第一个是SET_ACTIVITY_WATCHER这个权限,它允许monkey对activity的生命周期进行全权控制。第二个就是INJECT_EVENTS这个权限它允许monkey去模拟触摸和按键事件。为了防止这个系统隐藏接口暴露出的漏洞,普通的App是不能请求到这些权限的,只有android系统同意的应用才会得到允许获得这些权限。为了防止坏人使用Monkey来进行这个事件的注入,Monkey也只被允许root运行或者是shell这个组的成员运行。

【Android测试】【第七节】Monkey——源码浅谈的更多相关文章

  1. 【Android测试】【第三节】ADB——源码浅谈

    ◆版权声明:本文出自carter_dream的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/4651724.html 前言 由于本人精力 ...

  2. 源码浅谈(一):java中的 toString()方法

    前言: toString()方法 相信大家都用到过,一般用于以字符串的形式返回对象的相关数据. 最近项目中需要对一个ArrayList<ArrayList<Integer>> ...

  3. 源码浅谈(二):java中的 Integer.parseInt(String str)方法

    这个方法是将字符串转换为整型 一.parseInt方法 ,可以看到默认又调用了parseInt(s,10) ,  第二个参数为基数,默认10 ,当然也可以自己设置  public static int ...

  4. 结合源码浅谈Spring容器与其子容器Spring MVC 冲突问题

    容器是整个Spring 框架的核心思想,用来管理Bean的整个生命周期. 一个项目中引入Spring和SpringMVC这两个框架,Spring是父容器,SpringMVC是其子容器,子容器可以看见父 ...

  5. glibc memcpy() 源码浅谈

    其实我本来只是想搞懂为什么memcpy()函数的参数类型是void *的: 我以为会在memcpy()源码中能找到答案,其实并没有,void *只是在传递参数的时候起了作用,可以让memcpy()接受 ...

  6. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  7. Monkey源码分析之事件源

    上一篇文章<Monkey源码分析之运行流程>给出了monkey运行的整个流程,让我们有一个概貌,那么往后的文章我们会尝试进一步的阐述相关的一些知识点. 这里先把整个monkey类的结构图给 ...

  8. Monkey源码分析之事件注入

    本系列的上一篇文章<Monkey源码分析之事件源>中我们描述了monkey是怎么从事件源取得命令,然后将命令转换成事件放到事件队列里面的,但是到现在位置我们还没有了解monkey里面的事件 ...

  9. monkey源码分析之事件注入方法变化

    在上一篇文章<Monkey源码分析之事件注入>中,我们看到了monkey在注入事件的时候用到了<Monkey源码分析番外篇之Android注入事件的三种方法比较>中的第一种方法 ...

随机推荐

  1. 【BZOJ】1055: [HAOI2008]玩具取名(dp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1055 我竟然都没往dp这个方向想.....百度了下看到标题是dp马上就会转移了QAQ... 设d[i ...

  2. Ubuntu根目录下各文件夹的功能详细介绍

    Ubuntu的根目录下存在着很多的文件夹,但你知道他们都存放着哪些文件呢?这些是深入了解Ubuntu系统必不缺少的知识,本文就关于此做一下介绍吧. /bin/    用以存储二进制可执行命令文件. / ...

  3. Ubuntu(Linux) 下 unzip 命令使用详解

    1.功能作用:解压缩zip文件 2.位置:/usr/bin/unzip 3.格式用法:unzip [-Z] [-opts[modifiers]] file[.zip] [list] [-x xlist ...

  4. Qt Examples Qt实例汇总

    ActiveQt Examples Using ActiveX from Qt applications. Animation Framework Examples Doing animations ...

  5. hdu 1735 字数统计

    这道题是到贪心的题目,首先用ans记录下所有的0的个数,然后尽量去掉更多的0,剩下的0的个数就是最少的字数.首先想到最后一行的0的个数可以减掉,然后就是m行开头的两个0可以减掉.然后思考最多还可以减掉 ...

  6. TCP和UDP的135、137、138、139、445端口的作用

    如果全是2000以上的系统,可以关闭137.138.139,只保留445 如果有xp系统,可能以上四个端口全部要打开 无论你的服务器中安装的是Windows 2000 Server,还是Windows ...

  7. NBUT 1525 Cow Xor(01字典树+前缀思想)

    [1525] Cow Xor 时间限制: 2000 ms 内存限制: 65535 K 问题描述 农民约翰在喂奶牛的时候被另一个问题卡住了.他的所有N(1 <= N <= 100,000)个 ...

  8. HDU 1078 FatMouse and Cheese(记忆化搜索)

    FatMouse and Cheese Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Othe ...

  9. 投芯片,现在要n+1模式

    给坚持理想的屌丝点个赞.投芯片,现在要n+1模式,n个小项目+一个大项目.团队是基础,屌丝创业要以营利为先导,先要短快,同时要有并行的大项目支撑.小项目赚的钱先解决面包问题,同时为大项目锻炼队伍积累经 ...

  10. Ubuntu 14.04 安装 VirtualBox

    参考: ubuntu14.04,安装VirtualBox 5.0(虚拟机软件)! 由于Vagrant工具的需要,安装了一下VirtualBox. 使用参考链接的法一居然在软件中心里面报错,我想可能是没 ...