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 import android.view.Surface;
     24 
     25 import java.io.BufferedReader;
     26 import java.io.DataInputStream;
     27 import java.io.FileInputStream;
     28 import java.io.IOException;
     29 import java.io.InputStreamReader;
     30 import java.util.NoSuchElementException;
     31 import java.util.Random;
     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     // process scripts in line-by-line mode (true) or batch processing mode (false)
     75     private boolean mReadScriptLineByLine = false;
     76 
     77     private static final boolean THIS_DEBUG = false;
     78 
     79     // a parameter that compensates the difference of real elapsed time and
     80     // time in theory
     81     private static final long SLEEP_COMPENSATE_DIFF = 16;
     82 
     83     // if this header is present, scripts are read and processed in line-by-line mode
     84     private static final String HEADER_LINE_BY_LINE = "linebyline";
     85 
     86     // maximum number of events that we read at one time
     87     private static final int MAX_ONE_TIME_READS = 100;
     88 
     89     // event key word in the capture log
     90     private static final String EVENT_KEYWORD_POINTER = "DispatchPointer";
     91 
     92     private static final String EVENT_KEYWORD_TRACKBALL = "DispatchTrackball";
     93 
     94     private static final String EVENT_KEYWORD_ROTATION = "RotateScreen";
     95 
     96     private static final String EVENT_KEYWORD_KEY = "DispatchKey";
     97 
     98     private static final String EVENT_KEYWORD_FLIP = "DispatchFlip";
     99 
    100     private static final String EVENT_KEYWORD_KEYPRESS = "DispatchPress";
    101 
    102     private static final String EVENT_KEYWORD_ACTIVITY = "LaunchActivity";
    103 
    104     private static final String EVENT_KEYWORD_INSTRUMENTATION = "LaunchInstrumentation";
    105 
    106     private static final String EVENT_KEYWORD_WAIT = "UserWait";
    107 
    108     private static final String EVENT_KEYWORD_LONGPRESS = "LongPress";
    109 
    110     private static final String EVENT_KEYWORD_POWERLOG = "PowerLog";
    111 
    112     private static final String EVENT_KEYWORD_WRITEPOWERLOG = "WriteLog";
    113 
    114     private static final String EVENT_KEYWORD_RUNCMD = "RunCmd";
    115 
    116     private static final String EVENT_KEYWORD_TAP = "Tap";
    117 
    118     private static final String EVENT_KEYWORD_PROFILE_WAIT = "ProfileWait";
    119 
    120     private static final String EVENT_KEYWORD_DEVICE_WAKEUP = "DeviceWakeUp";
    121 
    122     private static final String EVENT_KEYWORD_INPUT_STRING = "DispatchString";
    123 
    124     private static final String EVENT_KEYWORD_PRESSANDHOLD = "PressAndHold";
    125 
    126     private static final String EVENT_KEYWORD_DRAG = "Drag";
    127 
    128     private static final String EVENT_KEYWORD_PINCH_ZOOM = "PinchZoom";
    129 
    130     private static final String EVENT_KEYWORD_START_FRAMERATE_CAPTURE = "StartCaptureFramerate";
    131 
    132     private static final String EVENT_KEYWORD_END_FRAMERATE_CAPTURE = "EndCaptureFramerate";
    133 
    134     private static final String EVENT_KEYWORD_START_APP_FRAMERATE_CAPTURE =
    135             "StartCaptureAppFramerate";
    136 
    137     private static final String EVENT_KEYWORD_END_APP_FRAMERATE_CAPTURE = "EndCaptureAppFramerate";
    138 
    139     // a line at the end of the header
    140     private static final String STARTING_DATA_LINE = "start data >>";
    141 
    142     private boolean mFileOpened = false;
    143 
    144     private static int LONGPRESS_WAIT_TIME = 2000; // wait time for the long
    145 
    146     private long mProfileWaitTime = 5000; //Wait time for each user profile
    147 
    148     private long mDeviceSleepTime = 30000; //Device sleep time
    149 
    150     FileInputStream mFStream;
    151 
    152     DataInputStream mInputStream;
    153 
    154     BufferedReader mBufferedReader;
    155 
    156     // X and Y coordincates of last touch event. Array Index is the pointerId
    157     private float mLastX[] = new float[2];
    158 
    159     private float mLastY[] = new float[2];
    160 
    161     private long mScriptStartTime = -1;
    162 
    163     private long mMonkeyStartTime = -1;
    164 
    165     /**
    166      * Creates a MonkeySourceScript instance.
    167      *
    168      * @param filename The filename of the script (on the device).
    169      * @param throttle The amount of time in ms to sleep between events.
    170      */
    171     public MonkeySourceScript(Random random, String filename, long throttle,
    172             boolean randomizeThrottle, long profileWaitTime, long deviceSleepTime) {
    173         mScriptFileName = filename;
    174         mQ = new MonkeyEventQueue(random, throttle, randomizeThrottle);
    175         mProfileWaitTime = profileWaitTime;
    176         mDeviceSleepTime = deviceSleepTime;
    177     }
    178 
    179     /**
    180      * Resets the globals used to timeshift events.
    181      */
    182     private void resetValue() {
    183         mLastRecordedDownTimeKey = 0;
    184         mLastRecordedDownTimeMotion = 0;
    185         mLastRecordedEventTime = -1;
    186         mLastExportDownTimeKey = 0;
    187         mLastExportDownTimeMotion = 0;
    188         mLastExportEventTime = -1;
    189     }
    190 
    191     /**
    192      * Reads the header of the script file.
    193      *
    194      * @return True if the file header could be parsed, and false otherwise.
    195      * @throws IOException If there was an error reading the file.
    196      */
    197     private boolean readHeader() throws IOException {
    198         mFileOpened = true;
    199 
    200         mFStream = new FileInputStream(mScriptFileName);
    201         mInputStream = new DataInputStream(mFStream);
    202         mBufferedReader = new BufferedReader(new InputStreamReader(mInputStream));
    203 
    204         String line;
    205 
    206         while ((line = mBufferedReader.readLine()) != null) {
    207             line = line.trim();
    208 
    209             if (line.indexOf(HEADER_COUNT) >= 0) {
    210                 try {
    211                     String value = line.substring(HEADER_COUNT.length() + 1).trim();
    212                     mEventCountInScript = Integer.parseInt(value);
    213                 } catch (NumberFormatException e) {
    214                     Logger.err.println("" + e);
    215                     return false;
    216                 }
    217             } else if (line.indexOf(HEADER_SPEED) >= 0) {
    218                 try {
    219                     String value = line.substring(HEADER_COUNT.length() + 1).trim();
    220                     mSpeed = Double.parseDouble(value);
    221                 } catch (NumberFormatException e) {
    222                     Logger.err.println("" + e);
    223                     return false;
    224                 }
    225             } else if (line.indexOf(HEADER_LINE_BY_LINE) >= 0) {
    226                 mReadScriptLineByLine = true;
    227             } else if (line.indexOf(STARTING_DATA_LINE) >= 0) {
    228                 return true;
    229             }
    230         }
    231 
    232         return false;
    233     }
    234 
    235     /**
    236      * Reads a number of lines and passes the lines to be processed.
    237      *
    238      * @return The number of lines read.
    239      * @throws IOException If there was an error reading the file.
    240      */
    241     private int readLines() throws IOException {
    242         String line;
    243         for (int i = 0; i < MAX_ONE_TIME_READS; i++) {
    244             line = mBufferedReader.readLine();
    245             if (line == null) {
    246                 return i;
    247             }
    248             line.trim();
    249             processLine(line);
    250         }
    251         return MAX_ONE_TIME_READS;
    252     }
    253 
    254      /**
    255       * Reads one line and processes it.
    256       *
    257       * @return the number of lines read
    258       * @throws IOException If there was an error reading the file.
    259       */
    260     private int readOneLine() throws IOException {
    261         String line = mBufferedReader.readLine();
    262         if (line == null) {
    263             return 0;
    264         }
    265         line.trim();
    266         processLine(line);
    267         return 1;
    268     }
    269 
    270 
    271 
    272     /**
    273      * Creates an event and adds it to the event queue. If the parameters are
    274      * not understood, they are ignored and no events are added.
    275      *
    276      * @param s The entire string from the script file.
    277      * @param args An array of arguments extracted from the script file line.
    278      */
    279     private void handleEvent(String s, String[] args) {
    280         // Handle key event
    281         if (s.indexOf(EVENT_KEYWORD_KEY) >= 0 && args.length == 8) {
    282             try {
    283                 Logger.out.println(" old key\n");
    284                 long downTime = Long.parseLong(args[0]);
    285                 long eventTime = Long.parseLong(args[1]);
    286                 int action = Integer.parseInt(args[2]);
    287                 int code = Integer.parseInt(args[3]);
    288                 int repeat = Integer.parseInt(args[4]);
    289                 int metaState = Integer.parseInt(args[5]);
    290                 int device = Integer.parseInt(args[6]);
    291                 int scancode = Integer.parseInt(args[7]);
    292 
    293                 MonkeyKeyEvent e = new MonkeyKeyEvent(downTime, eventTime, action, code, repeat,
    294                         metaState, device, scancode);
    295                 Logger.out.println(" Key code " + code + "\n");
    296 
    297                 mQ.addLast(e);
    298                 Logger.out.println("Added key up \n");
    299             } catch (NumberFormatException e) {
    300             }
    301             return;
    302         }
    303 
    304         // Handle trackball or pointer events
    305         if ((s.indexOf(EVENT_KEYWORD_POINTER) >= 0 || s.indexOf(EVENT_KEYWORD_TRACKBALL) >= 0)
    306                 && args.length == 12) {
    307             try {
    308                 long downTime = Long.parseLong(args[0]);
    309                 long eventTime = Long.parseLong(args[1]);
    310                 int action = Integer.parseInt(args[2]);
    311                 float x = Float.parseFloat(args[3]);
    312                 float y = Float.parseFloat(args[4]);
    313                 float pressure = Float.parseFloat(args[5]);
    314                 float size = Float.parseFloat(args[6]);
    315                 int metaState = Integer.parseInt(args[7]);
    316                 float xPrecision = Float.parseFloat(args[8]);
    317                 float yPrecision = Float.parseFloat(args[9]);
    318                 int device = Integer.parseInt(args[10]);
    319                 int edgeFlags = Integer.parseInt(args[11]);
    320 
    321                 MonkeyMotionEvent e;
    322                 if (s.indexOf("Pointer") > 0) {
    323                     e = new MonkeyTouchEvent(action);
    324                 } else {
    325                     e = new MonkeyTrackballEvent(action);
    326                 }
    327 
    328                 e.setDownTime(downTime)
    329                         .setEventTime(eventTime)
    330                         .setMetaState(metaState)
    331                         .setPrecision(xPrecision, yPrecision)
    332                         .setDeviceId(device)
    333                         .setEdgeFlags(edgeFlags)
    334                         .addPointer(0, x, y, pressure, size);
    335                 mQ.addLast(e);
    336             } catch (NumberFormatException e) {
    337             }
    338             return;
    339         }
    340 
    341         // Handle trackball or multi-touch  pointer events. pointer ID is the 13th parameter
    342         if ((s.indexOf(EVENT_KEYWORD_POINTER) >= 0 || s.indexOf(EVENT_KEYWORD_TRACKBALL) >= 0)
    343                 && args.length == 13) {
    344             try {
    345                 long downTime = Long.parseLong(args[0]);
    346                 long eventTime = Long.parseLong(args[1]);
    347                 int action = Integer.parseInt(args[2]);
    348                 float x = Float.parseFloat(args[3]);
    349                 float y = Float.parseFloat(args[4]);
    350                 float pressure = Float.parseFloat(args[5]);
    351                 float size = Float.parseFloat(args[6]);
    352                 int metaState = Integer.parseInt(args[7]);
    353                 float xPrecision = Float.parseFloat(args[8]);
    354                 float yPrecision = Float.parseFloat(args[9]);
    355                 int device = Integer.parseInt(args[10]);
    356                 int edgeFlags = Integer.parseInt(args[11]);
    357                 int pointerId = Integer.parseInt(args[12]);
    358 
    359                 MonkeyMotionEvent e;
    360                 if (s.indexOf("Pointer") > 0) {
    361                     if (action == MotionEvent.ACTION_POINTER_DOWN) {
    362                         e = new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_DOWN
    363                                 | (pointerId << MotionEvent.ACTION_POINTER_INDEX_SHIFT))
    364                                         .setIntermediateNote(true);
    365                     } else {
    366                         e = new MonkeyTouchEvent(action);
    367                     }
    368                     if (mScriptStartTime < 0) {
    369                         mMonkeyStartTime = SystemClock.uptimeMillis();
    370                         mScriptStartTime = eventTime;
    371                     }
    372                 } else {
    373                     e = new MonkeyTrackballEvent(action);
    374                 }
    375 
    376                 if (pointerId == 1) {
    377                     e.setDownTime(downTime)
    378                             .setEventTime(eventTime)
    379                             .setMetaState(metaState)
    380                             .setPrecision(xPrecision, yPrecision)
    381                             .setDeviceId(device)
    382                             .setEdgeFlags(edgeFlags)
    383                             .addPointer(0, mLastX[0], mLastY[0], pressure, size)
    384                             .addPointer(1, x, y, pressure, size);
    385                     mLastX[1] = x;
    386                     mLastY[1] = y;
    387                 } else if (pointerId == 0) {
    388                     e.setDownTime(downTime)
    389                             .setEventTime(eventTime)
    390                             .setMetaState(metaState)
    391                             .setPrecision(xPrecision, yPrecision)
    392                             .setDeviceId(device)
    393                             .setEdgeFlags(edgeFlags)
    394                             .addPointer(0, x, y, pressure, size);
    395                      if(action == MotionEvent.ACTION_POINTER_UP) {
    396                          e.addPointer(1, mLastX[1], mLastY[1]);
    397                      }
    398                      mLastX[0] = x;
    399                      mLastY[0] = y;
    400                 }
    401 
    402                 // Dynamically adjust waiting time to ensure that simulated evnets follow
    403                 // the time tap specified in the script
    404                 if (mReadScriptLineByLine) {
    405                     long curUpTime = SystemClock.uptimeMillis();
    406                     long realElapsedTime = curUpTime - mMonkeyStartTime;
    407                     long scriptElapsedTime = eventTime - mScriptStartTime;
    408                     if (realElapsedTime < scriptElapsedTime) {
    409                         long waitDuration = scriptElapsedTime - realElapsedTime;
    410                         mQ.addLast(new MonkeyWaitEvent(waitDuration));
    411                     }
    412                 }
    413                 mQ.addLast(e);
    414             } catch (NumberFormatException e) {
    415             }
    416             return;
    417         }
    418 
    419         // Handle screen rotation events
    420         if ((s.indexOf(EVENT_KEYWORD_ROTATION) >= 0) && args.length == 2) {
    421             try {
    422                 int rotationDegree = Integer.parseInt(args[0]);
    423                 int persist = Integer.parseInt(args[1]);
    424                 if ((rotationDegree == Surface.ROTATION_0) ||
    425                     (rotationDegree == Surface.ROTATION_90) ||
    426                     (rotationDegree == Surface.ROTATION_180) ||
    427                     (rotationDegree == Surface.ROTATION_270)) {
    428                     mQ.addLast(new MonkeyRotationEvent(rotationDegree,
    429                                                        persist != 0));
    430                 }
    431             } catch (NumberFormatException e) {
    432             }
    433             return;
    434         }
    435 
    436         // Handle tap event
    437         if ((s.indexOf(EVENT_KEYWORD_TAP) >= 0) && args.length >= 2) {
    438             try {
    439                 float x = Float.parseFloat(args[0]);
    440                 float y = Float.parseFloat(args[1]);
    441                 long tapDuration = 0;
    442                 if (args.length == 3) {
    443                     tapDuration = Long.parseLong(args[2]);
    444                 }
    445 
    446                 // Set the default parameters
    447                 long downTime = SystemClock.uptimeMillis();
    448                 MonkeyMotionEvent e1 = new MonkeyTouchEvent(MotionEvent.ACTION_DOWN)
    449                         .setDownTime(downTime)
    450                         .setEventTime(downTime)
    451                         .addPointer(0, x, y, 1, 5);
    452                 mQ.addLast(e1);
    453                 if (tapDuration > 0){
    454                     mQ.addLast(new MonkeyWaitEvent(tapDuration));
    455                 }
    456                 MonkeyMotionEvent e2 = new MonkeyTouchEvent(MotionEvent.ACTION_UP)
    457                         .setDownTime(downTime)
    458                         .setEventTime(downTime)
    459                         .addPointer(0, x, y, 1, 5);
    460                 mQ.addLast(e2);
    461             } catch (NumberFormatException e) {
    462                 Logger.err.println("// " + e.toString());
    463             }
    464             return;
    465         }
    466 
    467         //Handle the press and hold
    468         if ((s.indexOf(EVENT_KEYWORD_PRESSANDHOLD) >= 0) && args.length == 3) {
    469             try {
    470                 float x = Float.parseFloat(args[0]);
    471                 float y = Float.parseFloat(args[1]);
    472                 long pressDuration = Long.parseLong(args[2]);
    473 
    474                 // Set the default parameters
    475                 long downTime = SystemClock.uptimeMillis();
    476 
    477                 MonkeyMotionEvent e1 = new MonkeyTouchEvent(MotionEvent.ACTION_DOWN)
    478                         .setDownTime(downTime)
    479                         .setEventTime(downTime)
    480                         .addPointer(0, x, y, 1, 5);
    481                 MonkeyWaitEvent e2 = new MonkeyWaitEvent(pressDuration);
    482                 MonkeyMotionEvent e3 = new MonkeyTouchEvent(MotionEvent.ACTION_UP)
    483                         .setDownTime(downTime + pressDuration)
    484                         .setEventTime(downTime + pressDuration)
    485                         .addPointer(0, x, y, 1, 5);
    486                 mQ.addLast(e1);
    487                 mQ.addLast(e2);
    488                 mQ.addLast(e2);
    489 
    490             } catch (NumberFormatException e) {
    491                 Logger.err.println("// " + e.toString());
    492             }
    493             return;
    494         }
    495 
    496         // Handle drag event
    497         if ((s.indexOf(EVENT_KEYWORD_DRAG) >= 0) && args.length == 5) {
    498             float xStart = Float.parseFloat(args[0]);
    499             float yStart = Float.parseFloat(args[1]);
    500             float xEnd = Float.parseFloat(args[2]);
    501             float yEnd = Float.parseFloat(args[3]);
    502             int stepCount = Integer.parseInt(args[4]);
    503 
    504             float x = xStart;
    505             float y = yStart;
    506             long downTime = SystemClock.uptimeMillis();
    507             long eventTime = SystemClock.uptimeMillis();
    508 
    509             if (stepCount > 0) {
    510                 float xStep = (xEnd - xStart) / stepCount;
    511                 float yStep = (yEnd - yStart) / stepCount;
    512 
    513                 MonkeyMotionEvent e =
    514                         new MonkeyTouchEvent(MotionEvent.ACTION_DOWN).setDownTime(downTime)
    515                                 .setEventTime(eventTime).addPointer(0, x, y, 1, 5);
    516                 mQ.addLast(e);
    517 
    518                 for (int i = 0; i < stepCount; ++i) {
    519                     x += xStep;
    520                     y += yStep;
    521                     eventTime = SystemClock.uptimeMillis();
    522                     e = new MonkeyTouchEvent(MotionEvent.ACTION_MOVE).setDownTime(downTime)
    523                         .setEventTime(eventTime).addPointer(0, x, y, 1, 5);
    524                     mQ.addLast(e);
    525                 }
    526 
    527                 eventTime = SystemClock.uptimeMillis();
    528                 e = new MonkeyTouchEvent(MotionEvent.ACTION_UP).setDownTime(downTime)
    529                     .setEventTime(eventTime).addPointer(0, x, y, 1, 5);
    530                 mQ.addLast(e);
    531             }
    532         }
    533 
    534         // Handle pinch or zoom action
    535         if ((s.indexOf(EVENT_KEYWORD_PINCH_ZOOM) >= 0) && args.length == 9) {
    536             //Parse the parameters
    537             float pt1xStart = Float.parseFloat(args[0]);
    538             float pt1yStart = Float.parseFloat(args[1]);
    539             float pt1xEnd = Float.parseFloat(args[2]);
    540             float pt1yEnd = Float.parseFloat(args[3]);
    541 
    542             float pt2xStart = Float.parseFloat(args[4]);
    543             float pt2yStart = Float.parseFloat(args[5]);
    544             float pt2xEnd = Float.parseFloat(args[6]);
    545             float pt2yEnd = Float.parseFloat(args[7]);
    546 
    547             int stepCount = Integer.parseInt(args[8]);
    548 
    549             float x1 = pt1xStart;
    550             float y1 = pt1yStart;
    551             float x2 = pt2xStart;
    552             float y2 = pt2yStart;
    553 
    554             long downTime = SystemClock.uptimeMillis();
    555             long eventTime = SystemClock.uptimeMillis();
    556 
    557             if (stepCount > 0) {
    558                 float pt1xStep = (pt1xEnd - pt1xStart) / stepCount;
    559                 float pt1yStep = (pt1yEnd - pt1yStart) / stepCount;
    560 
    561                 float pt2xStep = (pt2xEnd - pt2xStart) / stepCount;
    562                 float pt2yStep = (pt2yEnd - pt2yStart) / stepCount;
    563 
    564                 mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_DOWN).setDownTime(downTime)
    565                         .setEventTime(eventTime).addPointer(0, x1, y1, 1, 5));
    566 
    567                 mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_DOWN
    568                         | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT)).setDownTime(downTime)
    569                         .addPointer(0, x1, y1).addPointer(1, x2, y2).setIntermediateNote(true));
    570 
    571                 for (int i = 0; i < stepCount; ++i) {
    572                     x1 += pt1xStep;
    573                     y1 += pt1yStep;
    574                     x2 += pt2xStep;
    575                     y2 += pt2yStep;
    576 
    577                     eventTime = SystemClock.uptimeMillis();
    578                     mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_MOVE).setDownTime(downTime)
    579                             .setEventTime(eventTime).addPointer(0, x1, y1, 1, 5).addPointer(1, x2,
    580                                     y2, 1, 5));
    581                 }
    582                 eventTime = SystemClock.uptimeMillis();
    583                 mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_POINTER_UP)
    584                         .setDownTime(downTime).setEventTime(eventTime).addPointer(0, x1, y1)
    585                         .addPointer(1, x2, y2));
    586             }
    587         }
    588 
    589         // Handle flip events
    590         if (s.indexOf(EVENT_KEYWORD_FLIP) >= 0 && args.length == 1) {
    591             boolean keyboardOpen = Boolean.parseBoolean(args[0]);
    592             MonkeyFlipEvent e = new MonkeyFlipEvent(keyboardOpen);
    593             mQ.addLast(e);
    594         }
    595 
    596         // Handle launch events
    597         if (s.indexOf(EVENT_KEYWORD_ACTIVITY) >= 0 && args.length >= 2) {
    598             String pkg_name = args[0];
    599             String cl_name = args[1];
    600             long alarmTime = 0;
    601 
    602             ComponentName mApp = new ComponentName(pkg_name, cl_name);
    603 
    604             if (args.length > 2) {
    605                 try {
    606                     alarmTime = Long.parseLong(args[2]);
    607                 } catch (NumberFormatException e) {
    608                     Logger.err.println("// " + e.toString());
    609                     return;
    610                 }
    611             }
    612 
    613             if (args.length == 2) {
    614                 MonkeyActivityEvent e = new MonkeyActivityEvent(mApp);
    615                 mQ.addLast(e);
    616             } else {
    617                 MonkeyActivityEvent e = new MonkeyActivityEvent(mApp, alarmTime);
    618                 mQ.addLast(e);
    619             }
    620             return;
    621         }
    622 
    623         //Handle the device wake up event
    624         if (s.indexOf(EVENT_KEYWORD_DEVICE_WAKEUP) >= 0){
    625             String pkg_name = "com.google.android.powerutil";
    626             String cl_name = "com.google.android.powerutil.WakeUpScreen";
    627             long deviceSleepTime = mDeviceSleepTime;
    628 
    629             //Start the wakeUpScreen test activity to turn off the screen.
    630             ComponentName mApp = new ComponentName(pkg_name, cl_name);
    631             mQ.addLast(new MonkeyActivityEvent(mApp, deviceSleepTime));
    632 
    633             //inject the special key for the wakeUpScreen test activity.
    634             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0));
    635             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0));
    636 
    637             //Add the wait event after the device sleep event so that the monkey
    638             //can continue after the device wake up.
    639             mQ.addLast(new MonkeyWaitEvent(deviceSleepTime + 3000));
    640 
    641             //Insert the menu key to unlock the screen
    642             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
    643             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU));
    644 
    645             //Insert the back key to dismiss the test activity
    646             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
    647             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK));
    648 
    649             return;
    650         }
    651 
    652         // Handle launch instrumentation events
    653         if (s.indexOf(EVENT_KEYWORD_INSTRUMENTATION) >= 0 && args.length == 2) {
    654             String test_name = args[0];
    655             String runner_name = args[1];
    656             MonkeyInstrumentationEvent e = new MonkeyInstrumentationEvent(test_name, runner_name);
    657             mQ.addLast(e);
    658             return;
    659         }
    660 
    661         // Handle wait events
    662         if (s.indexOf(EVENT_KEYWORD_WAIT) >= 0 && args.length == 1) {
    663             try {
    664                 long sleeptime = Integer.parseInt(args[0]);
    665                 MonkeyWaitEvent e = new MonkeyWaitEvent(sleeptime);
    666                 mQ.addLast(e);
    667             } catch (NumberFormatException e) {
    668             }
    669             return;
    670         }
    671 
    672 
    673         // Handle the profile wait time
    674         if (s.indexOf(EVENT_KEYWORD_PROFILE_WAIT) >= 0) {
    675             MonkeyWaitEvent e = new MonkeyWaitEvent(mProfileWaitTime);
    676             mQ.addLast(e);
    677             return;
    678         }
    679 
    680         // Handle keypress events
    681         if (s.indexOf(EVENT_KEYWORD_KEYPRESS) >= 0 && args.length == 1) {
    682             String key_name = args[0];
    683             int keyCode = MonkeySourceRandom.getKeyCode(key_name);
    684             if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
    685                 return;
    686             }
    687             MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, keyCode);
    688             mQ.addLast(e);
    689             e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, keyCode);
    690             mQ.addLast(e);
    691             return;
    692         }
    693 
    694         // Handle longpress events
    695         if (s.indexOf(EVENT_KEYWORD_LONGPRESS) >= 0) {
    696             MonkeyKeyEvent e;
    697             e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);
    698             mQ.addLast(e);
    699             MonkeyWaitEvent we = new MonkeyWaitEvent(LONGPRESS_WAIT_TIME);
    700             mQ.addLast(we);
    701             e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER);
    702             mQ.addLast(e);
    703         }
    704 
    705         //The power log event is mainly for the automated power framework
    706         if (s.indexOf(EVENT_KEYWORD_POWERLOG) >= 0 && args.length > 0) {
    707             String power_log_type = args[0];
    708             String test_case_status;
    709 
    710             if (args.length == 1){
    711                 MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type);
    712                 mQ.addLast(e);
    713             } else if (args.length == 2){
    714                 test_case_status = args[1];
    715                 MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type, test_case_status);
    716                 mQ.addLast(e);
    717             }
    718         }
    719 
    720         //Write power log to sdcard
    721         if (s.indexOf(EVENT_KEYWORD_WRITEPOWERLOG) >= 0) {
    722             MonkeyPowerEvent e = new MonkeyPowerEvent();
    723             mQ.addLast(e);
    724         }
    725 
    726         //Run the shell command
    727         if (s.indexOf(EVENT_KEYWORD_RUNCMD) >= 0 && args.length == 1) {
    728             String cmd = args[0];
    729             MonkeyCommandEvent e = new MonkeyCommandEvent(cmd);
    730             mQ.addLast(e);
    731         }
    732 
    733         //Input the string through the shell command
    734         if (s.indexOf(EVENT_KEYWORD_INPUT_STRING) >= 0 && args.length == 1) {
    735             String input = args[0];
    736             String cmd = "input text " + input;
    737             MonkeyCommandEvent e = new MonkeyCommandEvent(cmd);
    738             mQ.addLast(e);
    739             return;
    740         }
    741 
    742         if (s.indexOf(EVENT_KEYWORD_START_FRAMERATE_CAPTURE) >= 0) {
    743             MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("start");
    744             mQ.addLast(e);
    745             return;
    746         }
    747 
    748         if (s.indexOf(EVENT_KEYWORD_END_FRAMERATE_CAPTURE) >= 0 && args.length == 1) {
    749             String input = args[0];
    750             MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("end", input);
    751             mQ.addLast(e);
    752             return;
    753         }
    754 
    755         if (s.indexOf(EVENT_KEYWORD_START_APP_FRAMERATE_CAPTURE) >= 0 && args.length == 1) {
    756             String app = args[0];
    757             MonkeyGetAppFrameRateEvent e = new MonkeyGetAppFrameRateEvent("start", app);
    758             mQ.addLast(e);
    759             return;
    760         }
    761 
    762         if (s.indexOf(EVENT_KEYWORD_END_APP_FRAMERATE_CAPTURE) >= 0 && args.length == 2) {
    763             String app = args[0];
    764             String label = args[1];
    765             MonkeyGetAppFrameRateEvent e = new MonkeyGetAppFrameRateEvent("end", app, label);
    766             mQ.addLast(e);
    767             return;
    768         }
    769 
    770 
    771     }
    772 
    773     /**
    774      * Extracts an event and a list of arguments from a line. If the line does
    775      * not match the format required, it is ignored.
    776      *
    777      * @param line A string in the form {@code cmd(arg1,arg2,arg3)}.
    778      */
    779     private void processLine(String line) {
    780         int index1 = line.indexOf('(');
    781         int index2 = line.indexOf(')');
    782 
    783         if (index1 < 0 || index2 < 0) {
    784             return;
    785         }
    786 
    787         String[] args = line.substring(index1 + 1, index2).split(",");
    788 
    789         for (int i = 0; i < args.length; i++) {
    790             args[i] = args[i].trim();
    791         }
    792 
    793         handleEvent(line, args);
    794     }
    795 
    796     /**
    797      * Closes the script file.
    798      *
    799      * @throws IOException If there was an error closing the file.
    800      */
    801     private void closeFile() throws IOException {
    802         mFileOpened = false;
    803 
    804         try {
    805             mFStream.close();
    806             mInputStream.close();
    807         } catch (NullPointerException e) {
    808             // File was never opened so it can't be closed.
    809         }
    810     }
    811 
    812     /**
    813      * Read next batch of events from the script file into the event queue.
    814      * Checks if the script is open and then reads the next MAX_ONE_TIME_READS
    815      * events or reads until the end of the file. If no events are read, then
    816      * the script is closed.
    817      *
    818      * @throws IOException If there was an error reading the file.
    819      */
    820     private void readNextBatch() throws IOException {
    821         int linesRead = 0;
    822 
    823         if (THIS_DEBUG) {
    824             Logger.out.println("readNextBatch(): reading next batch of events");
    825         }
    826 
    827         if (!mFileOpened) {
    828             resetValue();
    829             readHeader();
    830         }
    831 
    832         if (mReadScriptLineByLine) {
    833             linesRead = readOneLine();
    834         } else {
    835             linesRead = readLines();
    836         }
    837 
    838         if (linesRead == 0) {
    839             closeFile();
    840         }
    841     }
    842 
    843     /**
    844      * Sleep for a period of given time. Used to introduce latency between
    845      * events.
    846      *
    847      * @param time The amount of time to sleep in ms
    848      */
    849     private void needSleep(long time) {
    850         if (time < 1) {
    851             return;
    852         }
    853         try {
    854             Thread.sleep(time);
    855         } catch (InterruptedException e) {
    856         }
    857     }
    858 
    859     /**
    860      * Checks if the file can be opened and if the header is valid.
    861      *
    862      * @return True if the file exists and the header is valid, false otherwise.
    863      */
    864     @Override
    865     public boolean validate() {
    866         boolean validHeader;
    867         try {
    868             validHeader = readHeader();
    869             closeFile();
    870         } catch (IOException e) {
    871             return false;
    872         }
    873 
    874         if (mVerbose > 0) {
    875             Logger.out.println("Replaying " + mEventCountInScript + " events with speed " + mSpeed);
    876         }
    877         return validHeader;
    878     }
    879 
    880     @Override
    881     public void setVerbose(int verbose) {
    882         mVerbose = verbose;
    883     }
    884 
    885     /**
    886      * Adjust key downtime and eventtime according to both recorded values and
    887      * current system time.
    888      *
    889      * @param e A KeyEvent
    890      */
    891     private void adjustKeyEventTime(MonkeyKeyEvent e) {
    892         if (e.getEventTime() < 0) {
    893             return;
    894         }
    895         long thisDownTime = 0;
    896         long thisEventTime = 0;
    897         long expectedDelay = 0;
    898 
    899         if (mLastRecordedEventTime <= 0) {
    900             // first time event
    901             thisDownTime = SystemClock.uptimeMillis();
    902             thisEventTime = thisDownTime;
    903         } else {
    904             if (e.getDownTime() != mLastRecordedDownTimeKey) {
    905                 thisDownTime = e.getDownTime();
    906             } else {
    907                 thisDownTime = mLastExportDownTimeKey;
    908             }
    909             expectedDelay = (long) ((e.getEventTime() - mLastRecordedEventTime) * mSpeed);
    910             thisEventTime = mLastExportEventTime + expectedDelay;
    911             // add sleep to simulate everything in recording
    912             needSleep(expectedDelay - SLEEP_COMPENSATE_DIFF);
    913         }
    914         mLastRecordedDownTimeKey = e.getDownTime();
    915         mLastRecordedEventTime = e.getEventTime();
    916         e.setDownTime(thisDownTime);
    917         e.setEventTime(thisEventTime);
    918         mLastExportDownTimeKey = thisDownTime;
    919         mLastExportEventTime = thisEventTime;
    920     }
    921 
    922     /**
    923      * Adjust motion downtime and eventtime according to current system time.
    924      *
    925      * @param e A MotionEvent
    926      */
    927     private void adjustMotionEventTime(MonkeyMotionEvent e) {
    928         long thisEventTime = SystemClock.uptimeMillis();
    929         long thisDownTime = e.getDownTime();
    930 
    931         if (thisDownTime == mLastRecordedDownTimeMotion) {
    932             // this event is the same batch as previous one
    933             e.setDownTime(mLastExportDownTimeMotion);
    934         } else {
    935             // this event is the start of a new batch
    936             mLastRecordedDownTimeMotion = thisDownTime;
    937             // update down time to match current time
    938             e.setDownTime(thisEventTime);
    939             mLastExportDownTimeMotion = thisEventTime;
    940         }
    941         // always refresh event time
    942         e.setEventTime(thisEventTime);
    943     }
    944 
    945     /**
    946      * Gets the next event to be injected from the script. If the event queue is
    947      * empty, reads the next n events from the script into the queue, where n is
    948      * the lesser of the number of remaining events and the value specified by
    949      * MAX_ONE_TIME_READS. If the end of the file is reached, no events are
    950      * added to the queue and null is returned.
    951      *
    952      * @return The first event in the event queue or null if the end of the file
    953      *         is reached or if an error is encountered reading the file.
    954      */
    955     @Override
    956     public MonkeyEvent getNextEvent() {
    957         long recordedEventTime = -1;
    958         MonkeyEvent ev;
    959 
    960         if (mQ.isEmpty()) {
    961             try {
    962                 readNextBatch();
    963             } catch (IOException e) {
    964                 return null;
    965             }
    966         }
    967 
    968         try {
    969             ev = mQ.getFirst();
    970             mQ.removeFirst();
    971         } catch (NoSuchElementException e) {
    972             return null;
    973         }
    974 
    975         if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) {
    976             adjustKeyEventTime((MonkeyKeyEvent) ev);
    977         } else if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_TOUCH
    978                 || ev.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) {
    979             adjustMotionEventTime((MonkeyMotionEvent) ev);
    980         }
    981         return ev;
    982     }
    983 }
    984