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 = 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 = 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(e3);
489 
490             } catch (NumberFormatException e) {
491                 Logger.err.println("// " + e);
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                         | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT))
585                         .setDownTime(downTime).setEventTime(eventTime).addPointer(0, x1, y1)
586                         .addPointer(1, x2, y2));
587                 mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_UP)
588                         .setDownTime(downTime).setEventTime(eventTime).addPointer(0, x1, y1));
589             }
590         }
591 
592         // Handle flip events
593         if (s.indexOf(EVENT_KEYWORD_FLIP) >= 0 && args.length == 1) {
594             boolean keyboardOpen = Boolean.parseBoolean(args[0]);
595             MonkeyFlipEvent e = new MonkeyFlipEvent(keyboardOpen);
596             mQ.addLast(e);
597         }
598 
599         // Handle launch events
600         if (s.indexOf(EVENT_KEYWORD_ACTIVITY) >= 0 && args.length >= 2) {
601             String pkg_name = args[0];
602             String cl_name = args[1];
603             long alarmTime = 0;
604 
605             ComponentName mApp = new ComponentName(pkg_name, cl_name);
606 
607             if (args.length > 2) {
608                 try {
609                     alarmTime = Long.parseLong(args[2]);
610                 } catch (NumberFormatException e) {
611                     Logger.err.println("// " + e.toString());
612                     return;
613                 }
614             }
615 
616             if (args.length == 2) {
617                 MonkeyActivityEvent e = new MonkeyActivityEvent(mApp);
618                 mQ.addLast(e);
619             } else {
620                 MonkeyActivityEvent e = new MonkeyActivityEvent(mApp, alarmTime);
621                 mQ.addLast(e);
622             }
623             return;
624         }
625 
626         //Handle the device wake up event
627         if (s.indexOf(EVENT_KEYWORD_DEVICE_WAKEUP) >= 0){
628             String pkg_name = "com.google.android.powerutil";
629             String cl_name = "com.google.android.powerutil.WakeUpScreen";
630             long deviceSleepTime = mDeviceSleepTime;
631 
632             //Start the wakeUpScreen test activity to turn off the screen.
633             ComponentName mApp = new ComponentName(pkg_name, cl_name);
634             mQ.addLast(new MonkeyActivityEvent(mApp, deviceSleepTime));
635 
636             //inject the special key for the wakeUpScreen test activity.
637             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0));
638             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0));
639 
640             //Add the wait event after the device sleep event so that the monkey
641             //can continue after the device wake up.
642             mQ.addLast(new MonkeyWaitEvent(deviceSleepTime + 3000));
643 
644             //Insert the menu key to unlock the screen
645             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
646             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU));
647 
648             //Insert the back key to dismiss the test activity
649             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK));
650             mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK));
651 
652             return;
653         }
654 
655         // Handle launch instrumentation events
656         if (s.indexOf(EVENT_KEYWORD_INSTRUMENTATION) >= 0 && args.length == 2) {
657             String test_name = args[0];
658             String runner_name = args[1];
659             MonkeyInstrumentationEvent e = new MonkeyInstrumentationEvent(test_name, runner_name);
660             mQ.addLast(e);
661             return;
662         }
663 
664         // Handle wait events
665         if (s.indexOf(EVENT_KEYWORD_WAIT) >= 0 && args.length == 1) {
666             try {
667                 long sleeptime = Integer.parseInt(args[0]);
668                 MonkeyWaitEvent e = new MonkeyWaitEvent(sleeptime);
669                 mQ.addLast(e);
670             } catch (NumberFormatException e) {
671             }
672             return;
673         }
674 
675 
676         // Handle the profile wait time
677         if (s.indexOf(EVENT_KEYWORD_PROFILE_WAIT) >= 0) {
678             MonkeyWaitEvent e = new MonkeyWaitEvent(mProfileWaitTime);
679             mQ.addLast(e);
680             return;
681         }
682 
683         // Handle keypress events
684         if (s.indexOf(EVENT_KEYWORD_KEYPRESS) >= 0 && args.length == 1) {
685             String key_name = args[0];
686             int keyCode = MonkeySourceRandom.getKeyCode(key_name);
687             if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
688                 return;
689             }
690             MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, keyCode);
691             mQ.addLast(e);
692             e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, keyCode);
693             mQ.addLast(e);
694             return;
695         }
696 
697         // Handle longpress events
698         if (s.indexOf(EVENT_KEYWORD_LONGPRESS) >= 0) {
699             MonkeyKeyEvent e;
700             e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);
701             mQ.addLast(e);
702             MonkeyWaitEvent we = new MonkeyWaitEvent(LONGPRESS_WAIT_TIME);
703             mQ.addLast(we);
704             e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER);
705             mQ.addLast(e);
706         }
707 
708         //The power log event is mainly for the automated power framework
709         if (s.indexOf(EVENT_KEYWORD_POWERLOG) >= 0 && args.length > 0) {
710             String power_log_type = args[0];
711             String test_case_status;
712 
713             if (args.length == 1){
714                 MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type);
715                 mQ.addLast(e);
716             } else if (args.length == 2){
717                 test_case_status = args[1];
718                 MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type, test_case_status);
719                 mQ.addLast(e);
720             }
721         }
722 
723         //Write power log to sdcard
724         if (s.indexOf(EVENT_KEYWORD_WRITEPOWERLOG) >= 0) {
725             MonkeyPowerEvent e = new MonkeyPowerEvent();
726             mQ.addLast(e);
727         }
728 
729         //Run the shell command
730         if (s.indexOf(EVENT_KEYWORD_RUNCMD) >= 0 && args.length == 1) {
731             String cmd = args[0];
732             MonkeyCommandEvent e = new MonkeyCommandEvent(cmd);
733             mQ.addLast(e);
734         }
735 
736         //Input the string through the shell command
737         if (s.indexOf(EVENT_KEYWORD_INPUT_STRING) >= 0 && args.length == 1) {
738             String input = args[0];
739             String cmd = "input text " + input;
740             MonkeyCommandEvent e = new MonkeyCommandEvent(cmd);
741             mQ.addLast(e);
742             return;
743         }
744 
745         if (s.indexOf(EVENT_KEYWORD_START_FRAMERATE_CAPTURE) >= 0) {
746             MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("start");
747             mQ.addLast(e);
748             return;
749         }
750 
751         if (s.indexOf(EVENT_KEYWORD_END_FRAMERATE_CAPTURE) >= 0 && args.length == 1) {
752             String input = args[0];
753             MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("end", input);
754             mQ.addLast(e);
755             return;
756         }
757 
758         if (s.indexOf(EVENT_KEYWORD_START_APP_FRAMERATE_CAPTURE) >= 0 && args.length == 1) {
759             String app = args[0];
760             MonkeyGetAppFrameRateEvent e = new MonkeyGetAppFrameRateEvent("start", app);
761             mQ.addLast(e);
762             return;
763         }
764 
765         if (s.indexOf(EVENT_KEYWORD_END_APP_FRAMERATE_CAPTURE) >= 0 && args.length == 2) {
766             String app = args[0];
767             String label = args[1];
768             MonkeyGetAppFrameRateEvent e = new MonkeyGetAppFrameRateEvent("end", app, label);
769             mQ.addLast(e);
770             return;
771         }
772 
773 
774     }
775 
776     /**
777      * Extracts an event and a list of arguments from a line. If the line does
778      * not match the format required, it is ignored.
779      *
780      * @param line A string in the form {@code cmd(arg1,arg2,arg3)}.
781      */
processLine(String line)782     private void processLine(String line) {
783         int index1 = line.indexOf('(');
784         int index2 = line.indexOf(')');
785 
786         if (index1 < 0 || index2 < 0) {
787             return;
788         }
789 
790         String[] args = line.substring(index1 + 1, index2).split(",");
791 
792         for (int i = 0; i < args.length; i++) {
793             args[i] = args[i].trim();
794         }
795 
796         handleEvent(line, args);
797     }
798 
799     /**
800      * Closes the script file.
801      *
802      * @throws IOException If there was an error closing the file.
803      */
closeFile()804     private void closeFile() throws IOException {
805         mFileOpened = false;
806 
807         try {
808             mFStream.close();
809             mInputStream.close();
810         } catch (NullPointerException e) {
811             // File was never opened so it can't be closed.
812         }
813     }
814 
815     /**
816      * Read next batch of events from the script file into the event queue.
817      * Checks if the script is open and then reads the next MAX_ONE_TIME_READS
818      * events or reads until the end of the file. If no events are read, then
819      * the script is closed.
820      *
821      * @throws IOException If there was an error reading the file.
822      */
readNextBatch()823     private void readNextBatch() throws IOException {
824         int linesRead = 0;
825 
826         if (THIS_DEBUG) {
827             Logger.out.println("readNextBatch(): reading next batch of events");
828         }
829 
830         if (!mFileOpened) {
831             resetValue();
832             readHeader();
833         }
834 
835         if (mReadScriptLineByLine) {
836             linesRead = readOneLine();
837         } else {
838             linesRead = readLines();
839         }
840 
841         if (linesRead == 0) {
842             closeFile();
843         }
844     }
845 
846     /**
847      * Sleep for a period of given time. Used to introduce latency between
848      * events.
849      *
850      * @param time The amount of time to sleep in ms
851      */
needSleep(long time)852     private void needSleep(long time) {
853         if (time < 1) {
854             return;
855         }
856         try {
857             Thread.sleep(time);
858         } catch (InterruptedException e) {
859         }
860     }
861 
862     /**
863      * Checks if the file can be opened and if the header is valid.
864      *
865      * @return True if the file exists and the header is valid, false otherwise.
866      */
867     @Override
validate()868     public boolean validate() {
869         boolean validHeader;
870         try {
871             validHeader = readHeader();
872             closeFile();
873         } catch (IOException e) {
874             return false;
875         }
876 
877         if (mVerbose > 0) {
878             Logger.out.println("Replaying " + mEventCountInScript + " events with speed " + mSpeed);
879         }
880         return validHeader;
881     }
882 
883     @Override
setVerbose(int verbose)884     public void setVerbose(int verbose) {
885         mVerbose = verbose;
886     }
887 
888     /**
889      * Adjust key downtime and eventtime according to both recorded values and
890      * current system time.
891      *
892      * @param e A KeyEvent
893      */
adjustKeyEventTime(MonkeyKeyEvent e)894     private void adjustKeyEventTime(MonkeyKeyEvent e) {
895         if (e.getEventTime() < 0) {
896             return;
897         }
898         long thisDownTime = 0;
899         long thisEventTime = 0;
900         long expectedDelay = 0;
901 
902         if (mLastRecordedEventTime <= 0) {
903             // first time event
904             thisDownTime = SystemClock.uptimeMillis();
905             thisEventTime = thisDownTime;
906         } else {
907             if (e.getDownTime() != mLastRecordedDownTimeKey) {
908                 thisDownTime = e.getDownTime();
909             } else {
910                 thisDownTime = mLastExportDownTimeKey;
911             }
912             expectedDelay = (long) ((e.getEventTime() - mLastRecordedEventTime) * mSpeed);
913             thisEventTime = mLastExportEventTime + expectedDelay;
914             // add sleep to simulate everything in recording
915             needSleep(expectedDelay - SLEEP_COMPENSATE_DIFF);
916         }
917         mLastRecordedDownTimeKey = e.getDownTime();
918         mLastRecordedEventTime = e.getEventTime();
919         e.setDownTime(thisDownTime);
920         e.setEventTime(thisEventTime);
921         mLastExportDownTimeKey = thisDownTime;
922         mLastExportEventTime = thisEventTime;
923     }
924 
925     /**
926      * Adjust motion downtime and eventtime according to current system time.
927      *
928      * @param e A MotionEvent
929      */
adjustMotionEventTime(MonkeyMotionEvent e)930     private void adjustMotionEventTime(MonkeyMotionEvent e) {
931         long thisEventTime = SystemClock.uptimeMillis();
932         long thisDownTime = e.getDownTime();
933 
934         if (thisDownTime == mLastRecordedDownTimeMotion) {
935             // this event is the same batch as previous one
936             e.setDownTime(mLastExportDownTimeMotion);
937         } else {
938             // this event is the start of a new batch
939             mLastRecordedDownTimeMotion = thisDownTime;
940             // update down time to match current time
941             e.setDownTime(thisEventTime);
942             mLastExportDownTimeMotion = thisEventTime;
943         }
944         // always refresh event time
945         e.setEventTime(thisEventTime);
946     }
947 
948     /**
949      * Gets the next event to be injected from the script. If the event queue is
950      * empty, reads the next n events from the script into the queue, where n is
951      * the lesser of the number of remaining events and the value specified by
952      * MAX_ONE_TIME_READS. If the end of the file is reached, no events are
953      * added to the queue and null is returned.
954      *
955      * @return The first event in the event queue or null if the end of the file
956      *         is reached or if an error is encountered reading the file.
957      */
958     @Override
getNextEvent()959     public MonkeyEvent getNextEvent() {
960         long recordedEventTime = -1;
961         MonkeyEvent ev;
962 
963         if (mQ.isEmpty()) {
964             try {
965                 readNextBatch();
966             } catch (IOException e) {
967                 return null;
968             }
969         }
970 
971         try {
972             ev = mQ.getFirst();
973             mQ.removeFirst();
974         } catch (NoSuchElementException e) {
975             return null;
976         }
977 
978         if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) {
979             adjustKeyEventTime((MonkeyKeyEvent) ev);
980         } else if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_TOUCH
981                 || ev.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) {
982             adjustMotionEventTime((MonkeyMotionEvent) ev);
983         }
984         return ev;
985     }
986 }
987