resolve merge conflicts of c0bb907996 to oc-dev

Test: I solemnly swear I tested this conflict resolution.
Change-Id: I20764be881a12c73ca0b01564b7fc408efcc58fe
merged-in: I49ba463417bbfa8b1ef69466c54bbb8b83b2fa8d
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 09fed81..970e35f 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;
@@ -404,7 +404,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 6887d48..4df29c6 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
@@ -17,7 +17,7 @@
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 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/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index 30d5f95..9f07307 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,15 +19,16 @@
 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.util.OptionHelper;
+import com.android.compatibility.common.tradefed.util.RetryFilterHelper;
+import com.android.compatibility.common.tradefed.util.RetryType;
 import com.android.compatibility.common.util.IInvocationResult;
 import com.android.compatibility.common.util.ResultHandler;
 import com.android.compatibility.common.util.TestFilter;
 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.Option;
 import com.android.tradefed.config.Option.Importance;
@@ -153,10 +154,6 @@
             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.",
@@ -671,113 +668,45 @@
      */
     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) {
-                throw new RuntimeException(e);
+            } else if (mTestName != null) {
+                throw new IllegalArgumentException(
+                        "Test name given without module name. Add --module <module-name>");
             }
-        } 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/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 bb96ed2..b6861db 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
@@ -25,7 +25,7 @@
 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.CompatibilityHostTestBaseTest;
@@ -36,6 +36,7 @@
 import com.android.compatibility.common.tradefed.testtype.SubPlanTest;
 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 org.junit.runner.RunWith;
 import org.junit.runners.Suite;
@@ -67,7 +68,7 @@
     ConsoleReporterTest.class,
     MetadataReporterTest.class,
     ResultReporterTest.class,
-    SubPlanCreatorTest.class,
+    SubPlanHelperTest.class,
 
     // targetprep
     PropertyCheckTest.class,
@@ -84,6 +85,7 @@
     // util
     CollectorUtilTest.class,
     OptionHelperTest.class,
+    RetryFilterHelperTest.class,
 })
 public class UnitTests {
     // empty on purpose
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
similarity index 96%
rename from common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
rename to common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
index e3240c1..a19eb30 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanHelperTest.java
@@ -39,7 +39,7 @@
 import java.util.Arrays;
 import java.util.Set;
 
-public class SubPlanCreatorTest extends TestCase {
+public class SubPlanHelperTest extends TestCase {
 
     // Values used to populate mock results
     private static final String SUITE_NAME = "CTS";
@@ -75,7 +75,7 @@
     private static final String SP_RESULT_TYPE_NOT_EXECUTED = "not_executed";
 
     private CompatibilityBuildHelper mBuildHelper;
-    private SubPlanCreator mSubPlanCreator;
+    private SubPlanHelper mSubPlanHelper;
 
     private File mResultsDir = null;
     private File mResultDir = null;
@@ -89,8 +89,8 @@
         mBuildHelper = new SpctMockCompatibilityBuildHelper(new BuildInfo("0", "", ""));
         populateResults();
 
-        mSubPlanCreator = new SubPlanCreator();
-        ArgsOptionParser optionParser = new ArgsOptionParser(mSubPlanCreator);
+        mSubPlanHelper = new SubPlanHelper();
+        ArgsOptionParser optionParser = new ArgsOptionParser(mSubPlanHelper);
         optionParser.parse(Arrays.asList(
             "-n", SP_NAME,
             "--session", SP_SESSION,
@@ -109,7 +109,7 @@
     }
 
     public void testCreateSubPlan() throws Exception {
-        ISubPlan plan = mSubPlanCreator.createSubPlan(mBuildHelper);
+        ISubPlan plan = mSubPlanHelper.createSubPlan(mBuildHelper);
         Set<String> planIncludes = plan.getIncludeFilters();
         Set<String> planExcludes = plan.getExcludeFilters();
         TestFilter mf1 = new TestFilter(ABI, NAME_A, null);
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);
+    }
+
+}