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      */
MonkeySourceScript(Random random, String filename, long throttle, boolean randomizeThrottle, long profileWaitTime, long deviceSleepTime)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      */
resetValue()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      */
readHeader()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      */
readLines()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       */
readOneLine()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      */
handleEvent(String s, String[] args)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      */
processLine(String line)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      */
closeFile()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      */
readNextBatch()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      */
needSleep(long time)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
validate()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
setVerbose(int verbose)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      */
adjustKeyEventTime(MonkeyKeyEvent e)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      */
adjustMotionEventTime(MonkeyMotionEvent e)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
getNextEvent()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