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 */ MonkeySourceScript(Random random, String filename, long throttle, boolean randomizeThrottle, long profileWaitTime, long deviceSleepTime)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 */ resetValue()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 */ readHeader()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 */ readLines()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 */ handleEvent(String s, String[] args)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 ComponentName mApp = new ComponentName(pkg_name, cl_name); 496 MonkeyActivityEvent e1 = new MonkeyActivityEvent(mApp, deviceSleepTime); 497 mQ.addLast(e1); 498 499 //Add the wait event after the device sleep event so that the monkey 500 //can continue after the device wake up. 501 MonkeyWaitEvent e2 = new MonkeyWaitEvent(deviceSleepTime + 3000); 502 mQ.addLast(e2); 503 return; 504 } 505 506 // Handle launch instrumentation events 507 if (s.indexOf(EVENT_KEYWORD_INSTRUMENTATION) >= 0 && args.length == 2) { 508 String test_name = args[0]; 509 String runner_name = args[1]; 510 MonkeyInstrumentationEvent e = new MonkeyInstrumentationEvent(test_name, runner_name); 511 mQ.addLast(e); 512 return; 513 } 514 515 // Handle wait events 516 if (s.indexOf(EVENT_KEYWORD_WAIT) >= 0 && args.length == 1) { 517 try { 518 long sleeptime = Integer.parseInt(args[0]); 519 MonkeyWaitEvent e = new MonkeyWaitEvent(sleeptime); 520 mQ.addLast(e); 521 } catch (NumberFormatException e) { 522 } 523 return; 524 } 525 526 527 // Handle the profile wait time 528 if (s.indexOf(EVENT_KEYWORD_PROFILE_WAIT) >= 0) { 529 MonkeyWaitEvent e = new MonkeyWaitEvent(mProfileWaitTime); 530 mQ.addLast(e); 531 return; 532 } 533 534 // Handle keypress events 535 if (s.indexOf(EVENT_KEYWORD_KEYPRESS) >= 0 && args.length == 1) { 536 String key_name = args[0]; 537 int keyCode = MonkeySourceRandom.getKeyCode(key_name); 538 MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, keyCode); 539 mQ.addLast(e); 540 e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, keyCode); 541 mQ.addLast(e); 542 return; 543 } 544 545 // Handle longpress events 546 if (s.indexOf(EVENT_KEYWORD_LONGPRESS) >= 0) { 547 MonkeyKeyEvent e; 548 e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER); 549 mQ.addLast(e); 550 MonkeyWaitEvent we = new MonkeyWaitEvent(LONGPRESS_WAIT_TIME); 551 mQ.addLast(we); 552 e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER); 553 mQ.addLast(e); 554 } 555 556 //The power log event is mainly for the automated power framework 557 if (s.indexOf(EVENT_KEYWORD_POWERLOG) >= 0 && args.length > 0) { 558 String power_log_type = args[0]; 559 String test_case_status; 560 561 if (args.length == 1){ 562 MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type); 563 mQ.addLast(e); 564 } else if (args.length == 2){ 565 test_case_status = args[1]; 566 MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type, test_case_status); 567 mQ.addLast(e); 568 } 569 } 570 571 //Write power log to sdcard 572 if (s.indexOf(EVENT_KEYWORD_WRITEPOWERLOG) >= 0) { 573 MonkeyPowerEvent e = new MonkeyPowerEvent(); 574 mQ.addLast(e); 575 } 576 577 //Run the shell command 578 if (s.indexOf(EVENT_KEYWORD_RUNCMD) >= 0 && args.length == 1) { 579 String cmd = args[0]; 580 MonkeyCommandEvent e = new MonkeyCommandEvent(cmd); 581 mQ.addLast(e); 582 } 583 584 //Input the string through the shell command 585 if (s.indexOf(EVENT_KEYWORD_INPUT_STRING) >= 0 && args.length == 1) { 586 String input = args[0]; 587 String cmd = "input text " + input; 588 MonkeyCommandEvent e = new MonkeyCommandEvent(cmd); 589 mQ.addLast(e); 590 return; 591 } 592 593 if (s.indexOf(EVENT_KEYWORD_START_FRAMERATE_CAPTURE) >= 0) { 594 MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("start"); 595 mQ.addLast(e); 596 return; 597 } 598 599 if (s.indexOf(EVENT_KEYWORD_END_FRAMERATE_CAPTURE) >= 0 && args.length == 1) { 600 String input = args[0]; 601 MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("end", input); 602 mQ.addLast(e); 603 return; 604 } 605 606 607 608 } 609 610 /** 611 * Extracts an event and a list of arguments from a line. If the line does 612 * not match the format required, it is ignored. 613 * 614 * @param line A string in the form {@code cmd(arg1,arg2,arg3)}. 615 */ processLine(String line)616 private void processLine(String line) { 617 int index1 = line.indexOf('('); 618 int index2 = line.indexOf(')'); 619 620 if (index1 < 0 || index2 < 0) { 621 return; 622 } 623 624 String[] args = line.substring(index1 + 1, index2).split(","); 625 626 for (int i = 0; i < args.length; i++) { 627 args[i] = args[i].trim(); 628 } 629 630 handleEvent(line, args); 631 } 632 633 /** 634 * Closes the script file. 635 * 636 * @throws IOException If there was an error closing the file. 637 */ closeFile()638 private void closeFile() throws IOException { 639 mFileOpened = false; 640 641 try { 642 mFStream.close(); 643 mInputStream.close(); 644 } catch (NullPointerException e) { 645 // File was never opened so it can't be closed. 646 } 647 } 648 649 /** 650 * Read next batch of events from the script file into the event queue. 651 * Checks if the script is open and then reads the next MAX_ONE_TIME_READS 652 * events or reads until the end of the file. If no events are read, then 653 * the script is closed. 654 * 655 * @throws IOException If there was an error reading the file. 656 */ readNextBatch()657 private void readNextBatch() throws IOException { 658 int linesRead = 0; 659 660 if (THIS_DEBUG) { 661 System.out.println("readNextBatch(): reading next batch of events"); 662 } 663 664 if (!mFileOpened) { 665 resetValue(); 666 readHeader(); 667 } 668 669 linesRead = readLines(); 670 671 if (linesRead == 0) { 672 closeFile(); 673 } 674 } 675 676 /** 677 * Sleep for a period of given time. Used to introduce latency between 678 * events. 679 * 680 * @param time The amount of time to sleep in ms 681 */ needSleep(long time)682 private void needSleep(long time) { 683 if (time < 1) { 684 return; 685 } 686 try { 687 Thread.sleep(time); 688 } catch (InterruptedException e) { 689 } 690 } 691 692 /** 693 * Checks if the file can be opened and if the header is valid. 694 * 695 * @return True if the file exists and the header is valid, false otherwise. 696 */ validate()697 public boolean validate() { 698 boolean validHeader; 699 try { 700 validHeader = readHeader(); 701 closeFile(); 702 } catch (IOException e) { 703 return false; 704 } 705 706 if (mVerbose > 0) { 707 System.out.println("Replaying " + mEventCountInScript + " events with speed " + mSpeed); 708 } 709 return validHeader; 710 } 711 setVerbose(int verbose)712 public void setVerbose(int verbose) { 713 mVerbose = verbose; 714 } 715 716 /** 717 * Adjust key downtime and eventtime according to both recorded values and 718 * current system time. 719 * 720 * @param e A KeyEvent 721 */ adjustKeyEventTime(MonkeyKeyEvent e)722 private void adjustKeyEventTime(MonkeyKeyEvent e) { 723 if (e.getEventTime() < 0) { 724 return; 725 } 726 long thisDownTime = 0; 727 long thisEventTime = 0; 728 long expectedDelay = 0; 729 730 if (mLastRecordedEventTime <= 0) { 731 // first time event 732 thisDownTime = SystemClock.uptimeMillis(); 733 thisEventTime = thisDownTime; 734 } else { 735 if (e.getDownTime() != mLastRecordedDownTimeKey) { 736 thisDownTime = e.getDownTime(); 737 } else { 738 thisDownTime = mLastExportDownTimeKey; 739 } 740 expectedDelay = (long) ((e.getEventTime() - mLastRecordedEventTime) * mSpeed); 741 thisEventTime = mLastExportEventTime + expectedDelay; 742 // add sleep to simulate everything in recording 743 needSleep(expectedDelay - SLEEP_COMPENSATE_DIFF); 744 } 745 mLastRecordedDownTimeKey = e.getDownTime(); 746 mLastRecordedEventTime = e.getEventTime(); 747 e.setDownTime(thisDownTime); 748 e.setEventTime(thisEventTime); 749 mLastExportDownTimeKey = thisDownTime; 750 mLastExportEventTime = thisEventTime; 751 } 752 753 /** 754 * Adjust motion downtime and eventtime according to current system time. 755 * 756 * @param e A KeyEvent 757 */ adjustMotionEventTime(MonkeyMotionEvent e)758 private void adjustMotionEventTime(MonkeyMotionEvent e) { 759 long updatedDownTime = 0; 760 761 if (e.getEventTime() < 0) { 762 return; 763 } 764 updatedDownTime = SystemClock.uptimeMillis(); 765 e.setDownTime(updatedDownTime); 766 e.setEventTime(updatedDownTime); 767 } 768 769 /** 770 * Gets the next event to be injected from the script. If the event queue is 771 * empty, reads the next n events from the script into the queue, where n is 772 * the lesser of the number of remaining events and the value specified by 773 * MAX_ONE_TIME_READS. If the end of the file is reached, no events are 774 * added to the queue and null is returned. 775 * 776 * @return The first event in the event queue or null if the end of the file 777 * is reached or if an error is encountered reading the file. 778 */ getNextEvent()779 public MonkeyEvent getNextEvent() { 780 long recordedEventTime = -1; 781 MonkeyEvent ev; 782 783 if (mQ.isEmpty()) { 784 try { 785 readNextBatch(); 786 } catch (IOException e) { 787 return null; 788 } 789 } 790 791 try { 792 ev = mQ.getFirst(); 793 mQ.removeFirst(); 794 } catch (NoSuchElementException e) { 795 return null; 796 } 797 798 if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) { 799 adjustKeyEventTime((MonkeyKeyEvent) ev); 800 } else if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_TOUCH 801 || ev.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) { 802 adjustMotionEventTime((MonkeyMotionEvent) ev); 803 } 804 return ev; 805 } 806 } 807