Code drop from //branches/cupcake/...@124589
This commit is contained in:
@@ -28,13 +28,8 @@ import android.os.Debug;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
import android.server.data.CrashData;
|
||||
import android.view.Display;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.WindowManagerImpl;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -43,12 +38,10 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Application that injects random key events and other actions into the system.
|
||||
@@ -62,117 +55,6 @@ public class Monkey {
|
||||
*/
|
||||
private final static int DEBUG_ALLOW_ANY_STARTS = 0;
|
||||
private final static int DEBUG_ALLOW_ANY_RESTARTS = 0;
|
||||
|
||||
/** Key events that move around the UI. */
|
||||
private final int[] NAV_KEYS = {
|
||||
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
|
||||
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||
};
|
||||
/**
|
||||
* Key events that perform major navigation options (so shouldn't be sent
|
||||
* as much).
|
||||
*/
|
||||
private final int[] MAJOR_NAV_KEYS = {
|
||||
KeyEvent.KEYCODE_MENU, /*KeyEvent.KEYCODE_SOFT_RIGHT,*/
|
||||
KeyEvent.KEYCODE_DPAD_CENTER,
|
||||
};
|
||||
/** Key events that perform system operations. */
|
||||
private final int[] SYS_KEYS = {
|
||||
KeyEvent.KEYCODE_HOME, KeyEvent.KEYCODE_BACK,
|
||||
KeyEvent.KEYCODE_CALL, KeyEvent.KEYCODE_ENDCALL,
|
||||
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN,
|
||||
};
|
||||
/** Nice names for all key events. */
|
||||
private final String[] KEY_NAMES = {
|
||||
"KEYCODE_UNKNOWN",
|
||||
"KEYCODE_MENU",
|
||||
"KEYCODE_SOFT_RIGHT",
|
||||
"KEYCODE_HOME",
|
||||
"KEYCODE_BACK",
|
||||
"KEYCODE_CALL",
|
||||
"KEYCODE_ENDCALL",
|
||||
"KEYCODE_0",
|
||||
"KEYCODE_1",
|
||||
"KEYCODE_2",
|
||||
"KEYCODE_3",
|
||||
"KEYCODE_4",
|
||||
"KEYCODE_5",
|
||||
"KEYCODE_6",
|
||||
"KEYCODE_7",
|
||||
"KEYCODE_8",
|
||||
"KEYCODE_9",
|
||||
"KEYCODE_STAR",
|
||||
"KEYCODE_POUND",
|
||||
"KEYCODE_DPAD_UP",
|
||||
"KEYCODE_DPAD_DOWN",
|
||||
"KEYCODE_DPAD_LEFT",
|
||||
"KEYCODE_DPAD_RIGHT",
|
||||
"KEYCODE_DPAD_CENTER",
|
||||
"KEYCODE_VOLUME_UP",
|
||||
"KEYCODE_VOLUME_DOWN",
|
||||
"KEYCODE_POWER",
|
||||
"KEYCODE_CAMERA",
|
||||
"KEYCODE_CLEAR",
|
||||
"KEYCODE_A",
|
||||
"KEYCODE_B",
|
||||
"KEYCODE_C",
|
||||
"KEYCODE_D",
|
||||
"KEYCODE_E",
|
||||
"KEYCODE_F",
|
||||
"KEYCODE_G",
|
||||
"KEYCODE_H",
|
||||
"KEYCODE_I",
|
||||
"KEYCODE_J",
|
||||
"KEYCODE_K",
|
||||
"KEYCODE_L",
|
||||
"KEYCODE_M",
|
||||
"KEYCODE_N",
|
||||
"KEYCODE_O",
|
||||
"KEYCODE_P",
|
||||
"KEYCODE_Q",
|
||||
"KEYCODE_R",
|
||||
"KEYCODE_S",
|
||||
"KEYCODE_T",
|
||||
"KEYCODE_U",
|
||||
"KEYCODE_V",
|
||||
"KEYCODE_W",
|
||||
"KEYCODE_X",
|
||||
"KEYCODE_Y",
|
||||
"KEYCODE_Z",
|
||||
"KEYCODE_COMMA",
|
||||
"KEYCODE_PERIOD",
|
||||
"KEYCODE_ALT_LEFT",
|
||||
"KEYCODE_ALT_RIGHT",
|
||||
"KEYCODE_SHIFT_LEFT",
|
||||
"KEYCODE_SHIFT_RIGHT",
|
||||
"KEYCODE_TAB",
|
||||
"KEYCODE_SPACE",
|
||||
"KEYCODE_SYM",
|
||||
"KEYCODE_EXPLORER",
|
||||
"KEYCODE_ENVELOPE",
|
||||
"KEYCODE_ENTER",
|
||||
"KEYCODE_DEL",
|
||||
"KEYCODE_GRAVE",
|
||||
"KEYCODE_MINUS",
|
||||
"KEYCODE_EQUALS",
|
||||
"KEYCODE_LEFT_BRACKET",
|
||||
"KEYCODE_RIGHT_BRACKET",
|
||||
"KEYCODE_BACKSLASH",
|
||||
"KEYCODE_SEMICOLON",
|
||||
"KEYCODE_APOSTROPHE",
|
||||
"KEYCODE_SLASH",
|
||||
"KEYCODE_AT",
|
||||
"KEYCODE_NUM",
|
||||
"KEYCODE_HEADSETHOOK",
|
||||
"KEYCODE_FOCUS",
|
||||
"KEYCODE_PLUS",
|
||||
"KEYCODE_MENU",
|
||||
"KEYCODE_NOTIFICATION",
|
||||
"KEYCODE_SEARCH",
|
||||
|
||||
"TAG_LAST_KEYCODE" // EOL. used to keep the lists in sync
|
||||
};
|
||||
|
||||
private IActivityManager mAm;
|
||||
private IWindowManager mWm;
|
||||
private IPackageManager mPm;
|
||||
@@ -238,25 +120,16 @@ public class Monkey {
|
||||
long mDroppedKeyEvents = 0;
|
||||
long mDroppedPointerEvents = 0;
|
||||
long mDroppedTrackballEvents = 0;
|
||||
|
||||
/** percentages for each type of event. These will be remapped to working
|
||||
* values after we read any optional values.
|
||||
**/
|
||||
public static final int FACTOR_TOUCH = 0;
|
||||
public static final int FACTOR_MOTION = 1;
|
||||
public static final int FACTOR_TRACKBALL = 2;
|
||||
public static final int FACTOR_NAV = 3;
|
||||
public static final int FACTOR_MAJORNAV = 4;
|
||||
public static final int FACTOR_SYSOPS = 5;
|
||||
public static final int FACTOR_APPSWITCH = 6;
|
||||
public static final int FACTOR_ANYTHING = 7;
|
||||
|
||||
public static final int FACTORZ_COUNT = 8; // should be last+1
|
||||
|
||||
float[] mFactors = new float[FACTORZ_COUNT];
|
||||
long mDroppedFlipEvents = 0;
|
||||
|
||||
/** a filename to the script (if any) **/
|
||||
private String mScriptFileName = null;
|
||||
|
||||
private static final File TOMBSTONES_PATH = new File("/data/tombstones");
|
||||
private HashSet<String> mTombstones = null;
|
||||
|
||||
float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT];
|
||||
MonkeyEventSource mEventSource;
|
||||
|
||||
/**
|
||||
* Monitor operations happening in the system.
|
||||
@@ -440,21 +313,15 @@ public class Monkey {
|
||||
mSeed = 0;
|
||||
mThrottle = 0;
|
||||
|
||||
// default values for random distributions
|
||||
// note, these are straight percentages, to match user input (cmd line args)
|
||||
// but they will be converted to 0..1 values before the main loop runs.
|
||||
mFactors[FACTOR_TOUCH] = 15.0f;
|
||||
mFactors[FACTOR_MOTION] = 10.0f;
|
||||
mFactors[FACTOR_TRACKBALL] = 15.0f;
|
||||
mFactors[FACTOR_NAV] = 25.0f;
|
||||
mFactors[FACTOR_MAJORNAV] = 15.0f;
|
||||
mFactors[FACTOR_SYSOPS] = 2.0f;
|
||||
mFactors[FACTOR_APPSWITCH] = 2.0f;
|
||||
mFactors[FACTOR_ANYTHING] = 16.0f;
|
||||
|
||||
// prepare for command-line processing
|
||||
mArgs = args;
|
||||
mNextArg = 0;
|
||||
|
||||
//set a positive value, indicating none of the factors is provided yet
|
||||
for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
|
||||
mFactors[i] = 1.0f;
|
||||
}
|
||||
|
||||
if (!processOptions()) {
|
||||
return -1;
|
||||
}
|
||||
@@ -493,16 +360,40 @@ public class Monkey {
|
||||
return -4;
|
||||
}
|
||||
|
||||
if (!adjustEventFactors()) {
|
||||
return -5;
|
||||
if (mScriptFileName != null) {
|
||||
// script mode, ignore other options
|
||||
mEventSource = new MonkeySourceScript(mScriptFileName);
|
||||
mEventSource.setVerbose(mVerbose);
|
||||
} else {
|
||||
// random source by default
|
||||
if (mVerbose >= 2) { // check seeding performance
|
||||
System.out.println("// Seeded: " + mSeed);
|
||||
}
|
||||
mEventSource = new MonkeySourceRandom(mSeed, mMainApps);
|
||||
mEventSource.setVerbose(mVerbose);
|
||||
//set any of the factors that has been set
|
||||
for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
|
||||
if (mFactors[i] <= 0.0f) {
|
||||
((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//in random mode, we start with a random activity
|
||||
((MonkeySourceRandom) mEventSource).generateActivity();
|
||||
}
|
||||
|
||||
// Java's Random doesn't scramble well at all on seeding, so we'll use
|
||||
// the better random source here.
|
||||
SecureRandom random = new SecureRandom();
|
||||
random.setSeed((mSeed == 0) ? -1 : mSeed);
|
||||
if (mVerbose >= 2) { // check seeding performance
|
||||
System.out.println("// Seeded: " + mSeed + " and pulling: " + random.nextFloat());
|
||||
//validate source generator
|
||||
if (!mEventSource.validate()) {
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (mScriptFileName != null) {
|
||||
// in random mode, count is the number of single events
|
||||
// while in script mode, count is the number of repetition
|
||||
// for a sequence of events, so we need do multiply the length of
|
||||
// that sequence
|
||||
mCount = mCount * ((MonkeySourceScript) mEventSource)
|
||||
.getOneRoundEventCount();
|
||||
}
|
||||
|
||||
// If we're profiling, do it immediately before/after the main monkey loop
|
||||
@@ -510,7 +401,7 @@ public class Monkey {
|
||||
signalPersistentProcesses();
|
||||
}
|
||||
|
||||
int crashedAtCycle = runMonkeyCycles(random);
|
||||
int crashedAtCycle = runMonkeyCycles();
|
||||
|
||||
synchronized (this) {
|
||||
if (mRequestAnrTraces) {
|
||||
@@ -536,7 +427,7 @@ public class Monkey {
|
||||
// just in case this was latent (after mCount cycles), make sure
|
||||
// we report it
|
||||
if (crashedAtCycle >= mCount) {
|
||||
crashedAtCycle = mCount -1;
|
||||
crashedAtCycle = mCount - 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,10 +438,12 @@ public class Monkey {
|
||||
System.out.print(" pointers=");
|
||||
System.out.print(mDroppedPointerEvents);
|
||||
System.out.print(" trackballs=");
|
||||
System.out.println(mDroppedTrackballEvents);
|
||||
System.out.print(mDroppedTrackballEvents);
|
||||
System.out.print(" flips=");
|
||||
System.out.println(mDroppedFlipEvents);
|
||||
}
|
||||
|
||||
if (crashedAtCycle < mCount) {
|
||||
if (crashedAtCycle < mCount - 1) {
|
||||
System.err.println("** System appears to have crashed at event "
|
||||
+ crashedAtCycle + " of " + mCount + " using seed " + mSeed);
|
||||
return crashedAtCycle;
|
||||
@@ -576,7 +469,7 @@ public class Monkey {
|
||||
|
||||
try {
|
||||
String opt;
|
||||
while ((opt=nextOption()) != null) {
|
||||
while ((opt = nextOption()) != null) {
|
||||
if (opt.equals("-s")) {
|
||||
mSeed = nextOptionLong("Seed");
|
||||
} else if (opt.equals("-p")) {
|
||||
@@ -598,27 +491,37 @@ public class Monkey {
|
||||
} else if (opt.equals("--hprof")) {
|
||||
mGenerateHprof = true;
|
||||
} else if (opt.equals("--pct-touch")) {
|
||||
mFactors[FACTOR_TOUCH] = -nextOptionLong("touch events percentage");
|
||||
mFactors[MonkeySourceRandom.FACTOR_TOUCH] =
|
||||
-nextOptionLong("touch events percentage");
|
||||
} else if (opt.equals("--pct-motion")) {
|
||||
mFactors[FACTOR_MOTION] = -nextOptionLong("motion events percentage");
|
||||
mFactors[MonkeySourceRandom.FACTOR_MOTION] =
|
||||
-nextOptionLong("motion events percentage");
|
||||
} else if (opt.equals("--pct-trackball")) {
|
||||
mFactors[FACTOR_TRACKBALL] = -nextOptionLong("trackball events percentage");
|
||||
} else if (opt.equals("--pct-syskeys")) {
|
||||
mFactors[FACTOR_SYSOPS] = -nextOptionLong("system key events percentage");
|
||||
mFactors[MonkeySourceRandom.FACTOR_TRACKBALL] =
|
||||
-nextOptionLong("trackball events percentage");
|
||||
} else if (opt.equals("--pct-nav")) {
|
||||
mFactors[FACTOR_NAV] = -nextOptionLong("nav events percentage");
|
||||
mFactors[MonkeySourceRandom.FACTOR_NAV] =
|
||||
-nextOptionLong("nav events percentage");
|
||||
} else if (opt.equals("--pct-majornav")) {
|
||||
mFactors[FACTOR_MAJORNAV] = -nextOptionLong("major nav events percentage");
|
||||
mFactors[MonkeySourceRandom.FACTOR_MAJORNAV] =
|
||||
-nextOptionLong("major nav events percentage");
|
||||
} else if (opt.equals("--pct-appswitch")) {
|
||||
mFactors[FACTOR_APPSWITCH] = -nextOptionLong("app switch events percentage");
|
||||
mFactors[MonkeySourceRandom.FACTOR_APPSWITCH] =
|
||||
-nextOptionLong("app switch events percentage");
|
||||
} else if (opt.equals("--pct-flip")) {
|
||||
mFactors[MonkeySourceRandom.FACTOR_FLIP] =
|
||||
-nextOptionLong("keyboard flip percentage");
|
||||
} else if (opt.equals("--pct-anyevent")) {
|
||||
mFactors[FACTOR_ANYTHING] = -nextOptionLong("any events percentage");
|
||||
mFactors[MonkeySourceRandom.FACTOR_ANYTHING] =
|
||||
-nextOptionLong("any events percentage");
|
||||
} else if (opt.equals("--throttle")) {
|
||||
mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");
|
||||
mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");
|
||||
} else if (opt.equals("--wait-dbg")) {
|
||||
// do nothing - it's caught at the very start of run()
|
||||
} else if (opt.equals("--dbg-no-events")) {
|
||||
mSendNoEvents = true;
|
||||
} else if (opt.equals("-f")) {
|
||||
mScriptFileName = nextOptionData();
|
||||
} else if (opt.equals("-h")) {
|
||||
showUsage();
|
||||
return false;
|
||||
@@ -648,10 +551,10 @@ public class Monkey {
|
||||
showUsage();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check for any internal configuration (primarily build-time) errors.
|
||||
*
|
||||
@@ -659,13 +562,13 @@ public class Monkey {
|
||||
*/
|
||||
private boolean checkInternalConfiguration() {
|
||||
// Check KEYCODE name array, make sure it's up to date.
|
||||
|
||||
|
||||
String lastKeyName = null;
|
||||
try {
|
||||
lastKeyName = KEY_NAMES[KeyEvent.MAX_KEYCODE+1];
|
||||
lastKeyName = MonkeySourceRandom.getLastKeyName();
|
||||
} catch (RuntimeException e) {
|
||||
}
|
||||
if (! "TAG_LAST_KEYCODE".equals(lastKeyName)) {
|
||||
if (!"TAG_LAST_KEYCODE".equals(lastKeyName)) {
|
||||
System.err.println("** Error: Key names array malformed (internal error).");
|
||||
return false;
|
||||
}
|
||||
@@ -685,8 +588,7 @@ public class Monkey {
|
||||
return false;
|
||||
}
|
||||
|
||||
mWm = IWindowManager.Stub.asInterface(
|
||||
ServiceManager.getService("window"));
|
||||
mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
|
||||
if (mWm == null) {
|
||||
System.err.println("** Error: Unable to connect to window manager; is the system running?");
|
||||
return false;
|
||||
@@ -697,7 +599,7 @@ public class Monkey {
|
||||
System.err.println("** Error: Unable to connect to package manager; is the system running?");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
mAm.setActivityWatcher(new ActivityWatcher());
|
||||
} catch (RemoteException e) {
|
||||
@@ -707,7 +609,7 @@ public class Monkey {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Using the restrictions provided (categories & packages), generate a list of activities
|
||||
* that we can actually switch to.
|
||||
@@ -717,7 +619,7 @@ public class Monkey {
|
||||
private boolean getMainApps() {
|
||||
try {
|
||||
final int N = mMainCategories.size();
|
||||
for (int i=0; i<N; i++) {
|
||||
for (int i = 0; i< N; i++) {
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
String category = mMainCategories.get(i);
|
||||
if (category.length() > 0) {
|
||||
@@ -732,7 +634,7 @@ public class Monkey {
|
||||
System.out.println("// Selecting main activities from category " + category);
|
||||
}
|
||||
final int NA = mainApps.size();
|
||||
for (int a=0; a<NA; a++) {
|
||||
for (int a = 0; a < NA; a++) {
|
||||
ResolveInfo r = mainApps.get(a);
|
||||
if (mValidPackages.size() == 0 ||
|
||||
mValidPackages.contains(r.activityInfo.applicationInfo.packageName)) {
|
||||
@@ -769,87 +671,20 @@ public class Monkey {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the percentages (after applying user values) and then normalize to a 0..1 scale.
|
||||
*/
|
||||
private boolean adjustEventFactors() {
|
||||
// go through all values and compute totals for user & default values
|
||||
float userSum = 0.0f;
|
||||
float defaultSum = 0.0f;
|
||||
int defaultCount = 0;
|
||||
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
||||
if (mFactors[i] <= 0.0f) { // user values are zero or negative
|
||||
userSum -= mFactors[i];
|
||||
} else {
|
||||
defaultSum += mFactors[i];
|
||||
++defaultCount;
|
||||
}
|
||||
}
|
||||
|
||||
// if the user request was > 100%, reject it
|
||||
if (userSum > 100.0f) {
|
||||
System.err.println("** Event weights > 100%");
|
||||
showUsage();
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the user specified all of the weights, then they need to be 100%
|
||||
if (defaultCount == 0 && (userSum < 99.9f || userSum > 100.1f)) {
|
||||
System.err.println("** Event weights != 100%");
|
||||
showUsage();
|
||||
return false;
|
||||
}
|
||||
|
||||
// compute the adjustment necessary
|
||||
float defaultsTarget = (100.0f - userSum);
|
||||
float defaultsAdjustment = defaultsTarget / defaultSum;
|
||||
|
||||
// fix all values, by adjusting defaults, or flipping user values back to >0
|
||||
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
||||
if (mFactors[i] <= 0.0f) { // user values are zero or negative
|
||||
mFactors[i] = -mFactors[i];
|
||||
} else {
|
||||
mFactors[i] *= defaultsAdjustment;
|
||||
}
|
||||
}
|
||||
|
||||
// if verbose, show factors
|
||||
if (mVerbose > 0) {
|
||||
System.out.println("// Event percentages:");
|
||||
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
||||
System.out.println("// " + i + ": " + mFactors[i] + "%");
|
||||
}
|
||||
}
|
||||
|
||||
// finally, normalize and convert to running sum
|
||||
float sum = 0.0f;
|
||||
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
||||
sum += mFactors[i] / 100.0f;
|
||||
mFactors[i] = sum;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Run mCount cycles and see if we hit any crashers.
|
||||
*
|
||||
* TODO: Meta state on keys
|
||||
* TODO: Meta state on keys
|
||||
*
|
||||
* @param random The random source to use
|
||||
* @return Returns the last cycle which executed. If the value == mCount, no errors detected.
|
||||
* @return Returns the last cycle which executed. If the value == mCount, no errors detected.
|
||||
*/
|
||||
private int runMonkeyCycles(Random random) {
|
||||
private int runMonkeyCycles() {
|
||||
int i = 0;
|
||||
int lastKey = 0;
|
||||
|
||||
boolean systemCrashed = false;
|
||||
|
||||
if (!startRandomActivity(random)) {
|
||||
systemCrashed = true;
|
||||
}
|
||||
|
||||
while (!systemCrashed && i < mCount) {
|
||||
synchronized (this) {
|
||||
if (mRequestAnrTraces) {
|
||||
@@ -873,14 +708,14 @@ public class Monkey {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
Thread.sleep(mThrottle);
|
||||
Thread.sleep(mThrottle);
|
||||
} catch (InterruptedException e1) {
|
||||
System.out.println("** Monkey interrupted in sleep.");
|
||||
return i;
|
||||
System.out.println("** Monkey interrupted in sleep.");
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
// In this debugging mode, we never send any events. This is primarily
|
||||
// here so you can manually test the package or category limits, while manually
|
||||
// exercising the system.
|
||||
@@ -889,220 +724,34 @@ public class Monkey {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((mVerbose > 0) && (i%100) == 0 && i != 0 && lastKey == 0) {
|
||||
if ((mVerbose > 0) && (i % 100) == 0 && i != 0 && lastKey == 0) {
|
||||
System.out.println(" // Sending event #" + i);
|
||||
}
|
||||
|
||||
// if the last event was a keydown, then this event is a key-up
|
||||
if (lastKey != 0) {
|
||||
if (mVerbose > 1) {
|
||||
try {
|
||||
System.out.println(":SendKey (ACTION_UP):" + lastKey + " // " + KEY_NAMES[lastKey]);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
System.out.println(":SendKey (ACTION_UP): " + lastKey + " // Unknown key event");
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (! mWm.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, lastKey), false)) {
|
||||
MonkeyEvent ev = mEventSource.getNextEvent();
|
||||
if (ev != null) {
|
||||
i++;
|
||||
int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
|
||||
if (injectCode == MonkeyEvent.INJECT_FAIL) {
|
||||
if (ev instanceof MonkeyKeyEvent) {
|
||||
mDroppedKeyEvents++;
|
||||
} else if (ev instanceof MonkeyMotionEvent) {
|
||||
mDroppedPointerEvents++;
|
||||
} else if (ev instanceof MonkeyFlipEvent) {
|
||||
mDroppedFlipEvents++;
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
} else if (injectCode == MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION) {
|
||||
systemCrashed = true;
|
||||
break;
|
||||
} else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) {
|
||||
systemCrashed = !mIgnoreSecurityExceptions;
|
||||
}
|
||||
lastKey = 0;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// otherwise begin a new event cycle
|
||||
float cls = random.nextFloat();
|
||||
|
||||
boolean touchEvent = cls < mFactors[FACTOR_TOUCH];
|
||||
boolean motionEvent = !touchEvent && (cls < mFactors[FACTOR_MOTION]);
|
||||
if (touchEvent || motionEvent) {
|
||||
try {
|
||||
generateMotionEvent(random, motionEvent);
|
||||
} catch (RemoteException ex) {
|
||||
systemCrashed = true;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cls < mFactors[FACTOR_TRACKBALL]) {
|
||||
try {
|
||||
generateTrackballEvent(random);
|
||||
} catch (RemoteException ex) {
|
||||
systemCrashed = true;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The remaining event categories are injected as key events
|
||||
if (cls < mFactors[FACTOR_NAV]) {
|
||||
lastKey = NAV_KEYS[random.nextInt(NAV_KEYS.length)];
|
||||
} else if (cls < mFactors[FACTOR_MAJORNAV]) {
|
||||
lastKey = MAJOR_NAV_KEYS[random.nextInt(MAJOR_NAV_KEYS.length)];
|
||||
} else if (cls < mFactors[FACTOR_SYSOPS]) {
|
||||
lastKey = SYS_KEYS[random.nextInt(SYS_KEYS.length)];
|
||||
} else if (cls < mFactors[FACTOR_APPSWITCH]) {
|
||||
if (!startRandomActivity(random)) {
|
||||
systemCrashed = true;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
continue;
|
||||
} else {
|
||||
lastKey = 1 + random.nextInt(KeyEvent.MAX_KEYCODE - 1);
|
||||
}
|
||||
if (mVerbose > 0) {
|
||||
try {
|
||||
System.out.println(":SendKey: " + lastKey + " // " + KEY_NAMES[lastKey]);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
System.out.println(":SendKey: " + lastKey + " // Unknown key event");
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (! mWm.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, lastKey), false)) {
|
||||
mDroppedKeyEvents++;
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
systemCrashed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If we got this far, we succeeded!
|
||||
return mCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random motion event. This method counts a down, move, and up as one event.
|
||||
*
|
||||
* TODO: Test & fix the selectors when non-zero percentages
|
||||
* TODO: Longpress.
|
||||
* TODO: Fling.
|
||||
* TODO: Meta state
|
||||
* TODO: More useful than the random walk here would be to pick a single random direction
|
||||
* and distance, and divvy it up into a random number of segments. (This would serve to
|
||||
* generate fling gestures, which are important).
|
||||
*
|
||||
* @param random Random number source for positioning
|
||||
* @param motionEvent If false, touch/release. If true, touch/move/release.
|
||||
*
|
||||
* @throws RemoteException
|
||||
*/
|
||||
private void generateMotionEvent(Random random, boolean motionEvent) throws RemoteException {
|
||||
Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
|
||||
|
||||
float x = Math.abs(random.nextInt() % display.getWidth());
|
||||
float y = Math.abs(random.nextInt() % display.getHeight());
|
||||
long downAt = SystemClock.uptimeMillis();
|
||||
boolean drop = false;
|
||||
drop = sendMotionEvent(MotionEvent.ACTION_DOWN, x, y, downAt, "Pointer ACTION_DOWN", false,
|
||||
true);
|
||||
|
||||
// sometimes we'll move during the touch
|
||||
if (motionEvent) {
|
||||
int count = random.nextInt(10);
|
||||
for (int i = 0 ; i < count ; i++) {
|
||||
// generate some slop in the up event
|
||||
x = (x + (random.nextInt() % 10)) % display.getWidth();
|
||||
y = (y + (random.nextInt() % 10)) % display.getHeight();
|
||||
drop |= sendMotionEvent(MotionEvent.ACTION_MOVE, x, y, downAt,
|
||||
"Pointer ACTION_MOVE", true, true);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO generate some slop in the up event
|
||||
drop |= sendMotionEvent(MotionEvent.ACTION_UP, x, y, downAt, "Pointer ACTION_UP", false,
|
||||
true);
|
||||
|
||||
if (drop) {
|
||||
mDroppedPointerEvents++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a single motion event, either as a pointer or a trackball.
|
||||
*
|
||||
* @param action Must be one of the ACTION values defined in {@link MotionEvent}
|
||||
* @param x The position, or movement, in the X axis
|
||||
* @param y The position, or movement, in the Y axis
|
||||
* @param downAt The time of the first DOWN must be sent here, and the same value must
|
||||
* be sent for all subsequent events that are related (through the eventual UP event), or
|
||||
* -1 to simply send the current time as the downTime.
|
||||
* @param note This will be displayed when in verbose mode
|
||||
* @param intermediateNote If true, this is an intermediate step (more verbose logging, only)
|
||||
* @param isPointer Use true to send a pointer event, and false to send a trackball event
|
||||
*
|
||||
* @return Returns false if event was dispatched, true if it was dropped for any reason
|
||||
*
|
||||
* @throws RemoteException
|
||||
*/
|
||||
private boolean sendMotionEvent(int action, float x, float y, long downAt, final String note,
|
||||
boolean intermediateNote, boolean isPointer) throws RemoteException {
|
||||
if ((mVerbose > 0 && !intermediateNote) || mVerbose > 1) {
|
||||
System.out.println(":Sending " + note + " x=" + x + " y=" + y);
|
||||
}
|
||||
long eventTime = SystemClock.uptimeMillis();
|
||||
if (downAt == -1) {
|
||||
downAt = eventTime;
|
||||
}
|
||||
final MotionEvent evt = MotionEvent.obtain(downAt, eventTime, action, x, y, 0);
|
||||
if (isPointer) {
|
||||
return ! mWm.injectPointerEvent(evt, false);
|
||||
} else {
|
||||
return ! mWm.injectTrackballEvent(evt, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random trackball event. This consists of a sequence of small moves, followed by
|
||||
* an optional single click.
|
||||
*
|
||||
* TODO: Longpress.
|
||||
* TODO: Meta state
|
||||
* TODO: Parameterize the % clicked
|
||||
* TODO: More useful than the random walk here would be to pick a single random direction
|
||||
* and distance, and divvy it up into a random number of segments. (This would serve to
|
||||
* generate fling gestures, which are important).
|
||||
*
|
||||
* @param random Random number source for positioning
|
||||
*
|
||||
* @throws RemoteException
|
||||
*/
|
||||
private void generateTrackballEvent(Random random) throws RemoteException {
|
||||
Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
|
||||
|
||||
boolean drop = false;
|
||||
int count = random.nextInt(10);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
// generate a small random step
|
||||
int dX = random.nextInt(10) - 5;
|
||||
int dY = random.nextInt(10) - 5;
|
||||
drop |= sendMotionEvent(MotionEvent.ACTION_MOVE, dX, dY, -1, "Trackball ACTION_MOVE",
|
||||
(i > 0), false);
|
||||
}
|
||||
|
||||
// 10% of trackball moves end with a click
|
||||
if (0 == random.nextInt(10)) {
|
||||
long downAt = SystemClock.uptimeMillis();
|
||||
drop |= sendMotionEvent(MotionEvent.ACTION_DOWN, 0, 0, downAt, "Trackball ACTION_DOWN",
|
||||
true, false);
|
||||
drop |= sendMotionEvent(MotionEvent.ACTION_UP, 0, 0, downAt, "Trackball ACTION_UP",
|
||||
false, false);
|
||||
}
|
||||
|
||||
if (drop) {
|
||||
mDroppedTrackballEvents++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send SIGNAL_USR1 to all processes. This will generate large (5mb) profiling reports
|
||||
* in data/misc, so use with care.
|
||||
@@ -1120,40 +769,6 @@ public class Monkey {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Have the activity manager start a new activity.
|
||||
*
|
||||
* @param random Random number source
|
||||
*
|
||||
* @return Returns true on success, false if there was an error calling
|
||||
* the activity manager.
|
||||
*/
|
||||
private boolean startRandomActivity(Random random) {
|
||||
int numApps = mMainApps.size();
|
||||
int which = random.nextInt(numApps);
|
||||
ComponentName app = mMainApps.get(which);
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
intent.setComponent(app);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
if (mVerbose > 0) {
|
||||
System.out.println(":Switch: " + intent.toURI());
|
||||
}
|
||||
try {
|
||||
mAm.startActivity(null, intent, null, null, 0, null, null, 0, false, false);
|
||||
} catch (RemoteException e) {
|
||||
System.err.println("** Failed talking with activity manager!");
|
||||
return false;
|
||||
} catch (SecurityException e) {
|
||||
if (mVerbose > 0) {
|
||||
System.out.println("** Permissions error starting activity " + intent.toURI());
|
||||
}
|
||||
return mIgnoreSecurityExceptions; // true = "launched ok" (pretend)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch for appearance of new tombstone files, which indicate native crashes.
|
||||
*
|
||||
@@ -1185,7 +800,7 @@ public class Monkey {
|
||||
/**
|
||||
* Return the next command line option. This has a number of special cases which
|
||||
* closely, but not exactly, follow the POSIX command line options patterns:
|
||||
*
|
||||
*
|
||||
* -- means to stop processing additional options
|
||||
* -z means option z
|
||||
* -z ARGS means option z with (non-optional) arguments ARGS
|
||||
@@ -1274,17 +889,18 @@ public class Monkey {
|
||||
* Print how to use this command.
|
||||
*/
|
||||
private void showUsage() {
|
||||
System.err.println("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]");
|
||||
System.err.println(" [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]");
|
||||
System.err.println(" [--ignore-crashes] [--ignore-timeouts]");
|
||||
System.err.println(" [--ignore-security-exceptions] [--monitor-native-crashes]");
|
||||
System.err.println(" [--kill-process-after-error] [--hprof]");
|
||||
System.err.println(" [--pct-touch PERCENT] [--pct-motion PERCENT]");
|
||||
System.err.println(" [--pct-trackball PERCENT] [--pct-syskeys PERCENT]");
|
||||
System.err.println(" [--pct-nav PERCENT] [--pct-majornav PERCENT]");
|
||||
System.err.println(" [--pct-appswitch PERCENT] [--pct-anyevent PERCENT]");
|
||||
System.err.println(" [--wait-dbg] [--dbg-no-events]");
|
||||
System.err.println(" [-s SEED] [-v [-v] ...] [--throttle MILLISEC]");
|
||||
System.err.println(" COUNT");
|
||||
}
|
||||
System.err.println("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]");
|
||||
System.err.println(" [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]");
|
||||
System.err.println(" [--ignore-crashes] [--ignore-timeouts]");
|
||||
System.err.println(" [--ignore-security-exceptions] [--monitor-native-crashes]");
|
||||
System.err.println(" [--kill-process-after-error] [--hprof]");
|
||||
System.err.println(" [--pct-touch PERCENT] [--pct-motion PERCENT]");
|
||||
System.err.println(" [--pct-trackball PERCENT] [--pct-syskeys PERCENT]");
|
||||
System.err.println(" [--pct-nav PERCENT] [--pct-majornav PERCENT]");
|
||||
System.err.println(" [--pct-appswitch PERCENT] [--pct-flip PERCENT]");
|
||||
System.err.println(" [--pct-anyevent PERCENT]");
|
||||
System.err.println(" [--wait-dbg] [--dbg-no-events] [-f scriptfile]");
|
||||
System.err.println(" [-s SEED] [-v [-v] ...] [--throttle MILLISEC]");
|
||||
System.err.println(" COUNT");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.commands.monkey;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.os.RemoteException;
|
||||
import android.view.IWindowManager;
|
||||
|
||||
/**
|
||||
* monkey activity event
|
||||
*/
|
||||
public class MonkeyActivityEvent extends MonkeyEvent {
|
||||
private ComponentName mApp;
|
||||
|
||||
public MonkeyActivityEvent(ComponentName app) {
|
||||
super(EVENT_TYPE_ACTIVITY);
|
||||
mApp = app;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Intent for the new activity
|
||||
*/
|
||||
private Intent getEvent() {
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
intent.setComponent(mApp);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
|
||||
Intent intent = getEvent();
|
||||
if (verbose > 0) {
|
||||
System.out.println(":Switch: " + intent.toURI());
|
||||
}
|
||||
try {
|
||||
iam.startActivity(null, intent, null, null, 0, null, null, 0,
|
||||
false, false);
|
||||
} catch (RemoteException e) {
|
||||
System.err.println("** Failed talking with activity manager!");
|
||||
return MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION;
|
||||
} catch (SecurityException e) {
|
||||
if (verbose > 0) {
|
||||
System.out.println("** Permissions error starting activity "
|
||||
+ intent.toURI());
|
||||
}
|
||||
return MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION;
|
||||
}
|
||||
return MonkeyEvent.INJECT_SUCCESS;
|
||||
}
|
||||
}
|
||||
61
cmds/monkey/src/com/android/commands/monkey/MonkeyEvent.java
Normal file
61
cmds/monkey/src/com/android/commands/monkey/MonkeyEvent.java
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.commands.monkey;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.view.IWindowManager;
|
||||
|
||||
/**
|
||||
* abstract class for monkey event
|
||||
*/
|
||||
public abstract class MonkeyEvent {
|
||||
protected int eventType;
|
||||
public static final int EVENT_TYPE_KEY = 0;
|
||||
public static final int EVENT_TYPE_POINTER = 1;
|
||||
public static final int EVENT_TYPE_TRACKBALL = 2;
|
||||
public static final int EVENT_TYPE_ACTIVITY = 3;
|
||||
public static final int EVENT_TYPE_FLIP = 4; // Keyboard flip
|
||||
|
||||
public static final int INJECT_SUCCESS = 1;
|
||||
public static final int INJECT_FAIL = 0;
|
||||
|
||||
// error code for remote exception during injection
|
||||
public static final int INJECT_ERROR_REMOTE_EXCEPTION = -1;
|
||||
// error code for security exception during injection
|
||||
public static final int INJECT_ERROR_SECURITY_EXCEPTION = -2;
|
||||
|
||||
public MonkeyEvent(int type) {
|
||||
eventType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return event type
|
||||
*/
|
||||
public int getEventType() {
|
||||
return eventType;
|
||||
}
|
||||
|
||||
/**
|
||||
* a method for injecting event
|
||||
* @param iwm wires to current window manager
|
||||
* @param iam wires to current activity manager
|
||||
* @param verbose a log switch
|
||||
* @return INJECT_SUCCESS if it goes through, and INJECT_FAIL if it fails
|
||||
* in the case of exceptions, return its corresponding error code
|
||||
*/
|
||||
public abstract int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.commands.monkey;
|
||||
|
||||
/**
|
||||
* event source interface
|
||||
*/
|
||||
public interface MonkeyEventSource {
|
||||
/**
|
||||
*
|
||||
* @return the next monkey event from the source
|
||||
*/
|
||||
public MonkeyEvent getNextEvent();
|
||||
|
||||
/**
|
||||
* set verbose to allow different level of log
|
||||
* @param verbose output mode? 1= verbose, 2=very verbose
|
||||
*/
|
||||
public void setVerbose(int verbose);
|
||||
|
||||
/**
|
||||
* check whether precondition is satisfied
|
||||
* @return false if something fails, e.g. factor failure in random source
|
||||
* or file can not open from script source etc
|
||||
*/
|
||||
public boolean validate();
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.commands.monkey;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.view.IWindowManager;
|
||||
/**
|
||||
* monkey keyboard flip event
|
||||
*/
|
||||
public class MonkeyFlipEvent extends MonkeyEvent {
|
||||
|
||||
// Raw keyboard flip event data
|
||||
// Works on emulator and dream
|
||||
|
||||
private static final byte[] FLIP_0 = {
|
||||
0x7f, 0x06,
|
||||
0x00, 0x00,
|
||||
(byte) 0xe0, 0x39,
|
||||
0x01, 0x00,
|
||||
0x05, 0x00,
|
||||
0x00, 0x00,
|
||||
0x01, 0x00,
|
||||
0x00, 0x00 };
|
||||
|
||||
private static final byte[] FLIP_1 = {
|
||||
(byte) 0x85, 0x06,
|
||||
0x00, 0x00,
|
||||
(byte) 0x9f, (byte) 0xa5,
|
||||
0x0c, 0x00,
|
||||
0x05, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00 };
|
||||
|
||||
private final boolean mKeyboardOpen;
|
||||
|
||||
public MonkeyFlipEvent(boolean keyboardOpen) {
|
||||
super(EVENT_TYPE_FLIP);
|
||||
mKeyboardOpen = keyboardOpen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
|
||||
if (verbose > 0) {
|
||||
System.out.println(":Sending Flip keyboardOpen=" + mKeyboardOpen);
|
||||
}
|
||||
|
||||
// inject flip event
|
||||
try {
|
||||
FileOutputStream f = new FileOutputStream("/dev/input/event0");
|
||||
f.write(mKeyboardOpen ? FLIP_0 : FLIP_1);
|
||||
f.close();
|
||||
return MonkeyEvent.INJECT_SUCCESS;
|
||||
} catch (IOException e) {
|
||||
System.out.println("Got IOException performing flip" + e);
|
||||
return MonkeyEvent.INJECT_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
125
cmds/monkey/src/com/android/commands/monkey/MonkeyKeyEvent.java
Normal file
125
cmds/monkey/src/com/android/commands/monkey/MonkeyKeyEvent.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.commands.monkey;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.os.RemoteException;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.KeyEvent;
|
||||
/**
|
||||
* monkey key event
|
||||
*/
|
||||
public class MonkeyKeyEvent extends MonkeyEvent {
|
||||
private long mDownTime = -1;
|
||||
private int mMetaState = -1;
|
||||
private int mAction = -1;
|
||||
private int mKeyCode = -1;
|
||||
private int mScancode = -1;
|
||||
private int mRepeatCount = -1;
|
||||
private int mDeviceId = -1;
|
||||
private long mEventTime = -1;
|
||||
|
||||
public MonkeyKeyEvent(int action, int keycode) {
|
||||
super(EVENT_TYPE_KEY);
|
||||
mAction = action;
|
||||
mKeyCode = keycode;
|
||||
}
|
||||
|
||||
public MonkeyKeyEvent(long downTime, long eventTime, int action,
|
||||
int code, int repeat, int metaState,
|
||||
int device, int scancode) {
|
||||
super(EVENT_TYPE_KEY);
|
||||
|
||||
mAction = action;
|
||||
mKeyCode = code;
|
||||
mMetaState = metaState;
|
||||
mScancode = scancode;
|
||||
mRepeatCount = repeat;
|
||||
mDeviceId = device;
|
||||
mDownTime = downTime;
|
||||
mEventTime = eventTime;
|
||||
}
|
||||
|
||||
public int getKeyCode() {
|
||||
return mKeyCode;
|
||||
}
|
||||
|
||||
public int getAction() {
|
||||
return mAction;
|
||||
}
|
||||
|
||||
public long getDownTime() {
|
||||
return mDownTime;
|
||||
}
|
||||
|
||||
public long getEventTime() {
|
||||
return mEventTime;
|
||||
}
|
||||
|
||||
public void setDownTime(long downTime) {
|
||||
mDownTime = downTime;
|
||||
}
|
||||
|
||||
public void setEventTime(long eventTime) {
|
||||
mEventTime = eventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the key event
|
||||
*/
|
||||
private KeyEvent getEvent() {
|
||||
if (mDeviceId < 0) {
|
||||
return new KeyEvent(mAction, mKeyCode);
|
||||
}
|
||||
|
||||
// for scripts
|
||||
return new KeyEvent(mDownTime, mEventTime, mAction,
|
||||
mKeyCode, mRepeatCount, mMetaState, mDeviceId, mScancode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
|
||||
if (verbose > 1) {
|
||||
String note;
|
||||
if (mAction == KeyEvent.ACTION_UP) {
|
||||
note = "ACTION_UP";
|
||||
} else {
|
||||
note = "ACTION_DOWN";
|
||||
}
|
||||
|
||||
try {
|
||||
System.out.println(":SendKey (" + note + "): "
|
||||
+ mKeyCode + " // "
|
||||
+ MonkeySourceRandom.getKeyName(mKeyCode));
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
System.out.println(":SendKey (ACTION_UP): "
|
||||
+ mKeyCode + " // Unknown key event");
|
||||
}
|
||||
}
|
||||
|
||||
// inject key event
|
||||
try {
|
||||
if (!iwm.injectKeyEvent(getEvent(), false)) {
|
||||
return MonkeyEvent.INJECT_FAIL;
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
return MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION;
|
||||
}
|
||||
|
||||
return MonkeyEvent.INJECT_SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.commands.monkey;
|
||||
|
||||
import android.app.IActivityManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.view.IWindowManager;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
|
||||
/**
|
||||
* monkey motion event
|
||||
*/
|
||||
public class MonkeyMotionEvent extends MonkeyEvent {
|
||||
private long mDownTime = -1;
|
||||
private long mEventTime = -1;
|
||||
private int mAction = -1;
|
||||
private float mX = -1;
|
||||
private float mY = -1;
|
||||
private float mPressure = -1;
|
||||
private float mSize = -1;
|
||||
private int mMetaState = -1;
|
||||
private float mXPrecision = -1;
|
||||
private float mYPrecision = -1;
|
||||
private int mDeviceId = -1;
|
||||
private int mEdgeFlags = -1;
|
||||
|
||||
//If true, this is an intermediate step (more verbose logging, only)
|
||||
private boolean mIntermediateNote;
|
||||
|
||||
public MonkeyMotionEvent(int type, long downAt, int action,
|
||||
float x, float y, int metaState) {
|
||||
super(type);
|
||||
mDownTime = downAt;
|
||||
mAction = action;
|
||||
mX = x;
|
||||
mY = y;
|
||||
mMetaState = metaState;
|
||||
}
|
||||
|
||||
public MonkeyMotionEvent(int type, long downTime, long eventTime, int action,
|
||||
float x, float y, float pressure, float size, int metaState,
|
||||
float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
|
||||
super(type);
|
||||
mDownTime = downTime;
|
||||
mEventTime = eventTime;
|
||||
mAction = action;
|
||||
mX = x;
|
||||
mY = y;
|
||||
mPressure = pressure;
|
||||
mSize = size;
|
||||
mMetaState = metaState;
|
||||
mXPrecision = xPrecision;
|
||||
mYPrecision = yPrecision;
|
||||
mDeviceId = deviceId;
|
||||
mEdgeFlags = edgeFlags;
|
||||
}
|
||||
|
||||
public void setIntermediateNote(boolean b) {
|
||||
mIntermediateNote = b;
|
||||
}
|
||||
|
||||
public boolean getIntermediateNote() {
|
||||
return mIntermediateNote;
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return mX;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return mY;
|
||||
}
|
||||
|
||||
public int getAction() {
|
||||
return mAction;
|
||||
}
|
||||
|
||||
public long getDownTime() {
|
||||
return mDownTime;
|
||||
}
|
||||
|
||||
public long getEventTime() {
|
||||
return mEventTime;
|
||||
}
|
||||
|
||||
public void setDownTime(long downTime) {
|
||||
mDownTime = downTime;
|
||||
}
|
||||
|
||||
public void setEventTime(long eventTime) {
|
||||
mEventTime = eventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return instance of a motion event
|
||||
*/
|
||||
private MotionEvent getEvent() {
|
||||
if (mDeviceId < 0) {
|
||||
return MotionEvent.obtain(mDownTime, SystemClock.uptimeMillis(),
|
||||
mAction, mX, mY, mMetaState);
|
||||
}
|
||||
|
||||
// for scripts
|
||||
return MotionEvent.obtain(mDownTime, mEventTime,
|
||||
mAction, mX, mY, mPressure, mSize, mMetaState,
|
||||
mXPrecision, mYPrecision, mDeviceId, mEdgeFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int injectEvent(IWindowManager iwm, IActivityManager iam, int verbose) {
|
||||
|
||||
String note;
|
||||
if ((verbose > 0 && !mIntermediateNote) || verbose > 1) {
|
||||
if (mAction == MotionEvent.ACTION_DOWN) {
|
||||
note = "DOWN";
|
||||
} else if (mAction == MotionEvent.ACTION_UP) {
|
||||
note = "UP";
|
||||
} else {
|
||||
note = "MOVE";
|
||||
}
|
||||
System.out.println(":Sending Pointer ACTION_" + note +
|
||||
" x=" + mX + " y=" + mY);
|
||||
}
|
||||
try {
|
||||
int type = this.getEventType();
|
||||
MotionEvent me = getEvent();
|
||||
|
||||
if ((type == MonkeyEvent.EVENT_TYPE_POINTER &&
|
||||
!iwm.injectPointerEvent(me, false))
|
||||
|| (type == MonkeyEvent.EVENT_TYPE_TRACKBALL &&
|
||||
!iwm.injectTrackballEvent(me, false))) {
|
||||
return MonkeyEvent.INJECT_FAIL;
|
||||
}
|
||||
} catch (RemoteException ex) {
|
||||
return MonkeyEvent.INJECT_ERROR_REMOTE_EXCEPTION;
|
||||
}
|
||||
return MonkeyEvent.INJECT_SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.commands.monkey;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.os.SystemClock;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.WindowManagerImpl;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* monkey event queue
|
||||
*/
|
||||
public class MonkeySourceRandom implements MonkeyEventSource{
|
||||
/** Key events that move around the UI. */
|
||||
private static final int[] NAV_KEYS = {
|
||||
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
|
||||
KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT,
|
||||
};
|
||||
/**
|
||||
* Key events that perform major navigation options (so shouldn't be sent
|
||||
* as much).
|
||||
*/
|
||||
private static final int[] MAJOR_NAV_KEYS = {
|
||||
KeyEvent.KEYCODE_MENU, /*KeyEvent.KEYCODE_SOFT_RIGHT,*/
|
||||
KeyEvent.KEYCODE_DPAD_CENTER,
|
||||
};
|
||||
/** Key events that perform system operations. */
|
||||
private static final int[] SYS_KEYS = {
|
||||
KeyEvent.KEYCODE_HOME, KeyEvent.KEYCODE_BACK,
|
||||
KeyEvent.KEYCODE_CALL, KeyEvent.KEYCODE_ENDCALL,
|
||||
KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_VOLUME_DOWN,
|
||||
};
|
||||
/** Nice names for all key events. */
|
||||
private static final String[] KEY_NAMES = {
|
||||
"KEYCODE_UNKNOWN",
|
||||
"KEYCODE_MENU",
|
||||
"KEYCODE_SOFT_RIGHT",
|
||||
"KEYCODE_HOME",
|
||||
"KEYCODE_BACK",
|
||||
"KEYCODE_CALL",
|
||||
"KEYCODE_ENDCALL",
|
||||
"KEYCODE_0",
|
||||
"KEYCODE_1",
|
||||
"KEYCODE_2",
|
||||
"KEYCODE_3",
|
||||
"KEYCODE_4",
|
||||
"KEYCODE_5",
|
||||
"KEYCODE_6",
|
||||
"KEYCODE_7",
|
||||
"KEYCODE_8",
|
||||
"KEYCODE_9",
|
||||
"KEYCODE_STAR",
|
||||
"KEYCODE_POUND",
|
||||
"KEYCODE_DPAD_UP",
|
||||
"KEYCODE_DPAD_DOWN",
|
||||
"KEYCODE_DPAD_LEFT",
|
||||
"KEYCODE_DPAD_RIGHT",
|
||||
"KEYCODE_DPAD_CENTER",
|
||||
"KEYCODE_VOLUME_UP",
|
||||
"KEYCODE_VOLUME_DOWN",
|
||||
"KEYCODE_POWER",
|
||||
"KEYCODE_CAMERA",
|
||||
"KEYCODE_CLEAR",
|
||||
"KEYCODE_A",
|
||||
"KEYCODE_B",
|
||||
"KEYCODE_C",
|
||||
"KEYCODE_D",
|
||||
"KEYCODE_E",
|
||||
"KEYCODE_F",
|
||||
"KEYCODE_G",
|
||||
"KEYCODE_H",
|
||||
"KEYCODE_I",
|
||||
"KEYCODE_J",
|
||||
"KEYCODE_K",
|
||||
"KEYCODE_L",
|
||||
"KEYCODE_M",
|
||||
"KEYCODE_N",
|
||||
"KEYCODE_O",
|
||||
"KEYCODE_P",
|
||||
"KEYCODE_Q",
|
||||
"KEYCODE_R",
|
||||
"KEYCODE_S",
|
||||
"KEYCODE_T",
|
||||
"KEYCODE_U",
|
||||
"KEYCODE_V",
|
||||
"KEYCODE_W",
|
||||
"KEYCODE_X",
|
||||
"KEYCODE_Y",
|
||||
"KEYCODE_Z",
|
||||
"KEYCODE_COMMA",
|
||||
"KEYCODE_PERIOD",
|
||||
"KEYCODE_ALT_LEFT",
|
||||
"KEYCODE_ALT_RIGHT",
|
||||
"KEYCODE_SHIFT_LEFT",
|
||||
"KEYCODE_SHIFT_RIGHT",
|
||||
"KEYCODE_TAB",
|
||||
"KEYCODE_SPACE",
|
||||
"KEYCODE_SYM",
|
||||
"KEYCODE_EXPLORER",
|
||||
"KEYCODE_ENVELOPE",
|
||||
"KEYCODE_ENTER",
|
||||
"KEYCODE_DEL",
|
||||
"KEYCODE_GRAVE",
|
||||
"KEYCODE_MINUS",
|
||||
"KEYCODE_EQUALS",
|
||||
"KEYCODE_LEFT_BRACKET",
|
||||
"KEYCODE_RIGHT_BRACKET",
|
||||
"KEYCODE_BACKSLASH",
|
||||
"KEYCODE_SEMICOLON",
|
||||
"KEYCODE_APOSTROPHE",
|
||||
"KEYCODE_SLASH",
|
||||
"KEYCODE_AT",
|
||||
"KEYCODE_NUM",
|
||||
"KEYCODE_HEADSETHOOK",
|
||||
"KEYCODE_FOCUS",
|
||||
"KEYCODE_PLUS",
|
||||
"KEYCODE_MENU",
|
||||
"KEYCODE_NOTIFICATION",
|
||||
"KEYCODE_SEARCH",
|
||||
"KEYCODE_PLAYPAUSE",
|
||||
"KEYCODE_STOP",
|
||||
"KEYCODE_NEXTSONG",
|
||||
"KEYCODE_PREVIOUSSONG",
|
||||
"KEYCODE_REWIND",
|
||||
"KEYCODE_FORWARD",
|
||||
|
||||
"TAG_LAST_KEYCODE" // EOL. used to keep the lists in sync
|
||||
};
|
||||
|
||||
public static final int FACTOR_TOUCH = 0;
|
||||
public static final int FACTOR_MOTION = 1;
|
||||
public static final int FACTOR_TRACKBALL = 2;
|
||||
public static final int FACTOR_NAV = 3;
|
||||
public static final int FACTOR_MAJORNAV = 4;
|
||||
public static final int FACTOR_SYSOPS = 5;
|
||||
public static final int FACTOR_APPSWITCH = 6;
|
||||
public static final int FACTOR_FLIP = 7;
|
||||
public static final int FACTOR_ANYTHING = 8;
|
||||
public static final int FACTORZ_COUNT = 9; // should be last+1
|
||||
|
||||
|
||||
/** percentages for each type of event. These will be remapped to working
|
||||
* values after we read any optional values.
|
||||
**/
|
||||
private float[] mFactors = new float[FACTORZ_COUNT];
|
||||
private ArrayList<ComponentName> mMainApps;
|
||||
private int mEventCount = 0; //total number of events generated so far
|
||||
private LinkedList<MonkeyEvent> mQ = new LinkedList<MonkeyEvent>();
|
||||
private Random mRandom;
|
||||
private int mVerbose = 0;
|
||||
|
||||
private boolean mKeyboardOpen = false;
|
||||
|
||||
/**
|
||||
* @return the last name in the key list
|
||||
*/
|
||||
public static String getLastKeyName() {
|
||||
return KEY_NAMES[KeyEvent.getMaxKeyCode() + 1];
|
||||
}
|
||||
|
||||
public static String getKeyName(int keycode) {
|
||||
return KEY_NAMES[keycode];
|
||||
}
|
||||
|
||||
public MonkeySourceRandom(long seed, ArrayList<ComponentName> MainApps) {
|
||||
// default values for random distributions
|
||||
// note, these are straight percentages, to match user input (cmd line args)
|
||||
// but they will be converted to 0..1 values before the main loop runs.
|
||||
mFactors[FACTOR_TOUCH] = 15.0f;
|
||||
mFactors[FACTOR_MOTION] = 10.0f;
|
||||
mFactors[FACTOR_TRACKBALL] = 15.0f;
|
||||
mFactors[FACTOR_NAV] = 25.0f;
|
||||
mFactors[FACTOR_MAJORNAV] = 15.0f;
|
||||
mFactors[FACTOR_SYSOPS] = 2.0f;
|
||||
mFactors[FACTOR_APPSWITCH] = 2.0f;
|
||||
mFactors[FACTOR_FLIP] = 1.0f;
|
||||
mFactors[FACTOR_ANYTHING] = 15.0f;
|
||||
|
||||
mRandom = new SecureRandom();
|
||||
mRandom.setSeed((seed == 0) ? -1 : seed);
|
||||
mMainApps = MainApps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the percentages (after applying user values) and then normalize to a 0..1 scale.
|
||||
*/
|
||||
private boolean adjustEventFactors() {
|
||||
// go through all values and compute totals for user & default values
|
||||
float userSum = 0.0f;
|
||||
float defaultSum = 0.0f;
|
||||
int defaultCount = 0;
|
||||
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
||||
if (mFactors[i] <= 0.0f) { // user values are zero or negative
|
||||
userSum -= mFactors[i];
|
||||
} else {
|
||||
defaultSum += mFactors[i];
|
||||
++defaultCount;
|
||||
}
|
||||
}
|
||||
|
||||
// if the user request was > 100%, reject it
|
||||
if (userSum > 100.0f) {
|
||||
System.err.println("** Event weights > 100%");
|
||||
return false;
|
||||
}
|
||||
|
||||
// if the user specified all of the weights, then they need to be 100%
|
||||
if (defaultCount == 0 && (userSum < 99.9f || userSum > 100.1f)) {
|
||||
System.err.println("** Event weights != 100%");
|
||||
return false;
|
||||
}
|
||||
|
||||
// compute the adjustment necessary
|
||||
float defaultsTarget = (100.0f - userSum);
|
||||
float defaultsAdjustment = defaultsTarget / defaultSum;
|
||||
|
||||
// fix all values, by adjusting defaults, or flipping user values back to >0
|
||||
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
||||
if (mFactors[i] <= 0.0f) { // user values are zero or negative
|
||||
mFactors[i] = -mFactors[i];
|
||||
} else {
|
||||
mFactors[i] *= defaultsAdjustment;
|
||||
}
|
||||
}
|
||||
|
||||
// if verbose, show factors
|
||||
|
||||
if (mVerbose > 0) {
|
||||
System.out.println("// Event percentages:");
|
||||
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
||||
System.out.println("// " + i + ": " + mFactors[i] + "%");
|
||||
}
|
||||
}
|
||||
|
||||
// finally, normalize and convert to running sum
|
||||
float sum = 0.0f;
|
||||
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
||||
sum += mFactors[i] / 100.0f;
|
||||
mFactors[i] = sum;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the factors
|
||||
*
|
||||
* @param factors: percentages for each type of event
|
||||
*/
|
||||
public void setFactors(float factors[]) {
|
||||
int c = FACTORZ_COUNT;
|
||||
if (factors.length < c) {
|
||||
c = factors.length;
|
||||
}
|
||||
for (int i = 0; i < c; i++)
|
||||
mFactors[i] = factors[i];
|
||||
}
|
||||
|
||||
public void setFactors(int index, float v) {
|
||||
mFactors[index] = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random motion event. This method counts a down, move, and up as multiple events.
|
||||
*
|
||||
* TODO: Test & fix the selectors when non-zero percentages
|
||||
* TODO: Longpress.
|
||||
* TODO: Fling.
|
||||
* TODO: Meta state
|
||||
* TODO: More useful than the random walk here would be to pick a single random direction
|
||||
* and distance, and divvy it up into a random number of segments. (This would serve to
|
||||
* generate fling gestures, which are important).
|
||||
*
|
||||
* @param random Random number source for positioning
|
||||
* @param motionEvent If false, touch/release. If true, touch/move/release.
|
||||
*
|
||||
*/
|
||||
private void generateMotionEvent(Random random, boolean motionEvent){
|
||||
|
||||
Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
|
||||
|
||||
float x = Math.abs(random.nextInt() % display.getWidth());
|
||||
float y = Math.abs(random.nextInt() % display.getHeight());
|
||||
long downAt = SystemClock.uptimeMillis();
|
||||
long eventTime = SystemClock.uptimeMillis();
|
||||
if (downAt == -1) {
|
||||
downAt = eventTime;
|
||||
}
|
||||
|
||||
MonkeyMotionEvent e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
|
||||
downAt, MotionEvent.ACTION_DOWN, x, y, 0);
|
||||
e.setIntermediateNote(false);
|
||||
mQ.addLast(e);
|
||||
|
||||
// sometimes we'll move during the touch
|
||||
if (motionEvent) {
|
||||
int count = random.nextInt(10);
|
||||
for (int i = 0; i < count; i++) {
|
||||
// generate some slop in the up event
|
||||
x = (x + (random.nextInt() % 10)) % display.getWidth();
|
||||
y = (y + (random.nextInt() % 10)) % display.getHeight();
|
||||
|
||||
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
|
||||
downAt, MotionEvent.ACTION_MOVE, x, y, 0);
|
||||
e.setIntermediateNote(true);
|
||||
mQ.addLast(e);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO generate some slop in the up event
|
||||
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
|
||||
downAt, MotionEvent.ACTION_UP, x, y, 0);
|
||||
e.setIntermediateNote(false);
|
||||
mQ.addLast(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random trackball event. This consists of a sequence of small moves, followed by
|
||||
* an optional single click.
|
||||
*
|
||||
* TODO: Longpress.
|
||||
* TODO: Meta state
|
||||
* TODO: Parameterize the % clicked
|
||||
* TODO: More useful than the random walk here would be to pick a single random direction
|
||||
* and distance, and divvy it up into a random number of segments. (This would serve to
|
||||
* generate fling gestures, which are important).
|
||||
*
|
||||
* @param random Random number source for positioning
|
||||
*
|
||||
*/
|
||||
private void generateTrackballEvent(Random random) {
|
||||
Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
|
||||
|
||||
boolean drop = false;
|
||||
int count = random.nextInt(10);
|
||||
MonkeyMotionEvent e;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
// generate a small random step
|
||||
int dX = random.nextInt(10) - 5;
|
||||
int dY = random.nextInt(10) - 5;
|
||||
|
||||
|
||||
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
|
||||
MotionEvent.ACTION_MOVE, dX, dY, 0);
|
||||
e.setIntermediateNote(i > 0);
|
||||
mQ.addLast(e);
|
||||
}
|
||||
|
||||
// 10% of trackball moves end with a click
|
||||
if (0 == random.nextInt(10)) {
|
||||
long downAt = SystemClock.uptimeMillis();
|
||||
|
||||
|
||||
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
|
||||
MotionEvent.ACTION_DOWN, 0, 0, 0);
|
||||
e.setIntermediateNote(true);
|
||||
mQ.addLast(e);
|
||||
|
||||
|
||||
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
|
||||
MotionEvent.ACTION_UP, 0, 0, 0);
|
||||
e.setIntermediateNote(false);
|
||||
mQ.addLast(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* generate a random event based on mFactor
|
||||
*/
|
||||
private void generateEvents() {
|
||||
float cls = mRandom.nextFloat();
|
||||
int lastKey = 0;
|
||||
|
||||
boolean touchEvent = cls < mFactors[FACTOR_TOUCH];
|
||||
boolean motionEvent = !touchEvent && (cls < mFactors[FACTOR_MOTION]);
|
||||
if (touchEvent || motionEvent) {
|
||||
generateMotionEvent(mRandom, motionEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cls < mFactors[FACTOR_TRACKBALL]) {
|
||||
generateTrackballEvent(mRandom);
|
||||
return;
|
||||
}
|
||||
|
||||
// The remaining event categories are injected as key events
|
||||
if (cls < mFactors[FACTOR_NAV]) {
|
||||
lastKey = NAV_KEYS[mRandom.nextInt(NAV_KEYS.length)];
|
||||
} else if (cls < mFactors[FACTOR_MAJORNAV]) {
|
||||
lastKey = MAJOR_NAV_KEYS[mRandom.nextInt(MAJOR_NAV_KEYS.length)];
|
||||
} else if (cls < mFactors[FACTOR_SYSOPS]) {
|
||||
lastKey = SYS_KEYS[mRandom.nextInt(SYS_KEYS.length)];
|
||||
} else if (cls < mFactors[FACTOR_APPSWITCH]) {
|
||||
MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get(
|
||||
mRandom.nextInt(mMainApps.size())));
|
||||
mQ.addLast(e);
|
||||
return;
|
||||
} else if (cls < mFactors[FACTOR_FLIP]) {
|
||||
MonkeyFlipEvent e = new MonkeyFlipEvent(mKeyboardOpen);
|
||||
mKeyboardOpen = !mKeyboardOpen;
|
||||
mQ.addLast(e);
|
||||
return;
|
||||
} else {
|
||||
lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1);
|
||||
}
|
||||
|
||||
MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, lastKey);
|
||||
mQ.addLast(e);
|
||||
|
||||
e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey);
|
||||
mQ.addLast(e);
|
||||
}
|
||||
|
||||
public boolean validate() {
|
||||
//check factors
|
||||
return adjustEventFactors();
|
||||
}
|
||||
|
||||
public void setVerbose(int verbose) {
|
||||
mVerbose = verbose;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate an activity event
|
||||
*/
|
||||
public void generateActivity() {
|
||||
MonkeyActivityEvent e = new MonkeyActivityEvent(mMainApps.get(
|
||||
mRandom.nextInt(mMainApps.size())));
|
||||
mQ.addLast(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* if the queue is empty, we generate events first
|
||||
* @return the first event in the queue
|
||||
*/
|
||||
public MonkeyEvent getNextEvent() {
|
||||
if (mQ.isEmpty()) {
|
||||
generateEvents();
|
||||
}
|
||||
mEventCount++;
|
||||
MonkeyEvent e = mQ.getFirst();
|
||||
mQ.removeFirst();
|
||||
return e;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.commands.monkey;
|
||||
|
||||
import android.os.SystemClock;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.LinkedList;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* monkey event queue. It takes a script to produce events
|
||||
*
|
||||
* sample script format:
|
||||
* type= raw events
|
||||
* count= 10
|
||||
* speed= 1.0
|
||||
* captureDispatchPointer(5109520,5109520,0,230.75429,458.1814,0.20784314,
|
||||
* 0.06666667,0,0.0,0.0,65539,0)
|
||||
* captureDispatchKey(5113146,5113146,0,20,0,0,0,0)
|
||||
* captureDispatchFlip(true)
|
||||
* ...
|
||||
*/
|
||||
public class MonkeySourceScript implements MonkeyEventSource{
|
||||
private int mEventCountInScript = 0; //total number of events in the file
|
||||
private int mVerbose = 0;
|
||||
private double mSpeed = 1.0;
|
||||
private String mScriptFileName;
|
||||
private LinkedList<MonkeyEvent> mQ = new LinkedList<MonkeyEvent>();
|
||||
|
||||
private static final String HEADER_TYPE = "type=";
|
||||
private static final String HEADER_COUNT = "count=";
|
||||
private static final String HEADER_SPEED = "speed=";
|
||||
|
||||
private long mLastRecordedDownTimeKey = 0;
|
||||
private long mLastRecordedDownTimeMotion = 0;
|
||||
private long mLastExportDownTimeKey = 0;
|
||||
private long mLastExportDownTimeMotion = 0;
|
||||
private long mLastExportEventTime = -1;
|
||||
private long mLastRecordedEventTime = -1;
|
||||
|
||||
private static final boolean THIS_DEBUG = false;
|
||||
// a parameter that compensates the difference of real elapsed time and
|
||||
// time in theory
|
||||
private static final long SLEEP_COMPENSATE_DIFF = 16;
|
||||
|
||||
// maximum number of events that we read at one time
|
||||
private static final int MAX_ONE_TIME_READS = 100;
|
||||
|
||||
// number of additional events added to the script
|
||||
// add HOME_KEY down and up events to make start UI consistent in each round
|
||||
private static final int POLICY_ADDITIONAL_EVENT_COUNT = 2;
|
||||
|
||||
// event key word in the capture log
|
||||
private static final String EVENT_KEYWORD_POINTER = "DispatchPointer";
|
||||
private static final String EVENT_KEYWORD_TRACKBALL = "DispatchTrackball";
|
||||
private static final String EVENT_KEYWORD_KEY = "DispatchKey";
|
||||
private static final String EVENT_KEYWORD_FLIP = "DispatchFlip";
|
||||
|
||||
// a line at the end of the header
|
||||
private static final String STARTING_DATA_LINE = "start data >>";
|
||||
private boolean mFileOpened = false;
|
||||
FileInputStream mFStream;
|
||||
DataInputStream mInputStream;
|
||||
BufferedReader mBufferReader;
|
||||
|
||||
public MonkeySourceScript(String filename) {
|
||||
mScriptFileName = filename;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the number of total events that will be generated in a round
|
||||
*/
|
||||
public int getOneRoundEventCount() {
|
||||
//plus one home key down and up event
|
||||
return mEventCountInScript + POLICY_ADDITIONAL_EVENT_COUNT;
|
||||
}
|
||||
|
||||
private void resetValue() {
|
||||
mLastRecordedDownTimeKey = 0;
|
||||
mLastRecordedDownTimeMotion = 0;
|
||||
mLastExportDownTimeKey = 0;
|
||||
mLastExportDownTimeMotion = 0;
|
||||
mLastRecordedEventTime = -1;
|
||||
mLastExportEventTime = -1;
|
||||
}
|
||||
|
||||
private boolean readScriptHeader() {
|
||||
mEventCountInScript = -1;
|
||||
mFileOpened = false;
|
||||
try {
|
||||
if (THIS_DEBUG) {
|
||||
System.out.println("reading script header");
|
||||
}
|
||||
|
||||
mFStream = new FileInputStream(mScriptFileName);
|
||||
mInputStream = new DataInputStream(mFStream);
|
||||
mBufferReader = new BufferedReader(
|
||||
new InputStreamReader(mInputStream));
|
||||
String sLine;
|
||||
while ((sLine = mBufferReader.readLine()) != null) {
|
||||
sLine = sLine.trim();
|
||||
if (sLine.indexOf(HEADER_TYPE) >= 0) {
|
||||
// at this point, we only have one type of script
|
||||
} else if (sLine.indexOf(HEADER_COUNT) >= 0) {
|
||||
try {
|
||||
mEventCountInScript = Integer.parseInt(sLine.substring(
|
||||
HEADER_COUNT.length() + 1).trim());
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
} else if (sLine.indexOf(HEADER_SPEED) >= 0) {
|
||||
try {
|
||||
mSpeed = Double.parseDouble(sLine.substring(
|
||||
HEADER_SPEED.length() + 1).trim());
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
} else if (sLine.indexOf(STARTING_DATA_LINE) >= 0) {
|
||||
// header ends until we read the start data mark
|
||||
mFileOpened = true;
|
||||
if (THIS_DEBUG) {
|
||||
System.out.println("read script header success");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
System.err.println(e);
|
||||
} catch (IOException e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
|
||||
if (THIS_DEBUG) {
|
||||
System.out.println("Error in reading script header");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void processLine(String s) {
|
||||
int index1 = s.indexOf('(');
|
||||
int index2 = s.indexOf(')');
|
||||
|
||||
if (index1 < 0 || index2 < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringTokenizer st = new StringTokenizer(
|
||||
s.substring(index1 + 1, index2), ",");
|
||||
|
||||
if (s.indexOf(EVENT_KEYWORD_KEY) >= 0) {
|
||||
// key events
|
||||
try {
|
||||
long downTime = Long.parseLong(st.nextToken());
|
||||
long eventTime = Long.parseLong(st.nextToken());
|
||||
int action = Integer.parseInt(st.nextToken());
|
||||
int code = Integer.parseInt(st.nextToken());
|
||||
int repeat = Integer.parseInt(st.nextToken());
|
||||
int metaState = Integer.parseInt(st.nextToken());
|
||||
int device = Integer.parseInt(st.nextToken());
|
||||
int scancode = Integer.parseInt(st.nextToken());
|
||||
|
||||
MonkeyKeyEvent e = new MonkeyKeyEvent(downTime, eventTime,
|
||||
action, code, repeat, metaState, device, scancode);
|
||||
mQ.addLast(e);
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
// something wrong with this line in the script
|
||||
}
|
||||
} else if (s.indexOf(EVENT_KEYWORD_POINTER) >= 0 ||
|
||||
s.indexOf(EVENT_KEYWORD_TRACKBALL) >= 0) {
|
||||
// trackball/pointer event
|
||||
try {
|
||||
long downTime = Long.parseLong(st.nextToken());
|
||||
long eventTime = Long.parseLong(st.nextToken());
|
||||
int action = Integer.parseInt(st.nextToken());
|
||||
float x = Float.parseFloat(st.nextToken());
|
||||
float y = Float.parseFloat(st.nextToken());
|
||||
float pressure = Float.parseFloat(st.nextToken());
|
||||
float size = Float.parseFloat(st.nextToken());
|
||||
int metaState = Integer.parseInt(st.nextToken());
|
||||
float xPrecision = Float.parseFloat(st.nextToken());
|
||||
float yPrecision = Float.parseFloat(st.nextToken());
|
||||
int device = Integer.parseInt(st.nextToken());
|
||||
int edgeFlags = Integer.parseInt(st.nextToken());
|
||||
|
||||
int type = MonkeyEvent.EVENT_TYPE_TRACKBALL;
|
||||
if (s.indexOf("Pointer") > 0) {
|
||||
type = MonkeyEvent.EVENT_TYPE_POINTER;
|
||||
}
|
||||
MonkeyMotionEvent e = new MonkeyMotionEvent(type, downTime, eventTime,
|
||||
action, x, y, pressure, size, metaState, xPrecision, yPrecision,
|
||||
device, edgeFlags);
|
||||
mQ.addLast(e);
|
||||
} catch (NumberFormatException e) {
|
||||
// we ignore this event
|
||||
}
|
||||
} else if (s.indexOf(EVENT_KEYWORD_FLIP) >= 0) {
|
||||
boolean keyboardOpen = Boolean.parseBoolean(st.nextToken());
|
||||
MonkeyFlipEvent e = new MonkeyFlipEvent(keyboardOpen);
|
||||
mQ.addLast(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void closeFile() {
|
||||
mFileOpened = false;
|
||||
if (THIS_DEBUG) {
|
||||
System.out.println("closing script file");
|
||||
}
|
||||
|
||||
try {
|
||||
mFStream.close();
|
||||
mInputStream.close();
|
||||
} catch (IOException e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* add home key press/release event to the queue
|
||||
*/
|
||||
private void addHomeKeyEvent() {
|
||||
MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_HOME);
|
||||
mQ.addLast(e);
|
||||
e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HOME);
|
||||
mQ.addLast(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* read next batch of events from the provided script file
|
||||
* @return true if success
|
||||
*/
|
||||
private boolean readNextBatch() {
|
||||
String sLine = null;
|
||||
int readCount = 0;
|
||||
|
||||
if (THIS_DEBUG) {
|
||||
System.out.println("readNextBatch(): reading next batch of events");
|
||||
}
|
||||
|
||||
if (!mFileOpened) {
|
||||
if (!readScriptHeader()) {
|
||||
closeFile();
|
||||
return false;
|
||||
}
|
||||
resetValue();
|
||||
|
||||
/*
|
||||
* In order to allow the Monkey to replay captured events multiple times
|
||||
* we need to define a default start UI, which is the home screen
|
||||
* Otherwise, it won't be accurate since the captured events
|
||||
* could end anywhere
|
||||
*/
|
||||
addHomeKeyEvent();
|
||||
}
|
||||
|
||||
try {
|
||||
while (readCount++ < MAX_ONE_TIME_READS &&
|
||||
(sLine = mBufferReader.readLine()) != null) {
|
||||
sLine = sLine.trim();
|
||||
processLine(sLine);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sLine == null) {
|
||||
// to the end of the file
|
||||
if (THIS_DEBUG) {
|
||||
System.out.println("readNextBatch(): to the end of file");
|
||||
}
|
||||
closeFile();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* sleep for a period of given time, introducing latency among events
|
||||
* @param time to sleep in millisecond
|
||||
*/
|
||||
private void needSleep(long time) {
|
||||
if (time < 1) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Thread.sleep(time);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check whether we can successfully read the header of the script file
|
||||
*/
|
||||
public boolean validate() {
|
||||
boolean b = readNextBatch();
|
||||
if (mVerbose > 0) {
|
||||
System.out.println("Replaying " + mEventCountInScript +
|
||||
" events with speed " + mSpeed);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
public void setVerbose(int verbose) {
|
||||
mVerbose = verbose;
|
||||
}
|
||||
|
||||
/**
|
||||
* adjust key downtime and eventtime according to both
|
||||
* recorded values and current system time
|
||||
* @param e KeyEvent
|
||||
*/
|
||||
private void adjustKeyEventTime(MonkeyKeyEvent e) {
|
||||
if (e.getEventTime() < 0) {
|
||||
return;
|
||||
}
|
||||
long thisDownTime = 0;
|
||||
long thisEventTime = 0;
|
||||
long expectedDelay = 0;
|
||||
|
||||
if (mLastRecordedEventTime <= 0) {
|
||||
// first time event
|
||||
thisDownTime = SystemClock.uptimeMillis();
|
||||
thisEventTime = thisDownTime;
|
||||
} else {
|
||||
if (e.getDownTime() != mLastRecordedDownTimeKey) {
|
||||
thisDownTime = e.getDownTime();
|
||||
} else {
|
||||
thisDownTime = mLastExportDownTimeKey;
|
||||
}
|
||||
expectedDelay = (long) ((e.getEventTime() -
|
||||
mLastRecordedEventTime) * mSpeed);
|
||||
thisEventTime = mLastExportEventTime + expectedDelay;
|
||||
// add sleep to simulate everything in recording
|
||||
needSleep(expectedDelay - SLEEP_COMPENSATE_DIFF);
|
||||
}
|
||||
mLastRecordedDownTimeKey = e.getDownTime();
|
||||
mLastRecordedEventTime = e.getEventTime();
|
||||
e.setDownTime(thisDownTime);
|
||||
e.setEventTime(thisEventTime);
|
||||
mLastExportDownTimeKey = thisDownTime;
|
||||
mLastExportEventTime = thisEventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* adjust motion downtime and eventtime according to both
|
||||
* recorded values and current system time
|
||||
* @param e KeyEvent
|
||||
*/
|
||||
private void adjustMotionEventTime(MonkeyMotionEvent e) {
|
||||
if (e.getEventTime() < 0) {
|
||||
return;
|
||||
}
|
||||
long thisDownTime = 0;
|
||||
long thisEventTime = 0;
|
||||
long expectedDelay = 0;
|
||||
|
||||
if (mLastRecordedEventTime <= 0) {
|
||||
// first time event
|
||||
thisDownTime = SystemClock.uptimeMillis();
|
||||
thisEventTime = thisDownTime;
|
||||
} else {
|
||||
if (e.getDownTime() != mLastRecordedDownTimeMotion) {
|
||||
thisDownTime = e.getDownTime();
|
||||
} else {
|
||||
thisDownTime = mLastExportDownTimeMotion;
|
||||
}
|
||||
expectedDelay = (long) ((e.getEventTime() -
|
||||
mLastRecordedEventTime) * mSpeed);
|
||||
thisEventTime = mLastExportEventTime + expectedDelay;
|
||||
// add sleep to simulate everything in recording
|
||||
needSleep(expectedDelay - SLEEP_COMPENSATE_DIFF);
|
||||
}
|
||||
|
||||
mLastRecordedDownTimeMotion = e.getDownTime();
|
||||
mLastRecordedEventTime = e.getEventTime();
|
||||
e.setDownTime(thisDownTime);
|
||||
e.setEventTime(thisEventTime);
|
||||
mLastExportDownTimeMotion = thisDownTime;
|
||||
mLastExportEventTime = thisEventTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* if the queue is empty, we generate events first
|
||||
* @return the first event in the queue, if null, indicating the system crashes
|
||||
*/
|
||||
public MonkeyEvent getNextEvent() {
|
||||
long recordedEventTime = -1;
|
||||
|
||||
if (mQ.isEmpty()) {
|
||||
readNextBatch();
|
||||
}
|
||||
MonkeyEvent e = mQ.getFirst();
|
||||
mQ.removeFirst();
|
||||
|
||||
if (e.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) {
|
||||
adjustKeyEventTime((MonkeyKeyEvent) e);
|
||||
} else if (e.getEventType() == MonkeyEvent.EVENT_TYPE_POINTER ||
|
||||
e.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) {
|
||||
adjustMotionEventTime((MonkeyMotionEvent) e);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user