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 >> 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