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