Merge "CP: Add EGL extensions to graphics device info" into nougat-cts-dev
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 541bcd6..b12792f 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -23,6 +23,9 @@
 import its.device
 from its.device import ItsSession
 
+CHART_DELAY = 1  # seconds
+
+
 def main():
     """Run all the automated tests, saving intermediate files, and producing
     a summary/report of the results.
@@ -223,6 +226,8 @@
                         cmd = ['python',
                                os.path.join(os.getcwd(), 'tools/load_scene.py'),
                                scene_arg, screen_id_arg]
+                    else:
+                        time.sleep(CHART_DELAY)
                 else:
                     # Skip scene validation for scene 5 running in parallel
                     if not merge_result_switch or scene != 'scene5':
diff --git a/apps/CameraITS/tools/wake_up_screen.py b/apps/CameraITS/tools/wake_up_screen.py
index 68a974a..f14c9f2 100644
--- a/apps/CameraITS/tools/wake_up_screen.py
+++ b/apps/CameraITS/tools/wake_up_screen.py
@@ -18,7 +18,8 @@
 import time
 
 DISPLAY_LEVEL = 96  # [0:255] Depends on tablet model. Adjust for best result.
-DISPLAY_WAIT = 0.5  # seconds. Screen commands take time to have effect
+DISPLAY_CMD_WAIT = 0.5  # seconds. Screen commands take time to have effect
+DISPLAY_TIMEOUT = 1800000  # ms.
 
 
 def main():
@@ -38,23 +39,32 @@
     process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
     cmd_ret = process.stdout.read()
     screen_state = re.split(r'[s|=]', cmd_ret)[-1]
-    if 'OFF' in screen_state:
-        print 'Screen OFF. Turning ON.'
-        wakeup = ('adb -s %s shell input keyevent POWER' % screen_id)
-        subprocess.Popen(wakeup.split())
-        time.sleep(DISPLAY_WAIT)
+    power_event = ('adb -s %s shell input keyevent POWER' % screen_id)
+    subprocess.Popen(power_event.split())
+    time.sleep(DISPLAY_CMD_WAIT)
+    if 'ON' in screen_state:
+        print 'Screen was ON. Toggling to refresh.'
+        subprocess.Popen(power_event.split())
+        time.sleep(DISPLAY_CMD_WAIT)
+    else:
+        print 'Screen was OFF. Powered ON.'
     unlock = ('adb -s %s wait-for-device shell wm dismiss-keyguard'
               % screen_id)
     subprocess.Popen(unlock.split())
-    time.sleep(DISPLAY_WAIT)
+    time.sleep(DISPLAY_CMD_WAIT)
 
     # set brightness
     print 'Tablet display brightness set to %d' % DISPLAY_LEVEL
     bright = ('adb -s %s shell settings put system screen_brightness %d'
               % (screen_id, DISPLAY_LEVEL))
     subprocess.Popen(bright.split())
-    time.sleep(DISPLAY_WAIT)
+    time.sleep(DISPLAY_CMD_WAIT)
 
+    # set screen to dim at max time (30min)
+    stay_bright = ('adb -s %s shell settings put system screen_off_timeout %d'
+                   % (screen_id, DISPLAY_TIMEOUT))
+    subprocess.Popen(stay_bright.split())
+    time.sleep(DISPLAY_CMD_WAIT)
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index bf7529a..50fd538 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -2094,8 +2094,7 @@
                 <category android:name="android.cts.intent.category.MANUAL_TEST" />
             </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_audio" />
-            <meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
-             <meta-data android:name="test_required_features" android:value="android.hardware.usb.host" />
+            <meta-data android:name="test_required_features" android:value="android.hardware.microphone:android.hardware.usb.host" />
         </activity>
 
         <service android:name=".tv.MockTvInputService"
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
index db9ad94..c8d86d08 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
@@ -18,7 +18,7 @@
 import com.android.compatibility.SuiteInfo;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider;
-import com.android.compatibility.common.tradefed.result.SubPlanCreator;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
 import com.android.compatibility.common.tradefed.testtype.ModuleRepo;
 import com.android.compatibility.common.util.IInvocationResult;
 import com.android.compatibility.common.util.ResultHandler;
@@ -405,7 +405,7 @@
     }
 
     private void addSubPlan(String[] flatArgs) {
-        SubPlanCreator creator = new SubPlanCreator();
+        SubPlanHelper creator = new SubPlanHelper();
         try {
             ArgsOptionParser optionParser = new ArgsOptionParser(creator);
             optionParser.parse(Arrays.asList(flatArgs));
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index 7d198ae..d16b0e0 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -19,7 +19,7 @@
 import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
 import com.android.compatibility.common.tradefed.result.TestRunHandler;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
-import com.android.compatibility.common.tradefed.testtype.CompatibilityTest.RetryType;
+import com.android.compatibility.common.tradefed.util.RetryType;
 import com.android.compatibility.common.util.ICaseResult;
 import com.android.compatibility.common.util.IInvocationResult;
 import com.android.compatibility.common.util.IModuleResult;
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
similarity index 89%
rename from common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
rename to common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
index 9dbbcbb..950a129 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanHelper.java
@@ -33,11 +33,15 @@
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.Option.Importance;
 import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
+import com.android.tradefed.util.StreamUtil;
 
 import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.InputStream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collection;
@@ -50,7 +54,9 @@
 /**
  * Class for creating subplans from compatibility result XML.
  */
-public class SubPlanCreator {
+public class SubPlanHelper {
+
+    private static final String XML_EXT = ".xml";
 
     // result types
     public static final String PASSED = "passed";
@@ -108,22 +114,45 @@
     IInvocationResult mResult = null;
 
     /**
-     * Create an empty {@link SubPlanCreator}.
+     * Create an empty {@link SubPlanHelper}.
      * <p/>
      * All {@link Option} fields must be populated via
      * {@link com.android.tradefed.config.ArgsOptionParser}
      */
-    public SubPlanCreator() {}
+    public SubPlanHelper() {}
 
     /**
-     * Create a {@link SubPlanCreator} using the specified option values.
+     * Create a {@link SubPlanHelper} using the specified option values.
      */
-    public SubPlanCreator(String name, int session, Collection<String> resultTypes) {
+    public SubPlanHelper(String name, int session, Collection<String> resultTypes) {
         mSubPlanName = name;
         mSessionId = session;
         mResultTypes.addAll(resultTypes);
     }
 
+    public static ISubPlan getSubPlanByName(CompatibilityBuildHelper buildHelper, String name) {
+        if (!name.endsWith(XML_EXT)) {
+            name = name + XML_EXT; // only append XML extension to name if not already there
+        }
+        InputStream subPlanInputStream = null;
+        try {
+            File subPlanFile = new File(buildHelper.getSubPlansDir(), name);
+            if (!subPlanFile.exists()) {
+                throw new IllegalArgumentException(
+                        String.format("Could not retrieve subplan \"%s\"", name));
+            }
+            subPlanInputStream = new FileInputStream(subPlanFile);
+            ISubPlan subPlan = new SubPlan();
+            subPlan.parse(subPlanInputStream);
+            return subPlan;
+        } catch (FileNotFoundException | ParseException e) {
+            throw new RuntimeException(
+                    String.format("Unable to find or parse subplan %s", name), e);
+        } finally {
+            StreamUtil.closeStream(subPlanInputStream);
+        }
+    }
+
     /**
      * Set the result from which to derive the subplan.
      * @param result
@@ -333,7 +362,7 @@
             mSubPlanName = createPlanName();
         }
         try {
-            mSubPlanFile = new File(buildHelper.getSubPlansDir(), mSubPlanName + ".xml");
+            mSubPlanFile = new File(buildHelper.getSubPlansDir(), mSubPlanName + XML_EXT);
             if (mSubPlanFile.exists()) {
                 throw new ConfigurationException(String.format("Subplan %s already exists",
                         mSubPlanName));
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/ApkInstrumentationPreparer.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/ApkInstrumentationPreparer.java
index 6b87977..72fe373 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/ApkInstrumentationPreparer.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/ApkInstrumentationPreparer.java
@@ -54,6 +54,9 @@
         BEFORE, AFTER, BOTH;
     }
 
+    @Option(name = "throw-error", description = "Whether to throw error for device test failure")
+    protected boolean mThrowError = true;
+
     @Option(name = "when", description = "When to instrument the apk", mandatory = true)
     protected When mWhen = null;
 
@@ -73,7 +76,7 @@
         try {
             if (instrument(device, buildInfo)) {
                 logInfo("Target preparation successful");
-            } else {
+            } else if (mThrowError) {
                 throw new TargetSetupError("Not all target preparation steps completed");
             }
         } catch (FileNotFoundException e) {
@@ -128,7 +131,12 @@
             for (TestIdentifier test : testFailures.keySet()) {
                 success = false;
                 String trace = testFailures.get(test);
-                logError("Target preparation step %s failed.\n%s", test.getTestName(), trace);
+                if (mThrowError) {
+                    logError("Target preparation step %s failed.\n%s", test.getTestName(), trace);
+                } else {
+                    logWarning("Target preparation step %s failed.\n%s", test.getTestName(),
+                            trace);
+                }
             }
         }
         return success;
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index 7609254..248b586 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -19,10 +19,12 @@
 import com.android.compatibility.SuiteInfo;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
-import com.android.compatibility.common.tradefed.result.SubPlanCreator;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
 import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker;
 import com.android.compatibility.common.tradefed.targetprep.SystemStatusChecker;
 import com.android.compatibility.common.tradefed.util.OptionHelper;
+import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
+import com.android.compatibility.common.tradefed.util.RetryType;
 import com.android.compatibility.common.util.AbiUtils;
 import com.android.compatibility.common.util.ICaseResult;
 import com.android.compatibility.common.util.IInvocationResult;
@@ -33,7 +35,6 @@
 import com.android.compatibility.common.util.TestStatus;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.config.ArgsOptionParser;
 import com.android.tradefed.config.ConfigurationException;
 import com.android.tradefed.config.ConfigurationFactory;
 import com.android.tradefed.config.IConfiguration;
@@ -150,20 +151,16 @@
 
     @Option(name = MODULE_ARG_OPTION,
             description = "the arguments to pass to a module. The expected format is"
-                    + "\"<module-name>:<arg-name>:<arg-value>\"",
+                    + "\"<module-name>:<arg-name>:[<arg-key>:]<arg-value>\"",
             importance = Importance.ALWAYS)
     private List<String> mModuleArgs = new ArrayList<>();
 
     @Option(name = TEST_ARG_OPTION,
             description = "the arguments to pass to a test. The expected format is"
-                    + "\"<test-class>:<arg-name>:<arg-value>\"",
+                    + "\"<test-class>:<arg-name>:[<arg-key>:]<arg-value>\"",
             importance = Importance.ALWAYS)
     private List<String> mTestArgs = new ArrayList<>();
 
-    public enum RetryType {
-        FAILED, NOT_EXECUTED;
-    }
-
     @Option(name = RETRY_OPTION,
             shortName = 'r',
             description = "retry a previous session's failed and not executed tests.",
@@ -605,117 +602,44 @@
      */
     void setupFilters() throws DeviceNotAvailableException {
         if (mRetrySessionId != null) {
-            // Track --module/-m and --test/-t options to ensure we don't overwrite non-null
-            // values on retry
-            String newModuleName = mModuleName;
-            String newTestName = mTestName;
-
-            // Load the invocation result
-            IInvocationResult result = null;
-            try {
-                result = ResultHandler.findResult(mBuildHelper.getResultsDir(), mRetrySessionId);
-            } catch (FileNotFoundException e) {
-                throw new RuntimeException(e);
-            }
-            if (result == null) {
-                throw new IllegalArgumentException(String.format(
-                        "Could not find session with id %d", mRetrySessionId));
-            }
-
-            String oldBuildFingerprint = result.getBuildFingerprint();
-            String currentBuildFingerprint = mDevice.getProperty("ro.build.fingerprint");
-            if (oldBuildFingerprint.equals(currentBuildFingerprint)) {
-                CLog.logAndDisplay(LogLevel.INFO, "Retrying session from: %s",
-                        CompatibilityBuildHelper.getDirSuffix(result.getStartTime()));
-            } else {
-                throw new IllegalArgumentException(String.format(
-                        "Device build fingerprint must match %s to retry session %d",
-                        oldBuildFingerprint, mRetrySessionId));
-            }
-
-            String retryCommandLineArgs = result.getCommandLineArgs();
-            if (retryCommandLineArgs != null) {
-                try {
-                    // parse the command-line string from the result file and set options
-                    ArgsOptionParser parser = new ArgsOptionParser(this);
-                    parser.parse(OptionHelper.getValidCliArgs(retryCommandLineArgs, this));
-                } catch (ConfigurationException e) {
-                    throw new RuntimeException(e);
-                }
-            }
-
-            if ((mModuleName != null && mModuleName != newModuleName)
-                    || (mTestName != null && mTestName != newTestName)) {
-                // These options cannot be changed on retry if non-null for the previous session
-                CLog.w("Cannot override non-null value(s) from session %d for option(s) \"%s\""
-                        + " or \"%s\" on retry", mRetrySessionId, MODULE_OPTION, TEST_OPTION);
-            }
-
-            SubPlanCreator retryPlanCreator = new SubPlanCreator();
-            retryPlanCreator.setResult(result);
-            if (RetryType.FAILED.equals(mRetryType)) {
-                // retry only failed tests
-                retryPlanCreator.addResultType(SubPlanCreator.FAILED);
-            } else if (RetryType.NOT_EXECUTED.equals(mRetryType)){
-                // retry only not executed tests
-                retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
-            } else {
-                // retry both failed and not executed tests
-                retryPlanCreator.addResultType(SubPlanCreator.FAILED);
-                retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
-            }
-            try {
-                ISubPlan retryPlan = retryPlanCreator.createSubPlan(mBuildHelper);
-                mIncludeFilters.addAll(retryPlan.getIncludeFilters());
-                mExcludeFilters.addAll(retryPlan.getExcludeFilters());
-            } catch (ConfigurationException e) {
-                throw new RuntimeException ("Failed to create subplan for retry", e);
-            }
-        }
-        if (mSubPlan != null) {
-            try {
-                File subPlanFile = new File(mBuildHelper.getSubPlansDir(), mSubPlan + ".xml");
-                if (!subPlanFile.exists()) {
-                    throw new IllegalArgumentException(
-                            String.format("Could not retrieve subplan \"%s\"", mSubPlan));
-                }
-                InputStream subPlanInputStream = new FileInputStream(subPlanFile);
-                ISubPlan subPlan = new SubPlan();
-                subPlan.parse(subPlanInputStream);
+            RetryFilterHelper helper = new RetryFilterHelper(mBuildHelper, mRetrySessionId);
+            helper.validateBuildFingerprint(mDevice);
+            helper.setAllOptionsFrom(this);
+            helper.setCommandLineOptionsFor(this);
+            helper.populateRetryFilters();
+            mIncludeFilters = helper.getIncludeFilters();
+            mExcludeFilters = helper.getExcludeFilters();
+            helper.tearDown();
+        } else {
+            if (mSubPlan != null) {
+                ISubPlan subPlan = SubPlanHelper.getSubPlanByName(mBuildHelper, mSubPlan);
                 mIncludeFilters.addAll(subPlan.getIncludeFilters());
                 mExcludeFilters.addAll(subPlan.getExcludeFilters());
-            } catch (FileNotFoundException | ParseException e) {
-                throw new RuntimeException(
-                        String.format("Unable to find or parse subplan %s", mSubPlan), e);
             }
-        }
-        if (mModuleName != null) {
-            try {
-                List<String> modules = ModuleRepo.getModuleNamesMatching(
-                        mBuildHelper.getTestsDir(), mModuleName);
-                if (modules.size() == 0) {
-                    throw new IllegalArgumentException(
-                            String.format("No modules found matching %s", mModuleName));
-                } else if (modules.size() > 1) {
-                    throw new IllegalArgumentException(String.format(
-                            "Multiple modules found matching %s:\n%s\nWhich one did you mean?\n",
-                            mModuleName, ArrayUtil.join("\n", modules)));
-                } else {
-                    String module = modules.get(0);
-                    cleanFilters(mIncludeFilters, module);
-                    cleanFilters(mExcludeFilters, module);
-                    mIncludeFilters.add(new TestFilter(mAbiName, module, mTestName).toString());
+            if (mModuleName != null) {
+                try {
+                    List<String> modules = ModuleRepo.getModuleNamesMatching(
+                            mBuildHelper.getTestsDir(), mModuleName);
+                    if (modules.size() == 0) {
+                        throw new IllegalArgumentException(
+                                String.format("No modules found matching %s", mModuleName));
+                    } else if (modules.size() > 1) {
+                        throw new IllegalArgumentException(String.format("Multiple modules found"
+                                + " matching %s:\n%s\nWhich one did you mean?\n",
+                                mModuleName, ArrayUtil.join("\n", modules)));
+                    } else {
+                        String module = modules.get(0);
+                        cleanFilters(mIncludeFilters, module);
+                        cleanFilters(mExcludeFilters, module);
+                        mIncludeFilters.add(
+                                new TestFilter(mAbiName, module, mTestName).toString());
+                    }
+                } catch (FileNotFoundException e) {
+                    throw new RuntimeException(e);
                 }
-            } catch (FileNotFoundException e) {
-                e.printStackTrace();
-            }
-        } else if (mTestName != null) {
-            throw new IllegalArgumentException(
-                    "Test name given without module name. Add --module <module-name>");
-        } else {
-            // If a module has an arg, assume it's included
-            for (String arg : mModuleArgs) {
-                mIncludeFilters.add(arg.split(":")[0]);
+            } else if (mTestName != null) {
+                throw new IllegalArgumentException(
+                        "Test name given without module name. Add --module <module-name>");
             }
         }
     }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
index 78995f0..89f6ded 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
@@ -299,7 +299,17 @@
                         }
                         if (args != null && args.size() > 0) {
                             for (Entry<String, String> entry : args.entrySet()) {
-                                config.injectOptionValue(entry.getKey(), entry.getValue());
+                                String entryName = entry.getKey();
+                                String entryValue = entry.getValue();
+                                if (entryValue.contains(":")) {
+                                    // entryValue is key-value pair
+                                    String key = entryValue.split(":")[0];
+                                    String value = entryValue.split(":")[1];
+                                    config.injectOptionValue(entryName, key, value);
+                                } else {
+                                    // entryValue is just the argument value
+                                    config.injectOptionValue(entryName, entryValue);
+                                }
                             }
                         }
                     }
@@ -312,7 +322,17 @@
                         }
                         if (args != null && args.size() > 0) {
                             for (Entry<String, String> entry : args.entrySet()) {
-                                config.injectOptionValue(entry.getKey(), entry.getValue());
+                                String entryName = entry.getKey();
+                                String entryValue = entry.getValue();
+                                if (entryValue.contains(":")) {
+                                    // entryValue is key-value pair
+                                    String key = entryValue.split(":")[0];
+                                    String value = entryValue.split(":")[1];
+                                    config.injectOptionValue(entryName, key, value);
+                                } else {
+                                    // entryValue is just the argument value
+                                    config.injectOptionValue(entryName, entryValue);
+                                }
                             }
                         }
                         addFiltersToTest(test, abi, name);
@@ -630,14 +650,20 @@
         for (String arg : args) {
             String[] parts = arg.split(":");
             String target = parts[0];
-            String key = parts[1];
-            String value = parts[2];
+            String name = parts[1];
+            String value;
+            if (parts.length == 4) {
+                // key and value given, keep the pair delimited by ':' and stored as value
+                value = String.format("%s:%s", parts[2], parts[3]);
+            } else {
+                value = parts[2];
+            }
             Map<String, String> map = argsMap.get(target);
             if (map == null) {
                 map = new HashMap<>();
                 argsMap.put(target, map);
             }
-            map.put(key, value);
+            map.put(name, value);
         }
     }
 
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java
new file mode 100644
index 0000000..9a68089
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryFilterHelper.java
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.tradefed.util;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.result.SubPlanHelper;
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.compatibility.common.tradefed.testtype.ModuleRepo;
+import com.android.compatibility.common.tradefed.testtype.ISubPlan;
+import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.LightInvocationResult;
+import com.android.compatibility.common.util.ResultHandler;
+import com.android.compatibility.common.util.TestFilter;
+import com.android.tradefed.config.ArgsOptionParser;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.Option.Importance;
+import com.android.tradefed.config.OptionCopier;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.ArrayUtil;
+
+import java.io.FileNotFoundException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helper for generating --include-filter and --exclude-filter values on compatibility retry.
+ */
+public class RetryFilterHelper {
+
+    @Option(name = CompatibilityTest.SUBPLAN_OPTION,
+            description = "the subplan to run",
+            importance = Importance.IF_UNSET)
+    protected String mSubPlan;
+
+    @Option(name = CompatibilityTest.INCLUDE_FILTER_OPTION,
+            description = "the include module filters to apply.",
+            importance = Importance.ALWAYS)
+    protected Set<String> mIncludeFilters = new HashSet<>();
+
+    @Option(name = CompatibilityTest.EXCLUDE_FILTER_OPTION,
+            description = "the exclude module filters to apply.",
+            importance = Importance.ALWAYS)
+    protected Set<String> mExcludeFilters = new HashSet<>();
+
+    @Option(name = CompatibilityTest.ABI_OPTION,
+            shortName = 'a',
+            description = "the abi to test.",
+            importance = Importance.IF_UNSET)
+    protected String mAbiName = null;
+
+    @Option(name = CompatibilityTest.MODULE_OPTION,
+            shortName = 'm',
+            description = "the test module to run.",
+            importance = Importance.IF_UNSET)
+    protected String mModuleName = null;
+
+    @Option(name = CompatibilityTest.TEST_OPTION,
+            shortName = CompatibilityTest.TEST_OPTION_SHORT_NAME,
+            description = "the test run.",
+            importance = Importance.IF_UNSET)
+    protected String mTestName = null;
+
+    @Option(name = CompatibilityTest.RETRY_TYPE_OPTION,
+            description = "used with " + CompatibilityTest.RETRY_OPTION + ", retry tests"
+            + " of a certain status. Possible values include \"failed\" and \"not_executed\".",
+            importance = Importance.IF_UNSET)
+    protected RetryType mRetryType = null;
+
+    /* Instance variables handy for retreiving the result to be retried */
+    private CompatibilityBuildHelper mBuild = null;
+    private int mSessionId;
+
+    /* Sets to be populated by retry logic and returned by getter methods */
+    private Set<String> mRetryIncludes;
+    private Set<String> mRetryExcludes;
+
+    /**
+     * Constructor for a {@link RetryFilterHelper}. Requires a CompatibilityBuildHelper for
+     * retrieving previous sessions and the ID of the session to retry.
+     */
+    public RetryFilterHelper(CompatibilityBuildHelper build, int sessionId) {
+        mBuild = build;
+        mSessionId = sessionId;
+    }
+
+    /**
+     * Throws an {@link IllegalArgumentException} if the device build fingerprint doesn't match
+     * the fingerprint recorded in the previous session's result.
+     */
+    public void validateBuildFingerprint(ITestDevice device) throws DeviceNotAvailableException {
+        String oldBuildFingerprint = new LightInvocationResult(getResult()).getBuildFingerprint();
+        String currentBuildFingerprint = device.getProperty("ro.build.fingerprint");
+        if (!oldBuildFingerprint.equals(currentBuildFingerprint)) {
+            throw new IllegalArgumentException(String.format(
+                    "Device build fingerprint must match %s to retry session %d",
+                    oldBuildFingerprint, mSessionId));
+        }
+    }
+
+    /**
+     * Copy all applicable options from an input object to this instance of RetryFilterHelper.
+     */
+    public void setAllOptionsFrom(Object obj) {
+        clearOptions(); // Remove existing options first
+        OptionCopier.copyOptionsNoThrow(obj, this);
+    }
+
+    /**
+     * Set a single option on this instance of RetryFilterHelper
+     * @throws {@link ConfigurationException} if the option cannot be set.
+     */
+    public void setOption(String option, String value) throws ConfigurationException {
+        OptionSetter setter = new OptionSetter(this);
+        setter.setOptionValue(option, value);
+    }
+
+    /**
+     * Clear all option values of this RetryFilterHelper.
+     */
+    public void clearOptions() {
+        mSubPlan = null;
+        mIncludeFilters = new HashSet<>();
+        mExcludeFilters = new HashSet<>();
+        mModuleName = null;
+        mTestName = null;
+        mRetryType = null;
+        mAbiName = null;
+    }
+
+    /**
+     * Using command-line arguments from the previous session's result, set the input object's
+     * option values to the values applied in the previous session.
+     */
+    public void setCommandLineOptionsFor(Object obj) {
+        // only need light version to retrieve command-line args
+        IInvocationResult result = new LightInvocationResult(getResult());
+        String retryCommandLineArgs = result.getCommandLineArgs();
+        if (retryCommandLineArgs != null) {
+            try {
+                // parse the command-line string from the result file and set options
+                ArgsOptionParser parser = new ArgsOptionParser(obj);
+                parser.parse(OptionHelper.getValidCliArgs(retryCommandLineArgs, obj));
+            } catch (ConfigurationException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Retrieve an instance of the result to retry using the instance variables referencing
+     * the build and the desired session ID. While it is faster to load this result once and
+     * store it as an instance variable, {@link IInvocationResult} objects are large, and
+     * memory is of greater concern.
+     */
+    public IInvocationResult getResult() {
+        IInvocationResult result = null;
+        try {
+            result = ResultHandler.findResult(mBuild.getResultsDir(), mSessionId);
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        if (result == null) {
+            throw new IllegalArgumentException(String.format(
+                    "Could not find session with id %d", mSessionId));
+        }
+        return result;
+    }
+
+    /**
+     * Populate mRetryIncludes and mRetryExcludes based on the options and the result set for
+     * this instance of RetryFilterHelper.
+     */
+    public void populateRetryFilters() {
+        mRetryIncludes = new HashSet<>(mIncludeFilters); // reset for each population
+        mRetryExcludes = new HashSet<>(mExcludeFilters); // reset for each population
+        if (RetryType.CUSTOM.equals(mRetryType)) {
+            Set<String> customIncludes = new HashSet<>(mIncludeFilters);
+            Set<String> customExcludes = new HashSet<>(mExcludeFilters);
+            if (mSubPlan != null) {
+                ISubPlan retrySubPlan = SubPlanHelper.getSubPlanByName(mBuild, mSubPlan);
+                customIncludes.addAll(retrySubPlan.getIncludeFilters());
+                customExcludes.addAll(retrySubPlan.getExcludeFilters());
+            }
+            // If includes were added, only use those includes. Also use excludes added directly
+            // or by subplan. Otherwise, default to normal retry.
+            if (!customIncludes.isEmpty()) {
+                mRetryIncludes.clear();
+                mRetryIncludes.addAll(customIncludes);
+                mRetryExcludes.addAll(customExcludes);
+                return;
+            }
+        }
+        // remove any extra filtering options
+        // TODO(aaronholden) remove non-plan includes (e.g. those in cts-vendor-interface)
+        // TODO(aaronholden) remove non-known-failure excludes
+        mModuleName = null;
+        mTestName = null;
+        mSubPlan = null;
+        populateFiltersBySubPlan();
+        populatePreviousSessionFilters();
+    }
+
+    /* Generation of filters based on previous sessions is implemented thoroughly in SubPlanHelper,
+     * and retry filter generation is just a subset of the use cases for the subplan retry logic.
+     * Use retry type to determine which result types SubPlanHelper targets. */
+    private void populateFiltersBySubPlan() {
+        SubPlanHelper retryPlanCreator = new SubPlanHelper();
+        retryPlanCreator.setResult(getResult());
+        if (RetryType.FAILED.equals(mRetryType)) {
+            // retry only failed tests
+            retryPlanCreator.addResultType(SubPlanHelper.FAILED);
+        } else if (RetryType.NOT_EXECUTED.equals(mRetryType)){
+            // retry only not executed tests
+            retryPlanCreator.addResultType(SubPlanHelper.NOT_EXECUTED);
+        } else {
+            // retry both failed and not executed tests
+            retryPlanCreator.addResultType(SubPlanHelper.FAILED);
+            retryPlanCreator.addResultType(SubPlanHelper.NOT_EXECUTED);
+        }
+        try {
+            ISubPlan retryPlan = retryPlanCreator.createSubPlan(mBuild);
+            mRetryIncludes.addAll(retryPlan.getIncludeFilters());
+            mRetryExcludes.addAll(retryPlan.getExcludeFilters());
+        } catch (ConfigurationException e) {
+            throw new RuntimeException ("Failed to create subplan for retry", e);
+        }
+    }
+
+    /* Retrieves the options set via command-line on the previous session, and generates/adds
+     * filters accordingly */
+    private void populatePreviousSessionFilters() {
+        // Temporarily store options from this instance in another instance
+        RetryFilterHelper tmpHelper = new RetryFilterHelper(mBuild, mSessionId);
+        tmpHelper.setAllOptionsFrom(this);
+        // Copy command-line args from previous session to this RetryFilterHelper's options
+        setCommandLineOptionsFor(this);
+
+        mRetryIncludes.addAll(mIncludeFilters);
+        mRetryExcludes.addAll(mExcludeFilters);
+        if (mSubPlan != null) {
+            ISubPlan retrySubPlan = SubPlanHelper.getSubPlanByName(mBuild, mSubPlan);
+            mRetryIncludes.addAll(retrySubPlan.getIncludeFilters());
+            mRetryExcludes.addAll(retrySubPlan.getExcludeFilters());
+        }
+        if (mModuleName != null) {
+            try {
+                List<String> modules = ModuleRepo.getModuleNamesMatching(
+                        mBuild.getTestsDir(), mModuleName);
+                if (modules.size() == 0) {
+                    throw new IllegalArgumentException(
+                            String.format("No modules found matching %s", mModuleName));
+                } else if (modules.size() > 1) {
+                    throw new IllegalArgumentException(String.format(
+                            "Multiple modules found matching %s:\n%s\nWhich one did you mean?\n",
+                            mModuleName, ArrayUtil.join("\n", modules)));
+                } else {
+                    String module = modules.get(0);
+                    cleanFilters(mRetryIncludes, module);
+                    cleanFilters(mRetryExcludes, module);
+                    mRetryIncludes.add(new TestFilter(mAbiName, module, mTestName).toString());
+                }
+            } catch (FileNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+        } else if (mTestName != null) {
+            throw new IllegalArgumentException(
+                "Test name given without module name. Add --module <module-name>");
+        }
+
+        // Copy options for current session back to this instance
+        setAllOptionsFrom(tmpHelper);
+    }
+
+    /* Helper method designed to remove filters in a list not applicable to the given module */
+    private static void cleanFilters(Set<String> filters, String module) {
+        Set<String> cleanedFilters = new HashSet<String>();
+        for (String filter : filters) {
+            if (module.equals(TestFilter.createFrom(filter).getName())) {
+                cleanedFilters.add(filter); // Module name matches, filter passes
+            }
+        }
+        filters.clear();
+        filters.addAll(cleanedFilters);
+    }
+
+    /** Retrieve include filters to be applied on retry */
+    public Set<String> getIncludeFilters() {
+        return new HashSet<>(mRetryIncludes);
+    }
+
+    /** Retrieve exclude filters to be applied on retry */
+    public Set<String> getExcludeFilters() {
+        return new HashSet<>(mRetryExcludes);
+    }
+
+    /** Clears retry filters and internal storage of options, except buildInfo and session ID */
+    public void tearDown() {
+        clearOptions();
+        mRetryIncludes = null;
+        mRetryExcludes = null;
+        // keep references to buildInfo and session ID
+    }
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java
new file mode 100644
index 0000000..b5d3cd5
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/RetryType.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.tradefed.util;
+
+/**
+ * Enum for --retry-type option value in compatibility testing.
+ */
+public enum RetryType {
+    FAILED,
+    NOT_EXECUTED,
+    CUSTOM;
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
index 860c830..43e2837 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
@@ -21,15 +21,16 @@
 import com.android.compatibility.common.tradefed.result.ConsoleReporterTest;
 import com.android.compatibility.common.tradefed.result.MetadataReporterTest;
 import com.android.compatibility.common.tradefed.result.ResultReporterTest;
-import com.android.compatibility.common.tradefed.result.SubPlanCreatorTest;
+import com.android.compatibility.common.tradefed.result.SubPlanHelperTest;
 import com.android.compatibility.common.tradefed.targetprep.PropertyCheckTest;
 import com.android.compatibility.common.tradefed.targetprep.SettingsPreparerTest;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTestTest;
 import com.android.compatibility.common.tradefed.testtype.ModuleDefTest;
 import com.android.compatibility.common.tradefed.testtype.ModuleRepoTest;
 import com.android.compatibility.common.tradefed.testtype.SubPlanTest;
-import com.android.compatibility.common.tradefed.util.OptionHelperTest;
 import com.android.compatibility.common.tradefed.util.CollectorUtilTest;
+import com.android.compatibility.common.tradefed.util.OptionHelperTest;
+import com.android.compatibility.common.tradefed.util.RetryFilterHelperTest;
 
 import junit.framework.Test;
 import junit.framework.TestSuite;
@@ -58,7 +59,8 @@
         addTestSuite(PropertyCheckTest.class);
         addTestSuite(SettingsPreparerTest.class);
         addTestSuite(SubPlanTest.class);
-        addTestSuite(SubPlanCreatorTest.class);
+        addTestSuite(SubPlanHelperTest.class);
+        addTestSuite(RetryFilterHelperTest.class);
     }
 
     public static Test suite() {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
new file mode 100644
index 0000000..a19eb30
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.tradefed.result;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.testtype.ISubPlan;
+import com.android.compatibility.common.util.ICaseResult;
+import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.IModuleResult;
+import com.android.compatibility.common.util.ITestResult;
+import com.android.compatibility.common.util.InvocationResult;
+import com.android.compatibility.common.util.ResultHandler;
+import com.android.compatibility.common.util.TestFilter;
+import com.android.compatibility.common.util.TestStatus;
+import com.android.tradefed.build.BuildInfo;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.ArgsOptionParser;
+import com.android.tradefed.util.AbiUtils;
+import com.android.tradefed.util.FileUtil;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Arrays;
+import java.util.Set;
+
+public class SubPlanHelperTest extends TestCase {
+
+    // Values used to populate mock results
+    private static final String SUITE_NAME = "CTS";
+    private static final String SUITE_VERSION = "5.0";
+    private static final String SUITE_PLAN = "cts";
+    private static final String SUITE_BUILD = "12345";
+    private static final String NAME_A = "ModuleA";
+    private static final String NAME_B = "ModuleB";
+    private static final String ABI = "mips64";
+    private static final String ID_A = AbiUtils.createId(ABI, NAME_A);
+    private static final String ID_B = AbiUtils.createId(ABI, NAME_B);
+    private static final String BUILD_ID = "build_id";
+    private static final String BUILD_PRODUCT = "build_product";
+    private static final String EXAMPLE_BUILD_ID = "XYZ";
+    private static final String EXAMPLE_BUILD_PRODUCT = "wolverine";
+    private static final String DEVICE_A = "device123";
+    private static final String DEVICE_B = "device456";
+    private static final String CLASS_A = "android.test.Foor";
+    private static final String CLASS_B = "android.test.Bar";
+    private static final String METHOD_1 = "testBlah1";
+    private static final String METHOD_2 = "testBlah2";
+    private static final String METHOD_3 = "testBlah3";
+    private static final String METHOD_4 = "testBlah4";
+    private static final long START_MS = 1431586801000L;
+    private static final long END_MS = 1431673199000L;
+    private static final String REFERENCE_URL="http://android.com";
+    private static final String LOG_URL ="file:///path/to/logs";
+    private static final String COMMAND_LINE_ARGS = "cts -m CtsMyModuleTestCases";
+
+    private static final String SP_NAME = "testsubplan";
+    private static final String SP_SESSION = "0";
+    private static final String SP_RESULT_TYPE_FAILED = "failed";
+    private static final String SP_RESULT_TYPE_NOT_EXECUTED = "not_executed";
+
+    private CompatibilityBuildHelper mBuildHelper;
+    private SubPlanHelper mSubPlanHelper;
+
+    private File mResultsDir = null;
+    private File mResultDir = null;
+    private File mSubPlansDir = null;
+
+    @Override
+    public void setUp() throws Exception {
+        mResultsDir = FileUtil.createTempDir("results");
+        mResultDir = FileUtil.createTempDir("12345", mResultsDir);
+        mSubPlansDir = FileUtil.createTempDir("subplans");
+        mBuildHelper = new SpctMockCompatibilityBuildHelper(new BuildInfo("0", "", ""));
+        populateResults();
+
+        mSubPlanHelper = new SubPlanHelper();
+        ArgsOptionParser optionParser = new ArgsOptionParser(mSubPlanHelper);
+        optionParser.parse(Arrays.asList(
+            "-n", SP_NAME,
+            "--session", SP_SESSION,
+            "--result-type", SP_RESULT_TYPE_FAILED,
+            "--result-type", SP_RESULT_TYPE_NOT_EXECUTED));
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        if (mResultsDir != null) {
+            FileUtil.recursiveDelete(mResultsDir);
+        }
+        if (mSubPlansDir != null) {
+            FileUtil.recursiveDelete(mSubPlansDir);
+        }
+    }
+
+    public void testCreateSubPlan() throws Exception {
+        ISubPlan plan = mSubPlanHelper.createSubPlan(mBuildHelper);
+        Set<String> planIncludes = plan.getIncludeFilters();
+        Set<String> planExcludes = plan.getExcludeFilters();
+        TestFilter mf1 = new TestFilter(ABI, NAME_A, null);
+        TestFilter tf1 = new TestFilter(ABI, NAME_A, String.format("%s#%s", CLASS_A, METHOD_1));
+        TestFilter tf3 = new TestFilter(ABI, NAME_B, String.format("%s#%s", CLASS_B, METHOD_3));
+        assertTrue(planIncludes.contains("CtsMyModuleTestCases")); // command-line '-m' arg
+        assertTrue(planIncludes.contains(mf1.toString())); // include module with not-executed test
+        assertTrue(planExcludes.contains(tf1.toString())); // exclude passing test in that module
+        assertTrue(planIncludes.contains(tf3.toString())); // include failure in executed module
+    }
+
+    private void populateResults() throws Exception {
+        // copied from ResultHandlerTest
+        IInvocationResult result = new InvocationResult();
+        result.setStartTime(START_MS);
+        result.setTestPlan(SUITE_PLAN);
+        result.addDeviceSerial(DEVICE_A);
+        result.addDeviceSerial(DEVICE_B);
+        result.addInvocationInfo(BUILD_ID, EXAMPLE_BUILD_ID);
+        result.addInvocationInfo(BUILD_PRODUCT, EXAMPLE_BUILD_PRODUCT);
+        IModuleResult moduleA = result.getOrCreateModule(ID_A);
+        moduleA.setDone(false);
+        ICaseResult moduleACase = moduleA.getOrCreateResult(CLASS_A);
+        ITestResult moduleATest1 = moduleACase.getOrCreateResult(METHOD_1);
+        moduleATest1.setResultStatus(TestStatus.PASS);
+        ITestResult moduleATest2 = moduleACase.getOrCreateResult(METHOD_2);
+        moduleATest2.setResultStatus(null); // not executed test
+        moduleA.setNotExecuted(1);
+
+        IModuleResult moduleB = result.getOrCreateModule(ID_B);
+        moduleB.setDone(true);
+        ICaseResult moduleBCase = moduleB.getOrCreateResult(CLASS_B);
+        ITestResult moduleBTest3 = moduleBCase.getOrCreateResult(METHOD_3);
+        moduleBTest3.setResultStatus(TestStatus.FAIL);
+        ITestResult moduleBTest4 = moduleBCase.getOrCreateResult(METHOD_4);
+        moduleBTest4.setResultStatus(TestStatus.PASS);
+
+        // Serialize to file
+        ResultHandler.writeResults(SUITE_NAME, SUITE_VERSION, SUITE_PLAN, SUITE_BUILD,
+                result, mResultDir, START_MS, END_MS, REFERENCE_URL, LOG_URL,
+                COMMAND_LINE_ARGS);
+    }
+
+    private class SpctMockCompatibilityBuildHelper extends CompatibilityBuildHelper {
+
+        public SpctMockCompatibilityBuildHelper(IBuildInfo buildInfo) {
+            super(buildInfo);
+        }
+
+        @Override
+        public File getResultsDir() throws FileNotFoundException {
+            return mResultsDir;
+        }
+
+        @Override
+        public File getSubPlansDir() throws FileNotFoundException {
+            return mSubPlansDir;
+        }
+    }
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java
new file mode 100644
index 0000000..05d35ec
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/RetryFilterHelperTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.compatibility.common.tradefed.util;
+
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.tradefed.config.OptionSetter;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link RetryFilterHelper}
+ */
+public class RetryFilterHelperTest extends TestCase {
+
+    private static final String TEST_STRING = "abcd";
+    private static final RetryType TEST_RETRY_TYPE = RetryType.FAILED;
+
+    public void testSetAllOptionsFrom() throws Exception {
+        RetryFilterHelper helper = new RetryFilterHelper(null, 0);
+        RetryFilterHelper otherObj = new RetryFilterHelper(null, 0);
+        OptionSetter otherObjSetter = new OptionSetter(otherObj);
+        otherObjSetter.setOptionValue(CompatibilityTest.SUBPLAN_OPTION, TEST_STRING);
+        helper.setAllOptionsFrom(otherObj);
+        assertEquals(TEST_STRING, helper.mSubPlan);
+    }
+
+    public void testClearOptions() throws Exception {
+        RetryFilterHelper helper = new RetryFilterHelper(null, 0);
+        OptionSetter setter = new OptionSetter(helper);
+        setter.setOptionValue(CompatibilityTest.SUBPLAN_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.INCLUDE_FILTER_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.EXCLUDE_FILTER_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.ABI_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.MODULE_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.TEST_OPTION, TEST_STRING);
+        setter.setOptionValue(CompatibilityTest.TEST_OPTION, TEST_RETRY_TYPE.name());
+        helper.clearOptions();
+        assertTrue(helper.mSubPlan == null);
+        assertTrue(helper.mIncludeFilters.isEmpty());
+        assertTrue(helper.mExcludeFilters.isEmpty());
+        assertTrue(helper.mAbiName == null);
+        assertTrue(helper.mModuleName == null);
+        assertTrue(helper.mTestName == null);
+        assertTrue(helper.mRetryType == null);
+    }
+
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
index a00b4eb..18db8df 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceOwnerPasswordTest.java
@@ -46,7 +46,7 @@
                 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
                 dpm.getPasswordQuality(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         String caseDescription = "initial";
         assertPasswordSucceeds("1234", caseDescription);
@@ -56,7 +56,7 @@
         dpm.setPasswordMinimumLength(mAdminComponent, 10);
         caseDescription = "minimum password length = 10";
         assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false); // length not checked for this quality
 
         assertPasswordFails("1234", caseDescription);
         assertPasswordFails("abcd", caseDescription);
@@ -66,7 +66,7 @@
         caseDescription = "minimum password length = 4";
         assertEquals(4, dpm.getPasswordMinimumLength(
                 mAdminComponent));
-        assertTrue(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(true);
 
         assertPasswordSucceeds("1234", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
@@ -78,7 +78,7 @@
                 DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
                 dpm.getPasswordQuality(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());            // failure
+        assertPasswordSufficiency(false);            // failure
 
         String caseDescription = "initial";
         assertPasswordSucceeds("1234", caseDescription);
@@ -88,7 +88,7 @@
         dpm.setPasswordMinimumLength(mAdminComponent, 10);
         caseDescription = "minimum password length = 10";
         assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         assertPasswordFails("1234", caseDescription);
         assertPasswordFails("abcd", caseDescription);
@@ -98,7 +98,7 @@
         caseDescription = "minimum password length = 4";
         assertEquals(4, dpm.getPasswordMinimumLength(
                 mAdminComponent));
-        assertTrue(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(true);
 
         assertPasswordSucceeds("1234", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
@@ -110,7 +110,7 @@
                 DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC);
         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
                 dpm.getPasswordQuality(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         String caseDescription = "initial";
         assertPasswordFails("1234", caseDescription);      // can't change
@@ -120,7 +120,7 @@
         dpm.setPasswordMinimumLength(mAdminComponent, 10);
         caseDescription = "minimum password length = 10";
         assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         assertPasswordFails("1234", caseDescription);
         assertPasswordFails("abcd", caseDescription);
@@ -130,7 +130,7 @@
         caseDescription = "minimum password length = 4";
         assertEquals(4, dpm.getPasswordMinimumLength(
                 mAdminComponent));
-        assertTrue(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(true);
 
         assertPasswordFails("1234", caseDescription);
         assertPasswordSucceeds("abcd", caseDescription);
@@ -142,7 +142,7 @@
                 DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC);
         assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
                 dpm.getPasswordQuality(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         String caseDescription = "initial";
         assertPasswordFails("1234", caseDescription);
@@ -152,7 +152,7 @@
         dpm.setPasswordMinimumLength(mAdminComponent, 10);
         caseDescription = "minimum password length = 10";
         assertEquals(10, dpm.getPasswordMinimumLength(mAdminComponent));
-        assertFalse(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(false);
 
         assertPasswordFails("1234", caseDescription);
         assertPasswordFails("abcd", caseDescription);
@@ -162,7 +162,7 @@
         caseDescription = "minimum password length = 4";
         assertEquals(4, dpm.getPasswordMinimumLength(
                 mAdminComponent));
-        assertTrue(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(true);
 
         assertPasswordFails("1234", caseDescription);
         assertPasswordFails("abcd", caseDescription);
@@ -371,6 +371,21 @@
     private void assertPasswordSucceeds(String password, String restriction) {
         boolean passwordResetResult = dpm.resetPassword(password, /* flags= */0);
         assertTrue("Password '" + password + "' failed on " + restriction, passwordResetResult);
-        assertTrue(dpm.isActivePasswordSufficient());
+        assertPasswordSufficiency(true);
+    }
+
+    private void assertPasswordSufficiency(boolean expectPasswordSufficient) {
+        int retries = 15;
+        // isActivePasswordSufficient() gets the result asynchronously so let's retry a few times
+        while (retries >= 0 && dpm.isActivePasswordSufficient() != expectPasswordSufficient) {
+            retries--;
+            try {
+                Thread.sleep(200);
+            } catch (InterruptedException e) {
+                break;
+            }
+        }
+        assertEquals(expectPasswordSufficient, dpm.isActivePasswordSufficient());
+
     }
 }
diff --git a/hostsidetests/services/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
index 6e9aead..2804d3b 100755
--- a/hostsidetests/services/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
@@ -159,6 +159,8 @@
             android:exported="true"
             android:launchMode="singleInstance"
         />
+        <activity android:name=".MultiWindowSupportObserver"
+            android:exported="true" />
     </application>
 </manifest>
 
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/MultiWindowSupportObserver.java b/hostsidetests/services/activitymanager/app/src/android/server/app/MultiWindowSupportObserver.java
new file mode 100644
index 0000000..0d54912
--- /dev/null
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/MultiWindowSupportObserver.java
@@ -0,0 +1,18 @@
+package android.server.app;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Log;
+
+public class MultiWindowSupportObserver extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        int id = Resources.getSystem().getIdentifier("config_supportsMultiWindow", "bool", "android");
+        boolean support = Resources.getSystem().getBoolean(id);
+        Log.i(getClass().getSimpleName(), "HEAD=OK");
+        Log.i(getClass().getSimpleName(), "DROP=OK");
+        Log.i(getClass().getSimpleName(), "config_supportsMultiWindow="+support);
+    }
+}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
index 3a21fda..09bb294 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
@@ -26,7 +26,9 @@
 import java.lang.Exception;
 import java.lang.Integer;
 import java.lang.String;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -78,6 +80,11 @@
     protected ITestDevice mDevice;
 
     private HashSet<String> mAvailableFeatures;
+    final private String RESULT_KEY_HEAD = "HEAD";
+    final private String SUPPORT_OBSERVER = "MultiWindowSupportObserver";
+    private static boolean mConfigLoaded = false;
+    private static boolean mSupportMultiWindow = true;
+
 
     protected static String getAmStartCmd(final String activityName) {
         return "am start -n " + getActivityComponentName(activityName);
@@ -218,6 +225,68 @@
                 || PRETEND_DEVICE_SUPPORTS_FREEFORM;
     }
 
+    protected boolean supportsMultiWindowMode() {
+        if (!mConfigLoaded) {
+            try {
+                executeShellCommand("am start -n " + "android.server.app/." + SUPPORT_OBSERVER);
+                waitForResume("android.server.app", SUPPORT_OBSERVER);
+                Map map = getLogResults(SUPPORT_OBSERVER);
+                String value = (String)map.get(RESULT_KEY_HEAD);
+                if (value != null && value.equals("OK")) {
+                    mConfigLoaded = true;
+                    mSupportMultiWindow = !map.get("config_supportsMultiWindow").equals("false");
+                }
+                executeShellCommand(AM_FORCE_STOP_TEST_PACKAGE);
+                clearLogs();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return mSupportMultiWindow;
+    }
+
+    private void clearLogs() throws DeviceNotAvailableException {
+        executeShellCommand("logcat -c");
+    }
+
+    private Map<String, String> getLogResults(String className) throws Exception {
+        int retryCount = 3;
+        Map<String, String> output = new HashMap<String, String>();
+        do {
+
+            String logs = executeShellCommand("logcat -v brief -d " + className + ":I" + " *:S");
+            for (String line : logs.split("\\n")) {
+                if (line.startsWith("I/" + className)) {
+                    String payload = line.split(":")[1].trim();
+                    final String[] split = payload.split("=");
+                    if (split.length > 1) {
+                        output.put(split[0], split[1]);
+                    }
+                }
+            }
+            if (output.containsKey(RESULT_KEY_HEAD)) {
+                return output;
+            }
+        } while (retryCount-- > 0);
+        return output;
+    }
+
+    private void waitForResume(String packageName, String activityName) throws Exception {
+        final String fullActivityName = packageName + "." + activityName;
+        int retryCount = 3;
+        do {
+            Thread.sleep(500);
+            String logs = executeShellCommand("logcat -d -b events");
+            for (String line : logs.split("\\n")) {
+                if(line.contains("am_on_resume_called") && line.contains(fullActivityName)) {
+                    return;
+                }
+            }
+        } while (retryCount-- > 0);
+
+        throw new Exception(fullActivityName + " has failed to start");
+    }
+
     protected boolean hasDeviceFeature(String requiredFeature) throws DeviceNotAvailableException {
         if (mAvailableFeatures == null) {
             // TODO: Move this logic to ITestDevice.
diff --git a/hostsidetests/services/windowmanager/dndsourceapp/AndroidManifest.xml b/hostsidetests/services/windowmanager/dndsourceapp/AndroidManifest.xml
index 296a979..a45b62f 100644
--- a/hostsidetests/services/windowmanager/dndsourceapp/AndroidManifest.xml
+++ b/hostsidetests/services/windowmanager/dndsourceapp/AndroidManifest.xml
@@ -23,7 +23,8 @@
                 <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
-
+        <activity android:name=".MultiWindowSupportObserver"
+            android:exported="true"/>
         <provider android:name="android.wm.cts.dndsourceapp.DragSourceContentProvider"
                   android:authorities="android.wm.cts.dndsource.contentprovider"
                   android:grantUriPermissions="true"/>
diff --git a/hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/MultiWindowSupportObserver.java b/hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/MultiWindowSupportObserver.java
new file mode 100644
index 0000000..b8bd3c6
--- /dev/null
+++ b/hostsidetests/services/windowmanager/dndsourceapp/src/android/wm/cts/dndsourceapp/MultiWindowSupportObserver.java
@@ -0,0 +1,18 @@
+package android.wm.cts.dndsourceapp;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.util.Log;
+
+public class MultiWindowSupportObserver extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        int id = Resources.getSystem().getIdentifier("config_supportsMultiWindow", "bool", "android");
+        boolean support = Resources.getSystem().getBoolean(id);
+        Log.i(getClass().getSimpleName(), "HEAD=OK");
+        Log.i(getClass().getSimpleName(), "DROP=OK");
+        Log.i(getClass().getSimpleName(), "config_supportsMultiWindow="+support);
+    }
+}
diff --git a/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java b/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java
index ccdba15..b63b633 100644
--- a/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java
+++ b/hostsidetests/services/windowmanager/src/android/wm/cts/CrossAppDragAndDropTests.java
@@ -52,7 +52,10 @@
     private static final String SOURCE_PACKAGE_NAME = "android.wm.cts.dndsourceapp";
     private static final String TARGET_PACKAGE_NAME = "android.wm.cts.dndtargetapp";
     private static final String TARGET_23_PACKAGE_NAME = "android.wm.cts.dndtargetappsdk23";
-
+    private static boolean mConfigLoaded = false;
+    private final String SUPPORT_OBSERVER = "MultiWindowSupportObserver";
+    private static boolean mSupportMultiWindow = true;
+    private final String AM_FORCE_STOP_TEST_PACKAGE = "am force-stop android.wm.cts.dndsourceapp";
 
     private static final String SOURCE_ACTIVITY_NAME = "DragSource";
     private static final String TARGET_ACTIVITY_NAME = "DropTarget";
@@ -393,4 +396,23 @@
     public void testGrantWriteRequestWrite() throws Exception {
         doTestDragAndDrop(GRANT_WRITE, REQUEST_WRITE, RESULT_OK);
     }
+    protected boolean supportsMultiWindowMode() {
+        if (!mConfigLoaded) {
+            try {
+                executeShellCommand("am start -n " + "android.wm.cts.dndsourceapp/." + SUPPORT_OBSERVER);
+                waitForResume("android.wm.cts.dndsourceapp", SUPPORT_OBSERVER);
+                Map map = getLogResults(SUPPORT_OBSERVER);
+                String value = (String)map.get(RESULT_KEY_DROP_RESULT);
+                if (value != null && value.equals("OK")) {
+                   mConfigLoaded = true;
+                    mSupportMultiWindow = !map.get("config_supportsMultiWindow").equals("false");
+                }
+                executeShellCommand(AM_FORCE_STOP_TEST_PACKAGE);
+                clearLogs();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return mSupportMultiWindow;
+    }
 }
diff --git a/hostsidetests/theme/assets/24/560dpi.zip b/hostsidetests/theme/assets/24/560dpi.zip
index a171f7c..9a65c38 100644
--- a/hostsidetests/theme/assets/24/560dpi.zip
+++ b/hostsidetests/theme/assets/24/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/24/mdpi.zip b/hostsidetests/theme/assets/24/mdpi.zip
new file mode 100755
index 0000000..6e4528f
--- /dev/null
+++ b/hostsidetests/theme/assets/24/mdpi.zip
Binary files differ
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index 1a474c5..3af213e 100644
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -1014,6 +1014,7 @@
         updateLocationAndWait(TEST_MOCK_PROVIDER_NAME, realProviderToFool, latitude, longitude);
     }
 
+    @UiThreadTest
     public void testGpsStatusListener() {
         MockGpsStatusListener listener = new MockGpsStatusListener();
         mManager.addGpsStatusListener(listener);
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/WorkDir.java b/tests/tests/mediastress/src/android/mediastress/cts/WorkDir.java
index 4a40fad..2af3c6b 100644
--- a/tests/tests/mediastress/src/android/mediastress/cts/WorkDir.java
+++ b/tests/tests/mediastress/src/android/mediastress/cts/WorkDir.java
@@ -16,13 +16,18 @@
 
 package android.mediastress.cts;
 
+import android.os.Bundle;
 import android.os.Environment;
+import android.support.test.InstrumentationRegistry;
 
 import java.io.File;
 
 import junit.framework.Assert;
 
 public class WorkDir {
+
+    private static final String MEDIA_PATH_INSTR_ARG_KEY = "media-path";
+
     static final File getTopDir() {
         Assert.assertEquals(Environment.getExternalStorageState(), Environment.MEDIA_MOUNTED);
         return Environment.getExternalStorageDirectory();
@@ -33,6 +38,13 @@
     }
 
     static final String getMediaDirString() {
-        return (getTopDirString() + "test/");
+        Bundle bundle = InstrumentationRegistry.getArguments();
+        String mediaDirString = bundle.getString(MEDIA_PATH_INSTR_ARG_KEY);
+        if (mediaDirString != null) {
+            // user has specified the mediaDirString via instrumentation-arg
+            return mediaDirString + ((mediaDirString.endsWith("/")) ? "" : "/");
+        } else {
+            return (getTopDirString() + "test/");
+        }
     }
 }
diff --git a/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java b/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
index 94b2d14..6164936 100644
--- a/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/BundledTvInputServiceTest.java
@@ -157,6 +157,10 @@
         if (!Utils.hasTvInputFramework(getActivity())) {
             return;
         }
+        if (mPassthroughInputList.size() == 0) {
+            // The device does not have any passthrough inputs. Skipping the stress test.
+            return;
+        }
         // Tuning should be completed within 3 seconds on average, therefore, we set 100 iterations
         // here to fit the test case running time in 5 minutes limitation of CTS test cases.
         final int ITERATIONS = 100;
diff --git a/tools/cts-tradefed/res/config/cts-preconditions.xml b/tools/cts-tradefed/res/config/cts-preconditions.xml
index 3430576..e521ebd 100644
--- a/tools/cts-tradefed/res/config/cts-preconditions.xml
+++ b/tools/cts-tradefed/res/config/cts-preconditions.xml
@@ -53,6 +53,7 @@
         <option name="src-dir" value="/sdcard/device-info-files/"/>
         <option name="dest-dir" value="device-info-files/"/>
         <option name="temp-dir" value="temp-device-info-files/"/>
+        <option name="throw-error" value="false"/>
     </target_preparer>
 
     <!-- The following values are used in cts/common/device-side/util/DeviceReportLog.java,