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 // 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 */ MonkeySourceScript(Random random, String filename, long throttle, boolean randomizeThrottle, long profileWaitTime, long deviceSleepTime)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 */ resetValue()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 */ readHeader()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 Logger.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 Logger.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 */ readLines()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 = 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 */ readOneLine()260 private int readOneLine() throws IOException { 261 String line = mBufferedReader.readLine(); 262 if (line == null) { 263 return 0; 264 } 265 line = 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 */ handleEvent(String s, String[] args)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 Logger.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 Logger.out.println(" Key code " + code + "\n"); 296 297 mQ.addLast(e); 298 Logger.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 Logger.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(e3); 489 490 } catch (NumberFormatException e) { 491 Logger.err.println("// " + e); 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 | (1 << MotionEvent.ACTION_POINTER_INDEX_SHIFT)) 585 .setDownTime(downTime).setEventTime(eventTime).addPointer(0, x1, y1) 586 .addPointer(1, x2, y2)); 587 mQ.addLast(new MonkeyTouchEvent(MotionEvent.ACTION_UP) 588 .setDownTime(downTime).setEventTime(eventTime).addPointer(0, x1, y1)); 589 } 590 } 591 592 // Handle flip events 593 if (s.indexOf(EVENT_KEYWORD_FLIP) >= 0 && args.length == 1) { 594 boolean keyboardOpen = Boolean.parseBoolean(args[0]); 595 MonkeyFlipEvent e = new MonkeyFlipEvent(keyboardOpen); 596 mQ.addLast(e); 597 } 598 599 // Handle launch events 600 if (s.indexOf(EVENT_KEYWORD_ACTIVITY) >= 0 && args.length >= 2) { 601 String pkg_name = args[0]; 602 String cl_name = args[1]; 603 long alarmTime = 0; 604 605 ComponentName mApp = new ComponentName(pkg_name, cl_name); 606 607 if (args.length > 2) { 608 try { 609 alarmTime = Long.parseLong(args[2]); 610 } catch (NumberFormatException e) { 611 Logger.err.println("// " + e.toString()); 612 return; 613 } 614 } 615 616 if (args.length == 2) { 617 MonkeyActivityEvent e = new MonkeyActivityEvent(mApp); 618 mQ.addLast(e); 619 } else { 620 MonkeyActivityEvent e = new MonkeyActivityEvent(mApp, alarmTime); 621 mQ.addLast(e); 622 } 623 return; 624 } 625 626 //Handle the device wake up event 627 if (s.indexOf(EVENT_KEYWORD_DEVICE_WAKEUP) >= 0){ 628 String pkg_name = "com.google.android.powerutil"; 629 String cl_name = "com.google.android.powerutil.WakeUpScreen"; 630 long deviceSleepTime = mDeviceSleepTime; 631 632 //Start the wakeUpScreen test activity to turn off the screen. 633 ComponentName mApp = new ComponentName(pkg_name, cl_name); 634 mQ.addLast(new MonkeyActivityEvent(mApp, deviceSleepTime)); 635 636 //inject the special key for the wakeUpScreen test activity. 637 mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0)); 638 mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0)); 639 640 //Add the wait event after the device sleep event so that the monkey 641 //can continue after the device wake up. 642 mQ.addLast(new MonkeyWaitEvent(deviceSleepTime + 3000)); 643 644 //Insert the menu key to unlock the screen 645 mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)); 646 mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU)); 647 648 //Insert the back key to dismiss the test activity 649 mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK)); 650 mQ.addLast(new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK)); 651 652 return; 653 } 654 655 // Handle launch instrumentation events 656 if (s.indexOf(EVENT_KEYWORD_INSTRUMENTATION) >= 0 && args.length == 2) { 657 String test_name = args[0]; 658 String runner_name = args[1]; 659 MonkeyInstrumentationEvent e = new MonkeyInstrumentationEvent(test_name, runner_name); 660 mQ.addLast(e); 661 return; 662 } 663 664 // Handle wait events 665 if (s.indexOf(EVENT_KEYWORD_WAIT) >= 0 && args.length == 1) { 666 try { 667 long sleeptime = Integer.parseInt(args[0]); 668 MonkeyWaitEvent e = new MonkeyWaitEvent(sleeptime); 669 mQ.addLast(e); 670 } catch (NumberFormatException e) { 671 } 672 return; 673 } 674 675 676 // Handle the profile wait time 677 if (s.indexOf(EVENT_KEYWORD_PROFILE_WAIT) >= 0) { 678 MonkeyWaitEvent e = new MonkeyWaitEvent(mProfileWaitTime); 679 mQ.addLast(e); 680 return; 681 } 682 683 // Handle keypress events 684 if (s.indexOf(EVENT_KEYWORD_KEYPRESS) >= 0 && args.length == 1) { 685 String key_name = args[0]; 686 int keyCode = MonkeySourceRandom.getKeyCode(key_name); 687 if (keyCode == KeyEvent.KEYCODE_UNKNOWN) { 688 return; 689 } 690 MonkeyKeyEvent e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, keyCode); 691 mQ.addLast(e); 692 e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, keyCode); 693 mQ.addLast(e); 694 return; 695 } 696 697 // Handle longpress events 698 if (s.indexOf(EVENT_KEYWORD_LONGPRESS) >= 0) { 699 MonkeyKeyEvent e; 700 e = new MonkeyKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER); 701 mQ.addLast(e); 702 MonkeyWaitEvent we = new MonkeyWaitEvent(LONGPRESS_WAIT_TIME); 703 mQ.addLast(we); 704 e = new MonkeyKeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER); 705 mQ.addLast(e); 706 } 707 708 //The power log event is mainly for the automated power framework 709 if (s.indexOf(EVENT_KEYWORD_POWERLOG) >= 0 && args.length > 0) { 710 String power_log_type = args[0]; 711 String test_case_status; 712 713 if (args.length == 1){ 714 MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type); 715 mQ.addLast(e); 716 } else if (args.length == 2){ 717 test_case_status = args[1]; 718 MonkeyPowerEvent e = new MonkeyPowerEvent(power_log_type, test_case_status); 719 mQ.addLast(e); 720 } 721 } 722 723 //Write power log to sdcard 724 if (s.indexOf(EVENT_KEYWORD_WRITEPOWERLOG) >= 0) { 725 MonkeyPowerEvent e = new MonkeyPowerEvent(); 726 mQ.addLast(e); 727 } 728 729 //Run the shell command 730 if (s.indexOf(EVENT_KEYWORD_RUNCMD) >= 0 && args.length == 1) { 731 String cmd = args[0]; 732 MonkeyCommandEvent e = new MonkeyCommandEvent(cmd); 733 mQ.addLast(e); 734 } 735 736 //Input the string through the shell command 737 if (s.indexOf(EVENT_KEYWORD_INPUT_STRING) >= 0 && args.length == 1) { 738 String input = args[0]; 739 String cmd = "input text " + input; 740 MonkeyCommandEvent e = new MonkeyCommandEvent(cmd); 741 mQ.addLast(e); 742 return; 743 } 744 745 if (s.indexOf(EVENT_KEYWORD_START_FRAMERATE_CAPTURE) >= 0) { 746 MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("start"); 747 mQ.addLast(e); 748 return; 749 } 750 751 if (s.indexOf(EVENT_KEYWORD_END_FRAMERATE_CAPTURE) >= 0 && args.length == 1) { 752 String input = args[0]; 753 MonkeyGetFrameRateEvent e = new MonkeyGetFrameRateEvent("end", input); 754 mQ.addLast(e); 755 return; 756 } 757 758 if (s.indexOf(EVENT_KEYWORD_START_APP_FRAMERATE_CAPTURE) >= 0 && args.length == 1) { 759 String app = args[0]; 760 MonkeyGetAppFrameRateEvent e = new MonkeyGetAppFrameRateEvent("start", app); 761 mQ.addLast(e); 762 return; 763 } 764 765 if (s.indexOf(EVENT_KEYWORD_END_APP_FRAMERATE_CAPTURE) >= 0 && args.length == 2) { 766 String app = args[0]; 767 String label = args[1]; 768 MonkeyGetAppFrameRateEvent e = new MonkeyGetAppFrameRateEvent("end", app, label); 769 mQ.addLast(e); 770 return; 771 } 772 773 774 } 775 776 /** 777 * Extracts an event and a list of arguments from a line. If the line does 778 * not match the format required, it is ignored. 779 * 780 * @param line A string in the form {@code cmd(arg1,arg2,arg3)}. 781 */ processLine(String line)782 private void processLine(String line) { 783 int index1 = line.indexOf('('); 784 int index2 = line.indexOf(')'); 785 786 if (index1 < 0 || index2 < 0) { 787 return; 788 } 789 790 String[] args = line.substring(index1 + 1, index2).split(","); 791 792 for (int i = 0; i < args.length; i++) { 793 args[i] = args[i].trim(); 794 } 795 796 handleEvent(line, args); 797 } 798 799 /** 800 * Closes the script file. 801 * 802 * @throws IOException If there was an error closing the file. 803 */ closeFile()804 private void closeFile() throws IOException { 805 mFileOpened = false; 806 807 try { 808 mFStream.close(); 809 mInputStream.close(); 810 } catch (NullPointerException e) { 811 // File was never opened so it can't be closed. 812 } 813 } 814 815 /** 816 * Read next batch of events from the script file into the event queue. 817 * Checks if the script is open and then reads the next MAX_ONE_TIME_READS 818 * events or reads until the end of the file. If no events are read, then 819 * the script is closed. 820 * 821 * @throws IOException If there was an error reading the file. 822 */ readNextBatch()823 private void readNextBatch() throws IOException { 824 int linesRead = 0; 825 826 if (THIS_DEBUG) { 827 Logger.out.println("readNextBatch(): reading next batch of events"); 828 } 829 830 if (!mFileOpened) { 831 resetValue(); 832 readHeader(); 833 } 834 835 if (mReadScriptLineByLine) { 836 linesRead = readOneLine(); 837 } else { 838 linesRead = readLines(); 839 } 840 841 if (linesRead == 0) { 842 closeFile(); 843 } 844 } 845 846 /** 847 * Sleep for a period of given time. Used to introduce latency between 848 * events. 849 * 850 * @param time The amount of time to sleep in ms 851 */ needSleep(long time)852 private void needSleep(long time) { 853 if (time < 1) { 854 return; 855 } 856 try { 857 Thread.sleep(time); 858 } catch (InterruptedException e) { 859 } 860 } 861 862 /** 863 * Checks if the file can be opened and if the header is valid. 864 * 865 * @return True if the file exists and the header is valid, false otherwise. 866 */ 867 @Override validate()868 public boolean validate() { 869 boolean validHeader; 870 try { 871 validHeader = readHeader(); 872 closeFile(); 873 } catch (IOException e) { 874 return false; 875 } 876 877 if (mVerbose > 0) { 878 Logger.out.println("Replaying " + mEventCountInScript + " events with speed " + mSpeed); 879 } 880 return validHeader; 881 } 882 883 @Override setVerbose(int verbose)884 public void setVerbose(int verbose) { 885 mVerbose = verbose; 886 } 887 888 /** 889 * Adjust key downtime and eventtime according to both recorded values and 890 * current system time. 891 * 892 * @param e A KeyEvent 893 */ adjustKeyEventTime(MonkeyKeyEvent e)894 private void adjustKeyEventTime(MonkeyKeyEvent e) { 895 if (e.getEventTime() < 0) { 896 return; 897 } 898 long thisDownTime = 0; 899 long thisEventTime = 0; 900 long expectedDelay = 0; 901 902 if (mLastRecordedEventTime <= 0) { 903 // first time event 904 thisDownTime = SystemClock.uptimeMillis(); 905 thisEventTime = thisDownTime; 906 } else { 907 if (e.getDownTime() != mLastRecordedDownTimeKey) { 908 thisDownTime = e.getDownTime(); 909 } else { 910 thisDownTime = mLastExportDownTimeKey; 911 } 912 expectedDelay = (long) ((e.getEventTime() - mLastRecordedEventTime) * mSpeed); 913 thisEventTime = mLastExportEventTime + expectedDelay; 914 // add sleep to simulate everything in recording 915 needSleep(expectedDelay - SLEEP_COMPENSATE_DIFF); 916 } 917 mLastRecordedDownTimeKey = e.getDownTime(); 918 mLastRecordedEventTime = e.getEventTime(); 919 e.setDownTime(thisDownTime); 920 e.setEventTime(thisEventTime); 921 mLastExportDownTimeKey = thisDownTime; 922 mLastExportEventTime = thisEventTime; 923 } 924 925 /** 926 * Adjust motion downtime and eventtime according to current system time. 927 * 928 * @param e A MotionEvent 929 */ adjustMotionEventTime(MonkeyMotionEvent e)930 private void adjustMotionEventTime(MonkeyMotionEvent e) { 931 long thisEventTime = SystemClock.uptimeMillis(); 932 long thisDownTime = e.getDownTime(); 933 934 if (thisDownTime == mLastRecordedDownTimeMotion) { 935 // this event is the same batch as previous one 936 e.setDownTime(mLastExportDownTimeMotion); 937 } else { 938 // this event is the start of a new batch 939 mLastRecordedDownTimeMotion = thisDownTime; 940 // update down time to match current time 941 e.setDownTime(thisEventTime); 942 mLastExportDownTimeMotion = thisEventTime; 943 } 944 // always refresh event time 945 e.setEventTime(thisEventTime); 946 } 947 948 /** 949 * Gets the next event to be injected from the script. If the event queue is 950 * empty, reads the next n events from the script into the queue, where n is 951 * the lesser of the number of remaining events and the value specified by 952 * MAX_ONE_TIME_READS. If the end of the file is reached, no events are 953 * added to the queue and null is returned. 954 * 955 * @return The first event in the event queue or null if the end of the file 956 * is reached or if an error is encountered reading the file. 957 */ 958 @Override getNextEvent()959 public MonkeyEvent getNextEvent() { 960 long recordedEventTime = -1; 961 MonkeyEvent ev; 962 963 if (mQ.isEmpty()) { 964 try { 965 readNextBatch(); 966 } catch (IOException e) { 967 return null; 968 } 969 } 970 971 try { 972 ev = mQ.getFirst(); 973 mQ.removeFirst(); 974 } catch (NoSuchElementException e) { 975 return null; 976 } 977 978 if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_KEY) { 979 adjustKeyEventTime((MonkeyKeyEvent) ev); 980 } else if (ev.getEventType() == MonkeyEvent.EVENT_TYPE_TOUCH 981 || ev.getEventType() == MonkeyEvent.EVENT_TYPE_TRACKBALL) { 982 adjustMotionEventTime((MonkeyMotionEvent) ev); 983 } 984 return ev; 985 } 986 } 987