| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.commands.monkey; |
| |
| import android.content.ComponentName; |
| import android.os.SystemClock; |
| import android.view.KeyEvent; |
| import android.view.MotionEvent; |
| import android.view.Surface; |
| |
| import java.io.BufferedReader; |
| import java.io.DataInputStream; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.util.NoSuchElementException; |
| import java.util.Random; |
| |
| /** |
| * monkey event queue. It takes a script to produce events sample script format: |
| * |
| * <pre> |
| * type= raw events |
| * count= 10 |
| * speed= 1.0 |
| * start data >> |
| * captureDispatchPointer(5109520,5109520,0,230.75429,458.1814,0.20784314,0.06666667,0,0.0,0.0,65539,0) |
| * captureDispatchKey(5113146,5113146,0,20,0,0,0,0) |
| * captureDispatchFlip(true) |
| * ... |
| * </pre> |
| */ |
| public class MonkeySourceScript implements MonkeyEventSource { |
| private int mEventCountInScript = 0; // total number of events in the file |
| |
| private int mVerbose = 0; |
| |
| private double mSpeed = 1.0; |
| |
| private String mScriptFileName; |
| |
| private MonkeyEventQueue mQ; |
| |
| private static final String HEADER_COUNT = "count="; |
| |
| private static final String HEADER_SPEED = "speed="; |
| |
| private long mLastRecordedDownTimeKey = 0; |
| |
| private long mLastRecordedDownTimeMotion = 0; |
| |
| private long mLastExportDownTimeKey = 0; |
| |
| private long mLastExportDownTimeMotion = 0; |
| |
| private long mLastExportEventTime = -1; |
| |
| private long mLastRecordedEventTime = -1; |
| |
| // process scripts in line-by-line mode (true) or batch processing mode (false) |
| private boolean mReadScriptLineByLine = false; |
| |
| private static final boolean THIS_DEBUG = false; |
| |
| // a parameter that compensates the difference of real elapsed time and |
| // time in theory |
| private static final long SLEEP_COMPENSATE_DIFF = 16; |
| |
| // if this header is present, scripts are read and processed in line-by-line mode |
| private static final String HEADER_LINE_BY_LINE = "linebyline"; |
| |
| // maximum number of events that we read at one time |
| private static final int MAX_ONE_TIME_READS = 100; |
| |
| // event key word in the capture log |
| private static final String EVENT_KEYWORD_POINTER = "DispatchPointer"; |
| |
| private static final String EVENT_KEYWORD_TRACKBALL = "DispatchTrackball"; |
| |
| private static final String EVENT_KEYWORD_ROTATION = "RotateScreen"; |
| |
| private static final String EVENT_KEYWORD_KEY = "DispatchKey"; |
| |
| private static final String EVENT_KEYWORD_FLIP = "DispatchFlip"; |
| |
| private static final String EVENT_KEYWORD_KEYPRESS = "DispatchPress"; |
| |
| private static final String EVENT_KEYWORD_ACTIVITY = "LaunchActivity"; |
| |
| private static final String EVENT_KEYWORD_INSTRUMENTATION = "LaunchInstrumentation"; |
| |
| private static final String EVENT_KEYWORD_WAIT = "UserWait"; |
| |
| private static final String EVENT_KEYWORD_LONGPRESS = "LongPress"; |
| |
| private static final String EVENT_KEYWORD_POWERLOG = "PowerLog"; |
| |
| private static final String EVENT_KEYWORD_WRITEPOWERLOG = "WriteLog"; |
| |
| private static final String EVENT_KEYWORD_RUNCMD = "RunCmd"; |
| |
| private static final String EVENT_KEYWORD_TAP = "Tap"; |
| |
| private static final String EVENT_KEYWORD_PROFILE_WAIT = "ProfileWait"; |
| |
| private static final String EVENT_KEYWORD_DEVICE_WAKEUP = "DeviceWakeUp"; |
| |
| private static final String EVENT_KEYWORD_INPUT_STRING = "DispatchString"; |
| |
| private static final String EVENT_KEYWORD_PRESSANDHOLD = "PressAndHold"; |
| |
| private static final String EVENT_KEYWORD_DRAG = "Drag"; |
| |
| private static final String EVENT_KEYWORD_PINCH_ZOOM = "PinchZoom"; |
| |
| private static final String EVENT_KEYWORD_START_FRAMERATE_CAPTURE = "StartCaptureFramerate"; |
| |
| private static final String EVENT_KEYWORD_END_FRAMERATE_CAPTURE = "EndCaptureFramerate"; |
| |
| private static final String EVENT_KEYWORD_START_APP_FRAMERATE_CAPTURE = |
| "StartCaptureAppFramerate"; |
| |
| private static final String EVENT_KEYWORD_END_APP_FRAMERATE_CAPTURE = "EndCaptureAppFramerate"; |
| |
| // a line at the end of the header |
| private static final String STARTING_DATA_LINE = "start data >>"; |
| |
| private boolean mFileOpened = false; |
| |
| private static int LONGPRESS_WAIT_TIME = 2000; // wait time for the long |
| |
| private long mProfileWaitTime = 5000; //Wait time for each user profile |
| |
| private long mDeviceSleepTime = 30000; //Device sleep time |
| |
| FileInputStream mFStream; |
| |
| DataInputStream mInputStream; |
| |
| BufferedReader mBufferedReader; |
| |
| // X and Y coordincates of last touch event. Array Index is the pointerId |
| private float mLastX[] = new float[2]; |
| |
| private float mLastY[] = new float[2]; |
| |
| private long mScriptStartTime = -1; |
| |
| private long mMonkeyStartTime = -1; |
| |
| /** |
| * 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(Random random, String filename, long throttle, |
| boolean randomizeThrottle, long profileWaitTime, long deviceSleepTime) { |
| mScriptFileName = filename; |
| mQ = new MonkeyEventQueue(random, throttle, randomizeThrottle); |
| mProfileWaitTime = profileWaitTime; |
| mDeviceSleepTime = deviceSleepTime; |
| } |
| |
| /** |
| * Resets the globals used to timeshift events. |
| */ |
| private void resetValue() { |
| mLastRecordedDownTimeKey = 0; |
| mLastRecordedDownTimeMotion = 0; |
| mLastRecordedEventTime = -1; |
| mLastExportDownTimeKey = 0; |
| mLastExportDownTimeMotion = 0; |
| mLastExportEventTime = -1; |
| } |
| |
| /** |
| * Reads the header of the script file. |
| * |
| * @return True if the file header could be parsed, and false otherwise. |
| * @throws IOException If there was an error reading the file. |
| */ |
| private boolean readHeader() throws IOException { |
| mFileOpened = true; |
| |
| mFStream = new FileInputStream(mScriptFileName); |
| mInputStream = new DataInputStream(mFStream); |
| mBufferedReader = new BufferedReader(new InputStreamReader(mInputStream)); |
| |
| String line; |
| |
| while ((line = mBufferedReader.readLine()) != null) { |
| line = line.trim(); |
| |
| if (line.indexOf(HEADER_COUNT) >= 0) { |
| try { |
| String value = line.substring(HEADER_COUNT.length() + 1).trim(); |
| mEventCountInScript = Integer.parseInt(value); |
| } catch (NumberFormatException e) { |
| System.err.println(e); |
| 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(HEADER_LINE_BY_LINE) >= 0) { |
| mReadScriptLineByLine = true; |
| } else if (line.indexOf(STARTING_DATA_LINE) >= 0) { |
| return true; |
| } |
| } |
| |
| 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; |
| } |
| |
| /** |
| * Reads one line and processes it. |
| * |
| * @return the number of lines read |
| * @throws IOException If there was an error reading the file. |
| */ |
| private int readOneLine() throws IOException { |
| String line = mBufferedReader.readLine(); |
| if (line == null) { |
| return 0; |
| } |
| line.trim(); |
| processLine(line); |
| return 1; |
| } |
| |
| |
| |
| /** |
| * 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) { |
| // Handle key event |
| if (s.indexOf(EVENT_KEYWORD_KEY) >= 0 && args.length == 8) { |
| try { |
| System.out.println(" old key\n"); |
| long downTime = Long.parseLong(args[0]); |
| long eventTime = Long.parseLong(args[1]); |
| int action = Integer.parseInt(args[2]); |
| int code = Integer.parseInt(args[3]); |
| int repeat = Integer.parseInt(args[4]); |
| int metaState = Integer.parseInt(args[5]); |
| int device = Integer.parseInt(args[6]); |
| int scancode = Integer.parseInt(args[7]); |
| |
| MonkeyKeyEvent e = new MonkeyKeyEvent(downTime, eventTime, action, code, repeat, |
| metaState, device, scancode); |
| System.out.println(" Key code " + code + "\n"); |
| |
| mQ.addLast(e); |
| System.out.println("Added key up \n"); |
| } catch (NumberFormatException e) { |
| } |
| return; |
| } |
| |
| // Handle trackball or pointer events |
| if ((s.indexOf(EVENT_KEYWORD_POINTER) >= 0 || s.indexOf(EVENT_KEYWORD_TRACKBALL) >= 0) |
| && args.length == 12) { |
| try { |
| long downTime = Long.parseLong(args[0]); |
| long eventTime = Long.parseLong(args[1]); |
| int action = Integer.parseInt(args[2]); |
| float x = Float.parseFloat(args[3]); |
| float y = Float.parseFloat(args[4]); |
| float pressure = Float.parseFloat(args[5]); |
| float size = Float.parseFloat(args[6]); |
| int metaState = Integer.parseInt(args[7]); |
| float xPrecision = Float.parseFloat(args[8]); |
| float yPrecision = Float.parseFloat(args[9]); |
| int device = Integer.parseInt(args[10]); |
| int edgeFlags = Integer.parseInt(args[11]); |
| |
| MonkeyMotionEvent e; |
| if (s.indexOf("Pointer") > 0) { |
| e = new MonkeyTouchEvent(action); |
| } else { |
| e = new MonkeyTrackballEvent(action); |
| } |
| |
| e.setDownTime(downTime) |
| .setEventTime(eventTime) |
| .setMetaState(metaState) |
| .setPrecision(xPrecision, yPrecision) |
| .setDeviceId(device) |
| .setEdgeFlags(edgeFlags) |
| .addPointer(0, x, y, pressure, size); |
| mQ.addLast(e); |
| } catch (NumberFormatException e) { |
| } |
| return; |
| } |
| |
| // Handle trackball or multi-touch pointer events. pointer ID is the 13th parameter |
| if ((s.indexOf(EVENT_KEYWORD_POINTER) >= 0 || s.indexOf(EVENT_KEYWORD_TRACKBALL) >= 0) |
| && args.length == 13) { |
| try { |
| long downTime = Long.parseLong(args[0]); |
| long eventTime = Long.parseLong(args[1]); |
| int action = Integer.parseInt(args[2]); |
| float x = Float.parseFloat(args[3]); |
| float y = Float.parseFloat(args[4]); |
| float pressure = Float.parseFloat(args[5]); |
| float size = Float.parseFloat(args[6]); |
| int metaState = Integer.parseInt(args[7]); |
| float xPrecision = Float.parseFloat(args[8]); |
| float yPrecision = Float.parseFloat(args[9]); |
| int device = Integer.parseInt(args[10]); |
| int edgeFlags = Integer.parseInt(args[11]); |
| int pointerId = Integer.parseInt(args[12]); |
| |
| MonkeyMotionEvent e; |
| if (s.indexOf("Pointer") > 0) { |
| if (action == MotionEvent.ACTION_POINTER_DOWN) { |
| e = new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_DOWN |
| | (pointerId << MotionEvent.ACTION_POINTER_INDEX_SHIFT)) |
| .setIntermediateNote(true); |
| } else { |
| e = new MonkeyTouchEvent(action); |
| } |
| if (mScriptStartTime < 0) { |
| mMonkeyStartTime = SystemClock.uptimeMillis(); |
| mScriptStartTime = eventTime; |
| } |
| } else { |
| e = new MonkeyTrackballEvent(action); |
| } |
| |
| if (pointerId == 1) { |
| e.setDownTime(downTime) |
| .setEventTime(eventTime) |
| .setMetaState(metaState) |
| .setPrecision(xPrecision, yPrecision) |
| .setDeviceId(device) |
| .setEdgeFlags(edgeFlags) |
| .addPointer(0, mLastX[0], mLastY[0], pressure, size) |
| .addPointer(1, x, y, pressure, size); |
| mLastX[1] = x; |
| mLastY[1] = y; |
| } else if (pointerId == 0) { |
| e.setDownTime(downTime) |
| .setEventTime(eventTime) |
| .setMetaState(metaState) |
| .setPrecision(xPrecision, yPrecision) |
| .setDeviceId(device) |
| .setEdgeFlags(edgeFlags) |
| .addPointer(0, x, y, pressure, size); |
| if(action == MotionEvent.ACTION_POINTER_UP) { |
| e.addPointer(1, mLastX[1], mLastY[1]); |
| } |
| mLastX[0] = x; |
| mLastY[0] = y; |
| } |
| |
| // Dynamically adjust waiting time to ensure that simulated evnets follow |
| // the time tap specified in the script |
| if (mReadScriptLineByLine) { |
| long curUpTime = SystemClock.uptimeMillis(); |
| long realElapsedTime = curUpTime - mMonkeyStartTime; |
| long scriptElapsedTime = eventTime - mScriptStartTime; |
| if (realElapsedTime < scriptElapsedTime) { |
| long waitDuration = scriptElapsedTime - realElapsedTime; |
| mQ.addLast(new MonkeyWaitEvent(waitDuration)); |
| } |
| } |
| mQ.addLast(e); |
| } catch (NumberFormatException e) { |
| } |
| return; |
| } |
| |
| // Handle screen rotation events |
| if ((s.indexOf(EVENT_KEYWORD_ROTATION) >= 0) && args.length == 2) { |
| try { |
| int rotationDegree = Integer.parseInt(args[0]); |
| int persist = Integer.parseInt(args[1]); |
| if ((rotationDegree == Surface.ROTATION_0) || |
| (rotationDegree == Surface.ROTATION_90) || |
| (rotationDegree == Surface.ROTATION_180) || |
| (rotationDegree == Surface.ROTATION_270)) { |
| mQ.addLast(new MonkeyRotationEvent(rotationDegree, |
| persist != 0)); |
| } |
| } catch (NumberFormatException e) { |
| } |
| return; |
| } |
| |
| // Handle tap event |
| if ((s.indexOf(EVENT_KEYWORD_TAP) >= 0) && args.length >= 2) { |
| try { |
| float x = Float.parseFloat(args[0]); |
| float y = Float.parseFloat(args[1]); |
| long tapDuration = 0; |
| if (args.length == 3) { |
| tapDuration = Long.parseLong(args[2]); |
| } |
| |
| // Set the default parameters |
| long downTime = SystemClock.uptimeMillis(); |
| MonkeyMotionEvent e1 = new MonkeyTouchEvent(MotionEvent.ACTION_DOWN) |
| .setDownTime(downTime) |
| .setEventTime(downTime) |
| .addPointer(0, x, y, 1, 5); |
| mQ.addLast(e1); |
| if (tapDuration > 0){ |
| mQ.addLast(new MonkeyWaitEvent(tapDuration)); |
| } |
| MonkeyMotionEvent e2 = new MonkeyTouchEvent(MotionEvent.ACTION_UP) |
| .setDownTime(downTime) |
| .setEventTime(downTime) |
| .addPointer(0, x, y, 1, 5); |
| mQ.addLast(e2); |
| } catch (NumberFormatException e) { |
| System.err.println("// " + e.toString()); |
| } |
| return; |
| } |
| |
| //Handle the press and hold |
| if ((s.indexOf(EVENT_KEYWORD_PRESSANDHOLD) >= 0) && args.length == 3) { |
| try { |
| float x = Float.parseFloat(args[0]); |
| float y = Float.parseFloat(args[1]); |
| long pressDuration = Long.parseLong(args[2]); |
| |
| // Set the default parameters |
| long downTime = SystemClock.uptimeMillis(); |
| |
| MonkeyMotionEvent e1 = new MonkeyTouchEvent(MotionEvent.ACTION_DOWN) |
| .setDownTime(downTime) |
| .setEventTime(downTime) |
| .addPointer(0, x, y, 1, 5); |
| MonkeyWaitEvent e2 = new MonkeyWaitEvent(pressDuration); |
| MonkeyMotionEvent e3 = new MonkeyTouchEvent(MotionEvent.ACTION_UP) |
| .setDownTime(downTime + pressDuration) |
| .setEventTime(downTime + pressDuration) |
| .addPointer(0, x, y, 1, 5); |
| mQ.addLast(e1); |
| mQ.addLast(e2); |
| mQ.addLast(e2); |
| |
| } catch (NumberFormatException e) { |
| System.err.println("// " + e.toString()); |
| } |
| return; |
| } |
| |
| // Handle drag event |
| if ((s.indexOf(EVENT_KEYWORD_DRAG) >= 0) && args.length == 5) { |
| float xStart = Float.parseFloat(args[0]); |
| float yStart = Float.parseFloat(args[1]); |
| float xEnd = Float.parseFloat(args[2]); |
| float yEnd = Float.parseFloat(args[3]); |
| int stepCount = Integer.parseInt(args[4]); |
| |
| float x = xStart; |
| float y = yStart; |
| long downTime = SystemClock.uptimeMillis(); |
| long eventTime = SystemClock.uptimeMillis(); |
| |
| if (stepCount > 0) { |
| float xStep = (xEnd - xStart) / stepCount; |
| float yStep = (yEnd - yStart) / stepCount; |
| |
| MonkeyMotionEvent e = |
| new MonkeyTouchEvent(MotionEvent.ACTION_DOWN).setDownTime(downTime) |
| .setEventTime(eventTime).addPointer(0, x, y, 1, 5); |
| mQ.addLast(e); |
| |
| for (int i = 0; i < stepCount; ++i) { |
| x += xStep; |
| y += yStep; |
| eventTime = SystemClock.uptimeMillis(); |
| e = new MonkeyTouchEvent(MotionEvent.ACTION_MOVE).setDownTime(downTime) |
| .setEventTime(eventTime).addPointer(0, x, y, 1, 5); |
| mQ.addLast(e); |
| } |
| |
| eventTime = SystemClock.uptimeMillis(); |
| e = new MonkeyTouchEvent(MotionEvent.ACTION_UP).setDownTime(downTime) |
| .setEventTime(eventTime).addPointer(0, x, y, 1, 5); |
| mQ.addLast(e); |
| } |
| } |
| |
| // Handle pinch or zoom action |
| if ((s.indexOf(EVENT_KEYWORD_PINCH_ZOOM) >= 0) && args.length == 9) { |
| //Parse the parameters |
| float pt1xStart = Float.parseFloat(args[0]); |
| float pt1yStart = Float.parseFloat(args[1]); |
| float pt1xEnd = Float.parseFloat(args[2]); |
| float pt1yEnd = Float.parseFloat(args[3]); |
| |
| float pt2xStart = Float.parseFloat(args[4]); |
| float pt2yStart = Float.parseFloat(args[5]); |
| float pt2xEnd = Float.parseFloat(args[6]); |
| float pt2yEnd = Float.parseFloat(args[7]); |
| |
| int stepCount = Integer.parseInt(args[8]); |
| |
| float x1 = pt1xStart; |
| float y1 = pt1yStart; |
| float x2 = pt2xStart; |
| float y2 = pt2yStart; |
| |
| long downTime = SystemClock.uptimeMillis(); |
| long eventTime = SystemClock.uptimeMillis(); |
| |
| if (stepCount > 0) { |
| float pt1xStep = (pt1xEnd - pt1xStart) / stepCount; |
| float pt1yStep = (pt1yEnd - pt1yStart) / stepCount; |
| |
| float pt2xStep = (pt2xEnd - pt2xStart) / stepCount; |
| float pt2yStep = (pt2yEnd - pt2yStart) / stepCount; |
| |
| mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_DOWN).setDownTime(downTime) |
| .setEventTime(eventTime).addPointer(0, x1, y1, 1, 5)); |
| |
| mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_DOWN |
| | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT)).setDownTime(downTime) |
| .addPointer(0, x1, y1).addPointer(1, x2, y2).setIntermediateNote(true)); |
| |
| for (int i = 0; i < stepCount; ++i) { |
| x1 += pt1xStep; |
| y1 += pt1yStep; |
| x2 += pt2xStep; |
| y2 += pt2yStep; |
| |
| eventTime = SystemClock.uptimeMillis(); |
| mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_MOVE).setDownTime(downTime) |
| .setEventTime(eventTime).addPointer(0, x1, y1, 1, 5).addPointer(1, x2, |
| y2, 1, 5)); |
| } |
| eventTime = SystemClock.uptimeMillis(); |
| mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_UP) |
| .setDownTime(downTime).setEventTime(eventTime).addPointer(0, x1, y1) |
| .addPointer(1, x2, y2)); |
| } |
| } |
| |
| // Handle flip events |
| if (s.indexOf(EVENT_KEYWORD_FLIP) >= 0 && args.length == 1) { |
| boolean keyboardOpen = Boolean.parseBoolean(args[0]); |
| MonkeyFlipEvent e = new MonkeyFlipEvent(keyboardOpen); |
| mQ.addLast(e); |
| } |
| |
| // Handle launch events |
| if (s.indexOf(EVENT_KEYWORD_ACTIVITY) >= 0 && args.length >= 2) { |
| String pkg_name = args[0]; |
| String cl_name = args[1]; |
| long alarmTime = 0; |
| |
| ComponentName mApp = new ComponentName(pkg_name, cl_name); |
| |
| if (args.length > 2) { |
| try { |
| alarmTime = Long.parseLong(args[2]); |
| } catch (NumberFormatException e) { |
| System.err.println("// " + e.toString()); |
| return; |
| } |
| } |
| |
| if (args.length == 2) { |
| MonkeyActivityEvent e = new MonkeyActivityEvent(mApp); |
| mQ.addLast(e); |
| } else { |
| MonkeyActivityEvent e = new MonkeyActivityEvent(mApp, alarmTime); |
| mQ.addLast(e); |
| } |
| return; |
| } |
| |
| //Handle the device wake up event |
| if (s.indexOf(EVENT_KEYWORD_DEVICE_WAKEUP) >= 0){ |
| String pkg_name = "com.google.android.powerutil"; |
| String cl_name = "com.google.android.powerutil.WakeUpScreen"; |
| long deviceSleepTime = mDeviceSleepTime; |
| |
| //Start the wakeUpScreen test activity to turn off the screen. |
| ComponentName mApp = new ComponentName(pkg_name, cl_name); |
| mQ.addLast(new MonkeyActivityEvent(mApp, deviceSleepTime)); |
| |
| //inject the special key for the wakeUpScreen test activity. |
| mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0)); |
| mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0)); |
| |
| //Add the wait event after the device sleep event so that the monkey |
| //can continue after the device wake up. |
| mQ.addLast(new MonkeyWaitEvent(deviceSleepTime + 3000)); |
| |
| //Insert the menu key to unlock the screen |
| mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)); |
| mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)); |
| |
| //Insert the back key to dismiss the test activity |
| mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)); |
| mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)); |
| |
| return; |
| } |
| |
| // Handle launch instrumentation events |
| if (s.indexOf(EVENT_KEYWORD_INSTRUMENTATION) >= 0 && args.length == 2) { |
| String test_name = args[0]; |
| String runner_name = args[1]; |
| MonkeyInstrumentationEvent e = new MonkeyInstrumentationEvent(test_name, runner_name); |
| mQ.addLast(e); |
| return; |
| } |
| |
| // Handle wait events |
| if (s.indexOf(EVENT_KEYWORD_WAIT) >= 0 && args.length == 1) { |
| try { |
| long sleeptime = Integer.parseInt(args[0]); |
| MonkeyWaitEvent e = new MonkeyWaitEvent(sleeptime); |
| mQ.addLast(e); |
| } catch (NumberFormatException e) { |
| } |
| return; |
| } |
| |
| |
| // Handle the profile wait time |
| if (s.indexOf(EVENT_KEYWORD_PROFILE_WAIT) >= 0) { |
| MonkeyWaitEvent e = new MonkeyWaitEvent(mProfileWaitTime); |
| mQ.addLast(e); |
| return; |
| } |
| |
| // Handle keypress events |
| if (s.indexOf(EVENT_KEYWORD_KEYPRESS) >= 0 && args.length == 1) { |
| String key_name = args[0]; |
| int keyCode = MonkeySourceRandom.getKeyCode(key_name); |
| if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { |
| return; |
| } |
| MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, keyCode); |
| mQ.addLast(e); |
| e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, keyCode); |
| mQ.addLast(e); |
| return; |
| } |
| |
| // Handle longpress events |
| if (s.indexOf(EVENT_KEYWORD_LONGPRESS) >= 0) { |
| MonkeyKeyEvent e; |
| e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER); |
| mQ.addLast(e); |
| MonkeyWaitEvent we = new MonkeyWaitEvent(LONGPRESS_WAIT_TIME); |
| mQ.addLast(we); |
| e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER); |
| mQ.addLast(e); |
| } |
| |
| //The power log event is mainly for the automated power framework |
| if (s.indexOf(EVENT_KEYWORD_POWERLOG) >= 0 && args.length > 0) { |
| String power_log_type = args[0]; |
| String test_case_status; |
| |
| if (args.length == 1){ |
| MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type); |
| mQ.addLast(e); |
| } else if (args.length == 2){ |
| test_case_status = args[1]; |
| MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type, test_case_status); |
| mQ.addLast(e); |
| } |
| } |
| |
| //Write power log to sdcard |
| if (s.indexOf(EVENT_KEYWORD_WRITEPOWERLOG) >= 0) { |
| MonkeyPowerEvent e = new MonkeyPowerEvent(); |
| mQ.addLast(e); |
| } |
| |
| //Run the shell command |
| if (s.indexOf(EVENT_KEYWORD_RUNCMD) >= 0 && args.length == 1) { |
| String cmd = args[0]; |
| MonkeyCommandEvent e = new MonkeyCommandEvent(cmd); |
| mQ.addLast(e); |
| } |
| |
| //Input the string through the shell command |
| if (s.indexOf(EVENT_KEYWORD_INPUT_STRING) >= 0 && args.length == 1) { |
| String input = args[0]; |
| String cmd = "input text " + input; |
| MonkeyCommandEvent e = new MonkeyCommandEvent(cmd); |
| mQ.addLast(e); |
| return; |
| } |
| |
| if (s.indexOf(EVENT_KEYWORD_START_FRAMERATE_CAPTURE) >= 0) { |
| MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("start"); |
| mQ.addLast(e); |
| return; |
| } |
| |
| if (s.indexOf(EVENT_KEYWORD_END_FRAMERATE_CAPTURE) >= 0 && args.length == 1) { |
| String input = args[0]; |
| MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("end", input); |
| mQ.addLast(e); |
| return; |
| } |
| |
| if (s.indexOf(EVENT_KEYWORD_START_APP_FRAMERATE_CAPTURE) >= 0 && args.length == 1) { |
| String app = args[0]; |
| MonkeyGetAppFrameRateEvent e = new MonkeyGetAppFrameRateEvent("start", app); |
| mQ.addLast(e); |
| return; |
| } |
| |
| if (s.indexOf(EVENT_KEYWORD_END_APP_FRAMERATE_CAPTURE) >= 0 && args.length == 2) { |
| String app = args[0]; |
| String label = args[1]; |
| MonkeyGetAppFrameRateEvent e = new MonkeyGetAppFrameRateEvent("end", app, label); |
| mQ.addLast(e); |
| return; |
| } |
| |
| |
| } |
| |
| /** |
| * Extracts an event and a list of arguments from a line. If the line does |
| * 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) { |
| return; |
| } |
| |
| String[] args = line.substring(index1 + 1, index2).split(","); |
| |
| for (int i = 0; i < args.length; i++) { |
| args[i] = args[i].trim(); |
| } |
| |
| handleEvent(line, args); |
| } |
| |
| /** |
| * Closes the script file. |
| * |
| * @throws IOException If there was an error closing the file. |
| */ |
| private void closeFile() throws IOException { |
| mFileOpened = false; |
| |
| try { |
| mFStream.close(); |
| mInputStream.close(); |
| } catch (NullPointerException e) { |
| // File was never opened so it can't be closed. |
| } |
| } |
| |
| /** |
| * 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. |
| * |
| * @throws IOException If there was an error reading the file. |
| */ |
| private void readNextBatch() throws IOException { |
| int linesRead = 0; |
| |
| if (THIS_DEBUG) { |
| System.out.println("readNextBatch(): reading next batch of events"); |
| } |
| |
| if (!mFileOpened) { |
| resetValue(); |
| readHeader(); |
| } |
| |
| if (mReadScriptLineByLine) { |
| linesRead = readOneLine(); |
| } else { |
| linesRead = readLines(); |
| } |
| |
| if (linesRead == 0) { |
| closeFile(); |
| } |
| } |
| |
| /** |
| * Sleep for a period of given time. Used to introduce latency between |
| * events. |
| * |
| * @param time The amount of time to sleep in ms |
| */ |
| private void needSleep(long time) { |
| if (time < 1) { |
| return; |
| } |
| try { |
| Thread.sleep(time); |
| } catch (InterruptedException e) { |
| } |
| } |
| |
| /** |
| * 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. |
| */ |
| @Override |
| public boolean validate() { |
| boolean validHeader; |
| try { |
| validHeader = readHeader(); |
| closeFile(); |
| } catch (IOException e) { |
| return false; |
| } |
| |
| if (mVerbose > 0) { |
| System.out.println("Replaying " + mEventCountInScript + " events with speed " + mSpeed); |
| } |
| return validHeader; |
| } |
| |
| @Override |
| public void setVerbose(int verbose) { |
| mVerbose = verbose; |
| } |
| |
| /** |
| * Adjust key downtime and eventtime according to both recorded values and |
| * current system time. |
| * |
| * @param e A KeyEvent |
| */ |
| private void adjustKeyEventTime(MonkeyKeyEvent e) { |
| if (e.getEventTime() < 0) { |
| return; |
| } |
| long thisDownTime = 0; |
| long thisEventTime = 0; |
| long expectedDelay = 0; |
| |
| if (mLastRecordedEventTime <= 0) { |
| // first time event |
| thisDownTime = SystemClock.uptimeMillis(); |
| thisEventTime = thisDownTime; |
| } else { |
| if (e.getDownTime() != mLastRecordedDownTimeKey) { |
| thisDownTime = e.getDownTime(); |
| } else { |
| thisDownTime = mLastExportDownTimeKey; |
| } |
| expectedDelay = (long) ((e.getEventTime() - mLastRecordedEventTime) * mSpeed); |
| thisEventTime = mLastExportEventTime + expectedDelay; |
| // add sleep to simulate everything in recording |
| needSleep(expectedDelay - SLEEP_COMPENSATE_DIFF); |
| } |
| mLastRecordedDownTimeKey = e.getDownTime(); |
| mLastRecordedEventTime = e.getEventTime(); |
| e.setDownTime(thisDownTime); |
| e.setEventTime(thisEventTime); |
| mLastExportDownTimeKey = thisDownTime; |
| mLastExportEventTime = thisEventTime; |
| } |
| |
| /** |
| * Adjust motion downtime and eventtime according to current system time. |
| * |
| * @param e A MotionEvent |
| */ |
| private void adjustMotionEventTime(MonkeyMotionEvent e) { |
| long thisEventTime = SystemClock.uptimeMillis(); |
| long thisDownTime = e.getDownTime(); |
| |
| if (thisDownTime == mLastRecordedDownTimeMotion) { |
| // this event is the same batch as previous one |
| e.setDownTime(mLastExportDownTimeMotion); |
| } else { |
| // this event is the start of a new batch |
| mLastRecordedDownTimeMotion = thisDownTime; |
| // update down time to match current time |
| e.setDownTime(thisEventTime); |
| mLastExportDownTimeMotion = thisEventTime; |
| } |
| // always refresh event time |
| e.setEventTime(thisEventTime); |
| } |
| |
| /** |
| * 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 event queue or null if the end of the file |
| * is reached or if an error is encountered reading the file. |
| */ |
| @Override |
| public MonkeyEvent getNextEvent() { |
| long recordedEventTime = -1; |
| MonkeyEvent ev; |
| |
| if (mQ.isEmpty()) { |
| try { |
| readNextBatch(); |
| } catch (IOException e) { |
| return null; |
| } |
| } |
| |
| try { |
| ev = mQ.getFirst(); |
| mQ.removeFirst(); |
| } catch (NoSuchElementException e) { |
| return null; |
| } |
| |
| if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) { |
| adjustKeyEventTime((MonkeyKeyEvent) ev); |
| } else if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_TOUCH |
| || ev.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) { |
| adjustMotionEventTime((MonkeyMotionEvent) ev); |
| } |
| return ev; |
| } |
| } |