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