Added in simple command scripting to monkey over a TCP socket.
This allows a host program to talk to the monkey over TCP (via adb) and script up specific commands to run.
This commit is contained in:
@@ -7,6 +7,7 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files)
|
|||||||
LOCAL_MODULE := monkey
|
LOCAL_MODULE := monkey
|
||||||
include $(BUILD_JAVA_LIBRARY)
|
include $(BUILD_JAVA_LIBRARY)
|
||||||
|
|
||||||
|
################################################################
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
ALL_PREBUILT += $(TARGET_OUT)/bin/monkey
|
ALL_PREBUILT += $(TARGET_OUT)/bin/monkey
|
||||||
$(TARGET_OUT)/bin/monkey : $(LOCAL_PATH)/monkey | $(ACP)
|
$(TARGET_OUT)/bin/monkey : $(LOCAL_PATH)/monkey | $(ACP)
|
||||||
|
|||||||
86
cmds/monkey/README.NETWORK.txt
Normal file
86
cmds/monkey/README.NETWORK.txt
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
MONKEY NETWORK SCRIPT
|
||||||
|
|
||||||
|
The Monkey Network Script was designed to be a low-level way to
|
||||||
|
programmability inject KeyEvents and MotionEvents into the input
|
||||||
|
system. The idea is that a process will run on a host computer that
|
||||||
|
will support higher-level operations (like conditionals, etc.) and
|
||||||
|
will talk (via TCP over ADB) to the device in Monkey Network Script.
|
||||||
|
For security reasons, the Monkey only binds to localhost, so you will
|
||||||
|
need to use adb to setup port forwarding to actually talk to the
|
||||||
|
device.
|
||||||
|
|
||||||
|
INITIAL SETUP
|
||||||
|
|
||||||
|
Setup port forwarding from a local port on your machine to a port on
|
||||||
|
the device:
|
||||||
|
|
||||||
|
$ adb forward tcp:1080 tcp:1080
|
||||||
|
|
||||||
|
Start the monkey server
|
||||||
|
|
||||||
|
$ adb shell monkey --port 1080
|
||||||
|
|
||||||
|
Now you're ready to run commands
|
||||||
|
|
||||||
|
COMMAND LIST
|
||||||
|
|
||||||
|
Individual commands are separated by newlines. The Monkey will
|
||||||
|
respond to every command with a line starting with OK for commands
|
||||||
|
that executed without a problem, or a line starting with ERROR for
|
||||||
|
commands that had problems being run. The Monkey may decide to return
|
||||||
|
more information about command execution. That information would come
|
||||||
|
on the same line after the OK or ERROR. A possible example:
|
||||||
|
|
||||||
|
key down menu
|
||||||
|
OK
|
||||||
|
touch monkey
|
||||||
|
ERROR: monkey not a number
|
||||||
|
|
||||||
|
The complete list of commands follows:
|
||||||
|
|
||||||
|
key [down|up] keycode
|
||||||
|
|
||||||
|
This command injects KeyEvent's into the input system. The keycode
|
||||||
|
parameter refers to the KEYCODE list in the KeyEvent class
|
||||||
|
(http://developer.android.com/reference/android/view/KeyEvent.html).
|
||||||
|
The format of that parameter is quite flexible. Using the menu key as
|
||||||
|
an example, it can be 82 (the integer value of the keycode),
|
||||||
|
KEYCODE_MENU (the name of the keycode), or just menu (and the Monkey
|
||||||
|
will add the KEYCODE part). Do note that this last part doesn't work
|
||||||
|
for things like KEYCODE_1 for obvious reasons.
|
||||||
|
|
||||||
|
Note that sending a full button press requires sending both the down
|
||||||
|
and the up event for that key
|
||||||
|
|
||||||
|
touch [down|up|move] x y
|
||||||
|
|
||||||
|
This command injects a MotionEvent into the input system that
|
||||||
|
simulates a user touching the touchscreen (or a pointer event). x and
|
||||||
|
y specify coordinates on the display (0 0 being the upper left) for
|
||||||
|
the touch event to happen. Just like key events, touch events at a
|
||||||
|
single location require both a down and an up. To simulate dragging,
|
||||||
|
send a "touch down", then a series of "touch move" events (to simulate
|
||||||
|
the drag), followed by a "touch up" at the final location.
|
||||||
|
|
||||||
|
trackball dx dy
|
||||||
|
|
||||||
|
This command injects a MotionEvent into the input system that
|
||||||
|
simulates a user using the trackball. dx and dy indicates the amount
|
||||||
|
of change in the trackball location (as opposed to exact coordinates
|
||||||
|
that the touch events use)
|
||||||
|
|
||||||
|
flip [open|close]
|
||||||
|
|
||||||
|
This simulates the opening or closing the keyboard (like on dream).
|
||||||
|
|
||||||
|
OTHER NOTES
|
||||||
|
|
||||||
|
There are some convenience features added to allow running without
|
||||||
|
needing a host process.
|
||||||
|
|
||||||
|
Lines starting with a # character are considered comments. The Monkey
|
||||||
|
eats them and returns no indication that it did anything (no ERROR and
|
||||||
|
no OK).
|
||||||
|
|
||||||
|
You can put the Monkey to sleep by using the "sleep" command with a
|
||||||
|
single argument, how many ms to sleep.
|
||||||
57
cmds/monkey/example_script.txt
Normal file
57
cmds/monkey/example_script.txt
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Touch the android
|
||||||
|
touch down 160 200
|
||||||
|
touch up 160 200
|
||||||
|
sleep 1000
|
||||||
|
|
||||||
|
# Hit Next
|
||||||
|
touch down 300 450
|
||||||
|
touch up 300 450
|
||||||
|
sleep 1000
|
||||||
|
|
||||||
|
# Hit Next
|
||||||
|
touch down 300 450
|
||||||
|
touch up 300 450
|
||||||
|
sleep 1000
|
||||||
|
|
||||||
|
# Hit Next
|
||||||
|
touch down 300 450
|
||||||
|
touch up 300 450
|
||||||
|
sleep 1000
|
||||||
|
|
||||||
|
# Go down and select the account username
|
||||||
|
key down dpad_down
|
||||||
|
key up dpad_down
|
||||||
|
key down dpad_down
|
||||||
|
key up dpad_down
|
||||||
|
key down dpad_center
|
||||||
|
key up dpad_center
|
||||||
|
# account name: bill
|
||||||
|
key down b
|
||||||
|
key up b
|
||||||
|
key down i
|
||||||
|
key up i
|
||||||
|
key down l
|
||||||
|
key up l
|
||||||
|
key down l
|
||||||
|
key up l
|
||||||
|
|
||||||
|
# Go down to the password field
|
||||||
|
key down dpad_down
|
||||||
|
key up dpad_down
|
||||||
|
|
||||||
|
# password: bill
|
||||||
|
key down b
|
||||||
|
key up b
|
||||||
|
key down i
|
||||||
|
key up i
|
||||||
|
key down l
|
||||||
|
key up l
|
||||||
|
key down l
|
||||||
|
key up l
|
||||||
|
|
||||||
|
# Select next
|
||||||
|
touch down 300 450
|
||||||
|
touch up 300 450
|
||||||
|
|
||||||
|
# quit
|
||||||
|
quit
|
||||||
@@ -47,10 +47,10 @@ import java.util.List;
|
|||||||
* Application that injects random key events and other actions into the system.
|
* Application that injects random key events and other actions into the system.
|
||||||
*/
|
*/
|
||||||
public class Monkey {
|
public class Monkey {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monkey Debugging/Dev Support
|
* Monkey Debugging/Dev Support
|
||||||
*
|
*
|
||||||
* All values should be zero when checking in.
|
* All values should be zero when checking in.
|
||||||
*/
|
*/
|
||||||
private final static int DEBUG_ALLOW_ANY_STARTS = 0;
|
private final static int DEBUG_ALLOW_ANY_STARTS = 0;
|
||||||
@@ -74,20 +74,20 @@ public class Monkey {
|
|||||||
|
|
||||||
/** Ignore any not responding timeouts while running? */
|
/** Ignore any not responding timeouts while running? */
|
||||||
private boolean mIgnoreTimeouts;
|
private boolean mIgnoreTimeouts;
|
||||||
|
|
||||||
/** Ignore security exceptions when launching activities */
|
/** Ignore security exceptions when launching activities */
|
||||||
/** (The activity launch still fails, but we keep pluggin' away) */
|
/** (The activity launch still fails, but we keep pluggin' away) */
|
||||||
private boolean mIgnoreSecurityExceptions;
|
private boolean mIgnoreSecurityExceptions;
|
||||||
|
|
||||||
/** Monitor /data/tombstones and stop the monkey if new files appear. */
|
/** Monitor /data/tombstones and stop the monkey if new files appear. */
|
||||||
private boolean mMonitorNativeCrashes;
|
private boolean mMonitorNativeCrashes;
|
||||||
|
|
||||||
/** Send no events. Use with long throttle-time to watch user operations */
|
/** Send no events. Use with long throttle-time to watch user operations */
|
||||||
private boolean mSendNoEvents;
|
private boolean mSendNoEvents;
|
||||||
|
|
||||||
/** This is set when we would like to abort the running of the monkey. */
|
/** This is set when we would like to abort the running of the monkey. */
|
||||||
private boolean mAbort;
|
private boolean mAbort;
|
||||||
|
|
||||||
/** This is set by the ActivityController thread to request collection of ANR trace files */
|
/** This is set by the ActivityController thread to request collection of ANR trace files */
|
||||||
private boolean mRequestAnrTraces = false;
|
private boolean mRequestAnrTraces = false;
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ public class Monkey {
|
|||||||
|
|
||||||
/** Kill the process after a timeout or crash. */
|
/** Kill the process after a timeout or crash. */
|
||||||
private boolean mKillProcessAfterError;
|
private boolean mKillProcessAfterError;
|
||||||
|
|
||||||
/** Generate hprof reports before/after monkey runs */
|
/** Generate hprof reports before/after monkey runs */
|
||||||
private boolean mGenerateHprof;
|
private boolean mGenerateHprof;
|
||||||
|
|
||||||
@@ -106,16 +106,16 @@ public class Monkey {
|
|||||||
ArrayList<String> mMainCategories = new ArrayList<String>();
|
ArrayList<String> mMainCategories = new ArrayList<String>();
|
||||||
/** Applications we can switch to. */
|
/** Applications we can switch to. */
|
||||||
private ArrayList<ComponentName> mMainApps = new ArrayList<ComponentName>();
|
private ArrayList<ComponentName> mMainApps = new ArrayList<ComponentName>();
|
||||||
|
|
||||||
/** The delay between event inputs **/
|
/** The delay between event inputs **/
|
||||||
long mThrottle = 0;
|
long mThrottle = 0;
|
||||||
|
|
||||||
/** The number of iterations **/
|
/** The number of iterations **/
|
||||||
int mCount = 1000;
|
int mCount = 1000;
|
||||||
|
|
||||||
/** The random number seed **/
|
/** The random number seed **/
|
||||||
long mSeed = 0;
|
long mSeed = 0;
|
||||||
|
|
||||||
/** Dropped-event statistics **/
|
/** Dropped-event statistics **/
|
||||||
long mDroppedKeyEvents = 0;
|
long mDroppedKeyEvents = 0;
|
||||||
long mDroppedPointerEvents = 0;
|
long mDroppedPointerEvents = 0;
|
||||||
@@ -124,14 +124,17 @@ public class Monkey {
|
|||||||
|
|
||||||
/** a filename to the script (if any) **/
|
/** a filename to the script (if any) **/
|
||||||
private String mScriptFileName = null;
|
private String mScriptFileName = null;
|
||||||
|
|
||||||
|
/** a TCP port to listen on for remote commands. */
|
||||||
|
private int mServerPort = -1;
|
||||||
|
|
||||||
private static final File TOMBSTONES_PATH = new File("/data/tombstones");
|
private static final File TOMBSTONES_PATH = new File("/data/tombstones");
|
||||||
private HashSet<String> mTombstones = null;
|
private HashSet<String> mTombstones = null;
|
||||||
|
|
||||||
float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT];
|
float[] mFactors = new float[MonkeySourceRandom.FACTORZ_COUNT];
|
||||||
MonkeyEventSource mEventSource;
|
MonkeyEventSource mEventSource;
|
||||||
private MonkeyNetworkMonitor mNetworkMonitor = new MonkeyNetworkMonitor();
|
private MonkeyNetworkMonitor mNetworkMonitor = new MonkeyNetworkMonitor();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monitor operations happening in the system.
|
* Monitor operations happening in the system.
|
||||||
*/
|
*/
|
||||||
@@ -144,7 +147,7 @@ public class Monkey {
|
|||||||
}
|
}
|
||||||
return allow;
|
return allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean activityResuming(String pkg) {
|
public boolean activityResuming(String pkg) {
|
||||||
System.out.println(" // activityResuming(" + pkg + ")");
|
System.out.println(" // activityResuming(" + pkg + ")");
|
||||||
boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_RESTARTS != 0);
|
boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_RESTARTS != 0);
|
||||||
@@ -156,7 +159,7 @@ public class Monkey {
|
|||||||
}
|
}
|
||||||
return allow;
|
return allow;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkEnteringPackage(String pkg) {
|
private boolean checkEnteringPackage(String pkg) {
|
||||||
if (pkg == null) {
|
if (pkg == null) {
|
||||||
return true;
|
return true;
|
||||||
@@ -168,7 +171,7 @@ public class Monkey {
|
|||||||
return mValidPackages.contains(pkg);
|
return mValidPackages.contains(pkg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean appCrashed(String processName, int pid, String shortMsg,
|
public boolean appCrashed(String processName, int pid, String shortMsg,
|
||||||
String longMsg, byte[] crashData) {
|
String longMsg, byte[] crashData) {
|
||||||
System.err.println("// CRASH: " + processName + " (pid " + pid
|
System.err.println("// CRASH: " + processName + " (pid " + pid
|
||||||
@@ -223,14 +226,14 @@ public class Monkey {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run the procrank tool to insert system status information into the debug report.
|
* Run the procrank tool to insert system status information into the debug report.
|
||||||
*/
|
*/
|
||||||
private void reportProcRank() {
|
private void reportProcRank() {
|
||||||
commandLineReport("procrank", "procrank");
|
commandLineReport("procrank", "procrank");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run "cat /data/anr/traces.txt". Wait about 5 seconds first, to let the asynchronous
|
* Run "cat /data/anr/traces.txt". Wait about 5 seconds first, to let the asynchronous
|
||||||
* report writing complete.
|
* report writing complete.
|
||||||
@@ -238,21 +241,21 @@ public class Monkey {
|
|||||||
private void reportAnrTraces() {
|
private void reportAnrTraces() {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(5 * 1000);
|
Thread.sleep(5 * 1000);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
}
|
}
|
||||||
commandLineReport("anr traces", "cat /data/anr/traces.txt");
|
commandLineReport("anr traces", "cat /data/anr/traces.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run "dumpsys meminfo"
|
* Run "dumpsys meminfo"
|
||||||
*
|
*
|
||||||
* NOTE: You cannot perform a dumpsys call from the ActivityController callback, as it will
|
* NOTE: You cannot perform a dumpsys call from the ActivityController callback, as it will
|
||||||
* deadlock. This should only be called from the main loop of the monkey.
|
* deadlock. This should only be called from the main loop of the monkey.
|
||||||
*/
|
*/
|
||||||
private void reportDumpsysMemInfo() {
|
private void reportDumpsysMemInfo() {
|
||||||
commandLineReport("meminfo", "dumpsys meminfo");
|
commandLineReport("meminfo", "dumpsys meminfo");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print report from a single command line.
|
* Print report from a single command line.
|
||||||
* @param reportName Simple tag that will print before the report and in various annotations.
|
* @param reportName Simple tag that will print before the report and in various annotations.
|
||||||
@@ -266,7 +269,7 @@ public class Monkey {
|
|||||||
try {
|
try {
|
||||||
// Process must be fully qualified here because android.os.Process is used elsewhere
|
// Process must be fully qualified here because android.os.Process is used elsewhere
|
||||||
java.lang.Process p = Runtime.getRuntime().exec(command);
|
java.lang.Process p = Runtime.getRuntime().exec(command);
|
||||||
|
|
||||||
// pipe everything from process stdout -> System.err
|
// pipe everything from process stdout -> System.err
|
||||||
InputStream inStream = p.getInputStream();
|
InputStream inStream = p.getInputStream();
|
||||||
InputStreamReader inReader = new InputStreamReader(inStream);
|
InputStreamReader inReader = new InputStreamReader(inStream);
|
||||||
@@ -275,7 +278,7 @@ public class Monkey {
|
|||||||
while ((s = inBuffer.readLine()) != null) {
|
while ((s = inBuffer.readLine()) != null) {
|
||||||
System.err.println(s);
|
System.err.println(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
int status = p.waitFor();
|
int status = p.waitFor();
|
||||||
System.err.println("// " + reportName + " status was " + status);
|
System.err.println("// " + reportName + " status was " + status);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -307,26 +310,26 @@ public class Monkey {
|
|||||||
Debug.waitForDebugger();
|
Debug.waitForDebugger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default values for some command-line options
|
// Default values for some command-line options
|
||||||
mVerbose = 0;
|
mVerbose = 0;
|
||||||
mCount = 1000;
|
mCount = 1000;
|
||||||
mSeed = 0;
|
mSeed = 0;
|
||||||
mThrottle = 0;
|
mThrottle = 0;
|
||||||
|
|
||||||
// prepare for command-line processing
|
// prepare for command-line processing
|
||||||
mArgs = args;
|
mArgs = args;
|
||||||
mNextArg = 0;
|
mNextArg = 0;
|
||||||
|
|
||||||
//set a positive value, indicating none of the factors is provided yet
|
//set a positive value, indicating none of the factors is provided yet
|
||||||
for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
|
for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
|
||||||
mFactors[i] = 1.0f;
|
mFactors[i] = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!processOptions()) {
|
if (!processOptions()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// now set up additional data in preparation for launch
|
// now set up additional data in preparation for launch
|
||||||
if (mMainCategories.size() == 0) {
|
if (mMainCategories.size() == 0) {
|
||||||
mMainCategories.add(Intent.CATEGORY_LAUNCHER);
|
mMainCategories.add(Intent.CATEGORY_LAUNCHER);
|
||||||
@@ -348,11 +351,11 @@ public class Monkey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkInternalConfiguration()) {
|
if (!checkInternalConfiguration()) {
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!getSystemInterfaces()) {
|
if (!getSystemInterfaces()) {
|
||||||
return -3;
|
return -3;
|
||||||
}
|
}
|
||||||
@@ -360,11 +363,14 @@ public class Monkey {
|
|||||||
if (!getMainApps()) {
|
if (!getMainApps()) {
|
||||||
return -4;
|
return -4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mScriptFileName != null) {
|
if (mScriptFileName != null) {
|
||||||
// script mode, ignore other options
|
// script mode, ignore other options
|
||||||
mEventSource = new MonkeySourceScript(mScriptFileName, mThrottle);
|
mEventSource = new MonkeySourceScript(mScriptFileName, mThrottle);
|
||||||
mEventSource.setVerbose(mVerbose);
|
mEventSource.setVerbose(mVerbose);
|
||||||
|
} else if (mServerPort != -1) {
|
||||||
|
mEventSource = new MonkeySourceNetwork(mServerPort);
|
||||||
|
mCount = Integer.MAX_VALUE;
|
||||||
} else {
|
} else {
|
||||||
// random source by default
|
// random source by default
|
||||||
if (mVerbose >= 2) { // check seeding performance
|
if (mVerbose >= 2) { // check seeding performance
|
||||||
@@ -378,7 +384,7 @@ public class Monkey {
|
|||||||
((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
|
((MonkeySourceRandom) mEventSource).setFactors(i, mFactors[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//in random mode, we start with a random activity
|
//in random mode, we start with a random activity
|
||||||
((MonkeySourceRandom) mEventSource).generateActivity();
|
((MonkeySourceRandom) mEventSource).generateActivity();
|
||||||
}
|
}
|
||||||
@@ -387,7 +393,7 @@ public class Monkey {
|
|||||||
if (!mEventSource.validate()) {
|
if (!mEventSource.validate()) {
|
||||||
return -5;
|
return -5;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mScriptFileName != null) {
|
if (mScriptFileName != null) {
|
||||||
// in random mode, count is the number of single events
|
// in random mode, count is the number of single events
|
||||||
// while in script mode, count is the number of repetition
|
// while in script mode, count is the number of repetition
|
||||||
@@ -396,12 +402,12 @@ public class Monkey {
|
|||||||
mCount = mCount * ((MonkeySourceScript) mEventSource)
|
mCount = mCount * ((MonkeySourceScript) mEventSource)
|
||||||
.getOneRoundEventCount();
|
.getOneRoundEventCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're profiling, do it immediately before/after the main monkey loop
|
// If we're profiling, do it immediately before/after the main monkey loop
|
||||||
if (mGenerateHprof) {
|
if (mGenerateHprof) {
|
||||||
signalPersistentProcesses();
|
signalPersistentProcesses();
|
||||||
}
|
}
|
||||||
|
|
||||||
mNetworkMonitor.start();
|
mNetworkMonitor.start();
|
||||||
int crashedAtCycle = runMonkeyCycles();
|
int crashedAtCycle = runMonkeyCycles();
|
||||||
mNetworkMonitor.stop();
|
mNetworkMonitor.stop();
|
||||||
@@ -423,7 +429,7 @@ public class Monkey {
|
|||||||
System.out.println("// Generated profiling reports in /data/misc");
|
System.out.println("// Generated profiling reports in /data/misc");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mAm.setActivityController(null);
|
mAm.setActivityController(null);
|
||||||
mNetworkMonitor.unregister(mAm);
|
mNetworkMonitor.unregister(mAm);
|
||||||
@@ -434,7 +440,7 @@ public class Monkey {
|
|||||||
crashedAtCycle = mCount - 1;
|
crashedAtCycle = mCount - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// report dropped event stats
|
// report dropped event stats
|
||||||
if (mVerbose > 0) {
|
if (mVerbose > 0) {
|
||||||
System.out.print(":Dropped: keys=");
|
System.out.print(":Dropped: keys=");
|
||||||
@@ -446,7 +452,7 @@ public class Monkey {
|
|||||||
System.out.print(" flips=");
|
System.out.print(" flips=");
|
||||||
System.out.println(mDroppedFlipEvents);
|
System.out.println(mDroppedFlipEvents);
|
||||||
}
|
}
|
||||||
|
|
||||||
// report network stats
|
// report network stats
|
||||||
mNetworkMonitor.dump();
|
mNetworkMonitor.dump();
|
||||||
|
|
||||||
@@ -461,10 +467,10 @@ public class Monkey {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the command-line options
|
* Process the command-line options
|
||||||
*
|
*
|
||||||
* @return Returns true if options were parsed with no apparent errors.
|
* @return Returns true if options were parsed with no apparent errors.
|
||||||
*/
|
*/
|
||||||
private boolean processOptions() {
|
private boolean processOptions() {
|
||||||
@@ -498,28 +504,28 @@ public class Monkey {
|
|||||||
} else if (opt.equals("--hprof")) {
|
} else if (opt.equals("--hprof")) {
|
||||||
mGenerateHprof = true;
|
mGenerateHprof = true;
|
||||||
} else if (opt.equals("--pct-touch")) {
|
} else if (opt.equals("--pct-touch")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_TOUCH] =
|
mFactors[MonkeySourceRandom.FACTOR_TOUCH] =
|
||||||
-nextOptionLong("touch events percentage");
|
-nextOptionLong("touch events percentage");
|
||||||
} else if (opt.equals("--pct-motion")) {
|
} else if (opt.equals("--pct-motion")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_MOTION] =
|
mFactors[MonkeySourceRandom.FACTOR_MOTION] =
|
||||||
-nextOptionLong("motion events percentage");
|
-nextOptionLong("motion events percentage");
|
||||||
} else if (opt.equals("--pct-trackball")) {
|
} else if (opt.equals("--pct-trackball")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_TRACKBALL] =
|
mFactors[MonkeySourceRandom.FACTOR_TRACKBALL] =
|
||||||
-nextOptionLong("trackball events percentage");
|
-nextOptionLong("trackball events percentage");
|
||||||
} else if (opt.equals("--pct-nav")) {
|
} else if (opt.equals("--pct-nav")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_NAV] =
|
mFactors[MonkeySourceRandom.FACTOR_NAV] =
|
||||||
-nextOptionLong("nav events percentage");
|
-nextOptionLong("nav events percentage");
|
||||||
} else if (opt.equals("--pct-majornav")) {
|
} else if (opt.equals("--pct-majornav")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_MAJORNAV] =
|
mFactors[MonkeySourceRandom.FACTOR_MAJORNAV] =
|
||||||
-nextOptionLong("major nav events percentage");
|
-nextOptionLong("major nav events percentage");
|
||||||
} else if (opt.equals("--pct-appswitch")) {
|
} else if (opt.equals("--pct-appswitch")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_APPSWITCH] =
|
mFactors[MonkeySourceRandom.FACTOR_APPSWITCH] =
|
||||||
-nextOptionLong("app switch events percentage");
|
-nextOptionLong("app switch events percentage");
|
||||||
} else if (opt.equals("--pct-flip")) {
|
} else if (opt.equals("--pct-flip")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_FLIP] =
|
mFactors[MonkeySourceRandom.FACTOR_FLIP] =
|
||||||
-nextOptionLong("keyboard flip percentage");
|
-nextOptionLong("keyboard flip percentage");
|
||||||
} else if (opt.equals("--pct-anyevent")) {
|
} else if (opt.equals("--pct-anyevent")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_ANYTHING] =
|
mFactors[MonkeySourceRandom.FACTOR_ANYTHING] =
|
||||||
-nextOptionLong("any events percentage");
|
-nextOptionLong("any events percentage");
|
||||||
} else if (opt.equals("--throttle")) {
|
} else if (opt.equals("--throttle")) {
|
||||||
mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");
|
mThrottle = nextOptionLong("delay (in milliseconds) to wait between events");
|
||||||
@@ -527,7 +533,9 @@ public class Monkey {
|
|||||||
// do nothing - it's caught at the very start of run()
|
// do nothing - it's caught at the very start of run()
|
||||||
} else if (opt.equals("--dbg-no-events")) {
|
} else if (opt.equals("--dbg-no-events")) {
|
||||||
mSendNoEvents = true;
|
mSendNoEvents = true;
|
||||||
} else if (opt.equals("-f")) {
|
} else if (opt.equals("--port")) {
|
||||||
|
mServerPort = (int) nextOptionLong("Server port to listen on for commands");
|
||||||
|
} else if (opt.equals("-f")) {
|
||||||
mScriptFileName = nextOptionData();
|
mScriptFileName = nextOptionData();
|
||||||
} else if (opt.equals("-h")) {
|
} else if (opt.equals("-h")) {
|
||||||
showUsage();
|
showUsage();
|
||||||
@@ -544,19 +552,23 @@ public class Monkey {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String countStr = nextArg();
|
// If a server port hasn't been specified, we need to specify
|
||||||
if (countStr == null) {
|
// a count
|
||||||
System.err.println("** Error: Count not specified");
|
if (mServerPort == -1) {
|
||||||
showUsage();
|
String countStr = nextArg();
|
||||||
return false;
|
if (countStr == null) {
|
||||||
}
|
System.err.println("** Error: Count not specified");
|
||||||
|
showUsage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mCount = Integer.parseInt(countStr);
|
mCount = Integer.parseInt(countStr);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
System.err.println("** Error: Count is not a number");
|
System.err.println("** Error: Count is not a number");
|
||||||
showUsage();
|
showUsage();
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -564,7 +576,7 @@ public class Monkey {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for any internal configuration (primarily build-time) errors.
|
* Check for any internal configuration (primarily build-time) errors.
|
||||||
*
|
*
|
||||||
* @return Returns true if ready to rock.
|
* @return Returns true if ready to rock.
|
||||||
*/
|
*/
|
||||||
private boolean checkInternalConfiguration() {
|
private boolean checkInternalConfiguration() {
|
||||||
@@ -585,7 +597,7 @@ public class Monkey {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Attach to the required system interfaces.
|
* Attach to the required system interfaces.
|
||||||
*
|
*
|
||||||
* @return Returns true if all system interfaces were available.
|
* @return Returns true if all system interfaces were available.
|
||||||
*/
|
*/
|
||||||
private boolean getSystemInterfaces() {
|
private boolean getSystemInterfaces() {
|
||||||
@@ -621,7 +633,7 @@ public class Monkey {
|
|||||||
/**
|
/**
|
||||||
* Using the restrictions provided (categories & packages), generate a list of activities
|
* Using the restrictions provided (categories & packages), generate a list of activities
|
||||||
* that we can actually switch to.
|
* that we can actually switch to.
|
||||||
*
|
*
|
||||||
* @return Returns true if it could successfully build a list of target activities
|
* @return Returns true if it could successfully build a list of target activities
|
||||||
*/
|
*/
|
||||||
private boolean getMainApps() {
|
private boolean getMainApps() {
|
||||||
@@ -644,7 +656,7 @@ public class Monkey {
|
|||||||
final int NA = mainApps.size();
|
final int NA = mainApps.size();
|
||||||
for (int a = 0; a < NA; a++) {
|
for (int a = 0; a < NA; a++) {
|
||||||
ResolveInfo r = mainApps.get(a);
|
ResolveInfo r = mainApps.get(a);
|
||||||
if (mValidPackages.size() == 0 ||
|
if (mValidPackages.size() == 0 ||
|
||||||
mValidPackages.contains(r.activityInfo.applicationInfo.packageName)) {
|
mValidPackages.contains(r.activityInfo.applicationInfo.packageName)) {
|
||||||
if (mVerbose >= 2) { // very verbose
|
if (mVerbose >= 2) { // very verbose
|
||||||
System.out.println("// + Using main activity "
|
System.out.println("// + Using main activity "
|
||||||
@@ -676,15 +688,15 @@ public class Monkey {
|
|||||||
System.out.println("** No activities found to run, monkey aborted.");
|
System.out.println("** No activities found to run, monkey aborted.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run mCount cycles and see if we hit any crashers.
|
* Run mCount cycles and see if we hit any crashers.
|
||||||
*
|
*
|
||||||
* TODO: Meta state on keys
|
* TODO: Meta state on keys
|
||||||
*
|
*
|
||||||
* @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() {
|
private int runMonkeyCycles() {
|
||||||
@@ -749,9 +761,11 @@ public class Monkey {
|
|||||||
} else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) {
|
} else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) {
|
||||||
systemCrashed = !mIgnoreSecurityExceptions;
|
systemCrashed = !mIgnoreSecurityExceptions;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Event Source has signaled that we have no more events to process
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we got this far, we succeeded!
|
// If we got this far, we succeeded!
|
||||||
return mCount;
|
return mCount;
|
||||||
}
|
}
|
||||||
@@ -775,18 +789,18 @@ public class Monkey {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Watch for appearance of new tombstone files, which indicate native crashes.
|
* Watch for appearance of new tombstone files, which indicate native crashes.
|
||||||
*
|
*
|
||||||
* @return Returns true if new files have appeared in the list
|
* @return Returns true if new files have appeared in the list
|
||||||
*/
|
*/
|
||||||
private boolean checkNativeCrashes() {
|
private boolean checkNativeCrashes() {
|
||||||
String[] tombstones = TOMBSTONES_PATH.list();
|
String[] tombstones = TOMBSTONES_PATH.list();
|
||||||
|
|
||||||
// shortcut path for usually empty directory, so we don't waste even more objects
|
// shortcut path for usually empty directory, so we don't waste even more objects
|
||||||
if ((tombstones == null) || (tombstones.length == 0)) {
|
if ((tombstones == null) || (tombstones.length == 0)) {
|
||||||
mTombstones = null;
|
mTombstones = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// use set logic to look for new files
|
// use set logic to look for new files
|
||||||
HashSet<String> newStones = new HashSet<String>();
|
HashSet<String> newStones = new HashSet<String>();
|
||||||
for (String x : tombstones) {
|
for (String x : tombstones) {
|
||||||
@@ -804,14 +818,14 @@ public class Monkey {
|
|||||||
/**
|
/**
|
||||||
* Return the next command line option. This has a number of special cases which
|
* 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:
|
* closely, but not exactly, follow the POSIX command line options patterns:
|
||||||
*
|
*
|
||||||
* -- means to stop processing additional options
|
* -- means to stop processing additional options
|
||||||
* -z means option z
|
* -z means option z
|
||||||
* -z ARGS means option z with (non-optional) arguments ARGS
|
* -z ARGS means option z with (non-optional) arguments ARGS
|
||||||
* -zARGS means option z with (optional) arguments ARGS
|
* -zARGS means option z with (optional) arguments ARGS
|
||||||
* --zz means option zz
|
* --zz means option zz
|
||||||
* --zz ARGS means option zz with (non-optional) arguments ARGS
|
* --zz ARGS means option zz with (non-optional) arguments ARGS
|
||||||
*
|
*
|
||||||
* Note that you cannot combine single letter options; -abc != -a -b -c
|
* Note that you cannot combine single letter options; -abc != -a -b -c
|
||||||
*
|
*
|
||||||
* @return Returns the option string, or null if there are no more options.
|
* @return Returns the option string, or null if there are no more options.
|
||||||
@@ -857,10 +871,10 @@ public class Monkey {
|
|||||||
mNextArg++;
|
mNextArg++;
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a long converted from the next data argument, with error handling if not available.
|
* Returns a long converted from the next data argument, with error handling if not available.
|
||||||
*
|
*
|
||||||
* @param opt The name of the option.
|
* @param opt The name of the option.
|
||||||
* @return Returns a long converted from the argument.
|
* @return Returns a long converted from the argument.
|
||||||
*/
|
*/
|
||||||
@@ -904,6 +918,7 @@ public class Monkey {
|
|||||||
System.err.println(" [--pct-appswitch PERCENT] [--pct-flip PERCENT]");
|
System.err.println(" [--pct-appswitch PERCENT] [--pct-flip PERCENT]");
|
||||||
System.err.println(" [--pct-anyevent PERCENT]");
|
System.err.println(" [--pct-anyevent PERCENT]");
|
||||||
System.err.println(" [--wait-dbg] [--dbg-no-events] [-f scriptfile]");
|
System.err.println(" [--wait-dbg] [--dbg-no-events] [-f scriptfile]");
|
||||||
|
System.err.println(" [--port port]");
|
||||||
System.err.println(" [-s SEED] [-v [-v] ...] [--throttle MILLISEC]");
|
System.err.println(" [-s SEED] [-v [-v] ...] [--throttle MILLISEC]");
|
||||||
System.err.println(" COUNT");
|
System.err.println(" COUNT");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,376 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, 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.util.Log;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.lang.Integer;
|
||||||
|
import java.lang.NumberFormatException;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Event source for getting Monkey Network Script commands from
|
||||||
|
* over the network.
|
||||||
|
*/
|
||||||
|
public class MonkeySourceNetwork implements MonkeyEventSource {
|
||||||
|
private static final String TAG = "MonkeyStub";
|
||||||
|
|
||||||
|
private interface MonkeyCommand {
|
||||||
|
MonkeyEvent translateCommand(List<String> command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to simulate closing and opening the keyboard.
|
||||||
|
*/
|
||||||
|
private static class FlipCommand implements MonkeyCommand {
|
||||||
|
// flip open
|
||||||
|
// flip closed
|
||||||
|
public MonkeyEvent translateCommand(List<String> command) {
|
||||||
|
if (command.size() > 1) {
|
||||||
|
String direction = command.get(1);
|
||||||
|
if ("open".equals(direction)) {
|
||||||
|
return new MonkeyFlipEvent(true);
|
||||||
|
} else if ("close".equals(direction)) {
|
||||||
|
return new MonkeyFlipEvent(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to send touch events to the input system.
|
||||||
|
*/
|
||||||
|
private static class TouchCommand implements MonkeyCommand {
|
||||||
|
// touch [down|up|move] [x] [y]
|
||||||
|
// touch down 120 120
|
||||||
|
// touch move 140 140
|
||||||
|
// touch up 140 140
|
||||||
|
public MonkeyEvent translateCommand(List<String> command) {
|
||||||
|
if (command.size() == 4) {
|
||||||
|
String actionName = command.get(1);
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
try {
|
||||||
|
x = Integer.parseInt(command.get(2));
|
||||||
|
y = Integer.parseInt(command.get(3));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// Ok, it wasn't a number
|
||||||
|
Log.e(TAG, "Got something that wasn't a number", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// figure out the action
|
||||||
|
int action = -1;
|
||||||
|
if ("down".equals(actionName)) {
|
||||||
|
action = MotionEvent.ACTION_DOWN;
|
||||||
|
} else if ("up".equals(actionName)) {
|
||||||
|
action = MotionEvent.ACTION_UP;
|
||||||
|
} else if ("move".equals(actionName)) {
|
||||||
|
action = MotionEvent.ACTION_MOVE;
|
||||||
|
}
|
||||||
|
if (action == -1) {
|
||||||
|
Log.e(TAG, "Got a bad action: " + actionName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
|
||||||
|
-1, action, x, y, 0);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to send Trackball events to the input system.
|
||||||
|
*/
|
||||||
|
private static class TrackballCommand implements MonkeyCommand {
|
||||||
|
// trackball [dx] [dy]
|
||||||
|
// trackball 1 0 -- move right
|
||||||
|
// trackball -1 0 -- move left
|
||||||
|
public MonkeyEvent translateCommand(List<String> command) {
|
||||||
|
if (command.size() == 3) {
|
||||||
|
int dx = 0;
|
||||||
|
int dy = 0;
|
||||||
|
try {
|
||||||
|
dx = Integer.parseInt(command.get(1));
|
||||||
|
dy = Integer.parseInt(command.get(2));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// Ok, it wasn't a number
|
||||||
|
Log.e(TAG, "Got something that wasn't a number", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
|
||||||
|
MotionEvent.ACTION_MOVE, dx, dy, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to send Key events to the input system.
|
||||||
|
*/
|
||||||
|
private static class KeyCommand implements MonkeyCommand {
|
||||||
|
// key [down|up] [keycode]
|
||||||
|
// key down 82
|
||||||
|
// key up 82
|
||||||
|
public MonkeyEvent translateCommand(List<String> command) {
|
||||||
|
if (command.size() == 3) {
|
||||||
|
int keyCode = -1;
|
||||||
|
String keyName = command.get(2);
|
||||||
|
try {
|
||||||
|
keyCode = Integer.parseInt(keyName);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// Ok, it wasn't a number, see if we have a
|
||||||
|
// keycode name for it
|
||||||
|
keyCode = MonkeySourceRandom.getKeyCode(keyName);
|
||||||
|
if (keyCode == -1) {
|
||||||
|
// OK, one last ditch effort to find a match.
|
||||||
|
// Build the KEYCODE_STRING from the string
|
||||||
|
// we've been given and see if that key
|
||||||
|
// exists. This would allow you to do "key
|
||||||
|
// down menu", for example.
|
||||||
|
keyCode = MonkeySourceRandom.getKeyCode("KEYCODE_" + keyName.toUpperCase());
|
||||||
|
if (keyCode == -1) {
|
||||||
|
// Ok, you gave us something bad.
|
||||||
|
Log.e(TAG, "Can't find keyname: " + keyName);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.d(TAG, "keycode: " + keyCode);
|
||||||
|
int action = -1;
|
||||||
|
if ("down".equals(command.get(1))) {
|
||||||
|
action = KeyEvent.ACTION_DOWN;
|
||||||
|
} else if ("up".equals(command.get(1))) {
|
||||||
|
action = KeyEvent.ACTION_UP;
|
||||||
|
}
|
||||||
|
if (action == -1) {
|
||||||
|
Log.e(TAG, "got unknown action.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new MonkeyKeyEvent(action, keyCode);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to put the Monkey to sleep.
|
||||||
|
*/
|
||||||
|
private static class SleepCommand implements MonkeyCommand {
|
||||||
|
// sleep 2000
|
||||||
|
public MonkeyEvent translateCommand(List<String> command) {
|
||||||
|
if (command.size() == 2) {
|
||||||
|
int sleep = -1;
|
||||||
|
String sleepStr = command.get(1);
|
||||||
|
try {
|
||||||
|
sleep = Integer.parseInt(sleepStr);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.e(TAG, "Not a number: " + sleepStr, e);
|
||||||
|
}
|
||||||
|
return new MonkeyThrottleEvent(sleep);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This maps from command names to command implementations.
|
||||||
|
private static final Map<String, MonkeyCommand> COMMAND_MAP = new HashMap<String, MonkeyCommand>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
// Add in all the commands we support
|
||||||
|
COMMAND_MAP.put("flip", new FlipCommand());
|
||||||
|
COMMAND_MAP.put("touch", new TouchCommand());
|
||||||
|
COMMAND_MAP.put("trackball", new TrackballCommand());
|
||||||
|
COMMAND_MAP.put("key", new KeyCommand());
|
||||||
|
COMMAND_MAP.put("sleep", new SleepCommand());
|
||||||
|
}
|
||||||
|
|
||||||
|
// QUIT command
|
||||||
|
private static final String QUIT = "quit";
|
||||||
|
|
||||||
|
// command response strings
|
||||||
|
private static final String OK = "OK";
|
||||||
|
private static final String ERROR = "ERROR";
|
||||||
|
|
||||||
|
|
||||||
|
private final int port;
|
||||||
|
private BufferedReader input;
|
||||||
|
private PrintWriter output;
|
||||||
|
private boolean started = false;
|
||||||
|
|
||||||
|
public MonkeySourceNetwork(int port) {
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a network server listening on the specified port. The
|
||||||
|
* network protocol is a line oriented protocol, where each line
|
||||||
|
* is a different command that can be run.
|
||||||
|
*
|
||||||
|
* @param port the port to listen on
|
||||||
|
*/
|
||||||
|
private void startServer() throws IOException {
|
||||||
|
// Only bind this to local host. This means that you can only
|
||||||
|
// talk to the monkey locally, or though adb port forwarding.
|
||||||
|
ServerSocket server = new ServerSocket(port,
|
||||||
|
0, // default backlog
|
||||||
|
InetAddress.getLocalHost());
|
||||||
|
Socket s = server.accept();
|
||||||
|
input = new BufferedReader(new InputStreamReader(s.getInputStream()));
|
||||||
|
// auto-flush
|
||||||
|
output = new PrintWriter(s.getOutputStream(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function splits the given line into String parts. It obey's quoted
|
||||||
|
* strings and returns them as a single part.
|
||||||
|
*
|
||||||
|
* "This is a test" -> returns only one element
|
||||||
|
* This is a test -> returns four elements
|
||||||
|
*
|
||||||
|
* @param line the line to parse
|
||||||
|
* @return the List of elements
|
||||||
|
*/
|
||||||
|
private static List<String> commandLineSplit(String line) {
|
||||||
|
ArrayList<String> result = new ArrayList<String>();
|
||||||
|
StringTokenizer tok = new StringTokenizer(line);
|
||||||
|
|
||||||
|
boolean insideQuote = false;
|
||||||
|
StringBuffer quotedWord = new StringBuffer();
|
||||||
|
while (tok.hasMoreTokens()) {
|
||||||
|
String cur = tok.nextToken();
|
||||||
|
if (!insideQuote && cur.startsWith("\"")) {
|
||||||
|
// begin quote
|
||||||
|
quotedWord.append(cur);
|
||||||
|
insideQuote = true;
|
||||||
|
} else if (insideQuote) {
|
||||||
|
// end quote
|
||||||
|
if (cur.endsWith("\"")) {
|
||||||
|
insideQuote = false;
|
||||||
|
quotedWord.append(cur);
|
||||||
|
String word = quotedWord.toString();
|
||||||
|
|
||||||
|
// trim off the quotes
|
||||||
|
result.add(word.substring(1, word.length() - 1));
|
||||||
|
} else {
|
||||||
|
quotedWord.append(cur);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.add(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate the given command line into a MonkeyEvent.
|
||||||
|
*
|
||||||
|
* @param commandLine the full command line given.
|
||||||
|
* @returns the MonkeyEvent corresponding to the command, or null
|
||||||
|
* if there was an issue.
|
||||||
|
*/
|
||||||
|
private MonkeyEvent translateCommand(String commandLine) {
|
||||||
|
Log.d(TAG, "translateCommand: " + commandLine);
|
||||||
|
List<String> parts = commandLineSplit(commandLine);
|
||||||
|
if (parts.size() > 0) {
|
||||||
|
MonkeyCommand command = COMMAND_MAP.get(parts.get(0));
|
||||||
|
if (command != null) {
|
||||||
|
return command.translateCommand(parts);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MonkeyEvent getNextEvent() {
|
||||||
|
if (!started) {
|
||||||
|
try {
|
||||||
|
startServer();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Got IOException from server", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, get the next command. This call may block, but that's OK
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
String command = input.readLine();
|
||||||
|
if (command == null) {
|
||||||
|
Log.d(TAG, "Connection dropped.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Do quit checking here
|
||||||
|
if (QUIT.equals(command)) {
|
||||||
|
// then we're done
|
||||||
|
Log.d(TAG, "Quit requested");
|
||||||
|
// let the host know the command ran OK
|
||||||
|
output.println(OK);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do comment checking here. Comments aren't a
|
||||||
|
// command, so we don't echo anything back to the
|
||||||
|
// user.
|
||||||
|
if (command.startsWith("#")) {
|
||||||
|
// keep going
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate the command line
|
||||||
|
MonkeyEvent event = translateCommand(command);
|
||||||
|
if (event != null) {
|
||||||
|
// let the host know the command ran OK
|
||||||
|
output.println(OK);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
// keep going. maybe the next command will make more sense
|
||||||
|
Log.e(TAG, "Got unknown command! \"" + command + "\"");
|
||||||
|
output.println(ERROR);
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Exception: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVerbose(int verbose) {
|
||||||
|
// We're not particualy verbose
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validate() {
|
||||||
|
// we have no pre-conditions to validate
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@ import java.util.Random;
|
|||||||
/**
|
/**
|
||||||
* monkey event queue
|
* monkey event queue
|
||||||
*/
|
*/
|
||||||
public class MonkeySourceRandom implements MonkeyEventSource {
|
public class MonkeySourceRandom implements MonkeyEventSource {
|
||||||
/** Key events that move around the UI. */
|
/** Key events that move around the UI. */
|
||||||
private static final int[] NAV_KEYS = {
|
private static final int[] NAV_KEYS = {
|
||||||
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
|
KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN,
|
||||||
@@ -55,7 +55,7 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
/** Nice names for all key events. */
|
/** Nice names for all key events. */
|
||||||
private static final String[] KEY_NAMES = {
|
private static final String[] KEY_NAMES = {
|
||||||
"KEYCODE_UNKNOWN",
|
"KEYCODE_UNKNOWN",
|
||||||
"KEYCODE_MENU",
|
"KEYCODE_SOFT_LEFT",
|
||||||
"KEYCODE_SOFT_RIGHT",
|
"KEYCODE_SOFT_RIGHT",
|
||||||
"KEYCODE_HOME",
|
"KEYCODE_HOME",
|
||||||
"KEYCODE_BACK",
|
"KEYCODE_BACK",
|
||||||
@@ -146,7 +146,7 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
"KEYCODE_REWIND",
|
"KEYCODE_REWIND",
|
||||||
"KEYCODE_FORWARD",
|
"KEYCODE_FORWARD",
|
||||||
"KEYCODE_MUTE",
|
"KEYCODE_MUTE",
|
||||||
|
|
||||||
"TAG_LAST_KEYCODE" // EOL. used to keep the lists in sync
|
"TAG_LAST_KEYCODE" // EOL. used to keep the lists in sync
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -158,34 +158,50 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
public static final int FACTOR_SYSOPS = 5;
|
public static final int FACTOR_SYSOPS = 5;
|
||||||
public static final int FACTOR_APPSWITCH = 6;
|
public static final int FACTOR_APPSWITCH = 6;
|
||||||
public static final int FACTOR_FLIP = 7;
|
public static final int FACTOR_FLIP = 7;
|
||||||
public static final int FACTOR_ANYTHING = 8;
|
public static final int FACTOR_ANYTHING = 8;
|
||||||
public static final int FACTORZ_COUNT = 9; // should be last+1
|
public static final int FACTORZ_COUNT = 9; // should be last+1
|
||||||
|
|
||||||
|
|
||||||
/** percentages for each type of event. These will be remapped to working
|
/** percentages for each type of event. These will be remapped to working
|
||||||
* values after we read any optional values.
|
* values after we read any optional values.
|
||||||
**/
|
**/
|
||||||
private float[] mFactors = new float[FACTORZ_COUNT];
|
private float[] mFactors = new float[FACTORZ_COUNT];
|
||||||
private ArrayList<ComponentName> mMainApps;
|
private ArrayList<ComponentName> mMainApps;
|
||||||
private int mEventCount = 0; //total number of events generated so far
|
private int mEventCount = 0; //total number of events generated so far
|
||||||
private MonkeyEventQueue mQ;
|
private MonkeyEventQueue mQ;
|
||||||
private Random mRandom;
|
private Random mRandom;
|
||||||
private int mVerbose = 0;
|
private int mVerbose = 0;
|
||||||
private long mThrottle = 0;
|
private long mThrottle = 0;
|
||||||
|
|
||||||
private boolean mKeyboardOpen = false;
|
private boolean mKeyboardOpen = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the last name in the key list
|
* @return the last name in the key list
|
||||||
*/
|
*/
|
||||||
public static String getLastKeyName() {
|
public static String getLastKeyName() {
|
||||||
return KEY_NAMES[KeyEvent.getMaxKeyCode() + 1];
|
return KEY_NAMES[KeyEvent.getMaxKeyCode() + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getKeyName(int keycode) {
|
public static String getKeyName(int keycode) {
|
||||||
return KEY_NAMES[keycode];
|
return KEY_NAMES[keycode];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up the keyCode from a given KEYCODE_NAME. NOTE: This may
|
||||||
|
* be an expensive operation.
|
||||||
|
*
|
||||||
|
* @param keyName the name of the KEYCODE_VALUE to lookup.
|
||||||
|
* @returns the intenger keyCode value, or -1 if not found
|
||||||
|
*/
|
||||||
|
public static int getKeyCode(String keyName) {
|
||||||
|
for (int x = 0; x < KEY_NAMES.length; x++) {
|
||||||
|
if (KEY_NAMES[x].equals(keyName)) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
public MonkeySourceRandom(long seed, ArrayList<ComponentName> MainApps, long throttle) {
|
public MonkeySourceRandom(long seed, ArrayList<ComponentName> MainApps, long throttle) {
|
||||||
// default values for random distributions
|
// default values for random distributions
|
||||||
// note, these are straight percentages, to match user input (cmd line args)
|
// note, these are straight percentages, to match user input (cmd line args)
|
||||||
@@ -199,7 +215,7 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
mFactors[FACTOR_APPSWITCH] = 2.0f;
|
mFactors[FACTOR_APPSWITCH] = 2.0f;
|
||||||
mFactors[FACTOR_FLIP] = 1.0f;
|
mFactors[FACTOR_FLIP] = 1.0f;
|
||||||
mFactors[FACTOR_ANYTHING] = 15.0f;
|
mFactors[FACTOR_ANYTHING] = 15.0f;
|
||||||
|
|
||||||
mRandom = new SecureRandom();
|
mRandom = new SecureRandom();
|
||||||
mRandom.setSeed((seed == 0) ? -1 : seed);
|
mRandom.setSeed((seed == 0) ? -1 : seed);
|
||||||
mMainApps = MainApps;
|
mMainApps = MainApps;
|
||||||
@@ -220,25 +236,25 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
} else {
|
} else {
|
||||||
defaultSum += mFactors[i];
|
defaultSum += mFactors[i];
|
||||||
++defaultCount;
|
++defaultCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the user request was > 100%, reject it
|
// if the user request was > 100%, reject it
|
||||||
if (userSum > 100.0f) {
|
if (userSum > 100.0f) {
|
||||||
System.err.println("** Event weights > 100%");
|
System.err.println("** Event weights > 100%");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the user specified all of the weights, then they need to be 100%
|
// if the user specified all of the weights, then they need to be 100%
|
||||||
if (defaultCount == 0 && (userSum < 99.9f || userSum > 100.1f)) {
|
if (defaultCount == 0 && (userSum < 99.9f || userSum > 100.1f)) {
|
||||||
System.err.println("** Event weights != 100%");
|
System.err.println("** Event weights != 100%");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// compute the adjustment necessary
|
// compute the adjustment necessary
|
||||||
float defaultsTarget = (100.0f - userSum);
|
float defaultsTarget = (100.0f - userSum);
|
||||||
float defaultsAdjustment = defaultsTarget / defaultSum;
|
float defaultsAdjustment = defaultsTarget / defaultSum;
|
||||||
|
|
||||||
// fix all values, by adjusting defaults, or flipping user values back to >0
|
// fix all values, by adjusting defaults, or flipping user values back to >0
|
||||||
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
||||||
if (mFactors[i] <= 0.0f) { // user values are zero or negative
|
if (mFactors[i] <= 0.0f) { // user values are zero or negative
|
||||||
@@ -247,46 +263,46 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
mFactors[i] *= defaultsAdjustment;
|
mFactors[i] *= defaultsAdjustment;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if verbose, show factors
|
// if verbose, show factors
|
||||||
|
|
||||||
if (mVerbose > 0) {
|
if (mVerbose > 0) {
|
||||||
System.out.println("// Event percentages:");
|
System.out.println("// Event percentages:");
|
||||||
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
||||||
System.out.println("// " + i + ": " + mFactors[i] + "%");
|
System.out.println("// " + i + ": " + mFactors[i] + "%");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally, normalize and convert to running sum
|
// finally, normalize and convert to running sum
|
||||||
float sum = 0.0f;
|
float sum = 0.0f;
|
||||||
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
for (int i = 0; i < FACTORZ_COUNT; ++i) {
|
||||||
sum += mFactors[i] / 100.0f;
|
sum += mFactors[i] / 100.0f;
|
||||||
mFactors[i] = sum;
|
mFactors[i] = sum;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the factors
|
* set the factors
|
||||||
*
|
*
|
||||||
* @param factors: percentages for each type of event
|
* @param factors: percentages for each type of event
|
||||||
*/
|
*/
|
||||||
public void setFactors(float factors[]) {
|
public void setFactors(float factors[]) {
|
||||||
int c = FACTORZ_COUNT;
|
int c = FACTORZ_COUNT;
|
||||||
if (factors.length < c) {
|
if (factors.length < c) {
|
||||||
c = factors.length;
|
c = factors.length;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < c; i++)
|
for (int i = 0; i < c; i++)
|
||||||
mFactors[i] = factors[i];
|
mFactors[i] = factors[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFactors(int index, float v) {
|
public void setFactors(int index, float v) {
|
||||||
mFactors[index] = v;
|
mFactors[index] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random motion event. This method counts a down, move, and up as multiple events.
|
* 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: Test & fix the selectors when non-zero percentages
|
||||||
* TODO: Longpress.
|
* TODO: Longpress.
|
||||||
* TODO: Fling.
|
* TODO: Fling.
|
||||||
@@ -294,13 +310,13 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
* TODO: More useful than the random walk here would be to pick a single random direction
|
* 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
|
* and distance, and divvy it up into a random number of segments. (This would serve to
|
||||||
* generate fling gestures, which are important).
|
* generate fling gestures, which are important).
|
||||||
*
|
*
|
||||||
* @param random Random number source for positioning
|
* @param random Random number source for positioning
|
||||||
* @param motionEvent If false, touch/release. If true, touch/move/release.
|
* @param motionEvent If false, touch/release. If true, touch/move/release.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private void generateMotionEvent(Random random, boolean motionEvent){
|
private void generateMotionEvent(Random random, boolean motionEvent){
|
||||||
|
|
||||||
Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
|
Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
|
||||||
|
|
||||||
float x = Math.abs(random.nextInt() % display.getWidth());
|
float x = Math.abs(random.nextInt() % display.getWidth());
|
||||||
@@ -310,12 +326,12 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
if (downAt == -1) {
|
if (downAt == -1) {
|
||||||
downAt = eventTime;
|
downAt = eventTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
MonkeyMotionEvent e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
|
MonkeyMotionEvent e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
|
||||||
downAt, MotionEvent.ACTION_DOWN, x, y, 0);
|
downAt, MotionEvent.ACTION_DOWN, x, y, 0);
|
||||||
e.setIntermediateNote(false);
|
e.setIntermediateNote(false);
|
||||||
mQ.addLast(e);
|
mQ.addLast(e);
|
||||||
|
|
||||||
// sometimes we'll move during the touch
|
// sometimes we'll move during the touch
|
||||||
if (motionEvent) {
|
if (motionEvent) {
|
||||||
int count = random.nextInt(10);
|
int count = random.nextInt(10);
|
||||||
@@ -323,34 +339,34 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
// generate some slop in the up event
|
// generate some slop in the up event
|
||||||
x = (x + (random.nextInt() % 10)) % display.getWidth();
|
x = (x + (random.nextInt() % 10)) % display.getWidth();
|
||||||
y = (y + (random.nextInt() % 10)) % display.getHeight();
|
y = (y + (random.nextInt() % 10)) % display.getHeight();
|
||||||
|
|
||||||
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
|
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
|
||||||
downAt, MotionEvent.ACTION_MOVE, x, y, 0);
|
downAt, MotionEvent.ACTION_MOVE, x, y, 0);
|
||||||
e.setIntermediateNote(true);
|
e.setIntermediateNote(true);
|
||||||
mQ.addLast(e);
|
mQ.addLast(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO generate some slop in the up event
|
// TODO generate some slop in the up event
|
||||||
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
|
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_POINTER,
|
||||||
downAt, MotionEvent.ACTION_UP, x, y, 0);
|
downAt, MotionEvent.ACTION_UP, x, y, 0);
|
||||||
e.setIntermediateNote(false);
|
e.setIntermediateNote(false);
|
||||||
mQ.addLast(e);
|
mQ.addLast(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random trackball event. This consists of a sequence of small moves, followed by
|
* Generates a random trackball event. This consists of a sequence of small moves, followed by
|
||||||
* an optional single click.
|
* an optional single click.
|
||||||
*
|
*
|
||||||
* TODO: Longpress.
|
* TODO: Longpress.
|
||||||
* TODO: Meta state
|
* TODO: Meta state
|
||||||
* TODO: Parameterize the % clicked
|
* TODO: Parameterize the % clicked
|
||||||
* TODO: More useful than the random walk here would be to pick a single random direction
|
* 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
|
* and distance, and divvy it up into a random number of segments. (This would serve to
|
||||||
* generate fling gestures, which are important).
|
* generate fling gestures, which are important).
|
||||||
*
|
*
|
||||||
* @param random Random number source for positioning
|
* @param random Random number source for positioning
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private void generateTrackballEvent(Random random) {
|
private void generateTrackballEvent(Random random) {
|
||||||
Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
|
Display display = WindowManagerImpl.getDefault().getDefaultDisplay();
|
||||||
@@ -362,47 +378,47 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
// generate a small random step
|
// generate a small random step
|
||||||
int dX = random.nextInt(10) - 5;
|
int dX = random.nextInt(10) - 5;
|
||||||
int dY = random.nextInt(10) - 5;
|
int dY = random.nextInt(10) - 5;
|
||||||
|
|
||||||
|
|
||||||
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
|
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, -1,
|
||||||
MotionEvent.ACTION_MOVE, dX, dY, 0);
|
MotionEvent.ACTION_MOVE, dX, dY, 0);
|
||||||
e.setIntermediateNote(i > 0);
|
e.setIntermediateNote(i > 0);
|
||||||
mQ.addLast(e);
|
mQ.addLast(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10% of trackball moves end with a click
|
// 10% of trackball moves end with a click
|
||||||
if (0 == random.nextInt(10)) {
|
if (0 == random.nextInt(10)) {
|
||||||
long downAt = SystemClock.uptimeMillis();
|
long downAt = SystemClock.uptimeMillis();
|
||||||
|
|
||||||
|
|
||||||
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
|
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
|
||||||
MotionEvent.ACTION_DOWN, 0, 0, 0);
|
MotionEvent.ACTION_DOWN, 0, 0, 0);
|
||||||
e.setIntermediateNote(true);
|
e.setIntermediateNote(true);
|
||||||
mQ.addLast(e);
|
mQ.addLast(e);
|
||||||
|
|
||||||
|
|
||||||
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
|
e = new MonkeyMotionEvent(MonkeyEvent.EVENT_TYPE_TRACKBALL, downAt,
|
||||||
MotionEvent.ACTION_UP, 0, 0, 0);
|
MotionEvent.ACTION_UP, 0, 0, 0);
|
||||||
e.setIntermediateNote(false);
|
e.setIntermediateNote(false);
|
||||||
mQ.addLast(e);
|
mQ.addLast(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generate a random event based on mFactor
|
* generate a random event based on mFactor
|
||||||
*/
|
*/
|
||||||
private void generateEvents() {
|
private void generateEvents() {
|
||||||
float cls = mRandom.nextFloat();
|
float cls = mRandom.nextFloat();
|
||||||
int lastKey = 0;
|
int lastKey = 0;
|
||||||
|
|
||||||
boolean touchEvent = cls < mFactors[FACTOR_TOUCH];
|
boolean touchEvent = cls < mFactors[FACTOR_TOUCH];
|
||||||
boolean motionEvent = !touchEvent && (cls < mFactors[FACTOR_MOTION]);
|
boolean motionEvent = !touchEvent && (cls < mFactors[FACTOR_MOTION]);
|
||||||
if (touchEvent || motionEvent) {
|
if (touchEvent || motionEvent) {
|
||||||
generateMotionEvent(mRandom, motionEvent);
|
generateMotionEvent(mRandom, motionEvent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cls < mFactors[FACTOR_TRACKBALL]) {
|
if (cls < mFactors[FACTOR_TRACKBALL]) {
|
||||||
generateTrackballEvent(mRandom);
|
generateTrackballEvent(mRandom);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -427,23 +443,23 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
} else {
|
} else {
|
||||||
lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1);
|
lastKey = 1 + mRandom.nextInt(KeyEvent.getMaxKeyCode() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, lastKey);
|
MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, lastKey);
|
||||||
mQ.addLast(e);
|
mQ.addLast(e);
|
||||||
|
|
||||||
e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey);
|
e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, lastKey);
|
||||||
mQ.addLast(e);
|
mQ.addLast(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean validate() {
|
public boolean validate() {
|
||||||
//check factors
|
//check factors
|
||||||
return adjustEventFactors();
|
return adjustEventFactors();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVerbose(int verbose) {
|
public void setVerbose(int verbose) {
|
||||||
mVerbose = verbose;
|
mVerbose = verbose;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generate an activity event
|
* generate an activity event
|
||||||
*/
|
*/
|
||||||
@@ -452,18 +468,18 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
mRandom.nextInt(mMainApps.size())));
|
mRandom.nextInt(mMainApps.size())));
|
||||||
mQ.addLast(e);
|
mQ.addLast(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if the queue is empty, we generate events first
|
* if the queue is empty, we generate events first
|
||||||
* @return the first event in the queue
|
* @return the first event in the queue
|
||||||
*/
|
*/
|
||||||
public MonkeyEvent getNextEvent() {
|
public MonkeyEvent getNextEvent() {
|
||||||
if (mQ.isEmpty()) {
|
if (mQ.isEmpty()) {
|
||||||
generateEvents();
|
generateEvents();
|
||||||
}
|
}
|
||||||
mEventCount++;
|
mEventCount++;
|
||||||
MonkeyEvent e = mQ.getFirst();
|
MonkeyEvent e = mQ.getFirst();
|
||||||
mQ.removeFirst();
|
mQ.removeFirst();
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user