Fix how the monkey counts events for scripts.
Instead of the monkey counting up to the count times the number of events in the script, the monkey will now only increment the count when the script reaches the end of the file. This allows us to have multiple script files with differing event counts in each.
This commit is contained in:
@@ -1,20 +1,19 @@
|
|||||||
/**
|
/*
|
||||||
** 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;
|
||||||
|
|
||||||
import android.app.ActivityManagerNative;
|
import android.app.ActivityManagerNative;
|
||||||
@@ -50,19 +49,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;
|
||||||
|
|
||||||
@@ -88,10 +93,22 @@ public class Monkey {
|
|||||||
/** 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. */
|
||||||
@@ -102,8 +119,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>();
|
||||||
|
|
||||||
@@ -118,8 +137,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) **/
|
||||||
@@ -129,14 +151,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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -146,8 +172,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;
|
||||||
@@ -171,7 +197,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 {
|
||||||
@@ -179,26 +206,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");
|
||||||
}
|
}
|
||||||
@@ -214,10 +237,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) {
|
||||||
@@ -235,15 +256,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 {
|
||||||
@@ -255,9 +277,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");
|
||||||
@@ -265,16 +288,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
|
||||||
@@ -375,6 +402,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);
|
||||||
@@ -406,16 +435,8 @@ public class Monkey {
|
|||||||
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();
|
||||||
}
|
}
|
||||||
@@ -469,8 +490,8 @@ 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) {
|
||||||
@@ -516,29 +537,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")) {
|
||||||
@@ -615,19 +636,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -643,10 +667,11 @@ 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 {
|
||||||
@@ -668,25 +693,20 @@ 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 " + 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
|
|
||||||
+ ")");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -706,18 +726,19 @@ 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 lastKey = 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();
|
||||||
@@ -728,37 +749,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 && lastKey == 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) {
|
||||||
@@ -773,18 +793,29 @@ public class Monkey {
|
|||||||
} else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) {
|
} else if (injectCode == MonkeyEvent.INJECT_ERROR_SECURITY_EXCEPTION) {
|
||||||
systemCrashed = !mIgnoreSecurityExceptions;
|
systemCrashed = !mIgnoreSecurityExceptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't count throttling as an event.
|
||||||
|
if (!(ev instanceof MonkeyThrottleEvent)) {
|
||||||
|
eventCounter++;
|
||||||
|
if (mCountEvents) {
|
||||||
|
cycleCounter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!mCountEvents) {
|
||||||
|
cycleCounter++;
|
||||||
} else {
|
} else {
|
||||||
// Event Source has signaled that we have no more events to process
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// If we got this far, we succeeded!
|
// If we got this far, we succeeded!
|
||||||
return mCount;
|
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 {
|
||||||
@@ -800,14 +831,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;
|
||||||
@@ -828,15 +861,18 @@ 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
|
||||||
*
|
*
|
||||||
@@ -885,7 +921,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.
|
||||||
@@ -919,19 +956,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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,21 +21,22 @@ package com.android.commands.monkey;
|
|||||||
*/
|
*/
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
while ((line = mBufferedReader.readLine()) != null) {
|
||||||
|
line = line.trim();
|
||||||
|
|
||||||
|
if (line.indexOf(HEADER_COUNT) >= 0) {
|
||||||
try {
|
try {
|
||||||
mEventCountInScript = Integer.parseInt(sLine.substring(
|
String value = line.substring(HEADER_COUNT.length() + 1).trim();
|
||||||
HEADER_COUNT.length() + 1).trim());
|
mEventCountInScript = Integer.parseInt(value);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
System.err.println(e);
|
System.err.println(e);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} else if (sLine.indexOf(HEADER_SPEED) >= 0) {
|
} else if (line.indexOf(HEADER_SPEED) >= 0) {
|
||||||
try {
|
try {
|
||||||
mSpeed = Double.parseDouble(sLine.substring(HEADER_SPEED.length() + 1)
|
String value = line.substring(HEADER_COUNT.length() + 1).trim();
|
||||||
.trim());
|
mSpeed = Double.parseDouble(value);
|
||||||
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
System.err.println(e);
|
System.err.println(e);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
} else if (sLine.indexOf(STARTING_DATA_LINE) >= 0) {
|
} else if (line.indexOf(STARTING_DATA_LINE) >= 0) {
|
||||||
// header ends until we read the start data mark
|
|
||||||
mFileOpened = true;
|
|
||||||
if (THIS_DEBUG) {
|
|
||||||
System.out.println("read script header success");
|
|
||||||
}
|
|
||||||
return true;
|
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();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeFile() {
|
handleEvent(line, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()) {
|
||||||
|
try {
|
||||||
readNextBatch();
|
readNextBatch();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
MonkeyEvent e = mQ.getFirst();
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ev = mQ.getFirst();
|
||||||
mQ.removeFirst();
|
mQ.removeFirst();
|
||||||
if (e.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) {
|
} catch (NoSuchElementException e) {
|
||||||
adjustKeyEventTime((MonkeyKeyEvent) e);
|
return null;
|
||||||
} else if (e.getEventType() == MonkeyEvent.EVENT_TYPE_POINTER
|
|
||||||
|| e.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) {
|
|
||||||
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