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