/cmds/monkey/src/com/android/commands/monkey/MonkeySourceRandom.java

https://github.com/CyanogenMod/android_development · Java · 500 lines · 337 code · 54 blank · 109 comment · 70 complexity · 26e262c780fee5dd5fa814012b48fd4c MD5 · raw file

  1. /*
  2. * Copyright (C) 2008 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.android.commands.monkey;
  17. import android.content.ComponentName;
  18. import android.graphics.PointF;
  19. import android.hardware.display.DisplayManagerGlobal;
  20. import android.os.SystemClock;
  21. import android.view.Display;
  22. import android.view.KeyCharacterMap;
  23. import android.view.KeyEvent;
  24. import android.view.MotionEvent;
  25. import android.view.Surface;
  26. import java.util.List;
  27. import java.util.Random;
  28. /**
  29. * monkey event queue
  30. */
  31. public class MonkeySourceRandom implements MonkeyEventSource {
  32. /** Key events that move around the UI. */
  33. private static final int[] NAV_KEYS = {
  34. KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
  35. KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
  36. };
  37. /**
  38. * Key events that perform major navigation options (so shouldn't be sent
  39. * as much).
  40. */
  41. private static final int[] MAJOR_NAV_KEYS = {
  42. KeyEvent.KEYCODE_MENU, /*KeyEvent.KEYCODE_SOFT_RIGHT,*/
  43. KeyEvent.KEYCODE_DPAD_CENTER,
  44. };
  45. /** Key events that perform system operations. */
  46. private static final int[] SYS_KEYS = {
  47. KeyEvent.KEYCODE_HOME, KeyEvent.KEYCODE_BACK,
  48. KeyEvent.KEYCODE_CALL, KeyEvent.KEYCODE_ENDCALL,
  49. KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_MUTE,
  50. KeyEvent.KEYCODE_MUTE,
  51. };
  52. /** If a physical key exists? */
  53. private static final boolean[] PHYSICAL_KEY_EXISTS = new boolean[KeyEvent.getMaxKeyCode() + 1];
  54. static {
  55. for (int i = 0; i < PHYSICAL_KEY_EXISTS.length; ++i) {
  56. PHYSICAL_KEY_EXISTS[i] = true;
  57. }
  58. // Only examine SYS_KEYS
  59. for (int i = 0; i < SYS_KEYS.length; ++i) {
  60. PHYSICAL_KEY_EXISTS[SYS_KEYS[i]] = KeyCharacterMap.deviceHasKey(SYS_KEYS[i]);
  61. }
  62. }
  63. /** Possible screen rotation degrees **/
  64. private static final int[] SCREEN_ROTATION_DEGREES = {
  65. Surface.ROTATION_0,
  66. Surface.ROTATION_90,
  67. Surface.ROTATION_180,
  68. Surface.ROTATION_270,
  69. };
  70. public static final int FACTOR_TOUCH = 0;
  71. public static final int FACTOR_MOTION = 1;
  72. public static final int FACTOR_PINCHZOOM = 2;
  73. public static final int FACTOR_TRACKBALL = 3;
  74. public static final int FACTOR_ROTATION = 4;
  75. public static final int FACTOR_PERMISSION = 5;
  76. public static final int FACTOR_NAV = 6;
  77. public static final int FACTOR_MAJORNAV = 7;
  78. public static final int FACTOR_SYSOPS = 8;
  79. public static final int FACTOR_APPSWITCH = 9;
  80. public static final int FACTOR_FLIP = 10;
  81. public static final int FACTOR_ANYTHING = 11;
  82. public static final int FACTORZ_COUNT = 12; // should be last+1
  83. private static final int GESTURE_TAP = 0;
  84. private static final int GESTURE_DRAG = 1;
  85. private static final int GESTURE_PINCH_OR_ZOOM = 2;
  86. /** percentages for each type of event. These will be remapped to working
  87. * values after we read any optional values.
  88. **/
  89. private float[] mFactors = new float[FACTORZ_COUNT];
  90. private List<ComponentName> mMainApps;
  91. private int mEventCount = 0; //total number of events generated so far
  92. private MonkeyEventQueue mQ;
  93. private Random mRandom;
  94. private int mVerbose = 0;
  95. private long mThrottle = 0;
  96. private MonkeyPermissionUtil mPermissionUtil;
  97. private boolean mKeyboardOpen = false;
  98. public static String getKeyName(int keycode) {
  99. return KeyEvent.keyCodeToString(keycode);
  100. }
  101. /**
  102. * Looks up the keyCode from a given KEYCODE_NAME. NOTE: This may
  103. * be an expensive operation.
  104. *
  105. * @param keyName the name of the KEYCODE_VALUE to lookup.
  106. * @returns the intenger keyCode value, or KeyEvent.KEYCODE_UNKNOWN if not found
  107. */
  108. public static int getKeyCode(String keyName) {
  109. return KeyEvent.keyCodeFromString(keyName);
  110. }
  111. public MonkeySourceRandom(Random random, List<ComponentName> MainApps,
  112. long throttle, boolean randomizeThrottle, boolean permissionTargetSystem) {
  113. // default values for random distributions
  114. // note, these are straight percentages, to match user input (cmd line args)
  115. // but they will be converted to 0..1 values before the main loop runs.
  116. mFactors[FACTOR_TOUCH] = 15.0f;
  117. mFactors[FACTOR_MOTION] = 10.0f;
  118. mFactors[FACTOR_TRACKBALL] = 15.0f;
  119. // Adjust the values if we want to enable rotation by default.
  120. mFactors[FACTOR_ROTATION] = 0.0f;
  121. mFactors[FACTOR_NAV] = 25.0f;
  122. mFactors[FACTOR_MAJORNAV] = 15.0f;
  123. mFactors[FACTOR_SYSOPS] = 2.0f;
  124. mFactors[FACTOR_APPSWITCH] = 2.0f;
  125. mFactors[FACTOR_FLIP] = 1.0f;
  126. // disbale permission by default
  127. mFactors[FACTOR_PERMISSION] = 0.0f;
  128. mFactors[FACTOR_ANYTHING] = 13.0f;
  129. mFactors[FACTOR_PINCHZOOM] = 2.0f;
  130. mRandom = random;
  131. mMainApps = MainApps;
  132. mQ = new MonkeyEventQueue(random, throttle, randomizeThrottle);
  133. mPermissionUtil = new MonkeyPermissionUtil();
  134. mPermissionUtil.setTargetSystemPackages(permissionTargetSystem);
  135. }
  136. /**
  137. * Adjust the percentages (after applying user values) and then normalize to a 0..1 scale.
  138. */
  139. private boolean adjustEventFactors() {
  140. // go through all values and compute totals for user & default values
  141. float userSum = 0.0f;
  142. float defaultSum = 0.0f;
  143. int defaultCount = 0;
  144. for (int i = 0; i < FACTORZ_COUNT; ++i) {
  145. if (mFactors[i] <= 0.0f) { // user values are zero or negative
  146. userSum -= mFactors[i];
  147. } else {
  148. defaultSum += mFactors[i];
  149. ++defaultCount;
  150. }
  151. }
  152. // if the user request was > 100%, reject it
  153. if (userSum > 100.0f) {
  154. System.err.println("** Event weights > 100%");
  155. return false;
  156. }
  157. // if the user specified all of the weights, then they need to be 100%
  158. if (defaultCount == 0 && (userSum < 99.9f || userSum > 100.1f)) {
  159. System.err.println("** Event weights != 100%");
  160. return false;
  161. }
  162. // compute the adjustment necessary
  163. float defaultsTarget = (100.0f - userSum);
  164. float defaultsAdjustment = defaultsTarget / defaultSum;
  165. // fix all values, by adjusting defaults, or flipping user values back to >0
  166. for (int i = 0; i < FACTORZ_COUNT; ++i) {
  167. if (mFactors[i] <= 0.0f) { // user values are zero or negative
  168. mFactors[i] = -mFactors[i];
  169. } else {
  170. mFactors[i] *= defaultsAdjustment;
  171. }
  172. }
  173. // if verbose, show factors
  174. if (mVerbose > 0) {
  175. System.out.println("// Event percentages:");
  176. for (int i = 0; i < FACTORZ_COUNT; ++i) {
  177. System.out.println("// " + i + ": " + mFactors[i] + "%");
  178. }
  179. }
  180. if (!validateKeys()) {
  181. return false;
  182. }
  183. // finally, normalize and convert to running sum
  184. float sum = 0.0f;
  185. for (int i = 0; i < FACTORZ_COUNT; ++i) {
  186. sum += mFactors[i] / 100.0f;
  187. mFactors[i] = sum;
  188. }
  189. return true;
  190. }
  191. private static boolean validateKeyCategory(String catName, int[] keys, float factor) {
  192. if (factor < 0.1f) {
  193. return true;
  194. }
  195. for (int i = 0; i < keys.length; ++i) {
  196. if (PHYSICAL_KEY_EXISTS[keys[i]]) {
  197. return true;
  198. }
  199. }
  200. System.err.println("** " + catName + " has no physical keys but with factor " + factor + "%.");
  201. return false;
  202. }
  203. /**
  204. * See if any key exists for non-zero factors.
  205. */
  206. private boolean validateKeys() {
  207. return validateKeyCategory("NAV_KEYS", NAV_KEYS, mFactors[FACTOR_NAV])
  208. && validateKeyCategory("MAJOR_NAV_KEYS", MAJOR_NAV_KEYS, mFactors[FACTOR_MAJORNAV])
  209. && validateKeyCategory("SYS_KEYS", SYS_KEYS, mFactors[FACTOR_SYSOPS]);
  210. }
  211. /**
  212. * set the factors
  213. *
  214. * @param factors percentages for each type of event
  215. */
  216. public void setFactors(float factors[]) {
  217. int c = FACTORZ_COUNT;
  218. if (factors.length < c) {
  219. c = factors.length;
  220. }
  221. for (int i = 0; i < c; i++)
  222. mFactors[i] = factors[i];
  223. }
  224. public void setFactors(int index, float v) {
  225. mFactors[index] = v;
  226. }
  227. /**
  228. * Generates a random motion event. This method counts a down, move, and up as multiple events.
  229. *
  230. * TODO: Test & fix the selectors when non-zero percentages
  231. * TODO: Longpress.
  232. * TODO: Fling.
  233. * TODO: Meta state
  234. * TODO: More useful than the random walk here would be to pick a single random direction
  235. * and distance, and divvy it up into a random number of segments. (This would serve to
  236. * generate fling gestures, which are important).
  237. *
  238. * @param random Random number source for positioning
  239. * @param gesture The gesture to perform.
  240. *
  241. */
  242. private void generatePointerEvent(Random random, int gesture) {
  243. Display display = DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
  244. PointF p1 = randomPoint(random, display);
  245. PointF v1 = randomVector(random);
  246. long downAt = SystemClock.uptimeMillis();
  247. mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_DOWN)
  248. .setDownTime(downAt)
  249. .addPointer(0, p1.x, p1.y)
  250. .setIntermediateNote(false));
  251. // sometimes we'll move during the touch
  252. if (gesture == GESTURE_DRAG) {
  253. int count = random.nextInt(10);
  254. for (int i = 0; i < count; i++) {
  255. randomWalk(random, display, p1, v1);
  256. mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_MOVE)
  257. .setDownTime(downAt)
  258. .addPointer(0, p1.x, p1.y)
  259. .setIntermediateNote(true));
  260. }
  261. } else if (gesture == GESTURE_PINCH_OR_ZOOM) {
  262. PointF p2 = randomPoint(random, display);
  263. PointF v2 = randomVector(random);
  264. randomWalk(random, display, p1, v1);
  265. mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_DOWN
  266. | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT))
  267. .setDownTime(downAt)
  268. .addPointer(0, p1.x, p1.y).addPointer(1, p2.x, p2.y)
  269. .setIntermediateNote(true));
  270. int count = random.nextInt(10);
  271. for (int i = 0; i < count; i++) {
  272. randomWalk(random, display, p1, v1);
  273. randomWalk(random, display, p2, v2);
  274. mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_MOVE)
  275. .setDownTime(downAt)
  276. .addPointer(0, p1.x, p1.y).addPointer(1, p2.x, p2.y)
  277. .setIntermediateNote(true));
  278. }
  279. randomWalk(random, display, p1, v1);
  280. randomWalk(random, display, p2, v2);
  281. mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_UP
  282. | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT))
  283. .setDownTime(downAt)
  284. .addPointer(0, p1.x, p1.y).addPointer(1, p2.x, p2.y)
  285. .setIntermediateNote(true));
  286. }
  287. randomWalk(random, display, p1, v1);
  288. mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_UP)
  289. .setDownTime(downAt)
  290. .addPointer(0, p1.x, p1.y)
  291. .setIntermediateNote(false));
  292. }
  293. private PointF randomPoint(Random random, Display display) {
  294. return new PointF(random.nextInt(display.getWidth()), random.nextInt(display.getHeight()));
  295. }
  296. private PointF randomVector(Random random) {
  297. return new PointF((random.nextFloat() - 0.5f) * 50, (random.nextFloat() - 0.5f) * 50);
  298. }
  299. private void randomWalk(Random random, Display display, PointF point, PointF vector) {
  300. point.x = (float) Math.max(Math.min(point.x + random.nextFloat() * vector.x,
  301. display.getWidth()), 0);
  302. point.y = (float) Math.max(Math.min(point.y + random.nextFloat() * vector.y,
  303. display.getHeight()), 0);
  304. }
  305. /**
  306. * Generates a random trackball event. This consists of a sequence of small moves, followed by
  307. * an optional single click.
  308. *
  309. * TODO: Longpress.
  310. * TODO: Meta state
  311. * TODO: Parameterize the % clicked
  312. * TODO: More useful than the random walk here would be to pick a single random direction
  313. * and distance, and divvy it up into a random number of segments. (This would serve to
  314. * generate fling gestures, which are important).
  315. *
  316. * @param random Random number source for positioning
  317. *
  318. */
  319. private void generateTrackballEvent(Random random) {
  320. for (int i = 0; i < 10; ++i) {
  321. // generate a small random step
  322. int dX = random.nextInt(10) - 5;
  323. int dY = random.nextInt(10) - 5;
  324. mQ.addLast(new MonkeyTrackballEvent(MotionEvent.ACTION_MOVE)
  325. .addPointer(0, dX, dY)
  326. .setIntermediateNote(i > 0));
  327. }
  328. // 10% of trackball moves end with a click
  329. if (0 == random.nextInt(10)) {
  330. long downAt = SystemClock.uptimeMillis();
  331. mQ.addLast(new MonkeyTrackballEvent(MotionEvent.ACTION_DOWN)
  332. .setDownTime(downAt)
  333. .addPointer(0, 0, 0)
  334. .setIntermediateNote(true));
  335. mQ.addLast(new MonkeyTrackballEvent(MotionEvent.ACTION_UP)
  336. .setDownTime(downAt)
  337. .addPointer(0, 0, 0)
  338. .setIntermediateNote(false));
  339. }
  340. }
  341. /**
  342. * Generates a random screen rotation event.
  343. *
  344. * @param random Random number source for rotation degree.
  345. */
  346. private void generateRotationEvent(Random random) {
  347. mQ.addLast(new MonkeyRotationEvent(
  348. SCREEN_ROTATION_DEGREES[random.nextInt(
  349. SCREEN_ROTATION_DEGREES.length)],
  350. random.nextBoolean()));
  351. }
  352. /**
  353. * generate a random event based on mFactor
  354. */
  355. private void generateEvents() {
  356. float cls = mRandom.nextFloat();
  357. int lastKey = 0;
  358. if (cls < mFactors[FACTOR_TOUCH]) {
  359. generatePointerEvent(mRandom, GESTURE_TAP);
  360. return;
  361. } else if (cls < mFactors[FACTOR_MOTION]) {
  362. generatePointerEvent(mRandom, GESTURE_DRAG);
  363. return;
  364. } else if (cls < mFactors[FACTOR_PINCHZOOM]) {
  365. generatePointerEvent(mRandom, GESTURE_PINCH_OR_ZOOM);
  366. return;
  367. } else if (cls < mFactors[FACTOR_TRACKBALL]) {
  368. generateTrackballEvent(mRandom);
  369. return;
  370. } else if (cls < mFactors[FACTOR_ROTATION]) {
  371. generateRotationEvent(mRandom);
  372. return;
  373. } else if (cls < mFactors[FACTOR_PERMISSION]) {
  374. mQ.add(mPermissionUtil.generateRandomPermissionEvent(mRandom));
  375. return;
  376. }
  377. // The remaining event categories are injected as key events
  378. for (;;) {
  379. if (cls < mFactors[FACTOR_NAV]) {
  380. lastKey = NAV_KEYS[mRandom.nextInt(NAV_KEYS.length)];
  381. } else if (cls < mFactors[FACTOR_MAJORNAV]) {
  382. lastKey = MAJOR_NAV_KEYS[mRandom.nextInt(MAJOR_NAV_KEYS.length)];
  383. } else if (cls < mFactors[FACTOR_SYSOPS]) {
  384. lastKey = SYS_KEYS[mRandom.nextInt(SYS_KEYS.length)];
  385. } else if (cls < mFactors[FACTOR_APPSWITCH]) {
  386. MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get(
  387. mRandom.nextInt(mMainApps.size())));
  388. mQ.addLast(e);
  389. return;
  390. } else if (cls < mFactors[FACTOR_FLIP]) {
  391. MonkeyFlipEvent e = new MonkeyFlipEvent(mKeyboardOpen);
  392. mKeyboardOpen = !mKeyboardOpen;
  393. mQ.addLast(e);
  394. return;
  395. } else {
  396. lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1);
  397. }
  398. if (lastKey != KeyEvent.KEYCODE_POWER
  399. && lastKey != KeyEvent.KEYCODE_ENDCALL
  400. && lastKey != KeyEvent.KEYCODE_SLEEP
  401. && lastKey != KeyEvent.KEYCODE_SOFT_SLEEP
  402. && PHYSICAL_KEY_EXISTS[lastKey]) {
  403. break;
  404. }
  405. }
  406. MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, lastKey);
  407. mQ.addLast(e);
  408. e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey);
  409. mQ.addLast(e);
  410. }
  411. public boolean validate() {
  412. boolean ret = true;
  413. // only populate & dump permissions if enabled
  414. if (mFactors[FACTOR_PERMISSION] != 0.0f) {
  415. ret &= mPermissionUtil.populatePermissionsMapping();
  416. if (ret && mVerbose >= 2) {
  417. mPermissionUtil.dump();
  418. }
  419. }
  420. return ret & adjustEventFactors();
  421. }
  422. public void setVerbose(int verbose) {
  423. mVerbose = verbose;
  424. }
  425. /**
  426. * generate an activity event
  427. */
  428. public void generateActivity() {
  429. MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get(
  430. mRandom.nextInt(mMainApps.size())));
  431. mQ.addLast(e);
  432. }
  433. /**
  434. * if the queue is empty, we generate events first
  435. * @return the first event in the queue
  436. */
  437. public MonkeyEvent getNextEvent() {
  438. if (mQ.isEmpty()) {
  439. generateEvents();
  440. }
  441. mEventCount++;
  442. MonkeyEvent e = mQ.getFirst();
  443. mQ.removeFirst();
  444. return e;
  445. }
  446. }