1 /* 2 * Copyright (C) 2015 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.media.tests; 18 19 import com.android.ddmlib.CollectingOutputReceiver; 20 import com.android.tradefed.config.IConfiguration; 21 import com.android.tradefed.config.IConfigurationReceiver; 22 import com.android.tradefed.config.Option; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.ITestDevice; 25 import com.android.tradefed.invoker.TestInformation; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.result.ByteArrayInputStreamSource; 28 import com.android.tradefed.result.CollectingTestListener; 29 import com.android.tradefed.result.ITestInvocationListener; 30 import com.android.tradefed.result.LogDataType; 31 import com.android.tradefed.result.TestDescription; 32 import com.android.tradefed.testtype.IDeviceTest; 33 import com.android.tradefed.testtype.IRemoteTest; 34 import com.android.tradefed.testtype.InstrumentationTest; 35 import com.android.tradefed.util.FileUtil; 36 import com.android.tradefed.util.IRunUtil; 37 import com.android.tradefed.util.RunUtil; 38 import com.android.tradefed.util.StreamUtil; 39 40 import org.junit.Assert; 41 42 import java.io.BufferedReader; 43 import java.io.BufferedWriter; 44 import java.io.File; 45 import java.io.FileWriter; 46 import java.io.IOException; 47 import java.io.StringReader; 48 import java.util.ArrayList; 49 import java.util.Collection; 50 import java.util.HashMap; 51 import java.util.Map; 52 import java.util.Timer; 53 import java.util.TimerTask; 54 import java.util.concurrent.TimeUnit; 55 56 /** 57 * Camera test base class 58 * 59 * Camera2StressTest, CameraStartupTest, Camera2LatencyTest and CameraPerformanceTest use this base 60 * class for Camera ivvavik and later. 61 */ 62 public class CameraTestBase implements IDeviceTest, IRemoteTest, IConfigurationReceiver { 63 64 private static final long SHELL_TIMEOUT_MS = 60 * 1000; // 1 min 65 private static final int SHELL_MAX_ATTEMPTS = 3; 66 protected static final String PROCESS_CAMERA_DAEMON = "mm-qcamera-daemon"; 67 protected static final String PROCESS_MEDIASERVER = "mediaserver"; 68 protected static final String PROCESS_CAMERA_APP = "com.google.android.GoogleCamera"; 69 protected static final String DUMP_ION_HEAPS_COMMAND = "cat /d/ion/heaps/system"; 70 protected static final String ARGUMENT_TEST_ITERATIONS = "iterations"; 71 72 @Option(name = "test-package", description = "Test package to run.") 73 private String mTestPackage = "com.google.android.camera"; 74 75 @Option(name = "test-class", description = "Test class to run.") 76 private String mTestClass = null; 77 78 @Option(name = "test-methods", description = "Test method to run. May be repeated.") 79 private Collection<String> mTestMethods = new ArrayList<>(); 80 81 @Option(name = "test-runner", description = "Test runner for test instrumentation.") 82 private String mTestRunner = "android.test.InstrumentationTestRunner"; 83 84 @Option(name = "test-timeout", description = "Max time allowed in ms for a test run.") 85 private int mTestTimeoutMs = 60 * 60 * 1000; // 1 hour 86 87 @Option(name = "shell-timeout", 88 description="The defined timeout (in milliseconds) is used as a maximum waiting time " 89 + "when expecting the command output from the device. At any time, if the " 90 + "shell command does not output anything for a period longer than defined " 91 + "timeout the TF run terminates. For no timeout, set to 0.") 92 private long mShellTimeoutMs = 60 * 60 * 1000; // default to 1 hour 93 94 @Option(name = "ru-key", description = "Result key to use when posting to the dashboard.") 95 private String mRuKey = null; 96 97 /** Rely on invocation level logcat capture rather than this. */ 98 @Deprecated 99 @Option( 100 name = "logcat-on-failure", 101 description = "take a logcat snapshot on every test failure." 102 ) 103 private boolean mLogcatOnFailure = false; 104 105 @Option( 106 name = "instrumentation-arg", 107 description = "Additional instrumentation arguments to provide." 108 ) 109 private Map<String, String> mInstrArgMap = new HashMap<>(); 110 111 @Option(name = "dump-meminfo", description = 112 "take a dumpsys meminfo at a given interval time.") 113 private boolean mDumpMeminfo = false; 114 115 @Option(name="dump-meminfo-interval-ms", 116 description="Interval of calling dumpsys meminfo in milliseconds.") 117 private int mMeminfoIntervalMs = 5 * 60 * 1000; // 5 minutes 118 119 @Option(name = "dump-ion-heap", description = 120 "dump ION allocations at the end of test.") 121 private boolean mDumpIonHeap = false; 122 123 @Option(name = "dump-thread-count", description = 124 "Count the number of threads in Camera process at a given interval time.") 125 private boolean mDumpThreadCount = false; 126 127 @Option(name="dump-thread-count-interval-ms", 128 description="Interval of calling ps to count the number of threads in milliseconds.") 129 private int mThreadCountIntervalMs = 5 * 60 * 1000; // 5 minutes 130 131 @Option(name="iterations", description="The number of iterations to run. Default to 1. " 132 + "This takes effect only when Camera2InstrumentationTestRunner is used to execute " 133 + "framework stress tests.") 134 private int mIterations = 1; 135 136 private ITestDevice mDevice = null; 137 138 // A base listener to collect the results from each test run. Test results will be forwarded 139 // to other listeners. 140 private CollectingTestListener mCollectingListener = null; 141 private ITestInvocationListener mListener = null; 142 143 private long mStartTimeMs = 0; 144 145 public MeminfoTimer mMeminfoTimer = null; 146 public ThreadTrackerTimer mThreadTrackerTimer = null; 147 148 protected IConfiguration mConfiguration; 149 150 /** {@inheritDoc} */ 151 @Override run(TestInformation testInfo, ITestInvocationListener listener)152 public void run(TestInformation testInfo, ITestInvocationListener listener) 153 throws DeviceNotAvailableException { 154 // ignore 155 } 156 157 /** 158 * Run Camera instrumentation test with a listener to gather the metrics from the individual 159 * test runs. 160 * 161 * @param listener the ITestInvocationListener of test results 162 * @param collectingListener the {@link CollectingTestListener} to collect the metrics from test 163 * runs 164 * @throws DeviceNotAvailableException 165 */ runInstrumentationTest( TestInformation testInfo, ITestInvocationListener listener, CameraTestMetricsCollectionListener.DefaultCollectingListener collectingListener)166 protected void runInstrumentationTest( 167 TestInformation testInfo, 168 ITestInvocationListener listener, 169 CameraTestMetricsCollectionListener.DefaultCollectingListener collectingListener) 170 throws DeviceNotAvailableException { 171 Assert.assertNotNull(collectingListener); 172 mCollectingListener = collectingListener; 173 mListener = listener; 174 InstrumentationTest instr = new InstrumentationTest(); 175 instr.setDevice(getDevice()); 176 instr.setConfiguration(mConfiguration); 177 instr.setPackageName(getTestPackage()); 178 instr.setRunnerName(getTestRunner()); 179 instr.setClassName(getTestClass()); 180 instr.setTestTimeout(getTestTimeoutMs()); 181 instr.setShellTimeout(getShellTimeoutMs()); 182 instr.setRunName(getRuKey()); 183 instr.setRerunMode(false); 184 if (!getIsolatedStorageFlag()) { 185 instr.setIsolatedStorage(false); 186 } 187 188 // Set test iteration. 189 if (getIterationCount() > 1) { 190 CLog.v("Setting test iterations: %d", getIterationCount()); 191 Map<String, String> instrArgMap = getInstrumentationArgMap(); 192 instrArgMap.put(ARGUMENT_TEST_ITERATIONS, String.valueOf(getIterationCount())); 193 } 194 195 for (Map.Entry<String, String> entry : getInstrumentationArgMap().entrySet()) { 196 instr.addInstrumentationArg(entry.getKey(), entry.getValue()); 197 } 198 199 // Check if dumpheap needs to be taken for native processes before test runs. 200 if (shouldDumpMeminfo()) { 201 mMeminfoTimer = new MeminfoTimer(0, mMeminfoIntervalMs); 202 } 203 if (shouldDumpThreadCount()) { 204 long delayMs = mThreadCountIntervalMs / 2; // Not to run all dump at the same interval. 205 mThreadTrackerTimer = new ThreadTrackerTimer(delayMs, mThreadCountIntervalMs); 206 } 207 208 // Run tests. 209 mStartTimeMs = System.currentTimeMillis(); 210 if (mTestMethods.size() > 0) { 211 CLog.d(String.format("The number of test methods is: %d", mTestMethods.size())); 212 for (String testName : mTestMethods) { 213 instr.setMethodName(testName); 214 instr.run(testInfo, mCollectingListener); 215 } 216 } else { 217 instr.run(testInfo, mCollectingListener); 218 } 219 220 dumpIonHeaps(mCollectingListener, getTestClass()); 221 } 222 223 private Map<String, String> mMetrics = new HashMap<>(); 224 private Map<String, String> mFatalErrors = new HashMap<>(); 225 226 private static final String INCOMPLETE_TEST_ERR_MSG_PREFIX = 227 "Test failed to run to completion. Reason: 'Instrumentation run failed"; 228 229 getAggregatedMetrics()230 public Map<String, String> getAggregatedMetrics() { 231 return mMetrics; 232 } 233 getListeners()234 public ITestInvocationListener getListeners() { 235 return mListener; 236 } 237 238 // TODO: Leverage AUPT to collect system logs (meminfo, ION allocations and processes/threads) 239 public class MeminfoTimer { 240 241 private static final String LOG_HEADER = 242 "uptime,pssCameraDaemon,pssCameraApp,ramTotal,ramFree,ramUsed"; 243 private static final String DUMPSYS_MEMINFO_COMMAND = 244 "dumpsys meminfo -c | grep -w -e " + "^ram -e ^time"; 245 private String[] mDumpsysMemInfoProc = { 246 PROCESS_CAMERA_DAEMON, PROCESS_CAMERA_APP, PROCESS_MEDIASERVER 247 }; 248 private static final int STATE_STOPPED = 0; 249 private static final int STATE_SCHEDULED = 1; 250 private static final int STATE_RUNNING = 2; 251 252 private int mState = STATE_STOPPED; 253 private Timer mTimer = new Timer(true); // run as a daemon thread 254 private long mDelayMs = 0; 255 private long mPeriodMs = 60 * 1000; // 60 sec 256 private File mOutputFile = null; 257 private String mCommand; 258 MeminfoTimer(long delayMs, long periodMs)259 public MeminfoTimer(long delayMs, long periodMs) { 260 mDelayMs = delayMs; 261 mPeriodMs = periodMs; 262 mCommand = DUMPSYS_MEMINFO_COMMAND; 263 for (String process : mDumpsysMemInfoProc) { 264 mCommand += " -e " + process; 265 } 266 } 267 start(TestDescription test)268 synchronized void start(TestDescription test) { 269 if (isRunning()) { 270 stop(); 271 } 272 // Create an output file. 273 if (createOutputFile(test) == null) { 274 CLog.w("Stop collecting meminfo since meminfo log file not found."); 275 mState = STATE_STOPPED; 276 return; // No-op 277 } 278 mTimer.scheduleAtFixedRate(new TimerTask() { 279 @Override 280 public void run() { 281 mState = STATE_RUNNING; 282 dumpMeminfo(mCommand, mOutputFile); 283 } 284 }, mDelayMs, mPeriodMs); 285 mState = STATE_SCHEDULED; 286 } 287 stop()288 synchronized void stop() { 289 mState = STATE_STOPPED; 290 mTimer.cancel(); 291 } 292 isRunning()293 synchronized boolean isRunning() { 294 return (mState == STATE_RUNNING); 295 } 296 getOutputFile()297 public File getOutputFile() { 298 return mOutputFile; 299 } 300 createOutputFile(TestDescription test)301 private File createOutputFile(TestDescription test) { 302 try { 303 mOutputFile = FileUtil.createTempFile( 304 String.format("meminfo_%s", test.getTestName()), "csv"); 305 BufferedWriter writer = new BufferedWriter(new FileWriter(mOutputFile, false)); 306 writer.write(LOG_HEADER); 307 writer.newLine(); 308 writer.flush(); 309 writer.close(); 310 } catch (IOException e) { 311 CLog.w("Failed to create meminfo log file %s:", mOutputFile.getAbsolutePath()); 312 CLog.e(e); 313 return null; 314 } 315 return mOutputFile; 316 } 317 } 318 dumpMeminfo(String command, File outputFile)319 void dumpMeminfo(String command, File outputFile) { 320 try { 321 CollectingOutputReceiver receiver = new CollectingOutputReceiver(); 322 // Dump meminfo in a compact form. 323 getDevice().executeShellCommand(command, receiver, 324 SHELL_TIMEOUT_MS, TimeUnit.MILLISECONDS, SHELL_MAX_ATTEMPTS); 325 printMeminfo(outputFile, receiver.getOutput()); 326 } catch (DeviceNotAvailableException e) { 327 CLog.w("Failed to dump meminfo:"); 328 CLog.e(e); 329 } 330 } 331 printMeminfo(File outputFile, String meminfo)332 void printMeminfo(File outputFile, String meminfo) { 333 // Parse meminfo and print each iteration in a line in a .csv format. The meminfo output 334 // are separated into three different formats: 335 // 336 // Format: time,<uptime>,<realtime> 337 // eg. "time,59459911,63354673" 338 // 339 // Format: proc,<oom_label>,<process_name>,<pid>,<pss>,<hasActivities> 340 // eg. "proc,native,mm-qcamera-daemon,522,12881,e" 341 // "proc,fore,com.google.android.GoogleCamera,26560,70880,a" 342 // 343 // Format: ram,<total>,<free>,<used> 344 // eg. "ram,1857364,810189,541516" 345 BufferedWriter writer = null; 346 BufferedReader reader = null; 347 try { 348 final String DELIMITER = ","; 349 writer = new BufferedWriter(new FileWriter(outputFile, true)); 350 reader = new BufferedReader(new StringReader(meminfo)); 351 String line; 352 String uptime = null; 353 String pssCameraNative = null; 354 String pssCameraApp = null; 355 String ramTotal = null; 356 String ramFree = null; 357 String ramUsed = null; 358 while ((line = reader.readLine()) != null) { 359 if (line.startsWith("time")) { 360 uptime = line.split(DELIMITER)[1]; 361 } else if (line.startsWith("ram")) { 362 String[] ram = line.split(DELIMITER); 363 ramTotal = ram[1]; 364 ramFree = ram[2]; 365 ramUsed = ram[3]; 366 } else if (line.contains(PROCESS_CAMERA_DAEMON)) { 367 pssCameraNative = line.split(DELIMITER)[4]; 368 } else if (line.contains(PROCESS_CAMERA_APP)) { 369 pssCameraApp = line.split(DELIMITER)[4]; 370 } 371 } 372 String printMsg = String.format( 373 "%s,%s,%s,%s,%s,%s", uptime, pssCameraNative, pssCameraApp, 374 ramTotal, ramFree, ramUsed); 375 writer.write(printMsg); 376 writer.newLine(); 377 writer.flush(); 378 } catch (IOException e) { 379 CLog.w("Failed to print meminfo to %s:", outputFile.getAbsolutePath()); 380 CLog.e(e); 381 } finally { 382 StreamUtil.close(writer); 383 } 384 } 385 386 // TODO: Leverage AUPT to collect system logs (meminfo, ION allocations and processes/threads) 387 public class ThreadTrackerTimer { 388 389 // list all threads in a given process, remove the first header line, squeeze whitespaces, 390 // select thread name (in 14th column), then sort and group by its name. 391 // Examples: 392 // 3 SoundPoolThread 393 // 3 SoundPool 394 // 2 Camera Job Disp 395 // 1 pool-7-thread-1 396 // 1 pool-6-thread-1 397 // FIXME: Resolve the error "sh: syntax error: '|' unexpected" using the command below 398 // $ /system/bin/ps -t -p %s | tr -s ' ' | cut -d' ' -f13- | sort | uniq -c | sort -nr" 399 private static final String PS_COMMAND_FORMAT = "/system/bin/ps -t -p %s"; 400 private static final String PGREP_COMMAND_FORMAT = "pgrep %s"; 401 private static final int STATE_STOPPED = 0; 402 private static final int STATE_SCHEDULED = 1; 403 private static final int STATE_RUNNING = 2; 404 405 private int mState = STATE_STOPPED; 406 private Timer mTimer = new Timer(true); // run as a daemon thread 407 private long mDelayMs = 0; 408 private long mPeriodMs = 60 * 1000; // 60 sec 409 private File mOutputFile = null; 410 ThreadTrackerTimer(long delayMs, long periodMs)411 public ThreadTrackerTimer(long delayMs, long periodMs) { 412 mDelayMs = delayMs; 413 mPeriodMs = periodMs; 414 } 415 start(TestDescription test)416 synchronized void start(TestDescription test) { 417 if (isRunning()) { 418 stop(); 419 } 420 // Create an output file. 421 if (createOutputFile(test) == null) { 422 CLog.w("Stop collecting thread counts since log file not found."); 423 mState = STATE_STOPPED; 424 return; // No-op 425 } 426 mTimer.scheduleAtFixedRate(new TimerTask() { 427 @Override 428 public void run() { 429 mState = STATE_RUNNING; 430 dumpThreadCount(PS_COMMAND_FORMAT, getPid(PROCESS_CAMERA_APP), mOutputFile); 431 } 432 }, mDelayMs, mPeriodMs); 433 mState = STATE_SCHEDULED; 434 } 435 stop()436 synchronized void stop() { 437 mState = STATE_STOPPED; 438 mTimer.cancel(); 439 } 440 isRunning()441 synchronized boolean isRunning() { 442 return (mState == STATE_RUNNING); 443 } 444 getOutputFile()445 public File getOutputFile() { 446 return mOutputFile; 447 } 448 createOutputFile(TestDescription test)449 File createOutputFile(TestDescription test) { 450 try { 451 mOutputFile = FileUtil.createTempFile( 452 String.format("ps_%s", test.getTestName()), "txt"); 453 new BufferedWriter(new FileWriter(mOutputFile, false)).close(); 454 } catch (IOException e) { 455 CLog.w("Failed to create processes and threads file %s:", 456 mOutputFile.getAbsolutePath()); 457 CLog.e(e); 458 return null; 459 } 460 return mOutputFile; 461 } 462 getPid(String processName)463 String getPid(String processName) { 464 String result = null; 465 try { 466 result = getDevice().executeShellCommand(String.format(PGREP_COMMAND_FORMAT, 467 processName)); 468 } catch (DeviceNotAvailableException e) { 469 CLog.w("Failed to get pid %s:", processName); 470 CLog.e(e); 471 } 472 return result; 473 } 474 getUptime()475 String getUptime() { 476 String uptime = null; 477 try { 478 // uptime will typically have a format like "5278.73 1866.80". Use the first one 479 // (which is wall-time) 480 uptime = getDevice().executeShellCommand("cat /proc/uptime").split(" ")[0]; 481 Float.parseFloat(uptime); 482 } catch (NumberFormatException e) { 483 CLog.w("Failed to get valid uptime %s: %s", uptime, e); 484 } catch (DeviceNotAvailableException e) { 485 CLog.w("Failed to get valid uptime: %s", e); 486 } 487 return uptime; 488 } 489 dumpThreadCount(String commandFormat, String pid, File outputFile)490 void dumpThreadCount(String commandFormat, String pid, File outputFile) { 491 try { 492 if ("".equals(pid)) { 493 return; 494 } 495 String result = getDevice().executeShellCommand(String.format(commandFormat, pid)); 496 String header = String.format("UPTIME: %s", getUptime()); 497 BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile, true)); 498 writer.write(header); 499 writer.newLine(); 500 writer.write(result); 501 writer.newLine(); 502 writer.flush(); 503 writer.close(); 504 } catch (DeviceNotAvailableException | IOException e) { 505 CLog.w("Failed to dump thread count:"); 506 CLog.e(e); 507 } 508 } 509 } 510 511 // TODO: Leverage AUPT to collect system logs (meminfo, ION allocations and 512 // processes/threads) dumpIonHeaps(ITestInvocationListener listener, String testClass)513 protected void dumpIonHeaps(ITestInvocationListener listener, String testClass) { 514 if (!shouldDumpIonHeap()) { 515 return; // No-op if option is not set. 516 } 517 try { 518 String result = getDevice().executeShellCommand(DUMP_ION_HEAPS_COMMAND); 519 if (!"".equals(result)) { 520 String fileName = String.format("ionheaps_%s_onEnd", testClass); 521 listener.testLog(fileName, LogDataType.TEXT, 522 new ByteArrayInputStreamSource(result.getBytes())); 523 } 524 } catch (DeviceNotAvailableException e) { 525 CLog.w("Failed to dump ION heaps:"); 526 CLog.e(e); 527 } 528 } 529 530 /** 531 * {@inheritDoc} 532 */ 533 @Override setDevice(ITestDevice device)534 public void setDevice(ITestDevice device) { 535 mDevice = device; 536 } 537 538 /** 539 * {@inheritDoc} 540 */ 541 @Override getDevice()542 public ITestDevice getDevice() { 543 return mDevice; 544 } 545 546 /** 547 * {@inheritDoc} 548 */ 549 @Override setConfiguration(IConfiguration configuration)550 public void setConfiguration(IConfiguration configuration) { 551 mConfiguration = configuration; 552 } 553 554 /** 555 * Get the {@link IRunUtil} instance to use. 556 * <p/> 557 * Exposed so unit tests can mock. 558 */ getRunUtil()559 IRunUtil getRunUtil() { 560 return RunUtil.getDefault(); 561 } 562 563 /** 564 * Get the duration of Camera test instrumentation in milliseconds. 565 * 566 * @return the duration of Camera instrumentation test until it is called. 567 */ getTestDurationMs()568 public long getTestDurationMs() { 569 return System.currentTimeMillis() - mStartTimeMs; 570 } 571 getTestPackage()572 public String getTestPackage() { 573 return mTestPackage; 574 } 575 setTestPackage(String testPackage)576 public void setTestPackage(String testPackage) { 577 mTestPackage = testPackage; 578 } 579 getTestClass()580 public String getTestClass() { 581 return mTestClass; 582 } 583 setTestClass(String testClass)584 public void setTestClass(String testClass) { 585 mTestClass = testClass; 586 } 587 getTestRunner()588 public String getTestRunner() { 589 return mTestRunner; 590 } 591 setTestRunner(String testRunner)592 public void setTestRunner(String testRunner) { 593 mTestRunner = testRunner; 594 } 595 getTestTimeoutMs()596 public int getTestTimeoutMs() { 597 return mTestTimeoutMs; 598 } 599 setTestTimeoutMs(int testTimeoutMs)600 public void setTestTimeoutMs(int testTimeoutMs) { 601 mTestTimeoutMs = testTimeoutMs; 602 } 603 getShellTimeoutMs()604 public long getShellTimeoutMs() { 605 return mShellTimeoutMs; 606 } 607 setShellTimeoutMs(long shellTimeoutMs)608 public void setShellTimeoutMs(long shellTimeoutMs) { 609 mShellTimeoutMs = shellTimeoutMs; 610 } 611 getRuKey()612 public String getRuKey() { 613 return mRuKey; 614 } 615 setRuKey(String ruKey)616 public void setRuKey(String ruKey) { 617 mRuKey = ruKey; 618 } 619 shouldDumpMeminfo()620 public boolean shouldDumpMeminfo() { 621 return mDumpMeminfo; 622 } 623 shouldDumpIonHeap()624 public boolean shouldDumpIonHeap() { 625 return mDumpIonHeap; 626 } 627 shouldDumpThreadCount()628 public boolean shouldDumpThreadCount() { 629 return mDumpThreadCount; 630 } 631 getCollectingListener()632 public ITestInvocationListener getCollectingListener() { 633 return mCollectingListener; 634 } 635 setLogcatOnFailure(boolean logcatOnFailure)636 public void setLogcatOnFailure(boolean logcatOnFailure) { 637 mLogcatOnFailure = logcatOnFailure; 638 } 639 getIterationCount()640 public int getIterationCount() { 641 return mIterations; 642 } 643 getIsolatedStorageFlag()644 public boolean getIsolatedStorageFlag() { 645 return mIsolatedStorageFlag; 646 } 647 setIsolatedStorageFlag(boolean isolatedStorage)648 public void setIsolatedStorageFlag(boolean isolatedStorage) { 649 mIsolatedStorageFlag = isolatedStorage; 650 } 651 boolean mIsolatedStorageFlag = true; 652 653 getInstrumentationArgMap()654 public Map<String, String> getInstrumentationArgMap() { return mInstrArgMap; } 655 } 656