Merge "Fix how the monkey counts events for scripts." into eclair
This commit is contained in:
@@ -1,19 +1,18 @@
|
|||||||
/**
|
/*
|
||||||
** Copyright 2007, The Android Open Source Project
|
* Copyright 2007, The Android Open Source Project
|
||||||
**
|
*
|
||||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
** you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
** You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
**
|
*
|
||||||
** http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
**
|
*
|
||||||
** Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
** See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
** limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package com.android.commands.monkey;
|
package com.android.commands.monkey;
|
||||||
|
|
||||||
@@ -51,19 +50,25 @@ public class Monkey {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Monkey Debugging/Dev Support
|
* Monkey Debugging/Dev Support
|
||||||
*
|
* <p>
|
||||||
* 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;
|
||||||
|
|
||||||
private final static int DEBUG_ALLOW_ANY_RESTARTS = 0;
|
private final static int DEBUG_ALLOW_ANY_RESTARTS = 0;
|
||||||
|
|
||||||
private IActivityManager mAm;
|
private IActivityManager mAm;
|
||||||
|
|
||||||
private IWindowManager mWm;
|
private IWindowManager mWm;
|
||||||
|
|
||||||
private IPackageManager mPm;
|
private IPackageManager mPm;
|
||||||
|
|
||||||
/** Command line arguments */
|
/** Command line arguments */
|
||||||
private String[] mArgs;
|
private String[] mArgs;
|
||||||
|
|
||||||
/** Current argument being parsed */
|
/** Current argument being parsed */
|
||||||
private int mNextArg;
|
private int mNextArg;
|
||||||
|
|
||||||
/** Data of current argument */
|
/** Data of current argument */
|
||||||
private String mCurArgData;
|
private String mCurArgData;
|
||||||
|
|
||||||
@@ -83,16 +88,28 @@ public class Monkey {
|
|||||||
/** 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 */
|
/**
|
||||||
|
* Count each event as a cycle. Set to false for scripts so that each time
|
||||||
|
* through the script increments the count.
|
||||||
|
*/
|
||||||
|
private boolean mCountEvents = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is set by the ActivityController thread to request collection of ANR
|
||||||
|
* trace files
|
||||||
|
*/
|
||||||
private boolean mRequestAnrTraces = false;
|
private boolean mRequestAnrTraces = false;
|
||||||
|
|
||||||
/** This is set by the ActivityController thread to request a "dumpsys meminfo" */
|
/**
|
||||||
|
* This is set by the ActivityController thread to request a
|
||||||
|
* "dumpsys meminfo"
|
||||||
|
*/
|
||||||
private boolean mRequestDumpsysMemInfo = false;
|
private boolean mRequestDumpsysMemInfo = false;
|
||||||
|
|
||||||
/** Kill the process after a timeout or crash. */
|
/** Kill the process after a timeout or crash. */
|
||||||
@@ -103,8 +120,10 @@ public class Monkey {
|
|||||||
|
|
||||||
/** Packages we are allowed to run, or empty if no restriction. */
|
/** Packages we are allowed to run, or empty if no restriction. */
|
||||||
private HashSet<String> mValidPackages = new HashSet<String>();
|
private HashSet<String> mValidPackages = new HashSet<String>();
|
||||||
|
|
||||||
/** Categories we are allowed to launch **/
|
/** Categories we are allowed to launch **/
|
||||||
ArrayList<String> mMainCategories = new ArrayList<String>();
|
private 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>();
|
||||||
|
|
||||||
@@ -119,8 +138,11 @@ public class Monkey {
|
|||||||
|
|
||||||
/** Dropped-event statistics **/
|
/** Dropped-event statistics **/
|
||||||
long mDroppedKeyEvents = 0;
|
long mDroppedKeyEvents = 0;
|
||||||
|
|
||||||
long mDroppedPointerEvents = 0;
|
long mDroppedPointerEvents = 0;
|
||||||
|
|
||||||
long mDroppedTrackballEvents = 0;
|
long mDroppedTrackballEvents = 0;
|
||||||
|
|
||||||
long mDroppedFlipEvents = 0;
|
long mDroppedFlipEvents = 0;
|
||||||
|
|
||||||
/** a filename to the script (if any) **/
|
/** a filename to the script (if any) **/
|
||||||
@@ -130,14 +152,18 @@ public class Monkey {
|
|||||||
private int mServerPort = -1;
|
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();
|
||||||
|
|
||||||
// information on the current activity.
|
// information on the current activity.
|
||||||
public static Intent currentIntent;
|
public static Intent currentIntent;
|
||||||
|
|
||||||
public static String currentPackage;
|
public static String currentPackage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -147,8 +173,8 @@ public class Monkey {
|
|||||||
public boolean activityStarting(Intent intent, String pkg) {
|
public boolean activityStarting(Intent intent, String pkg) {
|
||||||
boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_STARTS != 0);
|
boolean allow = checkEnteringPackage(pkg) || (DEBUG_ALLOW_ANY_STARTS != 0);
|
||||||
if (mVerbose > 0) {
|
if (mVerbose > 0) {
|
||||||
System.out.println(" // " + (allow ? "Allowing" : "Rejecting")
|
System.out.println(" // " + (allow ? "Allowing" : "Rejecting") + " start of "
|
||||||
+ " start of " + intent + " in package " + pkg);
|
+ intent + " in package " + pkg);
|
||||||
}
|
}
|
||||||
currentPackage = pkg;
|
currentPackage = pkg;
|
||||||
currentIntent = intent;
|
currentIntent = intent;
|
||||||
@@ -161,7 +187,7 @@ public class Monkey {
|
|||||||
if (!allow) {
|
if (!allow) {
|
||||||
if (mVerbose > 0) {
|
if (mVerbose > 0) {
|
||||||
System.out.println(" // " + (allow ? "Allowing" : "Rejecting")
|
System.out.println(" // " + (allow ? "Allowing" : "Rejecting")
|
||||||
+ " resume of package " + pkg);
|
+ " resume of package " + pkg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentPackage = pkg;
|
currentPackage = pkg;
|
||||||
@@ -172,7 +198,8 @@ public class Monkey {
|
|||||||
if (pkg == null) {
|
if (pkg == null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// preflight the hash lookup to avoid the cost of hash key generation
|
// preflight the hash lookup to avoid the cost of hash key
|
||||||
|
// generation
|
||||||
if (mValidPackages.size() == 0) {
|
if (mValidPackages.size() == 0) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@@ -180,26 +207,22 @@ public class Monkey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean appCrashed(String processName, int pid, String shortMsg,
|
public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
|
||||||
String longMsg, byte[] crashData) {
|
byte[] crashData) {
|
||||||
System.err.println("// CRASH: " + processName + " (pid " + pid
|
System.err.println("// CRASH: " + processName + " (pid " + pid + ")");
|
||||||
+ ")");
|
|
||||||
System.err.println("// Short Msg: " + shortMsg);
|
System.err.println("// Short Msg: " + shortMsg);
|
||||||
System.err.println("// Long Msg: " + longMsg);
|
System.err.println("// Long Msg: " + longMsg);
|
||||||
if (crashData != null) {
|
if (crashData != null) {
|
||||||
try {
|
try {
|
||||||
CrashData cd = new CrashData(new DataInputStream(
|
CrashData cd = new CrashData(new DataInputStream(new ByteArrayInputStream(
|
||||||
new ByteArrayInputStream(crashData)));
|
crashData)));
|
||||||
System.err.println("// Build Label: "
|
System.err.println("// Build Label: " + cd.getBuildData().getFingerprint());
|
||||||
+ cd.getBuildData().getFingerprint());
|
|
||||||
System.err.println("// Build Changelist: "
|
System.err.println("// Build Changelist: "
|
||||||
+ cd.getBuildData().getIncrementalVersion());
|
+ cd.getBuildData().getIncrementalVersion());
|
||||||
System.err.println("// Build Time: "
|
System.err.println("// Build Time: " + cd.getBuildData().getTime());
|
||||||
+ cd.getBuildData().getTime());
|
|
||||||
System.err.println("// ID: " + cd.getId());
|
System.err.println("// ID: " + cd.getId());
|
||||||
System.err.println("// Tag: " + cd.getActivity());
|
System.err.println("// Tag: " + cd.getActivity());
|
||||||
System.err.println(cd.getThrowableData().toString(
|
System.err.println(cd.getThrowableData().toString("// "));
|
||||||
"// "));
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
System.err.println("// BAD STACK CRAWL");
|
System.err.println("// BAD STACK CRAWL");
|
||||||
}
|
}
|
||||||
@@ -215,10 +238,8 @@ public class Monkey {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int appNotResponding(String processName, int pid,
|
public int appNotResponding(String processName, int pid, String processStats) {
|
||||||
String processStats) {
|
System.err.println("// NOT RESPONDING: " + processName + " (pid " + pid + ")");
|
||||||
System.err.println("// NOT RESPONDING: " + processName
|
|
||||||
+ " (pid " + pid + ")");
|
|
||||||
System.err.println(processStats);
|
System.err.println(processStats);
|
||||||
reportProcRank();
|
reportProcRank();
|
||||||
synchronized (Monkey.this) {
|
synchronized (Monkey.this) {
|
||||||
@@ -236,15 +257,16 @@ public class Monkey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
* report writing complete.
|
* asynchronous report writing complete.
|
||||||
*/
|
*/
|
||||||
private void reportAnrTraces() {
|
private void reportAnrTraces() {
|
||||||
try {
|
try {
|
||||||
@@ -256,9 +278,10 @@ public class Monkey {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Run "dumpsys meminfo"
|
* Run "dumpsys meminfo"
|
||||||
*
|
* <p>
|
||||||
* NOTE: You cannot perform a dumpsys call from the ActivityController callback, as it will
|
* NOTE: You cannot perform a dumpsys call from the ActivityController
|
||||||
* deadlock. This should only be called from the main loop of the monkey.
|
* callback, as it will 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");
|
||||||
@@ -266,16 +289,20 @@ public class Monkey {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
* <p>
|
||||||
|
* TODO: Use ProcessBuilder & redirectErrorStream(true) to capture both
|
||||||
|
* streams (might be important for some command lines)
|
||||||
|
*
|
||||||
|
* @param reportName Simple tag that will print before the report and in
|
||||||
|
* various annotations.
|
||||||
* @param command Command line to execute.
|
* @param command Command line to execute.
|
||||||
* TODO: Use ProcessBuilder & redirectErrorStream(true) to capture both streams (might be
|
|
||||||
* important for some command lines)
|
|
||||||
*/
|
*/
|
||||||
private void commandLineReport(String reportName, String command) {
|
private void commandLineReport(String reportName, String command) {
|
||||||
System.err.println(reportName + ":");
|
System.err.println(reportName + ":");
|
||||||
Runtime rt = Runtime.getRuntime();
|
Runtime rt = Runtime.getRuntime();
|
||||||
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
|
||||||
@@ -312,7 +339,7 @@ public class Monkey {
|
|||||||
* Run the command!
|
* Run the command!
|
||||||
*
|
*
|
||||||
* @param args The command-line arguments
|
* @param args The command-line arguments
|
||||||
* @return Returns a posix-style result code. 0 for no error.
|
* @return Returns a posix-style result code. 0 for no error.
|
||||||
*/
|
*/
|
||||||
private int run(String[] args) {
|
private int run(String[] args) {
|
||||||
// Super-early debugger wait
|
// Super-early debugger wait
|
||||||
@@ -332,7 +359,7 @@ public class Monkey {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
@@ -379,6 +406,8 @@ public class Monkey {
|
|||||||
// 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);
|
||||||
|
|
||||||
|
mCountEvents = false;
|
||||||
} else if (mServerPort != -1) {
|
} else if (mServerPort != -1) {
|
||||||
try {
|
try {
|
||||||
mEventSource = new MonkeySourceNetwork(mServerPort);
|
mEventSource = new MonkeySourceNetwork(mServerPort);
|
||||||
@@ -389,37 +418,29 @@ public class Monkey {
|
|||||||
mCount = Integer.MAX_VALUE;
|
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
|
||||||
System.out.println("// Seeded: " + mSeed);
|
System.out.println("// Seeded: " + mSeed);
|
||||||
}
|
}
|
||||||
mEventSource = new MonkeySourceRandom(mSeed, mMainApps, mThrottle);
|
mEventSource = new MonkeySourceRandom(mSeed, mMainApps, mThrottle);
|
||||||
mEventSource.setVerbose(mVerbose);
|
mEventSource.setVerbose(mVerbose);
|
||||||
//set any of the factors that has been set
|
// set any of the factors that has been set
|
||||||
for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
|
for (int i = 0; i < MonkeySourceRandom.FACTORZ_COUNT; i++) {
|
||||||
if (mFactors[i] <= 0.0f) {
|
if (mFactors[i] <= 0.0f) {
|
||||||
((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();
|
||||||
}
|
}
|
||||||
|
|
||||||
//validate source generator
|
// validate source generator
|
||||||
if (!mEventSource.validate()) {
|
if (!mEventSource.validate()) {
|
||||||
return -5;
|
return -5;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mScriptFileName != null) {
|
// If we're profiling, do it immediately before/after the main monkey
|
||||||
// in random mode, count is the number of single events
|
// loop
|
||||||
// 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
|
|
||||||
if (mGenerateHprof) {
|
if (mGenerateHprof) {
|
||||||
signalPersistentProcesses();
|
signalPersistentProcesses();
|
||||||
}
|
}
|
||||||
@@ -473,9 +494,9 @@ public class Monkey {
|
|||||||
mNetworkMonitor.dump();
|
mNetworkMonitor.dump();
|
||||||
|
|
||||||
if (crashedAtCycle < mCount - 1) {
|
if (crashedAtCycle < mCount - 1) {
|
||||||
System.err.println("** System appears to have crashed at event "
|
System.err.println("** System appears to have crashed at event " + crashedAtCycle
|
||||||
+ crashedAtCycle + " of " + mCount + " using seed " + mSeed);
|
+ " of " + mCount + " using seed " + mSeed);
|
||||||
return crashedAtCycle;
|
return crashedAtCycle;
|
||||||
} else {
|
} else {
|
||||||
if (mVerbose > 0) {
|
if (mVerbose > 0) {
|
||||||
System.out.println("// Monkey finished");
|
System.out.println("// Monkey finished");
|
||||||
@@ -520,29 +541,29 @@ 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] =
|
int i = MonkeySourceRandom.FACTOR_TOUCH;
|
||||||
-nextOptionLong("touch events percentage");
|
mFactors[i] = -nextOptionLong("touch events percentage");
|
||||||
} else if (opt.equals("--pct-motion")) {
|
} else if (opt.equals("--pct-motion")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_MOTION] =
|
int i = MonkeySourceRandom.FACTOR_MOTION;
|
||||||
-nextOptionLong("motion events percentage");
|
mFactors[i] = -nextOptionLong("motion events percentage");
|
||||||
} else if (opt.equals("--pct-trackball")) {
|
} else if (opt.equals("--pct-trackball")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_TRACKBALL] =
|
int i = MonkeySourceRandom.FACTOR_TRACKBALL;
|
||||||
-nextOptionLong("trackball events percentage");
|
mFactors[i] = -nextOptionLong("trackball events percentage");
|
||||||
} else if (opt.equals("--pct-nav")) {
|
} else if (opt.equals("--pct-nav")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_NAV] =
|
int i = MonkeySourceRandom.FACTOR_NAV;
|
||||||
-nextOptionLong("nav events percentage");
|
mFactors[i] = -nextOptionLong("nav events percentage");
|
||||||
} else if (opt.equals("--pct-majornav")) {
|
} else if (opt.equals("--pct-majornav")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_MAJORNAV] =
|
int i = MonkeySourceRandom.FACTOR_MAJORNAV;
|
||||||
-nextOptionLong("major nav events percentage");
|
mFactors[i] = -nextOptionLong("major nav events percentage");
|
||||||
} else if (opt.equals("--pct-appswitch")) {
|
} else if (opt.equals("--pct-appswitch")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_APPSWITCH] =
|
int i = MonkeySourceRandom.FACTOR_APPSWITCH;
|
||||||
-nextOptionLong("app switch events percentage");
|
mFactors[i] = -nextOptionLong("app switch events percentage");
|
||||||
} else if (opt.equals("--pct-flip")) {
|
} else if (opt.equals("--pct-flip")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_FLIP] =
|
int i = MonkeySourceRandom.FACTOR_FLIP;
|
||||||
-nextOptionLong("keyboard flip percentage");
|
mFactors[i] = -nextOptionLong("keyboard flip percentage");
|
||||||
} else if (opt.equals("--pct-anyevent")) {
|
} else if (opt.equals("--pct-anyevent")) {
|
||||||
mFactors[MonkeySourceRandom.FACTOR_ANYTHING] =
|
int i = MonkeySourceRandom.FACTOR_ANYTHING;
|
||||||
-nextOptionLong("any events percentage");
|
mFactors[i] = -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");
|
||||||
} else if (opt.equals("--wait-dbg")) {
|
} else if (opt.equals("--wait-dbg")) {
|
||||||
@@ -619,19 +640,22 @@ public class Monkey {
|
|||||||
private boolean getSystemInterfaces() {
|
private boolean getSystemInterfaces() {
|
||||||
mAm = ActivityManagerNative.getDefault();
|
mAm = ActivityManagerNative.getDefault();
|
||||||
if (mAm == null) {
|
if (mAm == null) {
|
||||||
System.err.println("** Error: Unable to connect to activity manager; is the system running?");
|
System.err.println("** Error: Unable to connect to activity manager; is the system "
|
||||||
|
+ "running?");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
|
mWm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
|
||||||
if (mWm == null) {
|
if (mWm == null) {
|
||||||
System.err.println("** Error: Unable to connect to window manager; is the system running?");
|
System.err.println("** Error: Unable to connect to window manager; is the system "
|
||||||
|
+ "running?");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
|
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
|
||||||
if (mPm == null) {
|
if (mPm == null) {
|
||||||
System.err.println("** Error: Unable to connect to package manager; is the system running?");
|
System.err.println("** Error: Unable to connect to package manager; is the system "
|
||||||
|
+ "running?");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,15 +671,16 @@ public class Monkey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using the restrictions provided (categories & packages), generate a list of activities
|
* Using the restrictions provided (categories & packages), generate a list
|
||||||
* that we can actually switch to.
|
* of activities 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() {
|
||||||
try {
|
try {
|
||||||
final int N = mMainCategories.size();
|
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);
|
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||||
String category = mMainCategories.get(i);
|
String category = mMainCategories.get(i);
|
||||||
if (category.length() > 0) {
|
if (category.length() > 0) {
|
||||||
@@ -666,31 +691,26 @@ public class Monkey {
|
|||||||
System.err.println("// Warning: no activities found for category " + category);
|
System.err.println("// Warning: no activities found for category " + category);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (mVerbose >= 2) { // very verbose
|
if (mVerbose >= 2) { // very verbose
|
||||||
System.out.println("// Selecting main activities from category " + category);
|
System.out.println("// Selecting main activities from category " + category);
|
||||||
}
|
}
|
||||||
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 " + r.activityInfo.name
|
||||||
+ r.activityInfo.name
|
|
||||||
+ " (from package "
|
+ " (from package "
|
||||||
+ r.activityInfo.applicationInfo.packageName
|
+ r.activityInfo.applicationInfo.packageName + ")");
|
||||||
+ ")");
|
|
||||||
}
|
}
|
||||||
mMainApps.add(new ComponentName(
|
mMainApps.add(new ComponentName(r.activityInfo.applicationInfo.packageName,
|
||||||
r.activityInfo.applicationInfo.packageName,
|
|
||||||
r.activityInfo.name));
|
r.activityInfo.name));
|
||||||
} else {
|
} else {
|
||||||
if (mVerbose >= 3) { // very very verbose
|
if (mVerbose >= 3) { // very very verbose
|
||||||
System.out.println("// - NOT USING main activity "
|
System.out.println("// - NOT USING main activity "
|
||||||
+ r.activityInfo.name
|
+ r.activityInfo.name + " (from package "
|
||||||
+ " (from package "
|
+ r.activityInfo.applicationInfo.packageName + ")");
|
||||||
+ r.activityInfo.applicationInfo.packageName
|
|
||||||
+ ")");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -710,17 +730,20 @@ public class Monkey {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Run mCount cycles and see if we hit any crashers.
|
* Run mCount cycles and see if we hit any crashers.
|
||||||
*
|
* <p>
|
||||||
* 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() {
|
||||||
int i = 0;
|
|
||||||
|
int eventCounter = 0;
|
||||||
|
int cycleCounter = 0;
|
||||||
|
|
||||||
boolean systemCrashed = false;
|
boolean systemCrashed = false;
|
||||||
|
|
||||||
while (!systemCrashed && i < mCount) {
|
while (!systemCrashed && cycleCounter < mCount) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (mRequestAnrTraces) {
|
if (mRequestAnrTraces) {
|
||||||
reportAnrTraces();
|
reportAnrTraces();
|
||||||
@@ -731,37 +754,36 @@ public class Monkey {
|
|||||||
mRequestDumpsysMemInfo = false;
|
mRequestDumpsysMemInfo = false;
|
||||||
}
|
}
|
||||||
if (mMonitorNativeCrashes) {
|
if (mMonitorNativeCrashes) {
|
||||||
// first time through, when i == 0, just set up the watcher (ignore the error)
|
// first time through, when eventCounter == 0, just set up
|
||||||
if (checkNativeCrashes() && (i > 0)) {
|
// the watcher (ignore the error)
|
||||||
|
if (checkNativeCrashes() && (eventCounter > 0)) {
|
||||||
System.out.println("** New native crash detected.");
|
System.out.println("** New native crash detected.");
|
||||||
mAbort = mAbort || mKillProcessAfterError;
|
mAbort = mAbort || mKillProcessAfterError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mAbort) {
|
if (mAbort) {
|
||||||
System.out.println("** Monkey aborted due to error.");
|
System.out.println("** Monkey aborted due to error.");
|
||||||
System.out.println("Events injected: " + i);
|
System.out.println("Events injected: " + eventCounter);
|
||||||
return i;
|
return eventCounter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this debugging mode, we never send any events. This is primarily
|
// In this debugging mode, we never send any events. This is
|
||||||
// here so you can manually test the package or category limits, while manually
|
// primarily here so you can manually test the package or category
|
||||||
// exercising the system.
|
// limits, while manually exercising the system.
|
||||||
if (mSendNoEvents) {
|
if (mSendNoEvents) {
|
||||||
i++;
|
eventCounter++;
|
||||||
|
cycleCounter++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((mVerbose > 0) && (i % 100) == 0 && i != 0) {
|
if ((mVerbose > 0) && (eventCounter % 100) == 0 && eventCounter != 0) {
|
||||||
System.out.println(" // Sending event #" + i);
|
System.out.println(" // Sending event #" + eventCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
MonkeyEvent ev = mEventSource.getNextEvent();
|
MonkeyEvent ev = mEventSource.getNextEvent();
|
||||||
if (ev != null) {
|
if (ev != null) {
|
||||||
// We don't want to count throttling as an event.
|
|
||||||
if (!(ev instanceof MonkeyThrottleEvent)) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
|
int injectCode = ev.injectEvent(mWm, mAm, mVerbose);
|
||||||
if (injectCode == MonkeyEvent.INJECT_FAIL) {
|
if (injectCode == MonkeyEvent.INJECT_FAIL) {
|
||||||
if (ev instanceof MonkeyKeyEvent) {
|
if (ev instanceof MonkeyKeyEvent) {
|
||||||
@@ -780,19 +802,32 @@ public class Monkey {
|
|||||||
System.err.println("** Error: SecurityException while injecting event.");
|
System.err.println("** Error: SecurityException while injecting event.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't count throttling as an event.
|
||||||
|
if (!(ev instanceof MonkeyThrottleEvent)) {
|
||||||
|
eventCounter++;
|
||||||
|
if (mCountEvents) {
|
||||||
|
cycleCounter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Event Source has signaled that we have no more events to process
|
if (!mCountEvents) {
|
||||||
System.err.println("** Error: Event source exhausted.");
|
cycleCounter++;
|
||||||
break;
|
} else {
|
||||||
|
System.err.println("** Error: Event source exhausted.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("Events injected: " + i);
|
|
||||||
return i;
|
// If we got this far, we succeeded!
|
||||||
|
System.out.println("Events injected: " + eventCounter);
|
||||||
|
return eventCounter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send SIGNAL_USR1 to all processes. This will generate large (5mb) profiling reports
|
* Send SIGNAL_USR1 to all processes. This will generate large (5mb)
|
||||||
* in data/misc, so use with care.
|
* profiling reports in data/misc, so use with care.
|
||||||
*/
|
*/
|
||||||
private void signalPersistentProcesses() {
|
private void signalPersistentProcesses() {
|
||||||
try {
|
try {
|
||||||
@@ -808,14 +843,16 @@ 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;
|
||||||
@@ -836,17 +873,20 @@ 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
|
||||||
* closely, but not exactly, follow the POSIX command line options patterns:
|
* which closely, but not exactly, follow the POSIX command line options
|
||||||
|
* patterns:
|
||||||
*
|
*
|
||||||
|
* <pre>
|
||||||
* -- 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
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
@@ -893,7 +933,8 @@ public class Monkey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@@ -927,19 +968,21 @@ public class Monkey {
|
|||||||
* Print how to use this command.
|
* Print how to use this command.
|
||||||
*/
|
*/
|
||||||
private void showUsage() {
|
private void showUsage() {
|
||||||
System.err.println("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]");
|
StringBuffer usage = new StringBuffer();
|
||||||
System.err.println(" [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]");
|
usage.append("usage: monkey [-p ALLOWED_PACKAGE [-p ALLOWED_PACKAGE] ...]\n");
|
||||||
System.err.println(" [--ignore-crashes] [--ignore-timeouts]");
|
usage.append(" [-c MAIN_CATEGORY [-c MAIN_CATEGORY] ...]\n");
|
||||||
System.err.println(" [--ignore-security-exceptions] [--monitor-native-crashes]");
|
usage.append(" [--ignore-crashes] [--ignore-timeouts]\n");
|
||||||
System.err.println(" [--kill-process-after-error] [--hprof]");
|
usage.append(" [--ignore-security-exceptions] [--monitor-native-crashes]\n");
|
||||||
System.err.println(" [--pct-touch PERCENT] [--pct-motion PERCENT]");
|
usage.append(" [--kill-process-after-error] [--hprof]\n");
|
||||||
System.err.println(" [--pct-trackball PERCENT] [--pct-syskeys PERCENT]");
|
usage.append(" [--pct-touch PERCENT] [--pct-motion PERCENT]\n");
|
||||||
System.err.println(" [--pct-nav PERCENT] [--pct-majornav PERCENT]");
|
usage.append(" [--pct-trackball PERCENT] [--pct-syskeys PERCENT]\n");
|
||||||
System.err.println(" [--pct-appswitch PERCENT] [--pct-flip PERCENT]");
|
usage.append(" [--pct-nav PERCENT] [--pct-majornav PERCENT]\n");
|
||||||
System.err.println(" [--pct-anyevent PERCENT]");
|
usage.append(" [--pct-appswitch PERCENT] [--pct-flip PERCENT]\n");
|
||||||
System.err.println(" [--wait-dbg] [--dbg-no-events] [-f scriptfile]");
|
usage.append(" [--pct-anyevent PERCENT]\n");
|
||||||
System.err.println(" [--port port]");
|
usage.append(" [--wait-dbg] [--dbg-no-events] [-f scriptfile]\n");
|
||||||
System.err.println(" [-s SEED] [-v [-v] ...] [--throttle MILLISEC]");
|
usage.append(" [--port port]\n");
|
||||||
System.err.println(" COUNT");
|
usage.append(" [-s SEED] [-v [-v] ...] [--throttle MILLISEC]\n");
|
||||||
}
|
usage.append(" COUNT");
|
||||||
|
System.err.println(usage.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,23 +19,24 @@ package com.android.commands.monkey;
|
|||||||
/**
|
/**
|
||||||
* event source interface
|
* event source interface
|
||||||
*/
|
*/
|
||||||
public interface MonkeyEventSource {
|
public interface MonkeyEventSource {
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @return the next monkey event from the source
|
* @return the next monkey event from the source
|
||||||
*/
|
*/
|
||||||
public MonkeyEvent getNextEvent();
|
public MonkeyEvent getNextEvent();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set verbose to allow different level of log
|
* set verbose to allow different level of log
|
||||||
|
*
|
||||||
* @param verbose output mode? 1= verbose, 2=very verbose
|
* @param verbose output mode? 1= verbose, 2=very verbose
|
||||||
*/
|
*/
|
||||||
public void setVerbose(int verbose);
|
public void setVerbose(int verbose);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check whether precondition is satisfied
|
* 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
|
* @return false if something fails, e.g. factor failure in random source or
|
||||||
|
* file can not open from script source etc
|
||||||
*/
|
*/
|
||||||
public boolean validate();
|
public boolean validate();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -475,7 +475,7 @@ public class MonkeySourceRandom implements MonkeyEventSource {
|
|||||||
*/
|
*/
|
||||||
public MonkeyEvent getNextEvent() {
|
public MonkeyEvent getNextEvent() {
|
||||||
if (mQ.isEmpty()) {
|
if (mQ.isEmpty()) {
|
||||||
generateEvents();
|
generateEvents();
|
||||||
}
|
}
|
||||||
mEventCount++;
|
mEventCount++;
|
||||||
MonkeyEvent e = mQ.getFirst();
|
MonkeyEvent e = mQ.getFirst();
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ import android.view.KeyEvent;
|
|||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* monkey event queue. It takes a script to produce events sample script format:
|
* monkey event queue. It takes a script to produce events sample script format:
|
||||||
@@ -77,10 +77,6 @@ public class MonkeySourceScript implements MonkeyEventSource {
|
|||||||
// maximum number of events that we read at one time
|
// maximum number of events that we read at one time
|
||||||
private static final int MAX_ONE_TIME_READS = 100;
|
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 = 0;
|
|
||||||
|
|
||||||
// event key word in the capture log
|
// event key word in the capture log
|
||||||
private static final String EVENT_KEYWORD_POINTER = "DispatchPointer";
|
private static final String EVENT_KEYWORD_POINTER = "DispatchPointer";
|
||||||
|
|
||||||
@@ -111,81 +107,99 @@ public class MonkeySourceScript implements MonkeyEventSource {
|
|||||||
|
|
||||||
DataInputStream mInputStream;
|
DataInputStream mInputStream;
|
||||||
|
|
||||||
BufferedReader mBufferReader;
|
BufferedReader mBufferedReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a MonkeySourceScript instance.
|
||||||
|
*
|
||||||
|
* @param filename The filename of the script (on the device).
|
||||||
|
* @param throttle The amount of time in ms to sleep between events.
|
||||||
|
*/
|
||||||
public MonkeySourceScript(String filename, long throttle) {
|
public MonkeySourceScript(String filename, long throttle) {
|
||||||
mScriptFileName = filename;
|
mScriptFileName = filename;
|
||||||
mQ = new MonkeyEventQueue(throttle);
|
mQ = new MonkeyEventQueue(throttle);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the number of total events that will be generated in a round
|
* Resets the globals used to timeshift events.
|
||||||
*/
|
*/
|
||||||
public int getOneRoundEventCount() {
|
|
||||||
// plus one home key down and up event
|
|
||||||
return mEventCountInScript + POLICY_ADDITIONAL_EVENT_COUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resetValue() {
|
private void resetValue() {
|
||||||
mLastRecordedDownTimeKey = 0;
|
mLastRecordedDownTimeKey = 0;
|
||||||
mLastRecordedDownTimeMotion = 0;
|
mLastRecordedDownTimeMotion = 0;
|
||||||
|
mLastRecordedEventTime = -1;
|
||||||
mLastExportDownTimeKey = 0;
|
mLastExportDownTimeKey = 0;
|
||||||
mLastExportDownTimeMotion = 0;
|
mLastExportDownTimeMotion = 0;
|
||||||
mLastRecordedEventTime = -1;
|
|
||||||
mLastExportEventTime = -1;
|
mLastExportEventTime = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean readScriptHeader() {
|
/**
|
||||||
mEventCountInScript = -1;
|
* Reads the header of the script file.
|
||||||
mFileOpened = false;
|
*
|
||||||
try {
|
* @return True if the file header could be parsed, and false otherwise.
|
||||||
if (THIS_DEBUG) {
|
* @throws IOException If there was an error reading the file.
|
||||||
System.out.println("reading script header");
|
*/
|
||||||
}
|
private boolean readHeader() throws IOException {
|
||||||
|
mFileOpened = true;
|
||||||
|
|
||||||
mFStream = new FileInputStream(mScriptFileName);
|
mFStream = new FileInputStream(mScriptFileName);
|
||||||
mInputStream = new DataInputStream(mFStream);
|
mInputStream = new DataInputStream(mFStream);
|
||||||
mBufferReader = new BufferedReader(new InputStreamReader(mInputStream));
|
mBufferedReader = new BufferedReader(new InputStreamReader(mInputStream));
|
||||||
String sLine;
|
|
||||||
while ((sLine = mBufferReader.readLine()) != null) {
|
|
||||||
sLine = sLine.trim();
|
|
||||||
|
|
||||||
if (sLine.indexOf(HEADER_COUNT) >= 0) {
|
String line;
|
||||||
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) {
|
while ((line = mBufferedReader.readLine()) != null) {
|
||||||
System.err.println(e);
|
line = line.trim();
|
||||||
}
|
|
||||||
} else if (sLine.indexOf(STARTING_DATA_LINE) >= 0) {
|
if (line.indexOf(HEADER_COUNT) >= 0) {
|
||||||
// header ends until we read the start data mark
|
try {
|
||||||
mFileOpened = true;
|
String value = line.substring(HEADER_COUNT.length() + 1).trim();
|
||||||
if (THIS_DEBUG) {
|
mEventCountInScript = Integer.parseInt(value);
|
||||||
System.out.println("read script header success");
|
} catch (NumberFormatException e) {
|
||||||
}
|
System.err.println(e);
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if (line.indexOf(HEADER_SPEED) >= 0) {
|
||||||
|
try {
|
||||||
|
String value = line.substring(HEADER_COUNT.length() + 1).trim();
|
||||||
|
mSpeed = Double.parseDouble(value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
System.err.println(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (line.indexOf(STARTING_DATA_LINE) >= 0) {
|
||||||
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads a number of lines and passes the lines to be processed.
|
||||||
|
*
|
||||||
|
* @return The number of lines read.
|
||||||
|
* @throws IOException If there was an error reading the file.
|
||||||
|
*/
|
||||||
|
private int readLines() throws IOException {
|
||||||
|
String line;
|
||||||
|
for (int i = 0; i < MAX_ONE_TIME_READS; i++) {
|
||||||
|
line = mBufferedReader.readLine();
|
||||||
|
if (line == null) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
line.trim();
|
||||||
|
processLine(line);
|
||||||
|
}
|
||||||
|
return MAX_ONE_TIME_READS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an event and adds it to the event queue. If the parameters are
|
||||||
|
* not understood, they are ignored and no events are added.
|
||||||
|
*
|
||||||
|
* @param s The entire string from the script file.
|
||||||
|
* @param args An array of arguments extracted from the script file line.
|
||||||
|
*/
|
||||||
private void handleEvent(String s, String[] args) {
|
private void handleEvent(String s, String[] args) {
|
||||||
// Handle key event
|
// Handle key event
|
||||||
if (s.indexOf(EVENT_KEYWORD_KEY) >= 0 && args.length == 8) {
|
if (s.indexOf(EVENT_KEYWORD_KEY) >= 0 && args.length == 8) {
|
||||||
@@ -291,82 +305,77 @@ public class MonkeySourceScript implements MonkeyEventSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processLine(String s) {
|
/**
|
||||||
int index1 = s.indexOf('(');
|
* Extracts an event and a list of arguments from a line. If the line does
|
||||||
int index2 = s.indexOf(')');
|
* not match the format required, it is ignored.
|
||||||
|
*
|
||||||
|
* @param line A string in the form {@code cmd(arg1,arg2,arg3)}.
|
||||||
|
*/
|
||||||
|
private void processLine(String line) {
|
||||||
|
int index1 = line.indexOf('(');
|
||||||
|
int index2 = line.indexOf(')');
|
||||||
|
|
||||||
if (index1 < 0 || index2 < 0) {
|
if (index1 < 0 || index2 < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] args = s.substring(index1 + 1, index2).split(",");
|
String[] args = line.substring(index1 + 1, index2).split(",");
|
||||||
|
|
||||||
handleEvent(s, args);
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
args[i] = args[i].trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(line, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeFile() {
|
/**
|
||||||
|
* Closes the script file.
|
||||||
|
*
|
||||||
|
* @throws IOException If there was an error closing the file.
|
||||||
|
*/
|
||||||
|
private void closeFile() throws IOException {
|
||||||
mFileOpened = false;
|
mFileOpened = false;
|
||||||
if (THIS_DEBUG) {
|
|
||||||
System.out.println("closing script file");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
mFStream.close();
|
mFStream.close();
|
||||||
mInputStream.close();
|
mInputStream.close();
|
||||||
} catch (IOException e) {
|
} catch (NullPointerException e) {
|
||||||
System.out.println(e);
|
// File was never opened so it can't be closed.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* read next batch of events from the provided script file
|
* Read next batch of events from the script file into the event queue.
|
||||||
|
* Checks if the script is open and then reads the next MAX_ONE_TIME_READS
|
||||||
|
* events or reads until the end of the file. If no events are read, then
|
||||||
|
* the script is closed.
|
||||||
*
|
*
|
||||||
* @return true if success
|
* @throws IOException If there was an error reading the file.
|
||||||
*/
|
*/
|
||||||
private boolean readNextBatch() {
|
private void readNextBatch() throws IOException {
|
||||||
/*
|
int linesRead = 0;
|
||||||
* The script should restore the original state when it run multiple
|
|
||||||
* times.
|
|
||||||
*/
|
|
||||||
String sLine = null;
|
|
||||||
int readCount = 0;
|
|
||||||
|
|
||||||
if (THIS_DEBUG) {
|
if (THIS_DEBUG) {
|
||||||
System.out.println("readNextBatch(): reading next batch of events");
|
System.out.println("readNextBatch(): reading next batch of events");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mFileOpened) {
|
if (!mFileOpened) {
|
||||||
if (!readScriptHeader()) {
|
|
||||||
closeFile();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
resetValue();
|
resetValue();
|
||||||
|
readHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
linesRead = readLines();
|
||||||
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) {
|
if (linesRead == 0) {
|
||||||
// to the end of the file
|
|
||||||
if (THIS_DEBUG) {
|
|
||||||
System.out.println("readNextBatch(): to the end of file");
|
|
||||||
}
|
|
||||||
closeFile();
|
closeFile();
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sleep for a period of given time, introducing latency among events
|
* Sleep for a period of given time. Used to introduce latency between
|
||||||
|
* events.
|
||||||
*
|
*
|
||||||
* @param time to sleep in millisecond
|
* @param time The amount of time to sleep in ms
|
||||||
*/
|
*/
|
||||||
private void needSleep(long time) {
|
private void needSleep(long time) {
|
||||||
if (time < 1) {
|
if (time < 1) {
|
||||||
@@ -379,14 +388,23 @@ public class MonkeySourceScript implements MonkeyEventSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* check whether we can successfully read the header of the script file
|
* Checks if the file can be opened and if the header is valid.
|
||||||
|
*
|
||||||
|
* @return True if the file exists and the header is valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean validate() {
|
public boolean validate() {
|
||||||
boolean b = readNextBatch();
|
boolean validHeader;
|
||||||
|
try {
|
||||||
|
validHeader = readHeader();
|
||||||
|
closeFile();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (mVerbose > 0) {
|
if (mVerbose > 0) {
|
||||||
System.out.println("Replaying " + mEventCountInScript + " events with speed " + mSpeed);
|
System.out.println("Replaying " + mEventCountInScript + " events with speed " + mSpeed);
|
||||||
}
|
}
|
||||||
return b;
|
return validHeader;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVerbose(int verbose) {
|
public void setVerbose(int verbose) {
|
||||||
@@ -394,10 +412,10 @@ public class MonkeySourceScript implements MonkeyEventSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* adjust key downtime and eventtime according to both recorded values and
|
* Adjust key downtime and eventtime according to both recorded values and
|
||||||
* current system time
|
* current system time.
|
||||||
*
|
*
|
||||||
* @param e KeyEvent
|
* @param e A KeyEvent
|
||||||
*/
|
*/
|
||||||
private void adjustKeyEventTime(MonkeyKeyEvent e) {
|
private void adjustKeyEventTime(MonkeyKeyEvent e) {
|
||||||
if (e.getEventTime() < 0) {
|
if (e.getEventTime() < 0) {
|
||||||
@@ -431,10 +449,10 @@ public class MonkeySourceScript implements MonkeyEventSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* adjust motion downtime and eventtime according to both recorded values
|
* Adjust motion downtime and eventtime according to both recorded values
|
||||||
* and current system time
|
* and current system time.
|
||||||
*
|
*
|
||||||
* @param e KeyEvent
|
* @param e A KeyEvent
|
||||||
*/
|
*/
|
||||||
private void adjustMotionEventTime(MonkeyMotionEvent e) {
|
private void adjustMotionEventTime(MonkeyMotionEvent e) {
|
||||||
if (e.getEventTime() < 0) {
|
if (e.getEventTime() < 0) {
|
||||||
@@ -469,25 +487,40 @@ public class MonkeySourceScript implements MonkeyEventSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if the queue is empty, we generate events first
|
* Gets the next event to be injected from the script. If the event queue is
|
||||||
|
* empty, reads the next n events from the script into the queue, where n is
|
||||||
|
* the lesser of the number of remaining events and the value specified by
|
||||||
|
* MAX_ONE_TIME_READS. If the end of the file is reached, no events are
|
||||||
|
* added to the queue and null is returned.
|
||||||
*
|
*
|
||||||
* @return the first event in the queue, if null, indicating the system
|
* @return The first event in the event queue or null if the end of the file
|
||||||
* crashes
|
* is reached or if an error is encountered reading the file.
|
||||||
*/
|
*/
|
||||||
public MonkeyEvent getNextEvent() {
|
public MonkeyEvent getNextEvent() {
|
||||||
long recordedEventTime = -1;
|
long recordedEventTime = -1;
|
||||||
|
MonkeyEvent ev;
|
||||||
|
|
||||||
if (mQ.isEmpty()) {
|
if (mQ.isEmpty()) {
|
||||||
readNextBatch();
|
try {
|
||||||
|
readNextBatch();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MonkeyEvent e = mQ.getFirst();
|
|
||||||
mQ.removeFirst();
|
try {
|
||||||
if (e.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) {
|
ev = mQ.getFirst();
|
||||||
adjustKeyEventTime((MonkeyKeyEvent) e);
|
mQ.removeFirst();
|
||||||
} else if (e.getEventType() == MonkeyEvent.EVENT_TYPE_POINTER
|
} catch (NoSuchElementException e) {
|
||||||
|| e.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) {
|
return null;
|
||||||
adjustMotionEventTime((MonkeyMotionEvent) e);
|
|
||||||
}
|
}
|
||||||
return e;
|
|
||||||
|
if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) {
|
||||||
|
adjustKeyEventTime((MonkeyKeyEvent) ev);
|
||||||
|
} else if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_POINTER
|
||||||
|
|| ev.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) {
|
||||||
|
adjustMotionEventTime((MonkeyMotionEvent) ev);
|
||||||
|
}
|
||||||
|
return ev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user