Home | History | Annotate | Download | only in monkey
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.commands.monkey;
     18 
     19 import android.content.ComponentName;
     20 import android.os.SystemClock;
     21 import android.view.KeyEvent;
     22 import android.view.MotionEvent;
     23 
     24 import java.io.BufferedReader;
     25 import java.io.DataInputStream;
     26 import java.io.FileInputStream;
     27 import java.io.IOException;
     28 import java.io.InputStreamReader;
     29 import java.util.NoSuchElementException;
     30 import java.util.Random;
     31 import android.util.Log;
     32 
     33 /**
     34  * monkey event queue. It takes a script to produce events sample script format:
     35  *
     36  * <pre>
     37  * type= raw events
     38  * count= 10
     39  * speed= 1.0
     40  * start data &gt;&gt;
     41  * captureDispatchPointer(5109520,5109520,0,230.75429,458.1814,0.20784314,0.06666667,0,0.0,0.0,65539,0)
     42  * captureDispatchKey(5113146,5113146,0,20,0,0,0,0)
     43  * captureDispatchFlip(true)
     44  * ...
     45  * </pre>
     46  */
     47 public class MonkeySourceScript implements MonkeyEventSource {
     48     private int mEventCountInScript = 0; // total number of events in the file
     49 
     50     private int mVerbose = 0;
     51 
     52     private double mSpeed = 1.0;
     53 
     54     private String mScriptFileName;
     55 
     56     private MonkeyEventQueue mQ;
     57 
     58     private static final String HEADER_COUNT = "count=";
     59 
     60     private static final String HEADER_SPEED = "speed=";
     61 
     62     private long mLastRecordedDownTimeKey = 0;
     63 
     64     private long mLastRecordedDownTimeMotion = 0;
     65 
     66     private long mLastExportDownTimeKey = 0;
     67 
     68     private long mLastExportDownTimeMotion = 0;
     69 
     70     private long mLastExportEventTime = -1;
     71 
     72     private long mLastRecordedEventTime = -1;
     73 
     74     private static final boolean THIS_DEBUG = false;
     75 
     76     // a parameter that compensates the difference of real elapsed time and
     77     // time in theory
     78     private static final long SLEEP_COMPENSATE_DIFF = 16;
     79 
     80     // maximum number of events that we read at one time
     81     private static final int MAX_ONE_TIME_READS = 100;
     82 
     83     // event key word in the capture log
     84     private static final String EVENT_KEYWORD_POINTER = "DispatchPointer";
     85 
     86     private static final String EVENT_KEYWORD_TRACKBALL = "DispatchTrackball";
     87 
     88     private static final String EVENT_KEYWORD_KEY = "DispatchKey";
     89 
     90     private static final String EVENT_KEYWORD_FLIP = "DispatchFlip";
     91 
     92     private static final String EVENT_KEYWORD_KEYPRESS = "DispatchPress";
     93 
     94     private static final String EVENT_KEYWORD_ACTIVITY = "LaunchActivity";
     95 
     96     private static final String EVENT_KEYWORD_INSTRUMENTATION = "LaunchInstrumentation";
     97 
     98     private static final String EVENT_KEYWORD_WAIT = "UserWait";
     99 
    100     private static final String EVENT_KEYWORD_LONGPRESS = "LongPress";
    101 
    102     private static final String EVENT_KEYWORD_POWERLOG = "PowerLog";
    103 
    104     private static final String EVENT_KEYWORD_WRITEPOWERLOG = "WriteLog";
    105 
    106     private static final String EVENT_KEYWORD_RUNCMD = "RunCmd";
    107 
    108     private static final String EVENT_KEYWORD_TAP = "Tap";
    109 
    110     private static final String EVENT_KEYWORD_PROFILE_WAIT = "ProfileWait";
    111 
    112     private static final String EVENT_KEYWORD_DEVICE_WAKEUP = "DeviceWakeUp";
    113 
    114     private static final String EVENT_KEYWORD_INPUT_STRING = "DispatchString";
    115 
    116     private static final String EVENT_KEYWORD_PRESSANDHOLD = "PressAndHold";
    117 
    118     private static final String EVENT_KEYWORD_DRAG = "Drag";
    119 
    120     private static final String EVENT_KEYWORD_PINCH_ZOOM = "PinchZoom";
    121 
    122     private static final String EVENT_KEYWORD_START_FRAMERATE_CAPTURE = "StartCaptureFramerate";
    123 
    124     private static final String EVENT_KEYWORD_END_FRAMERATE_CAPTURE = "EndCaptureFramerate";
    125 
    126     // a line at the end of the header
    127     private static final String STARTING_DATA_LINE = "start data >>";
    128 
    129     private boolean mFileOpened = false;
    130 
    131     private static int LONGPRESS_WAIT_TIME = 2000; // wait time for the long
    132 
    133     private long mProfileWaitTime = 5000; //Wait time for each user profile
    134 
    135     private long mDeviceSleepTime = 30000; //Device sleep time
    136 
    137     FileInputStream mFStream;
    138 
    139     DataInputStream mInputStream;
    140 
    141     BufferedReader mBufferedReader;
    142 
    143     /**
    144      * Creates a MonkeySourceScript instance.
    145      *
    146      * @param filename The filename of the script (on the device).
    147      * @param throttle The amount of time in ms to sleep between events.
    148      */
    149     public MonkeySourceScript(Random random, String filename, long throttle,
    150             boolean randomizeThrottle, long profileWaitTime, long deviceSleepTime) {
    151         mScriptFileName = filename;
    152         mQ = new MonkeyEventQueue(random, throttle, randomizeThrottle);
    153         mProfileWaitTime = profileWaitTime;
    154         mDeviceSleepTime = deviceSleepTime;
    155     }
    156 
    157     /**
    158      * Resets the globals used to timeshift events.
    159      */
    160     private void resetValue() {
    161         mLastRecordedDownTimeKey = 0;
    162         mLastRecordedDownTimeMotion = 0;
    163         mLastRecordedEventTime = -1;
    164         mLastExportDownTimeKey = 0;
    165         mLastExportDownTimeMotion = 0;
    166         mLastExportEventTime = -1;
    167     }
    168 
    169     /**
    170      * Reads the header of the script file.
    171      *
    172      * @return True if the file header could be parsed, and false otherwise.
    173      * @throws IOException If there was an error reading the file.
    174      */
    175     private boolean readHeader() throws IOException {
    176         mFileOpened = true;
    177 
    178         mFStream = new FileInputStream(mScriptFileName);
    179         mInputStream = new DataInputStream(mFStream);
    180         mBufferedReader = new BufferedReader(new InputStreamReader(mInputStream));
    181 
    182         String line;
    183 
    184         while ((line = mBufferedReader.readLine()) != null) {
    185             line = line.trim();
    186 
    187             if (line.indexOf(HEADER_COUNT) >= 0) {
    188                 try {
    189                     String value = line.substring(HEADER_COUNT.length() + 1).trim();
    190                     mEventCountInScript = Integer.parseInt(value);
    191                 } catch (NumberFormatException e) {
    192                     System.err.println(e);
    193                     return false;
    194                 }
    195             } else if (line.indexOf(HEADER_SPEED) >= 0) {
    196                 try {
    197                     String value = line.substring(HEADER_COUNT.length() + 1).trim();
    198                     mSpeed = Double.parseDouble(value);
    199                 } catch (NumberFormatException e) {
    200                     System.err.println(e);
    201                     return false;
    202                 }
    203             } else if (line.indexOf(STARTING_DATA_LINE) >= 0) {
    204                 return true;
    205             }
    206         }
    207 
    208         return false;
    209     }
    210 
    211     /**
    212      * Reads a number of lines and passes the lines to be processed.
    213      *
    214      * @return The number of lines read.
    215      * @throws IOException If there was an error reading the file.
    216      */
    217     private int readLines() throws IOException {
    218         String line;
    219         for (int i = 0; i < MAX_ONE_TIME_READS; i++) {
    220             line = mBufferedReader.readLine();
    221             if (line == null) {
    222                 return i;
    223             }
    224             line.trim();
    225             processLine(line);
    226         }
    227         return MAX_ONE_TIME_READS;
    228     }
    229 
    230 
    231 
    232 
    233     /**
    234      * Creates an event and adds it to the event queue. If the parameters are
    235      * not understood, they are ignored and no events are added.
    236      *
    237      * @param s The entire string from the script file.
    238      * @param args An array of arguments extracted from the script file line.
    239      */
    240     private void handleEvent(String s, String[] args) {
    241         // Handle key event
    242         if (s.indexOf(EVENT_KEYWORD_KEY) >= 0 && args.length == 8) {
    243             try {
    244                 System.out.println(" old key\n");
    245                 long downTime = Long.parseLong(args[0]);
    246                 long eventTime = Long.parseLong(args[1]);
    247                 int action = Integer.parseInt(args[2]);
    248                 int code = Integer.parseInt(args[3]);
    249                 int repeat = Integer.parseInt(args[4]);
    250                 int metaState = Integer.parseInt(args[5]);
    251                 int device = Integer.parseInt(args[6]);
    252                 int scancode = Integer.parseInt(args[7]);
    253 
    254                 MonkeyKeyEvent e = new MonkeyKeyEvent(downTime, eventTime, action, code, repeat,
    255                         metaState, device, scancode);
    256                 System.out.println(" Key code " + code + "\n");
    257 
    258                 mQ.addLast(e);
    259                 System.out.println("Added key up \n");
    260             } catch (NumberFormatException e) {
    261             }
    262             return;
    263         }
    264 
    265         // Handle trackball or pointer events
    266         if ((s.indexOf(EVENT_KEYWORD_POINTER) >= 0 || s.indexOf(EVENT_KEYWORD_TRACKBALL) >= 0)
    267                 && args.length == 12) {
    268             try {
    269                 long downTime = Long.parseLong(args[0]);
    270                 long eventTime = Long.parseLong(args[1]);
    271                 int action = Integer.parseInt(args[2]);
    272                 float x = Float.parseFloat(args[3]);
    273                 float y = Float.parseFloat(args[4]);
    274                 float pressure = Float.parseFloat(args[5]);
    275                 float size = Float.parseFloat(args[6]);
    276                 int metaState = Integer.parseInt(args[7]);
    277                 float xPrecision = Float.parseFloat(args[8]);
    278                 float yPrecision = Float.parseFloat(args[9]);
    279                 int device = Integer.parseInt(args[10]);
    280                 int edgeFlags = Integer.parseInt(args[11]);
    281 
    282                 MonkeyMotionEvent e;
    283                 if (s.indexOf("Pointer") > 0) {
    284                     e = new MonkeyTouchEvent(action);
    285                 } else {
    286                     e = new MonkeyTrackballEvent(action);
    287                 }
    288 
    289                 e.setDownTime(downTime)
    290                         .setEventTime(eventTime)
    291                         .setMetaState(metaState)
    292                         .setPrecision(xPrecision, yPrecision)
    293                         .setDeviceId(device)
    294                         .setEdgeFlags(edgeFlags)
    295                         .addPointer(0, x, y, pressure, size);
    296                 mQ.addLast(e);
    297             } catch (NumberFormatException e) {
    298             }
    299             return;
    300         }
    301 
    302         // Handle tap event
    303         if ((s.indexOf(EVENT_KEYWORD_TAP) >= 0) && args.length >= 2) {
    304             try {
    305                 float x = Float.parseFloat(args[0]);
    306                 float y = Float.parseFloat(args[1]);
    307                 long tapDuration = 0;
    308                 if (args.length == 3) {
    309                     tapDuration = Long.parseLong(args[2]);
    310                 }
    311 
    312                 // Set the default parameters
    313                 long downTime = SystemClock.uptimeMillis();
    314                 MonkeyMotionEvent e1 = new MonkeyTouchEvent(MotionEvent.ACTION_DOWN)
    315                         .setDownTime(downTime)
    316                         .setEventTime(downTime)
    317                         .addPointer(0, x, y, 1, 5);
    318                 mQ.addLast(e1);
    319                 if (tapDuration > 0){
    320                     mQ.addLast(new MonkeyWaitEvent(tapDuration));
    321                 }
    322                 MonkeyMotionEvent e2 = new MonkeyTouchEvent(MotionEvent.ACTION_UP)
    323                         .setDownTime(downTime)
    324                         .setEventTime(downTime)
    325                         .addPointer(0, x, y, 1, 5);
    326                 mQ.addLast(e2);
    327             } catch (NumberFormatException e) {
    328                 System.err.println("// " + e.toString());
    329             }
    330             return;
    331         }
    332 
    333         //Handle the press and hold
    334         if ((s.indexOf(EVENT_KEYWORD_PRESSANDHOLD) >= 0) && args.length == 3) {
    335             try {
    336                 float x = Float.parseFloat(args[0]);
    337                 float y = Float.parseFloat(args[1]);
    338                 long pressDuration = Long.parseLong(args[2]);
    339 
    340                 // Set the default parameters
    341                 long downTime = SystemClock.uptimeMillis();
    342 
    343                 MonkeyMotionEvent e1 = new MonkeyTouchEvent(MotionEvent.ACTION_DOWN)
    344                         .setDownTime(downTime)
    345                         .setEventTime(downTime)
    346                         .addPointer(0, x, y, 1, 5);
    347                 MonkeyWaitEvent e2 = new MonkeyWaitEvent(pressDuration);
    348                 MonkeyMotionEvent e3 = new MonkeyTouchEvent(MotionEvent.ACTION_UP)
    349                         .setDownTime(downTime + pressDuration)
    350                         .setEventTime(downTime + pressDuration)
    351                         .addPointer(0, x, y, 1, 5);
    352                 mQ.addLast(e1);
    353                 mQ.addLast(e2);
    354                 mQ.addLast(e2);
    355 
    356             } catch (NumberFormatException e) {
    357                 System.err.println("// " + e.toString());
    358             }
    359             return;
    360         }
    361 
    362         // Handle drag event
    363         if ((s.indexOf(EVENT_KEYWORD_DRAG) >= 0) && args.length == 5) {
    364             float xStart = Float.parseFloat(args[0]);
    365             float yStart = Float.parseFloat(args[1]);
    366             float xEnd = Float.parseFloat(args[2]);
    367             float yEnd = Float.parseFloat(args[3]);
    368             int stepCount = Integer.parseInt(args[4]);
    369 
    370             float x = xStart;
    371             float y = yStart;
    372             long downTime = SystemClock.uptimeMillis();
    373             long eventTime = SystemClock.uptimeMillis();
    374 
    375             if (stepCount > 0) {
    376                 float xStep = (xEnd - xStart) / stepCount;
    377                 float yStep = (yEnd - yStart) / stepCount;
    378 
    379                 MonkeyMotionEvent e =
    380                         new MonkeyTouchEvent(MotionEvent.ACTION_DOWN).setDownTime(downTime)
    381                                 .setEventTime(eventTime).addPointer(0, x, y, 1, 5);
    382                 mQ.addLast(e);
    383 
    384                 for (int i = 0; i < stepCount; ++i) {
    385                     x += xStep;
    386                     y += yStep;
    387                     eventTime = SystemClock.uptimeMillis();
    388                     e = new MonkeyTouchEvent(MotionEvent.ACTION_MOVE).setDownTime(downTime)
    389                         .setEventTime(eventTime).addPointer(0, x, y, 1, 5);
    390                     mQ.addLast(e);
    391                 }
    392 
    393                 eventTime = SystemClock.uptimeMillis();
    394                 e = new MonkeyTouchEvent(MotionEvent.ACTION_UP).setDownTime(downTime)
    395                     .setEventTime(eventTime).addPointer(0, x, y, 1, 5);
    396                 mQ.addLast(e);
    397             }
    398         }
    399 
    400         // Handle pinch or zoom action
    401         if ((s.indexOf(EVENT_KEYWORD_PINCH_ZOOM) >= 0) && args.length == 9) {
    402             //Parse the parameters
    403             float pt1xStart = Float.parseFloat(args[0]);
    404             float pt1yStart = Float.parseFloat(args[1]);
    405             float pt1xEnd = Float.parseFloat(args[2]);
    406             float pt1yEnd = Float.parseFloat(args[3]);
    407 
    408             float pt2xStart = Float.parseFloat(args[4]);
    409             float pt2yStart = Float.parseFloat(args[5]);
    410             float pt2xEnd = Float.parseFloat(args[6]);
    411             float pt2yEnd = Float.parseFloat(args[7]);
    412 
    413             int stepCount = Integer.parseInt(args[8]);
    414 
    415             float x1 = pt1xStart;
    416             float y1 = pt1yStart;
    417             float x2 = pt2xStart;
    418             float y2 = pt2yStart;
    419 
    420             long downTime = SystemClock.uptimeMillis();
    421             long eventTime = SystemClock.uptimeMillis();
    422 
    423             if (stepCount > 0) {
    424                 float pt1xStep = (pt1xEnd - pt1xStart) / stepCount;
    425                 float pt1yStep = (pt1yEnd - pt1yStart) / stepCount;
    426 
    427                 float pt2xStep = (pt2xEnd - pt2xStart) / stepCount;
    428                 float pt2yStep = (pt2yEnd - pt2yStart) / stepCount;
    429 
    430                 mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_DOWN).setDownTime(downTime)
    431                         .setEventTime(eventTime).addPointer(0, x1, y1, 1, 5));
    432 
    433                 mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_DOWN
    434                         | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT)).setDownTime(downTime)
    435                         .addPointer(0, x1, y1).addPointer(1, x2, y2).setIntermediateNote(true));
    436 
    437                 for (int i = 0; i < stepCount; ++i) {
    438                     x1 += pt1xStep;
    439                     y1 += pt1yStep;
    440                     x2 += pt2xStep;
    441                     y2 += pt2yStep;
    442 
    443                     eventTime = SystemClock.uptimeMillis();
    444                     mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_MOVE).setDownTime(downTime)
    445                             .setEventTime(eventTime).addPointer(0, x1, y1, 1, 5).addPointer(1, x2,
    446                                     y2, 1, 5));
    447                 }
    448                 eventTime = SystemClock.uptimeMillis();
    449                 mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_UP)
    450                         .setDownTime(downTime).setEventTime(eventTime).addPointer(0, x1, y1)
    451                         .addPointer(1, x2, y2));
    452             }
    453         }
    454 
    455         // Handle flip events
    456         if (s.indexOf(EVENT_KEYWORD_FLIP) >= 0 && args.length == 1) {
    457             boolean keyboardOpen = Boolean.parseBoolean(args[0]);
    458             MonkeyFlipEvent e = new MonkeyFlipEvent(keyboardOpen);
    459             mQ.addLast(e);
    460         }
    461 
    462         // Handle launch events
    463         if (s.indexOf(EVENT_KEYWORD_ACTIVITY) >= 0 && args.length >= 2) {
    464             String pkg_name = args[0];
    465             String cl_name = args[1];
    466             long alarmTime = 0;
    467 
    468             ComponentName mApp = new ComponentName(pkg_name, cl_name);
    469 
    470             if (args.length > 2) {
    471                 try {
    472                     alarmTime = Long.parseLong(args[2]);
    473                 } catch (NumberFormatException e) {
    474                     System.err.println("// " + e.toString());
    475                     return;
    476                 }
    477             }
    478 
    479             if (args.length == 2) {
    480                 MonkeyActivityEvent e = new MonkeyActivityEvent(mApp);
    481                 mQ.addLast(e);
    482             } else {
    483                 MonkeyActivityEvent e = new MonkeyActivityEvent(mApp, alarmTime);
    484                 mQ.addLast(e);
    485             }
    486             return;
    487         }
    488 
    489         //Handle the device wake up event
    490         if (s.indexOf(EVENT_KEYWORD_DEVICE_WAKEUP) >= 0){
    491             String pkg_name = "com.google.android.powerutil";
    492             String cl_name = "com.google.android.powerutil.WakeUpScreen";
    493             long deviceSleepTime = mDeviceSleepTime;
    494 
    495             //Start the wakeUpScreen test activity to turn off the screen.
    496             ComponentName mApp = new ComponentName(pkg_name, cl_name);
    497             mQ.addLast(new MonkeyActivityEvent(mApp, deviceSleepTime));
    498 
    499             //inject the special key for the wakeUpScreen test activity.
    500             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0));
    501             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0));
    502 
    503             //Add the wait event after the device sleep event so that the monkey
    504             //can continue after the device wake up.
    505             mQ.addLast(new MonkeyWaitEvent(deviceSleepTime + 3000));
    506 
    507             //Insert the menu key to unlock the screen
    508             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
    509             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU));
    510 
    511             //Insert the back key to dismiss the test activity
    512             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
    513             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK));
    514 
    515             return;
    516         }
    517 
    518        // Handle launch instrumentation events
    519         if (s.indexOf(EVENT_KEYWORD_INSTRUMENTATION) >= 0 && args.length == 2) {
    520             String test_name = args[0];
    521             String runner_name = args[1];
    522             MonkeyInstrumentationEvent e = new MonkeyInstrumentationEvent(test_name, runner_name);
    523             mQ.addLast(e);
    524             return;
    525         }
    526 
    527         // Handle wait events
    528         if (s.indexOf(EVENT_KEYWORD_WAIT) >= 0 && args.length == 1) {
    529             try {
    530                 long sleeptime = Integer.parseInt(args[0]);
    531                 MonkeyWaitEvent e = new MonkeyWaitEvent(sleeptime);
    532                 mQ.addLast(e);
    533             } catch (NumberFormatException e) {
    534             }
    535             return;
    536         }
    537 
    538 
    539         // Handle the profile wait time
    540         if (s.indexOf(EVENT_KEYWORD_PROFILE_WAIT) >= 0) {
    541             MonkeyWaitEvent e = new MonkeyWaitEvent(mProfileWaitTime);
    542             mQ.addLast(e);
    543             return;
    544         }
    545 
    546         // Handle keypress events
    547         if (s.indexOf(EVENT_KEYWORD_KEYPRESS) >= 0 && args.length == 1) {
    548             String key_name = args[0];
    549             int keyCode = MonkeySourceRandom.getKeyCode(key_name);
    550             MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, keyCode);
    551             mQ.addLast(e);
    552             e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, keyCode);
    553             mQ.addLast(e);
    554             return;
    555         }
    556 
    557         // Handle longpress events
    558         if (s.indexOf(EVENT_KEYWORD_LONGPRESS) >= 0) {
    559             MonkeyKeyEvent e;
    560             e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);
    561             mQ.addLast(e);
    562             MonkeyWaitEvent we = new MonkeyWaitEvent(LONGPRESS_WAIT_TIME);
    563             mQ.addLast(we);
    564             e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER);
    565             mQ.addLast(e);
    566         }
    567 
    568         //The power log event is mainly for the automated power framework
    569         if (s.indexOf(EVENT_KEYWORD_POWERLOG) >= 0 && args.length > 0) {
    570             String power_log_type = args[0];
    571             String test_case_status;
    572 
    573             if (args.length == 1){
    574                 MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type);
    575                 mQ.addLast(e);
    576             } else if (args.length == 2){
    577                 test_case_status = args[1];
    578                 MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type, test_case_status);
    579                 mQ.addLast(e);
    580             }
    581         }
    582 
    583         //Write power log to sdcard
    584         if (s.indexOf(EVENT_KEYWORD_WRITEPOWERLOG) >= 0) {
    585             MonkeyPowerEvent e = new MonkeyPowerEvent();
    586             mQ.addLast(e);
    587         }
    588 
    589       //Run the shell command
    590         if (s.indexOf(EVENT_KEYWORD_RUNCMD) >= 0 && args.length == 1) {
    591             String cmd = args[0];
    592             MonkeyCommandEvent e = new MonkeyCommandEvent(cmd);
    593             mQ.addLast(e);
    594         }
    595 
    596         //Input the string through the shell command
    597         if (s.indexOf(EVENT_KEYWORD_INPUT_STRING) >= 0 && args.length == 1) {
    598             String input = args[0];
    599             String cmd = "input text " + input;
    600             MonkeyCommandEvent e = new MonkeyCommandEvent(cmd);
    601             mQ.addLast(e);
    602             return;
    603         }
    604 
    605         if (s.indexOf(EVENT_KEYWORD_START_FRAMERATE_CAPTURE) >= 0) {
    606             MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("start");
    607             mQ.addLast(e);
    608             return;
    609         }
    610 
    611         if (s.indexOf(EVENT_KEYWORD_END_FRAMERATE_CAPTURE) >= 0 && args.length == 1) {
    612             String input = args[0];
    613             MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("end", input);
    614             mQ.addLast(e);
    615             return;
    616         }
    617 
    618 
    619 
    620     }
    621 
    622     /**
    623      * Extracts an event and a list of arguments from a line. If the line does
    624      * not match the format required, it is ignored.
    625      *
    626      * @param line A string in the form {@code cmd(arg1,arg2,arg3)}.
    627      */
    628     private void processLine(String line) {
    629         int index1 = line.indexOf('(');
    630         int index2 = line.indexOf(')');
    631 
    632         if (index1 < 0 || index2 < 0) {
    633             return;
    634         }
    635 
    636         String[] args = line.substring(index1 + 1, index2).split(",");
    637 
    638         for (int i = 0; i < args.length; i++) {
    639             args[i] = args[i].trim();
    640         }
    641 
    642         handleEvent(line, args);
    643     }
    644 
    645     /**
    646      * Closes the script file.
    647      *
    648      * @throws IOException If there was an error closing the file.
    649      */
    650     private void closeFile() throws IOException {
    651         mFileOpened = false;
    652 
    653         try {
    654             mFStream.close();
    655             mInputStream.close();
    656         } catch (NullPointerException e) {
    657             // File was never opened so it can't be closed.
    658         }
    659     }
    660 
    661     /**
    662      * Read next batch of events from the script file into the event queue.
    663      * Checks if the script is open and then reads the next MAX_ONE_TIME_READS
    664      * events or reads until the end of the file. If no events are read, then
    665      * the script is closed.
    666      *
    667      * @throws IOException If there was an error reading the file.
    668      */
    669     private void readNextBatch() throws IOException {
    670         int linesRead = 0;
    671 
    672         if (THIS_DEBUG) {
    673             System.out.println("readNextBatch(): reading next batch of events");
    674         }
    675 
    676         if (!mFileOpened) {
    677             resetValue();
    678             readHeader();
    679         }
    680 
    681         linesRead = readLines();
    682 
    683         if (linesRead == 0) {
    684             closeFile();
    685         }
    686     }
    687 
    688     /**
    689      * Sleep for a period of given time. Used to introduce latency between
    690      * events.
    691      *
    692      * @param time The amount of time to sleep in ms
    693      */
    694     private void needSleep(long time) {
    695         if (time < 1) {
    696             return;
    697         }
    698         try {
    699             Thread.sleep(time);
    700         } catch (InterruptedException e) {
    701         }
    702     }
    703 
    704     /**
    705      * Checks if the file can be opened and if the header is valid.
    706      *
    707      * @return True if the file exists and the header is valid, false otherwise.
    708      */
    709     public boolean validate() {
    710         boolean validHeader;
    711         try {
    712             validHeader = readHeader();
    713             closeFile();
    714         } catch (IOException e) {
    715             return false;
    716         }
    717 
    718         if (mVerbose > 0) {
    719             System.out.println("Replaying " + mEventCountInScript + " events with speed " + mSpeed);
    720         }
    721         return validHeader;
    722     }
    723 
    724     public void setVerbose(int verbose) {
    725         mVerbose = verbose;
    726     }
    727 
    728     /**
    729      * Adjust key downtime and eventtime according to both recorded values and
    730      * current system time.
    731      *
    732      * @param e A KeyEvent
    733      */
    734     private void adjustKeyEventTime(MonkeyKeyEvent e) {
    735         if (e.getEventTime() < 0) {
    736             return;
    737         }
    738         long thisDownTime = 0;
    739         long thisEventTime = 0;
    740         long expectedDelay = 0;
    741 
    742         if (mLastRecordedEventTime <= 0) {
    743             // first time event
    744             thisDownTime = SystemClock.uptimeMillis();
    745             thisEventTime = thisDownTime;
    746         } else {
    747             if (e.getDownTime() != mLastRecordedDownTimeKey) {
    748                 thisDownTime = e.getDownTime();
    749             } else {
    750                 thisDownTime = mLastExportDownTimeKey;
    751             }
    752             expectedDelay = (long) ((e.getEventTime() - mLastRecordedEventTime) * mSpeed);
    753             thisEventTime = mLastExportEventTime + expectedDelay;
    754             // add sleep to simulate everything in recording
    755             needSleep(expectedDelay - SLEEP_COMPENSATE_DIFF);
    756         }
    757         mLastRecordedDownTimeKey = e.getDownTime();
    758         mLastRecordedEventTime = e.getEventTime();
    759         e.setDownTime(thisDownTime);
    760         e.setEventTime(thisEventTime);
    761         mLastExportDownTimeKey = thisDownTime;
    762         mLastExportEventTime = thisEventTime;
    763     }
    764 
    765     /**
    766      * Adjust motion downtime and eventtime according to current system time.
    767      *
    768      * @param e A KeyEvent
    769      */
    770     private void adjustMotionEventTime(MonkeyMotionEvent e) {
    771         long updatedDownTime = 0;
    772 
    773         if (e.getEventTime() < 0) {
    774             return;
    775         }
    776         updatedDownTime = SystemClock.uptimeMillis();
    777         e.setDownTime(updatedDownTime);
    778         e.setEventTime(updatedDownTime);
    779     }
    780 
    781     /**
    782      * Gets the next event to be injected from the script. If the event queue is
    783      * empty, reads the next n events from the script into the queue, where n is
    784      * the lesser of the number of remaining events and the value specified by
    785      * MAX_ONE_TIME_READS. If the end of the file is reached, no events are
    786      * added to the queue and null is returned.
    787      *
    788      * @return The first event in the event queue or null if the end of the file
    789      *         is reached or if an error is encountered reading the file.
    790      */
    791     public MonkeyEvent getNextEvent() {
    792         long recordedEventTime = -1;
    793         MonkeyEvent ev;
    794 
    795         if (mQ.isEmpty()) {
    796             try {
    797                 readNextBatch();
    798             } catch (IOException e) {
    799                 return null;
    800             }
    801         }
    802 
    803         try {
    804             ev = mQ.getFirst();
    805             mQ.removeFirst();
    806         } catch (NoSuchElementException e) {
    807             return null;
    808         }
    809 
    810         if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) {
    811             adjustKeyEventTime((MonkeyKeyEvent) ev);
    812         } else if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_TOUCH
    813                 || ev.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) {
    814             adjustMotionEventTime((MonkeyMotionEvent) ev);
    815         }
    816         return ev;
    817     }
    818 }
    819