Merge "Add 64bit support to CTS Tradefed" into lmp-dev
diff --git a/tools/cts-xml-generator/src/Android.mk b/tools/cts-xml-generator/src/Android.mk
index 62f8692..a6d85b6 100644
--- a/tools/cts-xml-generator/src/Android.mk
+++ b/tools/cts-xml-generator/src/Android.mk
@@ -14,12 +14,14 @@
 
 LOCAL_PATH := $(call my-dir)
 
-
 # cts-xml-generator java library
 # ============================================================
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES := \
+    $(call all-subdir-java-files) \
+    ../../../libs/commonutil/src/com/android/cts/util/AbiUtils.java
+
 LOCAL_JAR_MANIFEST := MANIFEST.mf
 
 LOCAL_MODULE := cts-xml-generator
diff --git a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
index 37e94ae..f194edf 100644
--- a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
+++ b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
@@ -39,7 +39,7 @@
         System.err.println("Usage: cts-xml-generator -p PACKAGE_NAME -n NAME [-t TEST_TYPE]"
                 + " [-j JAR_PATH] [-i INSTRUMENTATION] [-m MANIFEST_FILE] [-e EXPECTATION_FILE]"
                 + " [-b UNSUPPORTED_ABI_FILE] [-a ARCHITECTURE] [-o OUTPUT_FILE]"
-                + " [-n APP_NAME_SPACE] [-x ADDITIONAL_ATTRIBUTE_KEY->VALUE]");
+                + " [-s APP_NAME_SPACE] [-x ADDITIONAL_ATTRIBUTE_KEY->VALUE]");
         System.exit(1);
     }
 
diff --git a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
index 59b9fb3..1680cae 100644
--- a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
+++ b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.xmlgenerator;
 
+import com.android.cts.util.AbiUtils;
+
 import vogar.Expectation;
 import vogar.ExpectationStore;
 
@@ -27,8 +29,6 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.List;
 import java.util.Set;
@@ -50,29 +50,6 @@
  */
 class XmlGenerator {
 
-    private static final Set<String> ARM_ABI = new HashSet<String>();
-    private static final Set<String> INTEL_ABI = new HashSet<String>();
-    private static final Set<String> MIPS_ABI = new HashSet<String>();
-    private static final Set<String> SUPPORTED_ABIS = new HashSet<String>();
-    private static final Map<String, Set<String>> ARCH_TO_ABIS = new HashMap<String, Set<String>>();
-    static {
-        ARM_ABI.add("armeabi-v7a");
-        ARM_ABI.add("arm64-v8a");
-        INTEL_ABI.add("x86");
-        INTEL_ABI.add("x86_64");
-        MIPS_ABI.add("mips");
-        MIPS_ABI.add("mips64");
-        ARCH_TO_ABIS.put("arm", ARM_ABI);
-        ARCH_TO_ABIS.put("arm64", ARM_ABI);
-        ARCH_TO_ABIS.put("x86", INTEL_ABI);
-        ARCH_TO_ABIS.put("x86_64", INTEL_ABI);
-        ARCH_TO_ABIS.put("mips", MIPS_ABI);
-        ARCH_TO_ABIS.put("mips64", MIPS_ABI);
-        SUPPORTED_ABIS.addAll(ARM_ABI);
-        SUPPORTED_ABIS.addAll(INTEL_ABI);
-        SUPPORTED_ABIS.addAll(MIPS_ABI);
-    }
-
     /** Example: com.android.cts.holo */
     private final String mAppNamespace;
 
@@ -256,7 +233,7 @@
     // Returns the list of ABIs supported by this TestCase on this architecture.
     public static Set<String> getSupportedAbis(ExpectationStore expectationStore,
             String architecture, String className) {
-        Set<String> supportedAbis = new HashSet<String>(ARCH_TO_ABIS.get(architecture));
+        Set<String> supportedAbis = AbiUtils.getAbisForArch(architecture);
         Expectation e = (expectationStore == null) ? null : expectationStore.get(className);
         if (e != null && !e.getDescription().isEmpty()) {
             // Description should be written in the form "blah blah: abi1, abi2..."
@@ -264,7 +241,7 @@
             String[] unsupportedAbis = description.split(",");
             for (String a : unsupportedAbis) {
                 String abi = a.trim();
-                if (!SUPPORTED_ABIS.contains(abi)) {
+                if (!AbiUtils.isAbiSupportedByCts(abi)) {
                     throw new RuntimeException(
                             String.format("Unrecognised ABI %s in %s", abi, e.getDescription()));
                 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java b/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
index 24190c5..7333de2 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
@@ -23,6 +23,7 @@
 import com.android.cts.tradefed.result.TestResultRepo;
 import com.android.cts.tradefed.testtype.ITestPackageRepo;
 import com.android.cts.tradefed.testtype.TestPackageRepo;
+import com.android.cts.util.AbiUtils;
 import com.android.tradefed.command.Console;
 import com.android.tradefed.config.ArgsOptionParser;
 import com.android.tradefed.config.ConfigurationException;
@@ -38,6 +39,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Specialization of trade federation console that adds CTS commands to list plans and packages.
@@ -78,7 +80,7 @@
             public void run() {
                 CtsBuildHelper ctsBuild = getCtsBuild();
                 if (ctsBuild != null) {
-                    listPackages(ctsBuild);
+                    listPackages(ctsBuild, AbiUtils.getAbisSupportedByCts());
                 }
             }
         }, LIST_PATTERN, "packages");
@@ -114,7 +116,8 @@
                 }
                 CtsBuildHelper ctsBuild = getCtsBuild();
                 if (ctsBuild != null) {
-                    addDerivedPlan(ctsBuild, flatArgs);
+                    // FIXME may want to only add certain ABIs
+                    addDerivedPlan(ctsBuild, AbiUtils.getAbisSupportedByCts(), flatArgs);
                 }
             }
         };
@@ -190,10 +193,10 @@
         }
     }
 
-    private void listPackages(CtsBuildHelper ctsBuild) {
-        ITestPackageRepo testCaseRepo = new TestPackageRepo(ctsBuild.getTestCasesDir(), false);
-        for (String packageUri : testCaseRepo.getPackageNames()) {
-            printLine(packageUri);
+    private void listPackages(CtsBuildHelper ctsBuild, Set<String> abis) {
+        ITestPackageRepo testCaseRepo = new TestPackageRepo(ctsBuild.getTestCasesDir(), abis, false);
+        for (String packageName : testCaseRepo.getPackageNames()) {
+            printLine(packageName);
         }
     }
 
@@ -215,12 +218,12 @@
         tableFormatter.displayTable(table, new PrintWriter(System.out, true));
     }
 
-    private void addDerivedPlan(CtsBuildHelper ctsBuild, String[] flatArgs) {
+    private void addDerivedPlan(CtsBuildHelper ctsBuild, Set<String> abis, String[] flatArgs) {
         PlanCreator creator = new PlanCreator();
         try {
             ArgsOptionParser optionParser = new ArgsOptionParser(creator);
             optionParser.parse(Arrays.asList(flatArgs));
-            creator.createAndSerializeDerivedPlan(ctsBuild);
+            creator.createAndSerializeDerivedPlan(ctsBuild, abis);
         } catch (ConfigurationException e) {
             printLine("Error: " + e.getMessage());
             printLine(ArgsOptionParser.getOptionHelp(false, creator));
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsReportUtil.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsReportUtil.java
deleted file mode 100644
index 97f607e..0000000
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsReportUtil.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 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.cts.tradefed.result;
-
-import java.util.Map;
-
-/**
- * Static utility class for handling Cts Results.
- */
-public class CtsReportUtil {
-    private static final String CTS_RESULT_KEY = "CTS_RESULT";
-
-    /**
-     * Utility method to extract CTS result from test metrics
-     * @param testMetrics
-     * @return result or null if not found
-     */
-    public static String getCtsResultFromMetrics(Map<String, String> testMetrics) {
-        for (Map.Entry<String, String> entry: testMetrics.entrySet()) {
-            if (CTS_RESULT_KEY.equals(entry.getKey())) {
-                return entry.getValue();
-            }
-        }
-        return null;
-    }
-}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsTestLogReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsTestLogReporter.java
index bbdcb05..4a1bfb5 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsTestLogReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsTestLogReporter.java
@@ -17,6 +17,7 @@
 package com.android.cts.tradefed.result;
 
 import com.android.cts.tradefed.device.DeviceInfoCollector;
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.ddmlib.testrunner.TestIdentifier;
@@ -49,24 +50,28 @@
     }
 
     /**
-     * {@inheritDoc}
+     * Reports the start of a test run.
+     *
+     * @param id the unique identifier of this test run, generated by
+     * {@link AbiUtils#createId(String, String)}.
+     * @param numTests total number of tests in test run
      */
     @Override
-    public void testRunStarted(String name, int numTests) {
-        if (mCurrentPkgResult != null && !name.equals(mCurrentPkgResult.getAppPackageName())) {
+    public void testRunStarted(String id, int numTests) {
+        if (mCurrentPkgResult != null && !id.equals(mCurrentPkgResult.getId())) {
             // display results from previous run
             logCompleteRun(mCurrentPkgResult);
         }
-        mIsDeviceInfoRun = name.equals(DeviceInfoCollector.APP_PACKAGE_NAME);
+        mIsDeviceInfoRun = DeviceInfoCollector.IDS.contains(id);
         if (mIsDeviceInfoRun) {
             logResult("Collecting device info");
         } else  {
-            if (mCurrentPkgResult == null || !name.equals(mCurrentPkgResult.getAppPackageName())) {
+            if (mCurrentPkgResult == null || !id.equals(mCurrentPkgResult.getId())) {
                 logResult("-----------------------------------------");
-                logResult("Test package %s started", name);
+                logResult("Test package %s started", id);
                 logResult("-----------------------------------------");
             }
-            mCurrentPkgResult = mResults.getOrCreatePackage(name);
+            mCurrentPkgResult = mResults.getOrCreatePackage(id);
         }
     }
 
@@ -91,7 +96,7 @@
      */
     @Override
     public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
-        mCurrentPkgResult.reportTestEnded(test);
+        mCurrentPkgResult.reportTestEnded(test, testMetrics);
         Test result = mCurrentPkgResult.findTest(test);
         String stack = result.getStackTrace() == null ? "" : "\n" + result.getStackTrace();
         logResult("%s#%s %s %s", test.getClassName(), test.getTestName(), result.getResult(),
@@ -123,7 +128,7 @@
             return;
         }
         logResult("%s package complete: Passed %d, Failed %d, Not Executed %d",
-                pkgResult.getAppPackageName(), pkgResult.countTests(CtsTestStatus.PASS),
+                pkgResult.getId(), pkgResult.countTests(CtsTestStatus.PASS),
                 pkgResult.countTests(CtsTestStatus.FAIL),
                 pkgResult.countTests(CtsTestStatus.NOT_EXECUTED));
     }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
index 5357588..0d21fc6 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
@@ -19,7 +19,6 @@
 import com.android.cts.tradefed.build.CtsBuildHelper;
 import com.android.cts.tradefed.device.DeviceInfoCollector;
 import com.android.cts.tradefed.testtype.CtsTest;
-import com.android.cts.tradefed.util.CtsHostStore;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.ddmlib.testrunner.TestIdentifier;
@@ -48,8 +47,6 @@
 import java.io.OutputStream;
 import java.util.List;
 import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * Writes results to an XML files in the CTS format.
@@ -104,13 +101,6 @@
     private String mSuiteName;
     private String mReferenceUrl;
 
-    private static final Pattern mCtsLogPattern = Pattern.compile("(.*)\\+\\+\\+\\+(.*)");
-
-    @Option(name = AbiFormatter.FORCE_ABI_STRING,
-            description = AbiFormatter.FORCE_ABI_DESCRIPTION,
-            importance = Importance.IF_UNSET)
-    private String mForceAbi = null;
-
     public void setReportDir(File reportDir) {
         mReportDir = reportDir;
     }
@@ -232,9 +222,9 @@
 
 
     @Override
-    public void testRunStarted(String name, int numTests) {
-        mCurrentPkgResult = mResults.getOrCreatePackage(name);
-        mIsDeviceInfoRun = name.equals(DeviceInfoCollector.APP_PACKAGE_NAME);
+    public void testRunStarted(String id, int numTests) {
+        mCurrentPkgResult = mResults.getOrCreatePackage(id);
+        mIsDeviceInfoRun = DeviceInfoCollector.IDS.contains(id);
     }
 
     /**
@@ -258,33 +248,7 @@
      */
     @Override
     public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
-        collectCtsResults(test, testMetrics);
-        mCurrentPkgResult.reportTestEnded(test);
-    }
-
-    /**
-     * Collect Cts results for both device and host tests to the package result.
-     * @param test test ran
-     * @param testMetrics test metrics which can contain performance result for device tests
-     */
-    private void collectCtsResults(TestIdentifier test, Map<String, String> testMetrics) {
-        // device test can have performance results in testMetrics
-        String perfResult = CtsReportUtil.getCtsResultFromMetrics(testMetrics);
-        // host test should be checked in CtsHostStore.
-        if (perfResult == null) {
-            perfResult = CtsHostStore.removeCtsResult(mDeviceSerial, "armeabi-v7a", test.toString());
-        }
-        if (perfResult != null) {
-            // CTS result is passed in Summary++++Details format.
-            // Extract Summary and Details, and pass them.
-            Matcher m = mCtsLogPattern.matcher(perfResult);
-            if (m.find()) {
-                mCurrentPkgResult.reportPerformanceResult(test, CtsTestStatus.PASS, m.group(1),
-                        m.group(2));
-            } else {
-                logResult("CTS Result unrecognizable:" + perfResult);
-            }
-        }
+        mCurrentPkgResult.reportTestEnded(test, testMetrics);
     }
 
     /**
@@ -377,9 +341,6 @@
         serializer.attribute(ns, "endtime", endTime);
         serializer.attribute(ns, "version", CTS_RESULT_FILE_VERSION);
         serializer.attribute(ns, "suite", mSuiteName);
-        if (mForceAbi != null) {
-            serializer.attribute(ns, "abi", mForceAbi);
-        }
         mResults.serialize(serializer);
         // TODO: not sure why, but the serializer doesn't like this statement
         //serializer.endTag(ns, RESULT_TAG);
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/DeviceInfoResult.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/DeviceInfoResult.java
index dd91af0..0c947c3 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/DeviceInfoResult.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/DeviceInfoResult.java
@@ -358,7 +358,8 @@
                 DeviceInfoConstants.BUILD_MANUFACTURER, DeviceInfoConstants.BUILD_BOARD,
                 DeviceInfoConstants.BUILD_DEVICE, DeviceInfoConstants.PRODUCT_NAME,
                 DeviceInfoConstants.BUILD_ABI, DeviceInfoConstants.BUILD_ABI2,
-                DeviceInfoConstants.SCREEN_SIZE);
+                DeviceInfoConstants.BUILD_ABIS, DeviceInfoConstants.BUILD_ABIS_32,
+                DeviceInfoConstants.BUILD_ABIS_64, DeviceInfoConstants.SCREEN_SIZE);
     }
 
     private void combineMetrics(Map<String, String> metrics, String... keysToCombine) {
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/ITestResultRepo.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/ITestResultRepo.java
index d672f41..19b0540 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/ITestResultRepo.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/ITestResultRepo.java
@@ -41,7 +41,7 @@
     /**
      * Get the report directory for given result
      * @param sessionId
-     * @return
+     * @return A {@link File} representing the report directory for the given sessionId
      */
     public File getReportDir(int sessionId);
 
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/ITestSummary.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/ITestSummary.java
index 98494ee..e7041ec 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/ITestSummary.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/ITestSummary.java
@@ -23,43 +23,43 @@
     /**
      * @return the session id
      */
-    int getId();
+    public int getId();
 
     /**
      * @return the starting timestamp, also known as result directory name
      */
-    String getTimestamp();
+    public String getTimestamp();
 
     /**
      * @return the num of not executed tests
      */
-    int getNumIncomplete();
+    public int getNumIncomplete();
 
     /**
      * @return the number of failed tests
      */
-    int getNumFailed();
+    public int getNumFailed();
 
     /**
      * @return the number of passed tests
      */
-    int getNumPassed();
+    public int getNumPassed();
 
     /**
      * @return the test plan associated with result
      */
-    String getTestPlan();
+    public String getTestPlan();
 
     /**
      * Return the user-friendly displayed start time stored in result XML.
      * <p/>
      * Expected format: {@link TimeUtil#getTimestamp()}
      */
-    String getStartTime();
+    public String getStartTime();
 
     /**
      * @return a comma separated list of device serials associated with result
      */
-    String getDeviceSerials();
+    public String getDeviceSerials();
 
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/IssueReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/IssueReporter.java
index 9d903dd..319dd54 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/IssueReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/IssueReporter.java
@@ -194,7 +194,7 @@
     }
 
     @Override
-    public void testRunStarted(String name, int numTests) {
+    public void testRunStarted(String id, int numTests) {
     }
 
     @Override
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/PlanCreator.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/PlanCreator.java
index d263bc8..713e8fa 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/PlanCreator.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/PlanCreator.java
@@ -22,6 +22,7 @@
 import com.android.cts.tradefed.testtype.ITestPlan;
 import com.android.cts.tradefed.testtype.TestPackageRepo;
 import com.android.cts.tradefed.testtype.TestPlan;
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.ddmlib.testrunner.TestIdentifier;
@@ -37,6 +38,7 @@
 import java.io.IOException;
 import java.util.Collection;
 import java.util.LinkedHashSet;
+import java.util.Set;
 
 /**
  * Class for creating test plans from CTS result XML.
@@ -88,8 +90,9 @@
      * {@link Option} values must all be set before this is called.
      * @throws ConfigurationException
      */
-    public void createAndSerializeDerivedPlan(CtsBuildHelper build) throws ConfigurationException {
-        ITestPlan derivedPlan = createDerivedPlan(build);
+    public void createAndSerializeDerivedPlan(CtsBuildHelper build, Set<String> abis)
+            throws ConfigurationException {
+        ITestPlan derivedPlan = createDerivedPlan(build, abis);
         if (derivedPlan != null) {
             try {
                 derivedPlan.serialize(new BufferedOutputStream(new FileOutputStream(mPlanFile)));
@@ -110,25 +113,24 @@
      * @return test plan
      * @throws ConfigurationException
      */
-    public ITestPlan createDerivedPlan(CtsBuildHelper build) throws ConfigurationException {
+    public ITestPlan createDerivedPlan(CtsBuildHelper build, Set<String> abis)
+            throws ConfigurationException {
         checkFields(build);
         ITestPackageRepo pkgDefRepo = new TestPackageRepo(build.getTestCasesDir(),
-                mIncludeKnownFailures);
-        ITestPlan derivedPlan = new TestPlan(mPlanName);
+                abis, mIncludeKnownFailures);
+        ITestPlan derivedPlan = new TestPlan(mPlanName, abis);
         for (TestPackageResult pkg : mResult.getPackages()) {
             Collection<TestIdentifier> filteredTests = pkg.getTestsWithStatus(mResultFilter);
-            if (!filteredTests.isEmpty()) {
-                String pkgUri = pkg.getAppPackageName();
-                ITestPackageDef pkgDef = pkgDefRepo.getTestPackage(pkgUri);
-                if (pkgDef != null) {
-                    Collection<TestIdentifier> excludedTests = new LinkedHashSet<TestIdentifier>(
-                            pkgDef.getTests());
-                    excludedTests.removeAll(filteredTests);
-                    derivedPlan.addPackage(pkgUri);
-                    derivedPlan.addExcludedTests(pkgUri, excludedTests);
-                } else {
-                    CLog.e("Could not find package %s in repository", pkgUri);
-                }
+            String pkgId = pkg.getId();
+            ITestPackageDef pkgDef = pkgDefRepo.getTestPackage(pkgId);
+            if (pkgDef != null) {
+                Collection<TestIdentifier> excludedTests =
+                        new LinkedHashSet<TestIdentifier>(pkgDef.getTests());
+                excludedTests.removeAll(filteredTests);
+                derivedPlan.addPackage(pkgId);
+                derivedPlan.addExcludedTests(pkgId, excludedTests);
+            } else {
+                CLog.e("Could not find package %s in repository", pkgId);
             }
         }
         return derivedPlan;
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
index 015a7ae..96c3fd6 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestPackageResult.java
@@ -15,10 +15,9 @@
  */
 package com.android.cts.tradefed.result;
 
-import android.tests.getinfo.DeviceInfoConstants;
-
-import com.android.cts.tradefed.device.DeviceInfoCollector;
 import com.android.cts.tradefed.testtype.CtsTest;
+import com.android.cts.tradefed.util.CtsHostStore;
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.log.LogUtil.CLog;
 
@@ -31,34 +30,56 @@
 import java.util.Collections;
 import java.util.Deque;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Data structure for a CTS test package result.
  * <p/>
  * Provides methods to serialize to XML.
  */
-class TestPackageResult  extends AbstractXmlPullParser {
+class TestPackageResult extends AbstractXmlPullParser {
 
     static final String TAG = "TestPackage";
+
+    public static final String CTS_RESULT_KEY = "CTS_TEST_RESULT";
+    public static final String CTS_ABI_KEY = "CTS_TEST_ABI";
+
     private static final String DIGEST_ATTR = "digest";
     private static final String APP_PACKAGE_NAME_ATTR = "appPackageName";
     private static final String NAME_ATTR = "name";
+    private static final String ABI_ATTR = "abi";
     private static final String ns = CtsXmlResultReporter.ns;
     private static final String SIGNATURE_TEST_PKG = "android.tests.sigtest";
 
+    private static final Pattern mCtsLogPattern = Pattern.compile("(.*)\\+\\+\\+\\+(.*)");
+
+    private String mDeviceSerial;
     private String mAppPackageName;
     private String mName;
+    private String mAbi;
     private String mDigest;
 
     private Map<String, String> mMetrics = new HashMap<String, String>();
+    private Map<TestIdentifier, Map<String, String>> mTestMetrics = new HashMap<TestIdentifier, Map<String, String>>();
 
     private TestSuite mSuiteRoot = new TestSuite(null);
 
+    public void setDeviceSerial(String deviceSerial) {
+        mDeviceSerial = deviceSerial;
+    }
+
+    public String getDeviceSerial() {
+        return mDeviceSerial;
+    }
+
+    public String getId() {
+        return AbiUtils.createId(getAbi(), getAppPackageName());
+    }
+
     public void setAppPackageName(String appPackageName) {
         mAppPackageName = appPackageName;
     }
@@ -75,6 +96,14 @@
         return mName;
     }
 
+    public void setAbi(String abi) {
+        mAbi = abi;
+    }
+
+    public String getAbi() {
+        return mAbi;
+    }
+
     public void setDigest(String digest) {
         mDigest = digest;
     }
@@ -94,7 +123,6 @@
      * Adds a test result to this test package
      *
      * @param testId
-     * @param testResult
      */
     public Test insertTest(TestIdentifier testId) {
         return findTest(testId, true);
@@ -109,8 +137,8 @@
             // should never happen
             classNameSegments.add("UnknownTestClass");
         }
-            String testCaseName = classNameSegments.remove(classNameSegments.size()-1);
-            return mSuiteRoot.findTest(classNameSegments, testCaseName, testId.getTestName(), insertIfMissing);
+        String testCaseName = classNameSegments.remove(classNameSegments.size()-1);
+        return mSuiteRoot.findTest(classNameSegments, testCaseName, testId.getTestName(), insertIfMissing);
     }
 
 
@@ -133,6 +161,7 @@
         serializer.startTag(ns, TAG);
         serializeAttribute(serializer, NAME_ATTR, mName);
         serializeAttribute(serializer, APP_PACKAGE_NAME_ATTR, mAppPackageName);
+        serializeAttribute(serializer, ABI_ATTR, mAbi);
         serializeAttribute(serializer, DIGEST_ATTR, getDigest());
         if (SIGNATURE_TEST_PKG.equals(mName)) {
             serializer.attribute(ns, "signatureCheck", "true");
@@ -170,6 +199,7 @@
         }
         setAppPackageName(getAttribute(parser, APP_PACKAGE_NAME_ATTR));
         setName(getAttribute(parser, NAME_ATTR));
+        setAbi(getAttribute(parser, ABI_ATTR));
         setDigest(getAttribute(parser, DIGEST_ATTR));
         int eventType = parser.getEventType();
         while (eventType != XmlPullParser.END_DOCUMENT) {
@@ -186,10 +216,10 @@
     }
 
     /**
-     * Return a list of {@link TestIdentifer}s contained in this result with the given status
+     * Return a list of {@link TestIdentifier}s contained in this result with the given status
      *
      * @param resultFilter the {@link CtsTestStatus} to filter by
-     * @return a collection of {@link TestIdentifer}s
+     * @return a collection of {@link TestIdentifier}s
      */
     public Collection<TestIdentifier> getTestsWithStatus(CtsTestStatus resultFilter) {
         Collection<TestIdentifier> tests = new LinkedList<TestIdentifier>();
@@ -200,63 +230,47 @@
 
     /**
      * Populate values in this package result from run metrics
-     * @param runResult
+     * @param metrics A map of metrics from the completed test run.
      */
     public void populateMetrics(Map<String, String> metrics) {
         String name = metrics.get(CtsTest.PACKAGE_NAME_METRIC);
         if (name != null) {
             setName(name);
         }
+        String abi = metrics.get(CtsTest.PACKAGE_ABI_METRIC);
+        if (abi != null) {
+            setAbi(abi);
+        }
         String digest = metrics.get(CtsTest.PACKAGE_DIGEST_METRIC);
         if (digest != null) {
             setDigest(digest);
         }
-        if (DeviceInfoCollector.APP_PACKAGE_NAME.equals(getAppPackageName())) {
-            storeDeviceMetrics(metrics);
-        } else {
-            mMetrics.putAll(metrics);
-        }
-    }
+        mMetrics.putAll(metrics);
 
-    /**
-     * Check that the provided device info metrics are consistent with the currently stored metrics.
-     * <p/>
-     * If any inconsistencies occur, logs errors and stores error messages in the metrics map
-     *
-     * @param metrics
-     */
-    private void storeDeviceMetrics(Map<String, String> metrics) {
-        // TODO centralize all the device metrics handling into a single class
-        if (mMetrics.isEmpty()) {
-            // nothing to check!
-            mMetrics.putAll(metrics);
-            return;
-        }
-        // ensure all the metrics we expect to be identical actually are
-        checkMetrics(metrics, DeviceInfoConstants.BUILD_FINGERPRINT,
-                DeviceInfoConstants.BUILD_MODEL, DeviceInfoConstants.BUILD_BRAND,
-                DeviceInfoConstants.BUILD_MANUFACTURER, DeviceInfoConstants.BUILD_BOARD,
-                DeviceInfoConstants.BUILD_DEVICE, DeviceInfoConstants.PRODUCT_NAME,
-                DeviceInfoConstants.BUILD_ABI, DeviceInfoConstants.BUILD_ABI2,
-                DeviceInfoConstants.SCREEN_SIZE);
-    }
-
-    private void checkMetrics(Map<String, String> metrics, String... keysToCheck) {
-        Set<String> keyCheckSet = new HashSet<String>();
-        Collections.addAll(keyCheckSet, keysToCheck);
-        for (Map.Entry<String, String> metricEntry : metrics.entrySet()) {
-            String currentValue = mMetrics.get(metricEntry.getKey());
-            if (keyCheckSet.contains(metricEntry.getKey()) && currentValue != null
-                    && !metricEntry.getValue().equals(currentValue)) {
-                CLog.e("Inconsistent info collected from devices. "
-                        + "Current result has %s='%s', Received '%s'. Are you sharding or " +
-                        "resuming a test run across different devices and/or builds?",
-                        metricEntry.getKey(), currentValue, metricEntry.getValue());
-                mMetrics.put(metricEntry.getKey(),
-                        String.format("ERROR: Inconsistent results: %s, %s",
-                                metricEntry.getValue(), currentValue));
-            } else {
-                mMetrics.put(metricEntry.getKey(), metricEntry.getValue());
+        // Collect performance results
+        for (TestIdentifier test : mTestMetrics.keySet()) {
+            // device test can have performance results in test metrics
+            String perfResult = null;
+            Map<String, String> map = mTestMetrics.get(test);
+            if(mAbi.equals(map.get(CTS_ABI_KEY))) {
+                perfResult = map.get(CTS_RESULT_KEY);
+            }
+            // host test should be checked in CtsHostStore.
+            if (perfResult == null) {
+                perfResult = CtsHostStore.removeCtsResult(mDeviceSerial, mAbi, test.toString());
+            }
+            if (perfResult != null) {
+                // CTS result is passed in Summary++++Details format.
+                // Extract Summary and Details, and pass them.
+                Matcher m = mCtsLogPattern.matcher(perfResult);
+                if (m.find()) {
+                    Test result = findTest(test);
+                    result.setResultStatus(CtsTestStatus.PASS);
+                    result.setSummary(m.group(1));
+                    result.setDetails(m.group(2));
+                } else {
+                    CLog.e("CTS Result unrecognizable:" + perfResult);
+                }
             }
         }
     }
@@ -275,29 +289,18 @@
     }
 
     /**
-     * report performance result
-     * @param test
-     * @param status
-     * @param perf
-     */
-    public void reportPerformanceResult(TestIdentifier test, CtsTestStatus status, String summary, String details) {
-        Test result = findTest(test);
-        result.setResultStatus(status);
-        result.setSummary(summary);
-        result.setDetails(details);
-    }
-
-    /**
      * Report that the given test has completed.
      *
-     * @param test
+     * @param test The {@link TestIdentifier} of the completed test.
+     * @param testMetrics A map holding metrics about the completed test, if any.
      */
-    public void reportTestEnded(TestIdentifier test) {
+    public void reportTestEnded(TestIdentifier test, Map<String, String> testMetrics) {
         Test result = findTest(test);
         if (!result.getResult().equals(CtsTestStatus.FAIL)) {
             result.setResultStatus(CtsTestStatus.PASS);
         }
         result.updateEndTime();
+        mTestMetrics.put(test, testMetrics);
     }
 
     /**
@@ -311,9 +314,10 @@
     }
 
     /**
-     * @return
+     * @return A map holding the metrics from the test run.
      */
     public Map<String, String> getMetrics() {
         return mMetrics;
     }
+
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestResults.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestResults.java
index 2f9eadd..1355ad7 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/TestResults.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/TestResults.java
@@ -16,6 +16,7 @@
 package com.android.cts.tradefed.result;
 
 import com.android.cts.tradefed.build.CtsBuildProvider;
+import com.android.cts.util.AbiUtils;
 import com.android.tradefed.log.LogUtil.CLog;
 
 import org.kxml2.io.KXmlSerializer;
@@ -49,7 +50,7 @@
     static final String NOT_EXECUTED_ATTR = "notExecuted";
     static final String FAILED_ATTR = "failed";
 
-    private Map<String, TestPackageResult> mPackageMap =
+    private Map<String, TestPackageResult> mPackageResults =
             new LinkedHashMap<String, TestPackageResult>();
     private DeviceInfoResult mDeviceInfo = new DeviceInfoResult();
 
@@ -68,10 +69,10 @@
                     TestPackageResult.TAG)) {
                 TestPackageResult pkg = new TestPackageResult();
                 pkg.parse(parser);
-                if (pkg.getAppPackageName() != null) {
-                    mPackageMap.put(pkg.getAppPackageName(), pkg);
+                if (pkg.getId() != null) {
+                    mPackageResults.put(pkg.getId(), pkg);
                 } else {
-                    CLog.w("Found package with no app package name");
+                    CLog.w("Found package with no id");
                 }
             }
             eventType = parser.next();
@@ -82,17 +83,16 @@
      * @return the list of {@link TestPackageResult}.
      */
     public Collection<TestPackageResult> getPackages() {
-        return mPackageMap.values();
+        return mPackageResults.values();
     }
 
     /**
      * Count the number of tests with given status
-     * @param pass
-     * @return
+     * @param status
      */
     public int countTests(CtsTestStatus status) {
         int total = 0;
-        for (TestPackageResult result : mPackageMap.values()) {
+        for (TestPackageResult result : mPackageResults.values()) {
             total += result.countTests(status);
         }
         return total;
@@ -109,7 +109,7 @@
         serializeHostInfo(serializer);
         serializeTestSummary(serializer);
         // sort before serializing
-        List<TestPackageResult> pkgs = new ArrayList<TestPackageResult>(mPackageMap.values());
+        List<TestPackageResult> pkgs = new ArrayList<TestPackageResult>(mPackageResults.values());
         Collections.sort(pkgs, new PkgComparator());
         for (TestPackageResult r : pkgs) {
             r.serialize(serializer);
@@ -174,27 +174,25 @@
 
     private static class PkgComparator implements Comparator<TestPackageResult> {
 
-        /**
-         * {@inheritDoc}
-         */
         @Override
-        public int compare(TestPackageResult o1, TestPackageResult o2) {
-            return o1.getAppPackageName().compareTo(o2.getAppPackageName());
+        public int compare(TestPackageResult lhs, TestPackageResult rhs) {
+            return lhs.getId().compareTo(rhs.getId());
         }
-
     }
 
     /**
-     * Return existing package with given app package name. If not found, create a new one.
-     * @param name
+     * Return existing package with given id. If not found, create a new one.
+     * @param id
      * @return
      */
-    public TestPackageResult getOrCreatePackage(String appPackageName) {
-        TestPackageResult pkgResult = mPackageMap.get(appPackageName);
+    public TestPackageResult getOrCreatePackage(String id) {
+        TestPackageResult pkgResult = mPackageResults.get(id);
         if (pkgResult == null) {
             pkgResult = new TestPackageResult();
-            pkgResult.setAppPackageName(appPackageName);
-            mPackageMap.put(appPackageName, pkgResult);
+            String[] parts = AbiUtils.parseId(id);
+            pkgResult.setAbi(parts[0]);
+            pkgResult.setAppPackageName(parts[1]);
+            mPackageResults.put(id, pkgResult);
         }
         return pkgResult;
     }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityServiceTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityServiceTestRunner.java
index 17391ed..eafd608 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityServiceTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityServiceTestRunner.java
@@ -73,7 +73,6 @@
     private void installApkAndAssert(String apkName) throws DeviceNotAvailableException {
         File file = FileUtil.getFileForPath(mCtsBuild.getTestCasesDir(), apkName);
         String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
-        Log.d(LOG_TAG, "installPackage options: " + options);
         String errorMessage = getDevice().installPackage(file, true, options);
         TestCase.assertNull("Error installing: " + apkName, errorMessage);
     }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityTestRunner.java
index 932a8a1..9ba4109 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityTestRunner.java
@@ -73,7 +73,6 @@
     private void installApkAndAssert(String apkName) throws DeviceNotAvailableException {
         File file = FileUtil.getFileForPath(mCtsBuild.getTestCasesDir(), apkName);
         String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
-        Log.d(LOG_TAG, "installPackage options: " + options);
         String errorMessage = getDevice().installPackage(file, true, options);
         TestCase.assertNull("Error installing: " + apkName, errorMessage);
     }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsInstrumentationApkTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsInstrumentationApkTest.java
index 1e42f3b..6765305 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsInstrumentationApkTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsInstrumentationApkTest.java
@@ -95,7 +95,6 @@
                 File apkFile = mCtsBuild.getTestApp(apkFileName);
                 String errorCode = null;
                 String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
-                Log.d(LOG_TAG, "installPackage options: " + options);
                 errorCode = mTestDevice.installPackage(apkFile, true, options);
                 if (errorCode != null) {
                     Log.e(LOG_TAG, String.format("Failed to install %s on %s. Reason: %s",
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
index 5917479..67f573d 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
@@ -20,13 +20,13 @@
 import com.android.cts.tradefed.device.DeviceInfoCollector;
 import com.android.cts.tradefed.result.CtsTestStatus;
 import com.android.cts.tradefed.result.PlanCreator;
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.ConfigurationException;
 import com.android.tradefed.config.Option;
-import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.config.Option.Importance;
 import com.android.tradefed.config.OptionCopier;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -42,7 +42,6 @@
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IResumableTest;
 import com.android.tradefed.testtype.IShardableTest;
-import com.android.tradefed.testtype.InstrumentationTest;
 import com.android.tradefed.util.AbiFormatter;
 import com.android.tradefed.util.RunUtil;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
@@ -83,6 +82,7 @@
     public static final String RUN_KNOWN_FAILURES_OPTION = "run-known-failures";
 
     public static final String PACKAGE_NAME_METRIC = "packageName";
+    public static final String PACKAGE_ABI_METRIC = "packageAbi";
     public static final String PACKAGE_DIGEST_METRIC = "packageDigest";
 
     private ITestDevice mDevice;
@@ -207,7 +207,7 @@
          * Return the test run name that should be used for the TestPackage
          */
         String getTestRunName() {
-            return mPackageDef.getUri();
+            return mPackageDef.getId();
         }
     }
 
@@ -434,7 +434,7 @@
         // packages are using the same prerequisite apk (I'm looking at you, CtsTestStubs!)
         Collection<String> prerequisiteApks = getPrerequisiteApks(mRemainingTestPkgs);
         Collection<String> uninstallPackages = getPrerequisitePackageNames(mRemainingTestPkgs);
-        ResultFilter filter = new ResultFilter(listener, mRemainingTestPkgs);
+        List<ResultFilter> filters = new ArrayList<ResultFilter>(mRemainingTestPkgs.size());
 
         try {
             installPrerequisiteApks(prerequisiteApks);
@@ -450,42 +450,15 @@
 
             while (!mRemainingTestPkgs.isEmpty()) {
                 TestPackage knownTests = mRemainingTestPkgs.get(0);
+                ResultFilter filter = new ResultFilter(listener, knownTests);
+                filters.add(filter);
 
                 IRemoteTest test = knownTests.getTestForPackage();
-
-                if (mForceAbi != null) {
-                    OptionSetter optionSetter = null;
-                    boolean hasField = false;
-                    try {
-                        optionSetter = new OptionSetter(test);
-                        if (optionSetter.getTypeForOption(AbiFormatter.FORCE_ABI_STRING)
-                                .equals("string")) {
-                            hasField = true;
-                        }
-                    } catch (ConfigurationException e) {
-                        // ignore if there are tests not taking force-abi option
-                        // for example native test do not need this option.
-                    }
-                    if (hasField) {
-                        try{
-                            optionSetter.setOptionValue(AbiFormatter.FORCE_ABI_STRING, mForceAbi);
-                        } catch (ConfigurationException e) {
-                            CLog.e(e);
-                            throw new RuntimeException(e);
-                        }
-                    }
-                }
-
-                if (test instanceof IDeviceTest) {
-                    ((IDeviceTest) test).setDevice(getDevice());
-                }
                 if (test instanceof IBuildReceiver) {
                     ((IBuildReceiver) test).setBuild(mBuildInfo);
                 }
-                // setForceAbi should be exposed on device test.
-                // This is not the best fix, but works.
-                if (test instanceof InstrumentationTest) {
-                    ((InstrumentationTest)test).setForceAbi(mForceAbi);
+                if (test instanceof IDeviceTest) {
+                    ((IDeviceTest) test).setDevice(getDevice());
                 }
                 if (test instanceof DeqpTestRunner) {
                     ((DeqpTestRunner)test).setCollectLogs(mCollectDeqpLogs);
@@ -520,7 +493,9 @@
             CLog.e(e);
             throw e;
         } finally {
-            filter.reportUnexecutedTests();
+            for (ResultFilter filter : filters) {
+                filter.reportUnexecutedTests();
+            }
         }
     }
 
@@ -538,21 +513,19 @@
                 "CtsViewTestCases",
                 "CtsWidgetTestCases" );
         long intervalInMSec = mRebootIntervalMin * 60 * 1000;
-        if (mDevice.getSerialNumber().startsWith("emulator-")) {
+        if (mDisableReboot || mDevice.getSerialNumber().startsWith("emulator-")) {
             return;
         }
-        if (!mDisableReboot) {
-            long currentTime = System.currentTimeMillis();
-            if (((currentTime - mPrevRebootTime) > intervalInMSec) ||
-                    rebootAfterList.contains(testFinished.getPackageDef().getName()) ||
-                    rebootBeforeList.contains(testToRun.getPackageDef().getName()) ) {
-                Log.i(LOG_TAG,
-                        String.format("Rebooting after running package %s, before package %s",
-                                testFinished.getPackageDef().getName(),
-                                testToRun.getPackageDef().getName()));
-                rebootDevice();
-                mPrevRebootTime = System.currentTimeMillis();
-            }
+        long currentTime = System.currentTimeMillis();
+        if (((currentTime - mPrevRebootTime) > intervalInMSec) ||
+                rebootAfterList.contains(testFinished.getPackageDef().getName()) ||
+                rebootBeforeList.contains(testToRun.getPackageDef().getName()) ) {
+            Log.i(LOG_TAG,
+                    String.format("Rebooting after running package %s, before package %s",
+                            testFinished.getPackageDef().getName(),
+                            testToRun.getPackageDef().getName()));
+            rebootDevice();
+            mPrevRebootTime = System.currentTimeMillis();
         }
     }
 
@@ -592,13 +565,13 @@
     }
     /**
      * Build the list of test packages to run
+     * @throws DeviceNotAvailableException
      */
-    private List<TestPackage> buildTestsToRun() {
+    private List<TestPackage> buildTestsToRun() throws DeviceNotAvailableException {
         List<TestPackage> testPkgList = new LinkedList<TestPackage>();
         try {
             ITestPackageRepo testRepo = createTestCaseRepo();
             Collection<ITestPackageDef> testPkgDefs = getTestPackagesToRun(testRepo);
-
             for (ITestPackageDef testPkgDef : testPkgDefs) {
                 addTestPackage(testPkgList, testPkgDef);
             }
@@ -636,9 +609,10 @@
      * @throws ParseException
      * @throws FileNotFoundException
      * @throws ConfigurationException
+     * @throws DeviceNotAvailableException
      */
     private Collection<ITestPackageDef> getTestPackagesToRun(ITestPackageRepo testRepo)
-            throws ParseException, FileNotFoundException, ConfigurationException {
+            throws ParseException, FileNotFoundException, ConfigurationException, DeviceNotAvailableException {
         // use LinkedHashSet to have predictable iteration order
         Set<ITestPackageDef> testPkgDefs = new LinkedHashSet<ITestPackageDef>();
         if (mPlanName != null) {
@@ -646,55 +620,60 @@
             File ctsPlanFile = mCtsBuild.getTestPlanFile(mPlanName);
             ITestPlan plan = createPlan(mPlanName);
             plan.parse(createXmlStream(ctsPlanFile));
-            for (String uri : plan.getTestUris()) {
-                if (!mExcludedPackageNames.contains(uri)) {
-                    ITestPackageDef testPackage = testRepo.getTestPackage(uri);
-                    if (testPackage != null) {
-                        testPackage.setExcludedTestFilter(plan.getExcludedTestFilter(uri));
-                        testPkgDefs.add(testPackage);
+            for (String id : plan.getTestIds()) {
+                if (!mExcludedPackageNames.contains(AbiUtils.parseId(id)[1])) {
+                    ITestPackageDef testPackageDef = testRepo.getTestPackage(id);
+                    if (testPackageDef != null) {
+                        testPackageDef.setTestFilter(plan.getTestFilter(id));
+                        testPkgDefs.add(testPackageDef);
                     } else {
-                        CLog.e("Could not find test package uri %s referenced in plan %s", uri,
+                        CLog.e("Could not find test package id %s referenced in plan %s", id,
                                 mPlanName);
                     }
                 }
             }
         } else if (mPackageNames.size() > 0){
             Log.i(LOG_TAG, String.format("Executing CTS test packages %s", mPackageNames));
-            for (String uri : mPackageNames) {
-                ITestPackageDef testPackage = testRepo.getTestPackage(uri);
-                if (testPackage != null) {
-                    testPkgDefs.add(testPackage);
+            for (String name : mPackageNames) {
+                Set<ITestPackageDef> testPackages = testRepo.getTestPackages(name);
+                if (!testPackages.isEmpty()) {
+                    testPkgDefs.addAll(testPackages);
                 } else {
                     throw new IllegalArgumentException(String.format(
                             "Could not find test package %s. " +
-                            "Use 'list packages' to see available packages." , uri));
+                            "Use 'list packages' to see available packages." , name));
                 }
             }
         } else if (mClassName != null) {
             Log.i(LOG_TAG, String.format("Executing CTS test class %s", mClassName));
-            // try to find package to run from class name
-            String packageUri = testRepo.findPackageForTest(mClassName);
-            if (packageUri != null) {
-                ITestPackageDef testPackageDef = testRepo.getTestPackage(packageUri);
-                testPackageDef.setClassName(mClassName, mMethodName);
-                testPkgDefs.add(testPackageDef);
+            // try to find packages to run from class name
+            Set<String> packageIds = testRepo.findPackageIdsForTest(mClassName);
+            if (!packageIds.isEmpty()) {
+                for (String packageId: packageIds) {
+                    ITestPackageDef testPackageDef = testRepo.getTestPackage(packageId);
+                    if (testPackageDef != null) {
+                        testPackageDef.setClassName(mClassName, mMethodName);
+                        testPkgDefs.add(testPackageDef);
+                    }
+                }
             } else {
                 Log.logAndDisplay(LogLevel.WARN, LOG_TAG, String.format(
                         "Could not find package for test class %s", mClassName));
             }
         } else if (mContinueSessionId != null) {
             // create an in-memory derived plan that contains the notExecuted tests from previous
-            // session
-            // use timestamp as plan name so it will hopefully be unique
+            // session use timestamp as plan name so it will hopefully be unique
             String uniquePlanName = Long.toString(System.currentTimeMillis());
             PlanCreator planCreator = new PlanCreator(uniquePlanName, mContinueSessionId,
                     CtsTestStatus.NOT_EXECUTED);
             ITestPlan plan = createPlan(planCreator);
-            for (String uri : plan.getTestUris()) {
-                if (!mExcludedPackageNames.contains(uri)) {
-                    ITestPackageDef testPackage = testRepo.getTestPackage(uri);
-                    testPackage.setExcludedTestFilter(plan.getExcludedTestFilter(uri));
-                    testPkgDefs.add(testPackage);
+            for (String id : plan.getTestIds()) {
+                if (!mExcludedPackageNames.contains(AbiUtils.parseId(id)[1])) {
+                    ITestPackageDef testPackageDef = testRepo.getTestPackage(id);
+                    if (testPackageDef != null) {
+                        testPackageDef.setTestFilter(plan.getTestFilter(id));
+                        testPkgDefs.add(testPackageDef);
+                    }
                 }
             }
         } else {
@@ -735,6 +714,9 @@
     }
 
     /**
+     * FIXME eventually this should be removed once we get rid of CtsTestStubs, any other
+     * prerequisite apks should be installed by the test runner
+     *
      * Install the collection of test apk file names
      *
      * @param prerequisiteApks
@@ -746,13 +728,8 @@
             try {
                 File apkFile = mCtsBuild.getTestApp(apkName);
                 String errorCode = null;
-                String[] options = {};
-                if (mForceAbi != null) {
-                    String abi = AbiFormatter.getDefaultAbi(getDevice(), mForceAbi);
-                    if (abi != null) {
-                        options = new String[]{String.format("--abi %s ", abi)};
-                    }
-                }
+                String abi = AbiFormatter.getDefaultAbi(getDevice(), mForceAbi);
+                String[] options = {AbiUtils.createAbiFlag(abi)};
                 errorCode = getDevice().installPackage(apkFile, true, options);
                 if (errorCode != null) {
                     CLog.e("Failed to install %s. Reason: %s", apkName, errorCode);
@@ -784,9 +761,14 @@
             return null;
         }
         checkFields();
-        List<TestPackage> allTests = buildTestsToRun();
+        List<TestPackage> allTests = null;
+        try {
+            allTests = buildTestsToRun();
+        } catch (DeviceNotAvailableException e) {
+            e.printStackTrace();
+        }
 
-        if (allTests.size() <= 1) {
+        if (allTests == null || allTests.size() <= 1) {
             Log.w(LOG_TAG, "no tests to shard!");
             return null;
         }
@@ -821,7 +803,8 @@
     void collectDeviceInfo(ITestDevice device, CtsBuildHelper ctsBuild,
             ITestInvocationListener listener) throws DeviceNotAvailableException {
         if (!mSkipDeviceInfo) {
-            DeviceInfoCollector.collectDeviceInfo(device, "armeabi-v7a", ctsBuild.getTestCasesDir(), listener);
+            String abi = AbiFormatter.getDefaultAbi(device, "");
+            DeviceInfoCollector.collectDeviceInfo(device, abi, ctsBuild.getTestCasesDir(), listener);
         }
     }
 
@@ -829,18 +812,39 @@
      * Factory method for creating a {@link ITestPackageRepo}.
      * <p/>
      * Exposed for unit testing
+     * @throws DeviceNotAvailableException
      */
-    ITestPackageRepo createTestCaseRepo() {
-        return new TestPackageRepo(mCtsBuild.getTestCasesDir(), mIncludeKnownFailures);
+    ITestPackageRepo createTestCaseRepo() throws DeviceNotAvailableException {
+        return new TestPackageRepo(mCtsBuild.getTestCasesDir(), getAbis(), mIncludeKnownFailures);
     }
 
     /**
      * Factory method for creating a {@link TestPlan}.
      * <p/>
      * Exposed for unit testing
+     * @throws DeviceNotAvailableException
      */
-    ITestPlan createPlan(String planName) {
-        return new TestPlan(planName);
+    ITestPlan createPlan(String planName) throws DeviceNotAvailableException {
+        return new TestPlan(planName, getAbis());
+    }
+
+    /**
+     * Gets the set of ABIs supported by both CTS and the device under test
+     * <p/>
+     * Exposed for unit testing
+     * @return The set of ABIs to run the tests on
+     * @throws DeviceNotAvailableException
+     */
+    Set<String> getAbis() throws DeviceNotAvailableException {
+        String bitness = (mForceAbi == null) ? "" : mForceAbi;
+        Set<String> abis = new HashSet<String>();
+        for (String abi : AbiFormatter.getSupportedAbis(mDevice, bitness)) {
+            if (AbiUtils.isAbiSupportedByCts(abi)) {
+                abis.add(abi);
+            }
+        }
+        Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "ABIs: " + abis);
+        return abis;
     }
 
     /**
@@ -848,9 +852,11 @@
      * <p/>
      * Exposed for unit testing
      * @throws ConfigurationException
+     * @throws DeviceNotAvailableException
      */
-    ITestPlan createPlan(PlanCreator planCreator) throws ConfigurationException {
-        return planCreator.createDerivedPlan(mCtsBuild);
+    ITestPlan createPlan(PlanCreator planCreator)
+            throws ConfigurationException, DeviceNotAvailableException {
+        return planCreator.createDerivedPlan(mCtsBuild, getAbis());
     }
 
     /**
@@ -907,10 +913,11 @@
      * @param listener
      */
     private void forwardPackageDetails(ITestPackageDef def, ITestInvocationListener listener) {
-        Map<String, String> metrics = new HashMap<String, String>(2);
+        Map<String, String> metrics = new HashMap<String, String>(3);
         metrics.put(PACKAGE_NAME_METRIC, def.getName());
+        metrics.put(PACKAGE_ABI_METRIC, def.getAbi().getName());
         metrics.put(PACKAGE_DIGEST_METRIC, def.getDigest());
-        listener.testRunStarted(def.getUri(), 0);
+        listener.testRunStarted(def.getId(), 0);
         listener.testRunEnded(0, metrics);
     }
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
index 6c6267f..9077b89 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DeqpTestRunner.java
@@ -1,5 +1,6 @@
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.MultiLineReceiver;
 import com.android.ddmlib.testrunner.ITestRunListener;
 import com.android.ddmlib.testrunner.TestIdentifier;
@@ -32,7 +33,7 @@
 
     private ITestDevice mDevice;
 
-    private final String mUri;
+    private final String mPackageName;
     private final String mName;
     private Collection<TestIdentifier> mTests;
     private IAbi mAbi;
@@ -43,8 +44,8 @@
 
     private ITestInvocationListener mListener;
 
-    public DeqpTestRunner(String uri, String name, Collection<TestIdentifier> tests) {
-        mUri = uri;
+    public DeqpTestRunner(String packageName, String name, Collection<TestIdentifier> tests) {
+        mPackageName = packageName;
         mName = name;
         mTests = tests;
         mLogData = false;
@@ -168,7 +169,8 @@
      * Handles beginning of dEQP session.
      */
     private void handleBeginSession(Map<String, String> values) {
-        mListener.testRunStarted(mUri, mTests.size());
+        String id = AbiUtils.createId(mAbi.getName(), mPackageName);
+        mListener.testRunStarted(id, mTests.size());
     }
 
     /**
@@ -457,7 +459,8 @@
         } else {
             /* Pass all tests if OpenGL ES version is not supported */
             Map <String, String> emptyMap = Collections.emptyMap();
-            mListener.testRunStarted(mUri, mTests.size());
+            String id = AbiUtils.createId(mAbi.getName(), mPackageName);
+            mListener.testRunStarted(id, mTests.size());
 
             for (TestIdentifier test : mTests) {
                 CLog.d("Skipping test '%s', Opengl ES version not supported", test.toString());
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTestResultParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTestResultParser.java
index c01da20..67bef35 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTestResultParser.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTestResultParser.java
@@ -99,7 +99,7 @@
     private long mTotalRunTime = 0;
     private boolean mTestInProgress = false;
     private boolean mTestRunInProgress = false;
-    private final String mTestRunName;
+    private final String mTestRunId;
     private final Collection<ITestRunListener> mTestListeners;
 
     /** Fake adding a package prefix if the test listener needs it. */
@@ -200,24 +200,24 @@
     /**
      * Creates the GTestResultParser.
      *
-     * @param testRunName the test run name to provide to
+     * @param testRunId the test run id to provide to
      *            {@link ITestRunListener#testRunStarted(String, int)}
      * @param listeners informed of test results as the tests are executing
      */
-    public GeeTestResultParser(String testRunName, Collection<ITestRunListener> listeners) {
-        mTestRunName = testRunName;
+    public GeeTestResultParser(String testRunId, Collection<ITestRunListener> listeners) {
+        mTestRunId = testRunId;
         mTestListeners = new ArrayList<ITestRunListener>(listeners);
     }
 
     /**
      * Creates the GTestResultParser for a single listener.
      *
-     * @param testRunName the test run name to provide to
+     * @param testRunId the test run id to provide to
      *            {@link ITestRunListener#testRunStarted(String, int)}
      * @param listener informed of test results as the tests are executing
      */
-    public GeeTestResultParser(String testRunName, ITestRunListener listener) {
-        mTestRunName = testRunName;
+    public GeeTestResultParser(String testRunId, ITestRunListener listener) {
+        mTestRunId = testRunId;
         mTestListeners = new ArrayList<ITestRunListener>(1);
         mTestListeners.add(listener);
     }
@@ -355,7 +355,7 @@
         // if start test run not reported yet
         if (!mTestRunStartReported) {
             for (ITestRunListener listener : mTestListeners) {
-                listener.testRunStarted(mTestRunName, mNumTestsExpected);
+                listener.testRunStarted(mTestRunId, mNumTestsExpected);
             }
             mTestRunStartReported = true;
         }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
index 0c3d9bc..ea01535 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
@@ -17,6 +17,7 @@
 package com.android.cts.tradefed.testtype;
 
 import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IRemoteTest;
 
 import java.io.File;
@@ -30,10 +31,16 @@
 public interface ITestPackageDef {
 
     /**
-     * Get the unique URI, aka the appPackageName, of the test package.
-     * @return the {@link String} uri
+     * Get the id of the test package.
+     * @return the {@link String} id
      */
-    public String getUri();
+    public String getId();
+
+    /**
+     * Get the appPackageName of the test package.
+     * @return the {@link String} appPackageName
+     */
+    public String getAppPackageName();
 
     /**
      * Creates a runnable {@link IRemoteTest} from info stored in this definition.
@@ -68,8 +75,7 @@
     /**
      * Return the sha1sum of the binary file for this test package.
      * <p/>
-     * Will only return a valid value after {@link #createTest(File, String, String)} has been
-     * called.
+     * Will only return a valid value after {@link #createTest(File)} has been called.
      *
      * @return the sha1sum in {@link String} form
      */
@@ -81,11 +87,16 @@
     public String getName();
 
     /**
-     * Set the filter to use to exclude tests
-     *
-     * @param excludedTestFilter
+     * @return the ABI of this test package.
      */
-    public void setExcludedTestFilter(TestFilter excludedTestFilter);
+    public IAbi getAbi();
+
+    /**
+     * Set the filter to use for tests
+     *
+     * @param testFilter
+     */
+    public void setTestFilter(TestFilter testFilter);
 
     /**
      * Restrict this test package to run a specific class and method name
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageRepo.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageRepo.java
index 53451f1..04ecb83 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageRepo.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageRepo.java
@@ -16,8 +16,10 @@
 
 package com.android.cts.tradefed.testtype;
 
-import java.util.Collection;
+import com.android.cts.util.AbiUtils;
 
+import java.util.Collection;
+import java.util.Set;
 
 /**
  * Interface for accessing tests from the CTS repository.
@@ -25,23 +27,37 @@
 public interface ITestPackageRepo {
 
     /**
-     * Get a {@link TestPackageDef} given a uri
+     * Get a {@link TestPackageDef} given an id.
      *
-     * @param testUri the string uris
-     * @return a {@link TestPackageDef} or <code>null</code> if the uri cannot be found in repo
+     * @param id the unique identifier of this test package, generated by
+     * {@link AbiUtils#createId(String, String)}.
+     * @return a {@link TestPackageDef}
      */
-    public ITestPackageDef getTestPackage(String testUri);
+    public ITestPackageDef getTestPackage(String id);
 
     /**
-     * Attempt to find the package uri for a given test class name
+     * Get a set of {@link TestPackageDef} given a name
+     *
+     * @param name the string package name
+     * @return a {@link Set} of {@link TestPackageDef}
+     */
+    public Set<ITestPackageDef> getTestPackages(String name);
+
+    /**
+     * Attempt to find the package ids for a given test class name
      *
      * @param testClassName the test class name
-     * @return the package uri or <code>null</code> if the package cannot be found
+     * @return a {@link Set} of package ids.
      */
-    public String findPackageForTest(String testClassName);
+    public Set<String> findPackageIdsForTest(String testClassName);
 
     /**
-     * Return a sorted {@link Collection} of all package names found in repo.
+     * return a sorted {@link Collection} of all package ids found in repo.
+     */
+    public Collection<String> getPackageIds();
+
+    /**
+     * return a sorted {@link Collection} of all package names found in repo.
      */
     public Collection<String> getPackageNames();
 
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPlan.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPlan.java
index 191cefd..21b2d0a 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPlan.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPlan.java
@@ -37,36 +37,36 @@
     public void parse(InputStream xmlStream) throws ParseException;
 
     /**
-     * Gets the list of test uris contained in this plan.
+     * Gets a sorted list of test ids contained in this plan.
      */
-    public Collection<String> getTestUris();
+    public Collection<String> getTestIds();
 
     /**
-     * Gets the {@link TestFilter} that should be used to exclude tests from given package.
+     * Gets the {@link TestFilter} that should be used to filter tests from given package.
      */
-    public TestFilter getExcludedTestFilter(String uri);
+    public TestFilter getTestFilter(String id);
 
     /**
      * Add a package to this test plan
-     * @param uri
+     * @param id
      */
-    public void addPackage(String uri);
+    public void addPackage(String id);
 
     /**
      * Add a excluded test to this test plan
      *
-     * @param uri the package uri
+     * @param id the package id
      * @param testToExclude the test to exclude for given package
      */
-    public void addExcludedTest(String uri, TestIdentifier testToExclude);
+    public void addExcludedTest(String id, TestIdentifier testToExclude);
 
     /**
      * Adds the list of excluded tests for given package
      *
-     * @param pkgUri
+     * @param id
      * @param excludedTests
      */
-    public void addExcludedTests(String uri, Collection<TestIdentifier> excludedTests);
+    public void addExcludedTests(String id, Collection<TestIdentifier> excludedTests);
 
     /**
      * Serialize the contents of this test plan.
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ResultFilter.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ResultFilter.java
index e8fdad8..e3fcdc4 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ResultFilter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ResultFilter.java
@@ -28,6 +28,7 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * A {@link ITestInvocationListener} that filters test results based on the set of expected tests
@@ -38,37 +39,35 @@
  */
 class ResultFilter extends ResultForwarder {
 
-    private final Map<String, Collection<TestIdentifier>> mKnownTestsMap;
-    private final Map<String, Collection<TestIdentifier>> mRemainingTestsMap;
-    private String mCurrentTestRun = null;
+    private final Set<TestIdentifier> mKnownTests;
+    private final Set<TestIdentifier> mRemainingTests;
+    private final String mTestRun;
 
     /**
      * Create a {@link ResultFilter}.
      *
      * @param listener the real {@link ITestInvocationListener} to forward results to
      */
-    ResultFilter(ITestInvocationListener listener, List<TestPackage> testPackages) {
+    ResultFilter(ITestInvocationListener listener, TestPackage testPackage) {
         super(listener);
-
-        mKnownTestsMap = new HashMap<String, Collection<TestIdentifier>>();
+        mTestRun = testPackage.getTestRunName();
+        Collection<TestIdentifier> tests = testPackage.getKnownTests();
+        mKnownTests = new HashSet<TestIdentifier>(tests);
         // use LinkedHashMap for predictable test order
-        mRemainingTestsMap = new LinkedHashMap<String, Collection<TestIdentifier>>();
-
-        for (TestPackage testPkg : testPackages) {
-            mKnownTestsMap.put(testPkg.getTestRunName(), new HashSet<TestIdentifier>(
-                    testPkg.getKnownTests()));
-            mRemainingTestsMap.put(testPkg.getTestRunName(), new LinkedHashSet<TestIdentifier>(
-                    testPkg.getKnownTests()));
-        }
+        mRemainingTests = new LinkedHashSet<TestIdentifier>(tests);
     }
 
+
     /**
      * {@inheritDoc}
      */
     @Override
     public void testRunStarted(String runName, int testCount) {
-        super.testRunStarted(runName, testCount);
-        mCurrentTestRun = runName;
+        if (mTestRun.equals(runName)) {
+            super.testRunStarted(runName, testCount);
+        } else {
+            CLog.d("Skipping reporting unknown test run %s", runName);
+        }
     }
 
     /**
@@ -109,10 +108,7 @@
      * @return
      */
     private boolean isKnownTest(TestIdentifier test) {
-        if (mCurrentTestRun != null && mKnownTestsMap.containsKey(mCurrentTestRun)) {
-            return mKnownTestsMap.get(mCurrentTestRun).contains(test);
-        }
-        return false;
+        return mKnownTests.contains(test);
     }
 
     /**
@@ -120,26 +116,23 @@
      * @param test
      */
     private void removeExecutedTest(TestIdentifier test) {
-        if (mCurrentTestRun != null && mRemainingTestsMap.containsKey(mCurrentTestRun)) {
-             mRemainingTestsMap.get(mCurrentTestRun).remove(test);
-        }
+        mRemainingTests.remove(test);
     }
 
     /**
      * Report the set of expected tests that were not executed
      */
     public void reportUnexecutedTests() {
-        for (Map.Entry<String, Collection<TestIdentifier>> entry : mRemainingTestsMap.entrySet()) {
-            if (!entry.getValue().isEmpty()) {
-                super.testRunStarted(entry.getKey(), entry.getValue().size());
-                for (TestIdentifier test : entry.getValue()) {
-                    // an unexecuted test is currently reported as a 'testStarted' event without a
-                    // 'testEnded'. TODO: consider adding an explict API for reporting an unexecuted
-                    // test
-                    super.testStarted(test);
-                }
-                super.testRunEnded(0, new HashMap<String,String>());
-            }
+        if (mRemainingTests.isEmpty()) {
+            return;
         }
+        super.testRunStarted(mTestRun, mRemainingTests.size());
+        for (TestIdentifier test : mRemainingTests) {
+            // an unexecuted test is currently reported as a 'testStarted' event without a
+            // 'testEnded'. TODO: consider adding an explict API for reporting an unexecuted
+            // test
+            super.testStarted(test);
+        }
+        super.testRunEnded(0, new HashMap<String, String>());
     }
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
index 863a7a7..45c176a 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
@@ -16,9 +16,11 @@
 
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.InstrumentationTest;
 import com.android.tradefed.util.StreamUtil;
@@ -61,7 +63,7 @@
     private static final String SIGNATURE_TEST_METHOD = "testSignature";
     private static final String SIGNATURE_TEST_CLASS = "android.tests.sigtest.SignatureTest";
 
-    private String mUri = null;
+    private String mAppPackageName = null;
     private String mAppNameSpace = null;
     private String mName = null;
     private String mRunner = null;
@@ -71,6 +73,7 @@
     private boolean mIsSignatureTest = false;
     private String mTestPackageName = null;
     private String mDigest = null;
+    private IAbi mAbi = null;
 
     // use a LinkedHashSet for predictable iteration insertion-order, and fast
     // lookups
@@ -81,7 +84,7 @@
     // dynamic options, not parsed from package xml
     private String mClassName;
     private String mMethodName;
-    private TestFilter mExcludedTestFilter = new TestFilter();
+    private TestFilter mTestFilter = new TestFilter();
     private String mTargetBinaryName;
     private String mTargetNameSpace;
     // only timeout per package is supported. To change this to method granularity,
@@ -89,16 +92,36 @@
     // So for now, only max timeout for the package is used.
     private int mTimeoutInMins = -1;
 
-    void setUri(String uri) {
-        mUri = uri;
+    @Override
+    public IAbi getAbi() {
+        return mAbi;
+    }
+
+    /**
+     * @param abi the ABI to run this package on
+     */
+    public void setAbi(IAbi abi) {
+        mAbi = abi;
+    }
+
+    /**
+     * @return unique id representing this test package for this ABI.
+     */
+    @Override
+    public String getId() {
+        return AbiUtils.createId(getAbi().getName(), getAppPackageName());
+    }
+
+    void setAppPackageName(String appPackageName) {
+        mAppPackageName = appPackageName;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public String getUri() {
-        return mUri;
+    public String getAppPackageName() {
+        return mAppPackageName;
     }
 
     void setRunTimeArgs(String runTimeArgs) {
@@ -193,8 +216,8 @@
      * {@inheritDoc}
      */
     @Override
-    public void setExcludedTestFilter(TestFilter excludeFilter) {
-        mExcludedTestFilter = excludeFilter;
+    public void setTestFilter(TestFilter testFilter) {
+        mTestFilter = testFilter;
     }
 
     /**
@@ -211,7 +234,7 @@
      */
     @Override
     public IRemoteTest createTest(File testCaseDir) {
-        mExcludedTestFilter.setTestInclusion(mClassName, mMethodName);
+        mTestFilter.setTestInclusion(mClassName, mMethodName);
         mTests = filterTests();
 
         if (HOST_SIDE_ONLY_TEST.equals(mTestType)) {
@@ -221,26 +244,34 @@
                 CLog.d("Setting new timeout to " + mTimeoutInMins + " mins");
                 hostTest.setTimeout(mTimeoutInMins * 60 * 1000);
             }
-            hostTest.setRunName(getUri());
+            hostTest.setRunName(mAppPackageName);
             hostTest.setJarFileName(mJarPath);
             hostTest.setTests(mTests);
+            hostTest.setAbi(mAbi);
             mDigest = generateDigest(testCaseDir, mJarPath);
             return hostTest;
         } else if (VM_HOST_TEST.equals(mTestType)) {
             CLog.d("Creating vm host test for %s", mName);
             VMHostTest vmHostTest = new VMHostTest();
-            vmHostTest.setRunName(getUri());
+            vmHostTest.setRunName(mAppPackageName);
             vmHostTest.setJarFileName(mJarPath);
             vmHostTest.setTests(mTests);
+            vmHostTest.setAbi(mAbi);
             mDigest = generateDigest(testCaseDir, mJarPath);
             return vmHostTest;
         } else if (DEQP_TEST.equals(mTestType)) {
-            return new DeqpTestRunner(mUri, mName, mTests);
+            DeqpTestRunner deqpTest = new DeqpTestRunner(mAppPackageName, mName, mTests);
+            deqpTest.setAbi(mAbi);
+            return deqpTest;
         } else if (NATIVE_TEST.equals(mTestType)) {
-            return new GeeTest(mUri, mName);
+            GeeTest geeTest = new GeeTest(mAppPackageName, mName);
+            geeTest.setAbi(mAbi);
+            return geeTest;
         } else if (WRAPPED_NATIVE_TEST.equals(mTestType)) {
             CLog.d("Creating new wrapped native test for %s", mName);
-            return new WrappedGTest(mAppNameSpace, mUri, mName, mRunner);
+            WrappedGTest wrappedGeeTest = new WrappedGTest(mAppNameSpace, mAppPackageName, mName, mRunner);
+            wrappedGeeTest.setAbi(mAbi);
+            return wrappedGeeTest;
         } else if (ACCESSIBILITY_TEST.equals(mTestType)) {
             AccessibilityTestRunner test = new AccessibilityTestRunner();
             return setInstrumentationTest(test, testCaseDir);
@@ -248,6 +279,7 @@
             PrintTestRunner test = new PrintTestRunner();
             return setPrintTest(test, testCaseDir);
         } else if (ACCESSIBILITY_SERVICE_TEST.equals(mTestType)) {
+            @SuppressWarnings("deprecation")
             AccessibilityServiceTestRunner test = new AccessibilityServiceTestRunner();
             return setInstrumentationTest(test, testCaseDir);
         } else if (DISPLAY_TEST.equals(mTestType)) {
@@ -266,9 +298,11 @@
             instrTest.setRunnerName("android.test.InstrumentationTestRunner");
             instrTest.setClassName(SIGNATURE_TEST_CLASS);
             instrTest.setMethodName(SIGNATURE_TEST_METHOD);
-            // set expected tests to the single signature test
-            TestIdentifier t = new TestIdentifier(SIGNATURE_TEST_CLASS, SIGNATURE_TEST_METHOD);
+            instrTest.setAbi(mAbi);
             mTests.clear();
+            // set expected tests to the single signature test
+            TestIdentifier t = new TestIdentifier(
+                    SIGNATURE_TEST_CLASS, SIGNATURE_TEST_METHOD);
             mTests.add(t);
             // mName means 'apk file name' for instrumentation tests
             instrTest.addInstallApk(String.format("%s.apk", mName), mAppNameSpace);
@@ -277,10 +311,11 @@
         } else if (JUNIT_DEVICE_TEST.equals(mTestType)){
             CLog.d("Creating JUnit device test %s", mName);
             JUnitDeviceTest jUnitDeviceTest = new JUnitDeviceTest();
-            jUnitDeviceTest.setRunName(getUri());
+            jUnitDeviceTest.setRunName(mAppPackageName);
             jUnitDeviceTest.addTestJarFileName(mJarPath);
             jUnitDeviceTest.addRunTimeArgs(mRunTimeArgs);
             jUnitDeviceTest.setTests(mTests);
+            jUnitDeviceTest.setAbi(mAbi);
             mDigest = generateDigest(testCaseDir, mJarPath);
             return jUnitDeviceTest;
         } else {
@@ -298,12 +333,13 @@
 
     private PrintTestRunner setPrintTest(PrintTestRunner printTest,
             File testCaseDir) {
-        printTest.setRunName(getUri());
+        printTest.setRunName(mAppPackageName);
         printTest.setPackageName(mAppNameSpace);
         printTest.setRunnerName(mRunner);
         printTest.setTestPackageName(mTestPackageName);
         printTest.setClassName(mClassName);
         printTest.setMethodName(mMethodName);
+        printTest.setAbi(mAbi);
         mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName));
         return printTest;
     }
@@ -317,12 +353,13 @@
      */
     private InstrumentationTest setInstrumentationTest(CtsInstrumentationApkTest instrTest,
             File testCaseDir) {
-        instrTest.setRunName(getUri());
+        instrTest.setRunName(mAppPackageName);
         instrTest.setPackageName(mAppNameSpace);
         instrTest.setRunnerName(mRunner);
         instrTest.setTestPackageName(mTestPackageName);
         instrTest.setClassName(mClassName);
         instrTest.setMethodName(mMethodName);
+        instrTest.setAbi(mAbi);
         instrTest.setTestsToRun(mTests, false
             /* force batch mode off to always run using testFile */);
         instrTest.setReRunUsingTestFile(true);
@@ -342,6 +379,7 @@
      * @param uiautomatorTest
      * @return the populated {@link UiAutomatorJarTest} or <code>null</code>
      */
+    @SuppressWarnings("deprecation")
     private IRemoteTest setUiAutomatorTest(UiAutomatorJarTest uiautomatorTest) {
         uiautomatorTest.setInstallArtifacts(getJarPath());
         if (mClassName != null) {
@@ -353,19 +391,19 @@
         } else {
             uiautomatorTest.addClassNames(mTestClasses);
         }
-        uiautomatorTest.setRunName(getUri());
+        uiautomatorTest.setRunName(mAppPackageName);
         uiautomatorTest.setCaptureLogs(false);
         return uiautomatorTest;
     }
 
     /**
-     * Filter the tests to run based on list of excluded tests, class and method name.
+     * Filter the tests to run based on list of included/excluded tests, class and method name.
      *
      * @return the filtered collection of tests
      */
     private Collection<TestIdentifier> filterTests() {
-        mExcludedTestFilter.setTestInclusion(mClassName, mMethodName);
-        return mExcludedTestFilter.filter(mTests);
+        mTestFilter.setTestInclusion(mClassName, mMethodName);
+        return mTestFilter.filter(mTests);
     }
 
     /**
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageRepo.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageRepo.java
index 0f3704d..6d10244 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageRepo.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageRepo.java
@@ -15,6 +15,7 @@
  */
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.Log;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
@@ -27,9 +28,12 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Hashtable;
+import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
 
 /**
  * Retrieves CTS test package definitions from the repository.
@@ -40,19 +44,27 @@
 
     private final File mTestCaseDir;
 
-    /** mapping of uri to test definition */
-    private final Map<String, TestPackageDef> mTestMap;
-
+    /** mapping of ABI to a mapping of appPackageName to test definition */
+    private final Map<String, Map<String, TestPackageDef>> mTestMap;
+    /** set of ABIs */
+    private final Set<String> mAbis;
     private final boolean mIncludeKnownFailures;
 
     /**
      * Creates a {@link TestPackageRepo}, initialized from provided repo files
      *
      * @param testCaseDir directory containing all test case definition xml and build files
+     * @param abis Holds the ABIs which the test must be run against. This must be a subset of the
+     * ABIs supported by the device under test.
+     * @param includeKnownFailures Whether to run tests which are known to fail.
      */
-    public TestPackageRepo(File testCaseDir, boolean includeKnownFailures) {
+    public TestPackageRepo(File testCaseDir, Set<String> abis, boolean includeKnownFailures) {
         mTestCaseDir = testCaseDir;
-        mTestMap = new Hashtable<String, TestPackageDef>();
+        mTestMap = new HashMap<String, Map<String, TestPackageDef>>();
+        mAbis = abis;
+        for (String abi : abis) {
+            mTestMap.put(abi, new HashMap<String, TestPackageDef>());
+        }
         mIncludeKnownFailures = includeKnownFailures;
         parse(mTestCaseDir);
     }
@@ -68,12 +80,14 @@
     }
 
     private void parseTestFromXml(File xmlFile)  {
-        TestPackageXmlParser parser = new TestPackageXmlParser(mIncludeKnownFailures);
+        TestPackageXmlParser parser = new TestPackageXmlParser(mAbis, mIncludeKnownFailures);
         try {
             parser.parse(createStreamFromFile(xmlFile));
-            TestPackageDef def = parser.getTestPackageDef();
-            if (def != null) {
-                mTestMap.put(def.getUri(), def);
+            Set<TestPackageDef> defs = parser.getTestPackageDefs();
+            if (!defs.isEmpty()) {
+                for (TestPackageDef def : defs) {
+                    mTestMap.get(def.getAbi().getName()).put(def.getAppPackageName(), def);
+                }
             } else {
                 Log.w(LOG_TAG, String.format("Could not find test package info in xml file %s",
                         xmlFile.getAbsolutePath()));
@@ -117,21 +131,60 @@
      * {@inheritDoc}
      */
     @Override
-    public ITestPackageDef getTestPackage(String testUri) {
-        return mTestMap.get(testUri);
+    public ITestPackageDef getTestPackage(String id) {
+        String[] parts = AbiUtils.parseId(id);
+        String abi = parts[0];
+        String name = parts[1];
+        if (mTestMap.containsKey(abi) && mTestMap.get(abi).containsKey(name)) {
+            return mTestMap.get(abi).get(name);
+        }
+        return null;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public String findPackageForTest(String testClassName) {
-        for (Map.Entry<String, TestPackageDef> entry : mTestMap.entrySet()) {
-            if (entry.getValue().isKnownTestClass(testClassName)) {
-                return entry.getKey();
+    public Set<ITestPackageDef> getTestPackages(String appPackageName) {
+        Set<ITestPackageDef> defs = new HashSet<ITestPackageDef>();
+        for (String abi : mAbis) {
+            if (mTestMap.get(abi).containsKey(appPackageName)) {
+                defs.add(mTestMap.get(abi).get(appPackageName));
             }
         }
-        return null;
+        return defs;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<String> findPackageIdsForTest(String testClassName) {
+        Set<String> ids = new HashSet<String>();
+        for (Map<String, TestPackageDef> map : mTestMap.values()) {
+            for (Entry<String, TestPackageDef> entry : map.entrySet()) {
+                if (entry.getValue().isKnownTestClass(testClassName)) {
+                    ids.add(entry.getKey());
+                }
+            }
+        }
+        return ids;
+    }
+
+    /**
+     * @return list of all package ids found in repo
+     */
+    @Override
+    public Collection<String> getPackageIds() {
+        Set<String> ids = new HashSet<String>();
+        for (String abi : mAbis) {
+            for (String name : mTestMap.get(abi).keySet()) {
+                ids.add(AbiUtils.createId(abi, name));
+            }
+        }
+        List<String> idList = new ArrayList<String>(ids);
+        Collections.sort(idList);
+        return idList;
     }
 
     /**
@@ -139,8 +192,11 @@
      */
     @Override
     public Collection<String> getPackageNames() {
-        List<String> packageNames = new ArrayList<String>();
-        packageNames.addAll(mTestMap.keySet());
+        Set<String> names = new HashSet<String>();
+        for (String abi : mAbis) {
+            names.addAll(mTestMap.get(abi).keySet());
+        }
+        List<String> packageNames = new ArrayList<String>(names);
         Collections.sort(packageNames);
         return packageNames;
     }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
index 5d10dfc..71279c7 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
@@ -15,6 +15,7 @@
  */
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.Log;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.util.xml.AbstractXmlParser;
@@ -22,7 +23,11 @@
 import org.xml.sax.Attributes;
 import org.xml.sax.helpers.DefaultHandler;
 
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
 import java.util.Stack;
 
 /**
@@ -35,11 +40,18 @@
 
     private static final String LOG_TAG = "TestPackageXmlParser";
 
+    private final Set<String> mAbis;
     private final boolean mIncludeKnownFailures;
 
-    private TestPackageDef mPackageDef;
+    private Map<String, TestPackageDef> mPackageDefs = new HashMap<String, TestPackageDef>();
 
-    public TestPackageXmlParser(boolean includeKnownFailures) {
+    /**
+     * @param abis Holds the ABIs which the test must be run against. This must be a subset of the
+     * ABIs supported by the device under test.
+     * @param includeKnownFailures Whether to run tests which are known to fail.
+     */
+    public TestPackageXmlParser(Set<String> abis, boolean includeKnownFailures) {
+        mAbis = abis;
         mIncludeKnownFailures = includeKnownFailures;
     }
 
@@ -65,32 +77,37 @@
         @Override
         public void startElement(String uri, String localName, String name, Attributes attributes) {
             if (TEST_PACKAGE_TAG.equals(localName)) {
-                // appPackageName is used as the uri
-                final String entryUriValue = attributes.getValue("appPackageName");
+                final String appPackageName = attributes.getValue("appPackageName");
                 final String testPackageNameSpace = attributes.getValue("appNameSpace");
                 final String packageName = attributes.getValue("name");
                 final String runnerName = attributes.getValue("runner");
                 final String jarPath = attributes.getValue("jarPath");
-                final String signatureCheck = attributes.getValue("signatureCheck");
+                final boolean signatureCheck = parseBoolean(attributes.getValue("signatureCheck"));
                 final String javaPackageFilter = attributes.getValue("javaPackageFilter");
                 final String targetBinaryName = attributes.getValue("targetBinaryName");
                 final String targetNameSpace = attributes.getValue("targetNameSpace");
                 final String runTimeArgs = attributes.getValue("runtimeArgs");
+                final String testType = getTestType(attributes);
 
-                mPackageDef = new TestPackageDef();
-                mPackageDef.setUri(entryUriValue);
-                mPackageDef.setAppNameSpace(testPackageNameSpace);
-                mPackageDef.setName(packageName);
-                mPackageDef.setRunner(runnerName);
-                mPackageDef.setTestType(getTestType(attributes));
-                mPackageDef.setJarPath(jarPath);
-                mPackageDef.setIsSignatureCheck(parseBoolean(signatureCheck));
-                mPackageDef.setRunTimeArgs(runTimeArgs);
-                if (!"".equals(javaPackageFilter)) {
-                    mPackageDef.setTestPackageName(javaPackageFilter);
+                for (String abiName : mAbis) {
+                    Abi abi = new Abi(abiName, AbiUtils.getBitness(abiName));
+                    TestPackageDef packageDef = new TestPackageDef();
+                    packageDef.setAppPackageName(appPackageName);
+                    packageDef.setAppNameSpace(testPackageNameSpace);
+                    packageDef.setName(packageName);
+                    packageDef.setRunner(runnerName);
+                    packageDef.setTestType(testType);
+                    packageDef.setJarPath(jarPath);
+                    packageDef.setIsSignatureCheck(signatureCheck);
+                    packageDef.setRunTimeArgs(runTimeArgs);
+                    if (!"".equals(javaPackageFilter)) {
+                        packageDef.setTestPackageName(javaPackageFilter);
+                    }
+                    packageDef.setTargetBinaryName(targetBinaryName);
+                    packageDef.setTargetNameSpace(targetNameSpace);
+                    packageDef.setAbi(abi);
+                    mPackageDefs.put(abiName, packageDef);
                 }
-                mPackageDef.setTargetBinaryName(targetBinaryName);
-                mPackageDef.setTargetNameSpace(targetNameSpace);
 
                 // reset the class name
                 mClassNameStack = new Stack<String>();
@@ -112,7 +129,7 @@
                 }
             } else if (TEST_TAG.equals(localName)) {
                 String methodName = attributes.getValue("name");
-                if (mPackageDef == null) {
+                if (mPackageDefs.isEmpty()) {
                     Log.e(LOG_TAG, String.format(
                             "Invalid XML: encountered a '%s' tag not enclosed within a '%s' tag",
                             TEST_TAG, TEST_PACKAGE_TAG));
@@ -133,11 +150,26 @@
                     if (timeoutStr != null) {
                         timeout = Integer.parseInt(timeoutStr);
                     }
-                    TestIdentifier testId = new TestIdentifier(classNameBuilder.toString(),
-                            methodName);
                     boolean isKnownFailure = "failure".equals(attributes.getValue("expectation"));
                     if (!isKnownFailure || mIncludeKnownFailures) {
-                        mPackageDef.addTest(testId, timeout);
+                        String abiList = attributes.getValue("abis");
+                        Set<String> abis = new HashSet<String>();
+                        if (abiList == null) {
+                            // If no specification, add all supported abis
+                            abis.addAll(mAbis);
+                        } else {
+                            for (String abi : abiList.split(", ")) {
+                                if (mAbis.contains(abi)) {
+                                    // Else only add the abi which are supported
+                                    abis.add(abi);
+                                }
+                            }
+                        }
+                        for (String abi : abis) {
+                            TestIdentifier testId = new TestIdentifier(
+                                    classNameBuilder.toString(), methodName);
+                            mPackageDefs.get(abi).addTest(testId, timeout);
+                        }
                     }
                 }
             }
@@ -170,19 +202,15 @@
         }
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
     protected DefaultHandler createXmlHandler() {
         return new TestPackageHandler();
     }
 
     /**
-     * @returns the {@link TestPackageDef} containing data parsed from xml or <code>null</code> if
-     *          xml did not contain the correct information.
+     * @returns the set of {@link TestPackageDef} containing data parsed from xml
      */
-    public TestPackageDef getTestPackageDef() {
-        return mPackageDef;
+    public Set<TestPackageDef> getTestPackageDefs() {
+        return new HashSet<TestPackageDef>(mPackageDefs.values());
     }
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPlan.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPlan.java
index c2a1348..8737db6 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPlan.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPlan.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.util.ArrayUtil;
 import com.android.tradefed.util.xml.AbstractXmlParser;
@@ -29,9 +30,11 @@
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Implementation of {@link TestPlan}.
@@ -39,18 +42,20 @@
 public class TestPlan extends AbstractXmlParser implements ITestPlan {
 
     /**
-     * Map of uri names found in plan, and their excluded tests
+     * Map of ids found in plan, and their filters
      */
-    private Map<String, TestFilter> mUriExcludedTestsMap;
+    private Map<String, TestFilter> mIdFilterMap;
 
     private static final String ENTRY_TAG = "Entry";
     private static final String TEST_DELIM = ";";
     private static final String METHOD_DELIM = "#";
     private static final String EXCLUDE_ATTR = "exclude";
     private static final String INCLUDE_ATTR = "include";
-    private static final String URI_ATTR = "uri";
+    private static final String ABI_ATTR = "abi";
+    private static final String NAME_ATTR = "name";
 
     private final String mName;
+    private final Set<String> mAbis;
 
     /**
      * SAX callback object. Handles parsing data from the xml tags.
@@ -61,10 +66,17 @@
         public void startElement(String uri, String localName, String name, Attributes attributes)
                 throws SAXException {
             if (ENTRY_TAG.equals(localName)) {
-                final String entryUriValue = attributes.getValue(URI_ATTR);
                 TestFilter filter = parseTestList(
                         attributes.getValue(EXCLUDE_ATTR), attributes.getValue(INCLUDE_ATTR));
-                mUriExcludedTestsMap.put(entryUriValue, filter);
+                final String entryNameValue = attributes.getValue(NAME_ATTR);
+                final String entryAbiValue = attributes.getValue(ABI_ATTR);
+                if (entryAbiValue != null) {
+                    mIdFilterMap.put(AbiUtils.createId(entryAbiValue, entryNameValue), filter);
+                } else {
+                    for (String abi : mAbis) {
+                        mIdFilterMap.put(AbiUtils.createId(abi, entryNameValue), filter);
+                    }
+                }
             }
         }
 
@@ -109,10 +121,11 @@
         }
     }
 
-    public TestPlan(String name) {
+    public TestPlan(String name, Set<String> abis) {
         mName = name;
+        mAbis = abis;
         // Uses a LinkedHashMap to have predictable iteration order
-        mUriExcludedTestsMap = new LinkedHashMap<String, TestFilter>();
+        mIdFilterMap = new LinkedHashMap<String, TestFilter>();
     }
 
     /**
@@ -127,24 +140,26 @@
      * {@inheritDoc}
      */
     @Override
-    public Collection<String> getTestUris() {
-        return mUriExcludedTestsMap.keySet();
+    public Collection<String> getTestIds() {
+        List<String> ids = new ArrayList<String>(mIdFilterMap.keySet());
+        Collections.sort(ids);
+        return ids;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public TestFilter getExcludedTestFilter(String uri) {
-        return mUriExcludedTestsMap.get(uri);
+    public TestFilter getTestFilter(String id) {
+        return mIdFilterMap.get(id);
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public void addPackage(String uri) {
-        mUriExcludedTestsMap.put(uri, new TestFilter());
+    public void addPackage(String id) {
+        mIdFilterMap.put(id, new TestFilter());
     }
 
     /**
@@ -159,12 +174,12 @@
      * {@inheritDoc}
      */
     @Override
-    public void addExcludedTest(String uri, TestIdentifier testToExclude) {
-        TestFilter filter = mUriExcludedTestsMap.get(uri);
+    public void addExcludedTest(String id, TestIdentifier testToExclude) {
+        TestFilter filter = mIdFilterMap.get(id);
         if (filter != null) {
             filter.addExcludedTest(testToExclude);
         } else {
-            throw new IllegalArgumentException(String.format("Could not find package %s", uri));
+            throw new IllegalArgumentException(String.format("Could not find package %s", id));
         }
     }
 
@@ -172,12 +187,12 @@
      * {@inheritDoc}
      */
     @Override
-    public void addExcludedTests(String uri, Collection<TestIdentifier> excludedTests) {
-        TestFilter filter = mUriExcludedTestsMap.get(uri);
+    public void addExcludedTests(String id, Collection<TestIdentifier> excludedTests) {
+        TestFilter filter = mIdFilterMap.get(id);
         if (filter != null) {
             filter.getExcludedTests().addAll(excludedTests);
         } else {
-            throw new IllegalArgumentException(String.format("Could not find package %s", uri));
+            throw new IllegalArgumentException(String.format("Could not find package %s", id));
         }
     }
 
@@ -193,9 +208,11 @@
                 "http://xmlpull.org/v1/doc/features.html#indent-output", true);
         serializer.startTag(null, "TestPlan");
         serializer.attribute(null, "version", "1.0");
-        for (Map.Entry<String, TestFilter> packageEntry : mUriExcludedTestsMap.entrySet()) {
+        for (Map.Entry<String, TestFilter> packageEntry : mIdFilterMap.entrySet()) {
             serializer.startTag(null, ENTRY_TAG);
-            serializer.attribute(null, "uri", packageEntry.getKey());
+            String[] parts = AbiUtils.parseId(packageEntry.getKey());
+            serializer.attribute(null, ABI_ATTR, parts[0]);
+            serializer.attribute(null, NAME_ATTR, parts[1]);
             serializeFilter(serializer, packageEntry.getValue());
             serializer.endTag(null, ENTRY_TAG);
         }
@@ -206,10 +223,10 @@
     /**
      * Adds an xml attribute containing {@link TestFilter} contents.
      * <p/>
-     * If {@link TestFilter} is empty, no data will be outputted.
+     * If {@link TestFilter} is empty, no data will be output.
      *
      * @param serializer
-     * @param value
+     * @param testFilter
      * @throws IOException
      */
     private void serializeFilter(KXmlSerializer serializer, TestFilter testFilter)
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
index 9c19f36..a5c3e4c 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
@@ -46,15 +46,15 @@
     private IAbi mAbi;
 
     private final String mAppNameSpace;
-    private final String mRunner;
+    private final String mPackageName;
     private final String mName;
-    private final String mUri;
+    private final String mRunner;
 
-    public WrappedGTest(String appNameSpace, String uri, String name, String runner) {
+    public WrappedGTest(String appNameSpace, String packageName, String name, String runner) {
         mAppNameSpace = appNameSpace;
-        mRunner = runner;
+        mPackageName = packageName;
         mName = name;
-        mUri = uri;
+        mRunner = runner;
     }
 
     /**
@@ -93,7 +93,6 @@
         try {
             File testApp = mCtsBuild.getTestApp(String.format("%s.apk", mName));
             String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
-            CLog.d(LOG_TAG, "installPackage options: " + options);
             String installCode = mDevice.installPackage(testApp, true, options);
 
             if (installCode != null) {
@@ -110,8 +109,9 @@
     }
 
     private void runTest(ITestRunListener listener) throws DeviceNotAvailableException {
-        WrappedGTestResultParser resultParser = new WrappedGTestResultParser(mUri, listener);
-        resultParser.setFakePackagePrefix(mUri + ".");
+        String id = AbiUtils.createId(mAbi.getName(), mPackageName);
+        WrappedGTestResultParser resultParser = new WrappedGTestResultParser(id, listener);
+        resultParser.setFakePackagePrefix(mPackageName + ".");
         try {
             String options = mAbi == null ? "" : String.format("--abi %s ", mAbi);
             String command = String.format("am instrument -w %s%s/.%s", options, mAppNameSpace, mRunner);
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTestResultParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTestResultParser.java
index cc3c53a..3050738 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTestResultParser.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTestResultParser.java
@@ -29,23 +29,23 @@
     /**
      * Creates the WrappedGTestResultParser.
      *
-     * @param testRunName the test run name to provide to
+     * @param testRunId the test run id to provide to
      *            {@link ITestRunListener#testRunStarted(String, int)}
      * @param listeners informed of test results as the tests are executing
      */
-    public WrappedGTestResultParser(String testRunName, Collection<ITestRunListener> listeners) {
-        super(testRunName, listeners);
+    public WrappedGTestResultParser(String testRunId, Collection<ITestRunListener> listeners) {
+        super(testRunId, listeners);
     }
 
     /**
      * Creates the WrappedGTestResultParser for a single listener.
      *
-     * @param testRunName the test run name to provide to
+     * @param testRunId the test run id to provide to
      *            {@link ITestRunListener#testRunStarted(String, int)}
      * @param listener informed of test results as the tests are executing
      */
-    public WrappedGTestResultParser(String testRunName, ITestRunListener listener) {
-        super(testRunName, listener);
+    public WrappedGTestResultParser(String testRunId, ITestRunListener listener) {
+        super(testRunId, listener);
     }
 
     /**
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
index 65528b7..e46cfae 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/UnitTests.java
@@ -21,14 +21,17 @@
 import com.android.cts.tradefed.result.TestResultsTest;
 import com.android.cts.tradefed.result.TestSummaryXmlTest;
 import com.android.cts.tradefed.result.TestTest;
+import com.android.cts.tradefed.testtype.Abi;
 import com.android.cts.tradefed.testtype.CtsTestTest;
-import com.android.cts.tradefed.testtype.DeqpTestTest;
+import com.android.cts.tradefed.testtype.DeqpTestRunnerTest;
 import com.android.cts.tradefed.testtype.JarHostTestTest;
 import com.android.cts.tradefed.testtype.TestFilterTest;
 import com.android.cts.tradefed.testtype.TestPackageDefTest;
 import com.android.cts.tradefed.testtype.TestPackageXmlParserTest;
 import com.android.cts.tradefed.testtype.TestPlanTest;
 import com.android.cts.tradefed.testtype.WrappedGTestResultParserTest;
+import com.android.cts.util.AbiUtils;
+import com.android.tradefed.testtype.IAbi;
 
 import junit.framework.Test;
 import junit.framework.TestSuite;
@@ -41,6 +44,8 @@
  */
 public class UnitTests extends TestSuite {
 
+    public static final IAbi ABI = new Abi("armeabi-v7a", "32");
+
     public UnitTests() {
         super();
 
@@ -60,7 +65,7 @@
         addTestSuite(TestPackageXmlParserTest.class);
         addTestSuite(TestPlanTest.class);
         addTestSuite(WrappedGTestResultParserTest.class);
-        addTestSuite(DeqpTestTest.class);
+        addTestSuite(DeqpTestRunnerTest.class);
     }
 
     public static Test suite() {
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/device/DeviceInfoCollectorFuncTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/device/DeviceInfoCollectorFuncTest.java
index b0c9e6d..52a205b 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/device/DeviceInfoCollectorFuncTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/device/DeviceInfoCollectorFuncTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.cts.tradefed.device;
 
+import com.android.cts.tradefed.UnitTests;
 import com.android.tradefed.build.BuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.result.CollectingTestListener;
@@ -34,7 +35,7 @@
         CollectingTestListener testListener = new CollectingTestListener();
 
         testListener.invocationStarted(new BuildInfo());
-        DeviceInfoCollector.collectDeviceInfo(getDevice(), new File(
+        DeviceInfoCollector.collectDeviceInfo(getDevice(), UnitTests.ABI.getName(), new File(
                 System.getProperty("java.io.tmpdir")), testListener);
         assertNotNull(testListener.getCurrentRunResults());
         assertTrue(testListener.getCurrentRunResults().getRunMetrics().size() > 0);
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
index 26cf39c..b312526 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
@@ -17,6 +17,8 @@
 
 import static com.android.cts.tradefed.result.CtsXmlResultReporter.CTS_RESULT_FILE_VERSION;
 
+import com.android.cts.tradefed.UnitTests;
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.testrunner.ITestRunListener.TestFailure;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.build.IFolderBuildInfo;
@@ -48,6 +50,7 @@
             new ArrayList<>(Arrays.asList(new TestSummary("TEST_SUMMARY_URL")));
     private CtsXmlResultReporter mResultReporter;
     private ByteArrayOutputStream mOutputStream;
+    private File mBuildDir;
     private File mReportDir;
     private IFolderBuildInfo mMockBuild;
 
@@ -73,8 +76,16 @@
         // TODO: use mock file dir instead
         mReportDir = FileUtil.createTempDir("foo");
         mResultReporter.setReportDir(mReportDir);
+        mBuildDir = FileUtil.createTempDir("build");
+        File ctsDir = new File(mBuildDir, "android-cts");
+        File repoDir = new File(ctsDir, "repository");
+        File casesDir = new File(repoDir, "testcases");
+        File plansDir = new File(repoDir, "plans");
+        assertTrue(casesDir.mkdirs());
+        assertTrue(plansDir.mkdirs());
         mMockBuild = EasyMock.createNiceMock(IFolderBuildInfo.class);
         EasyMock.expect(mMockBuild.getDeviceSerial()).andStubReturn(null);
+        EasyMock.expect(mMockBuild.getRootDir()).andStubReturn(mBuildDir);
         EasyMock.replay(mMockBuild);
     }
 
@@ -83,6 +94,9 @@
         if (mReportDir != null) {
             FileUtil.recursiveDelete(mReportDir);
         }
+        if (mBuildDir != null) {
+            FileUtil.recursiveDelete(mBuildDir);
+        }
         super.tearDown();
     }
 
@@ -117,19 +131,18 @@
         Map<String, String> emptyMap = Collections.emptyMap();
         final TestIdentifier testId = new TestIdentifier("com.foo.FooTest", "testFoo");
         mResultReporter.invocationStarted(mMockBuild);
-        mResultReporter.testRunStarted("run", 1);
+        mResultReporter.testRunStarted(AbiUtils.createId(UnitTests.ABI.getName(), "run"), 1);
         mResultReporter.testStarted(testId);
         mResultReporter.testEnded(testId, emptyMap);
         mResultReporter.testRunEnded(3000, emptyMap);
         mResultReporter.invocationEnded(1);
         mResultReporter.putSummary(SUMMARY_LIST);
         String output =  getOutput();
-        CLog.d("Actual output: %s", output);
-        System.out.println(output);
         // TODO: consider doing xml based compare
         assertTrue(output.contains(
-                "<Summary failed=\"0\" notExecuted=\"0\" timeout=\"0\" pass=\"1\" />"));
-        assertTrue(output.contains("<TestPackage name=\"\" appPackageName=\"run\" digest=\"\">"));
+              "<Summary failed=\"0\" notExecuted=\"0\" timeout=\"0\" pass=\"1\" />"));
+        assertTrue(output.contains("<TestPackage name=\"\" appPackageName=\"run\" abi=\"" +
+              UnitTests.ABI.getName() + "\" digest=\"\">"));
         assertTrue(output.contains("<TestCase name=\"FooTest\" priority=\"\">"));
 
         final String testCaseTag = String.format(
@@ -145,20 +158,19 @@
         final TestIdentifier testId = new TestIdentifier("FooTest", "testFoo");
         final String trace = "this is a trace\nmore trace\nyet more trace";
         mResultReporter.invocationStarted(mMockBuild);
-        mResultReporter.testRunStarted("run", 1);
+        mResultReporter.testRunStarted(AbiUtils.createId(UnitTests.ABI.getName(), "run"), 1);
         mResultReporter.testStarted(testId);
         mResultReporter.testFailed(TestFailure.FAILURE, testId, trace);
         mResultReporter.testEnded(testId, emptyMap);
         mResultReporter.testRunEnded(3, emptyMap);
         mResultReporter.invocationEnded(1);
-        String output =  getOutput();
-        System.out.print(getOutput());
+        String output = getOutput();
         // TODO: consider doing xml based compare
         assertTrue(output.contains(
                 "<Summary failed=\"1\" notExecuted=\"0\" timeout=\"0\" pass=\"0\" />"));
         final String failureTag =
-            "<FailedScene message=\"this is a trace&#10;more trace\">     " +
-            "<StackTrace>this is a tracemore traceyet more trace</StackTrace>";
+                "<FailedScene message=\"this is a trace&#10;more trace\">     " +
+                "<StackTrace>this is a tracemore traceyet more trace</StackTrace>";
         assertTrue(output.contains(failureTag));
     }
 
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/TestPackageResultTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/TestPackageResultTest.java
index df80dbb..57d322e 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/TestPackageResultTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/TestPackageResultTest.java
@@ -20,6 +20,7 @@
 import junit.framework.TestCase;
 
 import java.util.Collection;
+import java.util.HashMap;
 
 /**
  * Unit tests for {@link TestPackageResult}.
@@ -33,7 +34,7 @@
         TestPackageResult pkgResult = new TestPackageResult();
         TestIdentifier excludedTest = new TestIdentifier("com.example.ExampleTest", "testPass");
         pkgResult.insertTest(excludedTest);
-        pkgResult.reportTestEnded(excludedTest);
+        pkgResult.reportTestEnded(excludedTest, new HashMap<String, String>());
         TestIdentifier includedTest = new TestIdentifier("com.example.ExampleTest",
                 "testNotExecuted");
         pkgResult.insertTest(includedTest);
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/TestSummaryXmlTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/TestSummaryXmlTest.java
index 3fc7dd2..f8b135a 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/TestSummaryXmlTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/TestSummaryXmlTest.java
@@ -38,7 +38,7 @@
             "<Foo failed=\"1\" notExecuted=\"2\" pass=\"3\" timeout=\"4\"/>" +
         "</TestResult>";
 
-    public void testConstructor() throws ParseException  {
+    public void testConstructor()  {
         TestSummaryXml result = new TestSummaryXml(1, "2011-11-01");
         assertEquals(1, result.getId());
         assertEquals("2011-11-01", result.getTimestamp());
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java
index f0fd536..40a9475 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java
@@ -15,8 +15,10 @@
  */
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.tradefed.UnitTests;
 import com.android.cts.tradefed.build.StubCtsBuildHelper;
 import com.android.cts.tradefed.result.PlanCreator;
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
@@ -33,14 +35,24 @@
 import java.io.FileNotFoundException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
 
 /**
  * Unit tests for {@link CtsTest}.
  */
 public class CtsTestTest extends TestCase {
 
-    private static final String PACKAGE_NAME = "test-uri";
+    private static final String PLAN_NAME = "CTS";
+    private static final String PACKAGE_NAME = "test-name";
+    private static final String ID = AbiUtils.createId(UnitTests.ABI.getName(), PACKAGE_NAME);
+    private static final Set<String> NAMES = new HashSet<>();
+    private static final Set<String> IDS = new HashSet<>();
+    static {
+        NAMES.add(PACKAGE_NAME);
+        IDS.add(ID);
+    }
+
     /** the test fixture under test, with all external dependencies mocked out */
     private CtsTest mCtsTest;
     private ITestPackageRepo mMockRepo;
@@ -49,10 +61,9 @@
     private ITestInvocationListener mMockListener;
     private StubCtsBuildHelper mStubBuildHelper;
     private ITestPackageDef mMockPackageDef;
+    private Set<ITestPackageDef> mMockPackageDefs;
     private IRemoteTest mMockTest;
 
-    private static final String PLAN_NAME = "CTS";
-
     /**
      * {@inheritDoc}
      */
@@ -64,7 +75,11 @@
         mMockDevice = EasyMock.createMock(ITestDevice.class);
         mMockListener = EasyMock.createNiceMock(ITestInvocationListener.class);
         mStubBuildHelper = new StubCtsBuildHelper();
+        mMockPackageDefs = new HashSet<ITestPackageDef>();
         mMockPackageDef = EasyMock.createMock(ITestPackageDef.class);
+        mMockPackageDefs.add(mMockPackageDef);
+        EasyMock.expect(mMockPackageDef.getTargetApkName()).andStubReturn(null);
+        EasyMock.expect(mMockPackageDef.getTargetPackageName()).andStubReturn(null);
         mMockTest = EasyMock.createMock(IRemoteTest.class);
 
         mCtsTest = new CtsTest() {
@@ -93,12 +108,13 @@
         mCtsTest.setBuildHelper(mStubBuildHelper);
         // turn off device collection for simplicity
         mCtsTest.setSkipDeviceInfo(true);
-        EasyMock.expect(mMockPackageDef.getTargetApkName()).andStubReturn(null);
-        EasyMock.expect(mMockPackageDef.getTargetPackageName()).andStubReturn(null);
+        // only run tests on one ABI
+        EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(
+                UnitTests.ABI.getName()).anyTimes();
     }
 
     /**
-     * Test normal case {@link CtsTest#run(java.util.List)} when running a plan.
+     * Test normal case {@link CtsTest#run(ITestInvocationListener)} when running a plan.
      */
     @SuppressWarnings("unchecked")
     public void testRun_plan() throws DeviceNotAvailableException, ParseException {
@@ -112,7 +128,7 @@
     }
 
     /**
-     * Test normal case {@link CtsTest#run(java.util.List)} when running a package.
+     * Test normal case {@link CtsTest#run(ITestInvocationListener)} when running a package.
      */
     @SuppressWarnings("unchecked")
     public void testRun_package() throws DeviceNotAvailableException {
@@ -138,8 +154,6 @@
 
         // now expect test to be resumed
         mMockTest.run((ITestInvocationListener)EasyMock.anyObject());
-        EasyMock.expect(mMockPackageDef.getName()).andReturn(PACKAGE_NAME);
-        EasyMock.expect(mMockPackageDef.getDigest()).andReturn("digest");
 
         replayMocks();
         try {
@@ -154,7 +168,7 @@
     }
 
     /**
-     * Test normal case {@link CtsTest#run(java.util.List)} when running a class.
+     * Test normal case {@link CtsTest#run(ITestInvocationListener)} when running a class.
      */
     @SuppressWarnings("unchecked")
     public void testRun_class() throws DeviceNotAvailableException {
@@ -163,7 +177,7 @@
         mCtsTest.setClassName(className);
         mCtsTest.setMethodName(methodName);
 
-        EasyMock.expect(mMockRepo.findPackageForTest(className)).andReturn(PACKAGE_NAME);
+        EasyMock.expect(mMockRepo.findPackageIdsForTest(className)).andReturn(IDS);
         mMockPackageDef.setClassName(className, methodName);
 
         setCreateAndRunTestExpectations();
@@ -174,14 +188,12 @@
     }
 
     /**
-     * Test {@link CtsTest#run(java.util.List)} when --excluded-package is specified
+     * Test {@link CtsTest#run(ITestInvocationListener)} when --excluded-package is specified
      */
     public void testRun_excludedPackage() throws DeviceNotAvailableException, ParseException {
         mCtsTest.setPlanName(PLAN_NAME);
-        mMockPlan.parse((InputStream)EasyMock.anyObject());
-        Collection<String> uris = new ArrayList<String>(1);
-        uris.add(PACKAGE_NAME);
-        EasyMock.expect(mMockPlan.getTestUris()).andReturn(uris);
+        mMockPlan.parse((InputStream) EasyMock.anyObject());
+        EasyMock.expect(mMockPlan.getTestIds()).andReturn(IDS);
 
         mCtsTest.addExcludedPackageName(PACKAGE_NAME);
 
@@ -192,17 +204,15 @@
     }
 
     /**
-     * Test {@link CtsTest#run(ITestInvocationListener))} when --continue-session is specified
+     * Test {@link CtsTest#run(ITestInvocationListener)} when --continue-session is specified
      */
-    public void testRun_continueSession() throws DeviceNotAvailableException, ParseException {
+    public void testRun_continueSession() throws DeviceNotAvailableException {
         mCtsTest.setContinueSessionId(1);
-        Collection<String> uris = new ArrayList<String>(1);
-        uris.add(PACKAGE_NAME);
-        EasyMock.expect(mMockPlan.getTestUris()).andReturn(uris);
+        EasyMock.expect(mMockPlan.getTestIds()).andReturn(IDS);
         TestFilter filter = new TestFilter();
-        EasyMock.expect(mMockPlan.getExcludedTestFilter(PACKAGE_NAME)).andReturn(
-                filter);
-        mMockPackageDef.setExcludedTestFilter(filter);
+        EasyMock.expect(mMockPlan.getTestFilter(ID)).andReturn(filter);
+
+        mMockPackageDef.setTestFilter(filter);
 
         setCreateAndRunTestExpectations();
 
@@ -216,34 +226,33 @@
      */
     private void setParsePlanExceptations() throws ParseException {
         mCtsTest.setPlanName(PLAN_NAME);
-        mMockPlan.parse((InputStream)EasyMock.anyObject());
-        Collection<String> uris = new ArrayList<String>(1);
-        uris.add(PACKAGE_NAME);
-        EasyMock.expect(mMockPlan.getTestUris()).andReturn(uris);
+        mMockPlan.parse((InputStream) EasyMock.anyObject());
+        EasyMock.expect(mMockPlan.getTestIds()).andReturn(IDS);
         TestFilter filter = new TestFilter();
-        EasyMock.expect(mMockPlan.getExcludedTestFilter(PACKAGE_NAME)).andReturn(
-                filter);
-        mMockPackageDef.setExcludedTestFilter(filter);
+        EasyMock.expect(mMockPlan.getTestFilter(ID)).andReturn(filter);
+        mMockPackageDef.setTestFilter(filter);
     }
 
     /**
      * Set EasyMock expectations for creating and running a package with PACKAGE_NAME
      */
     private void setCreateAndRunTestExpectations() throws DeviceNotAvailableException {
-        EasyMock.expect(mMockRepo.getTestPackage(PACKAGE_NAME)).andReturn(mMockPackageDef);
-        EasyMock.expect(mMockPackageDef.createTest((File)EasyMock.anyObject())).andReturn(
-                mMockTest);
+        EasyMock.expect(mMockRepo.getPackageNames()).andReturn(NAMES).anyTimes();
+        EasyMock.expect(mMockRepo.getPackageIds()).andReturn(IDS).anyTimes();
+        EasyMock.expect(mMockRepo.getTestPackages(PACKAGE_NAME)).andReturn(mMockPackageDefs).anyTimes();
+        EasyMock.expect(mMockRepo.getTestPackage(ID)).andReturn(mMockPackageDef).anyTimes();
+        EasyMock.expect(mMockPackageDef.createTest((File) EasyMock.anyObject())).andReturn(mMockTest);
         EasyMock.expect(mMockPackageDef.getTests()).andReturn(new ArrayList<TestIdentifier>());
-        EasyMock.expect(mMockPackageDef.getUri()).andStubReturn(PACKAGE_NAME);
-        EasyMock.expect(mMockPackageDef.getName()).andReturn(PACKAGE_NAME);
-        EasyMock.expect(mMockPackageDef.getDigest()).andReturn("digest");
-
-        mMockTest.run((ITestInvocationListener)EasyMock.anyObject());
+        EasyMock.expect(mMockPackageDef.getName()).andReturn(PACKAGE_NAME).atLeastOnce();
+        EasyMock.expect(mMockPackageDef.getAbi()).andReturn(UnitTests.ABI).atLeastOnce();
+        EasyMock.expect(mMockPackageDef.getId()).andReturn(ID).atLeastOnce();
+        EasyMock.expect(mMockPackageDef.getDigest()).andReturn("digest").atLeastOnce();
+        mMockTest.run((ITestInvocationListener) EasyMock.anyObject());
     }
 
     /**
-     * Test {@link CtsTest#run(java.util.List)} when --plan and --package options have not been
-     * specified
+     * Test {@link CtsTest#run(ITestInvocationListener)} when --plan and --package options have not
+     * been specified
      */
     public void testRun_nothingToRun() throws DeviceNotAvailableException {
         try {
@@ -255,7 +264,7 @@
     }
 
     /**
-     * Test {@link CtsTest#run(ITestInvocationListener))} when --plan and --package options have
+     * Test {@link CtsTest#run(ITestInvocationListener)} when --plan and --package options have
      * been specified.
      */
     public void testRun_packagePlan() throws DeviceNotAvailableException {
@@ -270,7 +279,7 @@
     }
 
     /**
-     * Test {@link CtsTest#run(java.util.List)} when --plan and --class options have been
+     * Test {@link CtsTest#run(ITestInvocationListener)} when --plan and --class options have been
      * specified
      */
     public void testRun_planClass() throws DeviceNotAvailableException {
@@ -285,8 +294,8 @@
     }
 
     /**
-     * Test {@link CtsTest#run(java.util.List)} when --package and --class options have been
-     * specified
+     * Test {@link CtsTest#run(ITestInvocationListener)} when --package and --class options have
+     * been specified
      */
     public void testRun_packageClass() throws DeviceNotAvailableException {
         mCtsTest.addPackageName(PACKAGE_NAME);
@@ -300,8 +309,8 @@
     }
 
     /**
-     * Test {@link CtsTest#run(java.util.List)} when --plan, --package and --class options have been
-     * specified
+     * Test {@link CtsTest#run(ITestInvocationListener)} when --plan, --package and --class options
+     * have been specified
      */
     public void testRun_planPackageClass() throws DeviceNotAvailableException {
         mCtsTest.setPlanName(PLAN_NAME);
@@ -316,8 +325,8 @@
     }
 
     /**
-     * Test {@link CtsTest#run(java.util.List)} when --plan, --continue-option options have been
-     * specified
+     * Test {@link CtsTest#run(ITestInvocationListener)} when --plan, --continue-option options
+     * have been specified
      */
     public void testRun_planContinue() throws DeviceNotAvailableException {
         mCtsTest.setPlanName(PLAN_NAME);
@@ -331,14 +340,12 @@
     }
 
     private void replayMocks(Object... mocks) {
-        EasyMock.replay(mMockRepo, mMockPlan, mMockDevice, mMockListener, mMockPackageDef,
-                mMockTest);
+        EasyMock.replay(mMockRepo, mMockPlan, mMockDevice, mMockPackageDef, mMockListener, mMockTest);
         EasyMock.replay(mocks);
     }
 
     private void verifyMocks(Object... mocks) {
-        EasyMock.verify(mMockRepo, mMockPlan, mMockDevice, mMockListener, mMockPackageDef,
-                mMockTest);
+        EasyMock.verify(mMockRepo, mMockPlan, mMockDevice, mMockPackageDef, mMockListener, mMockTest);
         EasyMock.verify(mocks);
     }
 }
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/DeqpTestTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/DeqpTestRunnerTest.java
similarity index 95%
rename from tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/DeqpTestTest.java
rename to tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/DeqpTestRunnerTest.java
index 0e7c26e..03ebfc5 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/DeqpTestTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/DeqpTestRunnerTest.java
@@ -15,11 +15,14 @@
  */
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.tradefed.UnitTests;
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.IShellOutputReceiver;
 import com.android.ddmlib.testrunner.ITestRunListener;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IAbi;
 
 import junit.framework.TestCase;
 
@@ -28,14 +31,14 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Map;
 
 /**
- * Unit tests for {@link DeqpTest}.
+ * Unit tests for {@link DeqpTestRunner}.
  */
-public class DeqpTestTest extends TestCase {
-    private static final String URI = "dEQP-GLES3";
+public class DeqpTestRunnerTest extends TestCase {
+    private static final String NAME = "dEQP-GLES3";
+    private static final String ID = AbiUtils.createId(UnitTests.ABI.getName(), NAME);
     private static final String CASE_LIST_FILE_NAME = "/sdcard/dEQP-TestCaseList.txt";
     private static final String LOG_FILE_NAME = "/sdcard/TestLog.qpa";
     private static final String INSTRUMENTATION_NAME =
@@ -102,10 +105,11 @@
 
         tests.add(testId);
 
-        DeqpTest deqpTest = new DeqpTest(URI,
+        DeqpTestRunner deqpTest = new DeqpTestRunner(NAME,
                 "dEQP-GLES" + Integer.toString(requiredMajorVersion)
                 + (requiredMinorVersion > 0 ? Integer.toString(requiredMinorVersion) : ""),
                 tests);
+        deqpTest.setAbi(UnitTests.ABI);
 
         int version = (majorVersion << 16) | minorVersion;
         EasyMock.expect(mockDevice.getProperty("ro.opengles.version"))
@@ -145,7 +149,7 @@
             });
         }
 
-        mockListener.testRunStarted(URI, 1);
+        mockListener.testRunStarted(ID, 1);
         EasyMock.expectLastCall().once();
 
         mockListener.testStarted(EasyMock.eq(testId));
@@ -210,11 +214,12 @@
 
         tests.add(testId);
 
-        DeqpTest deqpTest = new DeqpTest(URI, "dEQP-GLES3", tests);
+        DeqpTestRunner deqpTest = new DeqpTestRunner(NAME, NAME, tests);
+        deqpTest.setAbi(UnitTests.ABI);
 
         int version = 3 << 16;
         EasyMock.expect(mockDevice.getProperty("ro.opengles.version"))
-            .andReturn(Integer.toString(version)).atLeastOnce();
+                .andReturn(Integer.toString(version)).atLeastOnce();
 
         EasyMock.expect(mockDevice.executeShellCommand(EasyMock.eq("rm " + CASE_LIST_FILE_NAME)))
                 .andReturn("").once();
@@ -246,7 +251,7 @@
             }
         });
 
-        mockListener.testRunStarted(URI, 1);
+        mockListener.testRunStarted(ID, 1);
         EasyMock.expectLastCall().once();
 
         mockListener.testStarted(EasyMock.eq(testId));
@@ -382,11 +387,12 @@
             tests.add(id);
         }
 
-        DeqpTest deqpTest = new DeqpTest(URI, "dEQP-GLES3", tests);
+        DeqpTestRunner deqpTest = new DeqpTestRunner(NAME, NAME, tests);
+        deqpTest.setAbi(UnitTests.ABI);
 
         int version = 3 << 16;
         EasyMock.expect(mockDevice.getProperty("ro.opengles.version"))
-            .andReturn(Integer.toString(version)).atLeastOnce();
+                .andReturn(Integer.toString(version)).atLeastOnce();
 
         EasyMock.expect(mockDevice.executeShellCommand(EasyMock.eq("rm " + CASE_LIST_FILE_NAME)))
                 .andReturn("").once();
@@ -418,7 +424,7 @@
             }
         });
 
-        mockListener.testRunStarted(URI, testPaths.length);
+        mockListener.testRunStarted(ID, testPaths.length);
         EasyMock.expectLastCall().once();
 
         for (int i = 0; i < testPaths.length; i++) {
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestPackageXmlParserTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestPackageXmlParserTest.java
index ac3d394..5591b65 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestPackageXmlParserTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestPackageXmlParserTest.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.tradefed.command.CtsConsole;
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
@@ -68,67 +70,76 @@
      * Test parsing test case xml containing an instrumentation test definition.
      */
     public void testParse_instrPackage() throws ParseException  {
-        TestPackageXmlParser parser = new TestPackageXmlParser(true);
+        TestPackageXmlParser parser = new TestPackageXmlParser(AbiUtils.getAbisSupportedByCts(),
+                true);
         parser.parse(getStringAsStream(INSTR_TEST_DATA));
-        TestPackageDef def = parser.getTestPackageDef();
-        assertEquals("com.example", def.getAppNameSpace());
-        assertEquals("android.example", def.getUri());
-        assertEquals("android.test.InstrumentationTestRunner", def.getRunner());
+        for (TestPackageDef def : parser.getTestPackageDefs()) {
+            assertEquals("com.example", def.getAppNameSpace());
+            assertEquals("android.example", def.getAppPackageName());
+            assertEquals("android.test.InstrumentationTestRunner", def.getRunner());
+            assertTrue(AbiUtils.isAbiSupportedByCts(def.getAbi().getName()));
+        }
     }
 
     /**
      * Test parsing test case xml containing an host test attribute and test data.
      */
     public void testParse_hostTest() throws ParseException  {
-        TestPackageXmlParser parser = new TestPackageXmlParser(true);
+        TestPackageXmlParser parser = new TestPackageXmlParser(AbiUtils.getAbisSupportedByCts(),
+                true);
         parser.parse(getStringAsStream(HOST_TEST_DATA));
-        TestPackageDef def = parser.getTestPackageDef();
-        assertEquals(TestPackageDef.HOST_SIDE_ONLY_TEST, def.getTestType());
-        assertEquals(3, def.getTests().size());
-        Iterator<TestIdentifier> iterator = def.getTests().iterator();
+        for (TestPackageDef def : parser.getTestPackageDefs()) {
+            assertEquals(TestPackageDef.HOST_SIDE_ONLY_TEST, def.getTestType());
+            assertEquals(3, def.getTests().size());
+            Iterator<TestIdentifier> iterator = def.getTests().iterator();
 
-        TestIdentifier firstTest = iterator.next();
-        assertEquals("com.example.ExampleTest", firstTest.getClassName());
-        assertEquals("testFoo", firstTest.getTestName());
+            TestIdentifier firstTest = iterator.next();
+            assertEquals("com.example.ExampleTest", firstTest.getClassName());
+            assertEquals("testFoo", firstTest.getTestName());
 
-        TestIdentifier secondTest = iterator.next();
-        assertEquals("com.example.ExampleTest", secondTest.getClassName());
-        assertEquals("testFoo2", secondTest.getTestName());
+            TestIdentifier secondTest = iterator.next();
+            assertEquals("com.example.ExampleTest", secondTest.getClassName());
+            assertEquals("testFoo2", secondTest.getTestName());
 
-        TestIdentifier thirdTest = iterator.next();
-        assertEquals("com.example2.Example2Test", thirdTest.getClassName());
-        assertEquals("testFoo", thirdTest.getTestName());
+            TestIdentifier thirdTest = iterator.next();
+            assertEquals("com.example2.Example2Test", thirdTest.getClassName());
+            assertEquals("testFoo", thirdTest.getTestName());
 
-        assertFalse(iterator.hasNext());
+            assertFalse(iterator.hasNext());
+        }
     }
 
     public void testParse_hostTest_noKnownFailures() throws ParseException  {
-        TestPackageXmlParser parser = new TestPackageXmlParser(false);
+        TestPackageXmlParser parser = new TestPackageXmlParser(AbiUtils.getAbisSupportedByCts(),
+                false);
         parser.parse(getStringAsStream(HOST_TEST_DATA));
-        TestPackageDef def = parser.getTestPackageDef();
-        assertEquals(TestPackageDef.HOST_SIDE_ONLY_TEST, def.getTestType());
-        assertEquals(2, def.getTests().size());
-        Iterator<TestIdentifier> iterator = def.getTests().iterator();
+        for (TestPackageDef def : parser.getTestPackageDefs()) {
+            assertEquals(TestPackageDef.HOST_SIDE_ONLY_TEST, def.getTestType());
+            assertEquals(2, def.getTests().size());
+            Iterator<TestIdentifier> iterator = def.getTests().iterator();
 
-        TestIdentifier firstTest = iterator.next();
-        assertEquals("com.example.ExampleTest", firstTest.getClassName());
-        assertEquals("testFoo", firstTest.getTestName());
+            TestIdentifier firstTest = iterator.next();
+            assertEquals("com.example.ExampleTest", firstTest.getClassName());
+            assertEquals("testFoo", firstTest.getTestName());
 
-        TestIdentifier thirdTest = iterator.next();
-        assertEquals("com.example2.Example2Test", thirdTest.getClassName());
-        assertEquals("testFoo", thirdTest.getTestName());
+            TestIdentifier thirdTest = iterator.next();
+            assertEquals("com.example2.Example2Test", thirdTest.getClassName());
+            assertEquals("testFoo", thirdTest.getTestName());
 
-        assertFalse(iterator.hasNext());
+            assertFalse(iterator.hasNext());
+        }
     }
 
     /**
      * Test parsing test case xml containing an invalid host test attribute.
      */
     public void testParse_badHostTest() throws ParseException  {
-        TestPackageXmlParser parser = new TestPackageXmlParser(true);
+        TestPackageXmlParser parser = new TestPackageXmlParser(AbiUtils.getAbisSupportedByCts(),
+                true);
         parser.parse(getStringAsStream(BAD_HOST_TEST_DATA));
-        TestPackageDef def = parser.getTestPackageDef();
-        assertFalse(TestPackageDef.HOST_SIDE_ONLY_TEST.equals(def.getTestType()));
+        for (TestPackageDef def : parser.getTestPackageDefs()) {
+            assertFalse(TestPackageDef.HOST_SIDE_ONLY_TEST.equals(def.getTestType()));
+        }
     }
 
     public void testParse_vmHostTest() throws ParseException  {
@@ -140,19 +151,22 @@
     }
 
     private void assertTestType(String expectedType, String xml) throws ParseException {
-        TestPackageXmlParser parser = new TestPackageXmlParser(true);
+        TestPackageXmlParser parser = new TestPackageXmlParser(AbiUtils.getAbisSupportedByCts(),
+                true);
         parser.parse(getStringAsStream(xml));
-        TestPackageDef def = parser.getTestPackageDef();
-        assertEquals(expectedType, def.getTestType());
+        for (TestPackageDef def : parser.getTestPackageDefs()) {
+            assertEquals(expectedType, def.getTestType());
+        }
     }
 
     /**
      * Test parsing a test case xml with no test package data.
      */
     public void testParse_noData() throws ParseException  {
-        TestPackageXmlParser parser = new TestPackageXmlParser(true);
+        TestPackageXmlParser parser = new TestPackageXmlParser(AbiUtils.getAbisSupportedByCts(),
+                true);
         parser.parse(getStringAsStream(NO_TEST_DATA));
-        assertNull(parser.getTestPackageDef());
+        assertTrue(parser.getTestPackageDefs().isEmpty());
     }
 
     private InputStream getStringAsStream(String input) {
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestPlanTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestPlanTest.java
index 18d4776..5b28539 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestPlanTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/TestPlanTest.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.tradefed.testtype;
 
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
@@ -25,15 +26,19 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
 
 /**
  * Unit tests for {@link TestPlan}.
  */
 public class TestPlanTest extends TestCase {
 
-    private static final String TEST_URI1 = "foo";
-    private static final String TEST_URI2 = "foo2";
+    private static final String TEST_NAME1 = "foo";
+    private static final String TEST_NAME2 = "foo2";
     private static final String EXCLUDE_TEST_CLASS = "com.example.FooTest";
     private static final String EXCLUDE_TEST_METHOD = "testFoo";
     private static final String EXCLUDE_TEST_METHOD2 = "testFoo2";
@@ -42,26 +47,26 @@
 
     static final String TEST_DATA =
         "<TestPlan version=\"1.0\">" +
-            String.format("<Entry uri=\"%s\" />", TEST_URI1) +
-            String.format("<Entry uri=\"%s\" />", TEST_URI2) +
+            String.format("<Entry name=\"%s\" />", TEST_NAME1) +
+            String.format("<Entry name=\"%s\" />", TEST_NAME2) +
         "</TestPlan>";
 
     static final String TEST_EXCLUDED_DATA =
         "<TestPlan version=\"1.0\">" +
-            String.format("<Entry uri=\"%s\" exclude=\"%s#%s\" />", TEST_URI1, EXCLUDE_TEST_CLASS,
+            String.format("<Entry name=\"%s\" exclude=\"%s#%s\" />", TEST_NAME1, EXCLUDE_TEST_CLASS,
                     EXCLUDE_TEST_METHOD) +
         "</TestPlan>";
 
     static final String TEST_MULTI_EXCLUDED_DATA =
         "<TestPlan version=\"1.0\">" +
-            String.format("<Entry uri=\"%s\" exclude=\"%s#%s;%s#%s\" />", TEST_URI1,
+            String.format("<Entry name=\"%s\" exclude=\"%s#%s;%s#%s\" />", TEST_NAME1,
                     EXCLUDE_TEST_CLASS, EXCLUDE_TEST_METHOD, EXCLUDE_TEST_CLASS,
                     EXCLUDE_TEST_METHOD2) +
         "</TestPlan>";
 
     static final String TEST_CLASS_EXCLUDED_DATA =
         "<TestPlan version=\"1.0\">" +
-            String.format("<Entry uri=\"%s\" exclude=\"%s\" />", TEST_URI1,
+            String.format("<Entry name=\"%s\" exclude=\"%s\" />", TEST_NAME1,
                     EXCLUDE_TEST_CLASS) +
         "</TestPlan>";
 
@@ -70,11 +75,11 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mPlan = new TestPlan("plan");
+        mPlan = new TestPlan("plan", AbiUtils.getAbisSupportedByCts());
     }
 
     /**
-     * Simple test for parsing a plan containing two uris
+     * Simple test for parsing a plan containing two names
      */
     public void testParse() throws ParseException  {
         mPlan.parse(getStringAsStream(TEST_DATA));
@@ -86,13 +91,20 @@
      * @param plan
      */
     private void assertTestData(TestPlan plan) {
-        assertEquals(2, plan.getTestUris().size());
-        Iterator<String> iter = plan.getTestUris().iterator();
-        // assert uris in order
-        assertEquals(TEST_URI1, iter.next());
-        assertEquals(TEST_URI2, iter.next());
-        assertFalse(plan.getExcludedTestFilter(TEST_URI1).hasExclusion());
-        assertFalse(plan.getExcludedTestFilter(TEST_URI2).hasExclusion());
+        Set<String> abis = AbiUtils.getAbisSupportedByCts();
+        assertEquals(2 * abis.size(), plan.getTestIds().size());
+        List<String> sortedAbis = new ArrayList<String>(abis);
+        Collections.sort(sortedAbis);
+        Iterator<String> iter = plan.getTestIds().iterator();
+        for (String abi : sortedAbis) {
+            String test1Id = AbiUtils.createId(abi, TEST_NAME1);
+            String test2Id = AbiUtils.createId(abi, TEST_NAME2);
+            // assert names in order
+            assertEquals(test1Id, iter.next());
+            assertEquals(test2Id, iter.next());
+            assertFalse(plan.getTestFilter(test1Id).hasExclusion());
+            assertFalse(plan.getTestFilter(test2Id).hasExclusion());
+        }
     }
 
     /**
@@ -100,10 +112,15 @@
      */
     public void testParse_exclude() throws ParseException  {
         mPlan.parse(getStringAsStream(TEST_EXCLUDED_DATA));
-        assertEquals(1, mPlan.getTestUris().size());
-        TestFilter filter = mPlan.getExcludedTestFilter(TEST_URI1);
-        assertTrue(filter.getExcludedTests().contains(new TestIdentifier(EXCLUDE_TEST_CLASS,
-                EXCLUDE_TEST_METHOD)));
+        Set<String> abis = AbiUtils.getAbisSupportedByCts();
+        assertEquals(abis.size(), mPlan.getTestIds().size());
+
+        for (String abi : abis) {
+            String test1Id = AbiUtils.createId(abi, TEST_NAME1);
+            TestFilter filter = mPlan.getTestFilter(test1Id);
+            assertTrue(filter.getExcludedTests().contains(new TestIdentifier(EXCLUDE_TEST_CLASS,
+                    EXCLUDE_TEST_METHOD)));
+        }
     }
 
     /**
@@ -119,12 +136,17 @@
      * @param plan
      */
     private void assertMultiExcluded(TestPlan plan) {
-        assertEquals(1, plan.getTestUris().size());
-        TestFilter filter = plan.getExcludedTestFilter(TEST_URI1);
-        assertTrue(filter.getExcludedTests().contains(new TestIdentifier(EXCLUDE_TEST_CLASS,
-                EXCLUDE_TEST_METHOD)));
-        assertTrue(filter.getExcludedTests().contains(new TestIdentifier(EXCLUDE_TEST_CLASS,
-                EXCLUDE_TEST_METHOD2)));
+        Set<String> abis = AbiUtils.getAbisSupportedByCts();
+        assertEquals(abis.size(), plan.getTestIds().size());
+
+        for (String abi : abis) {
+            String test1Id = AbiUtils.createId(abi, TEST_NAME1);
+            TestFilter filter = plan.getTestFilter(test1Id);
+            assertTrue(filter.getExcludedTests().contains(new TestIdentifier(EXCLUDE_TEST_CLASS,
+                    EXCLUDE_TEST_METHOD)));
+            assertTrue(filter.getExcludedTests().contains(new TestIdentifier(EXCLUDE_TEST_CLASS,
+                    EXCLUDE_TEST_METHOD2)));
+        }
     }
 
     /**
@@ -132,16 +154,21 @@
      */
     public void testParse_classExclude() throws ParseException  {
         mPlan.parse(getStringAsStream(TEST_CLASS_EXCLUDED_DATA));
-        assertEquals(1, mPlan.getTestUris().size());
-        TestFilter filter = mPlan.getExcludedTestFilter(TEST_URI1);
-        assertTrue(filter.getExcludedClasses().contains(EXCLUDE_TEST_CLASS));
+        Set<String> abis = AbiUtils.getAbisSupportedByCts();
+        assertEquals(abis.size(), mPlan.getTestIds().size());
+
+        for (String abi : abis) {
+            String test1Id = AbiUtils.createId(abi, TEST_NAME1);
+            TestFilter filter = mPlan.getTestFilter(test1Id);
+            assertTrue(filter.getExcludedClasses().contains(EXCLUDE_TEST_CLASS));
+        }
     }
 
     /**
      * Test serializing an empty plan
      * @throws IOException
      */
-    public void testSerialize_empty() throws ParseException, IOException  {
+    public void testSerialize_empty() throws IOException  {
         ByteArrayOutputStream outStream = new ByteArrayOutputStream();
         mPlan.serialize(outStream);
         assertTrue(outStream.toString().contains(EMPTY_DATA));
@@ -152,11 +179,14 @@
      * @throws IOException
      */
     public void testSerialize_packages() throws ParseException, IOException  {
-        mPlan.addPackage(TEST_URI1);
-        mPlan.addPackage(TEST_URI2);
+        Set<String> abis = AbiUtils.getAbisSupportedByCts();
+        for (String abi : abis) {
+            mPlan.addPackage(AbiUtils.createId(abi, TEST_NAME1));
+            mPlan.addPackage(AbiUtils.createId(abi, TEST_NAME2));
+        }
         ByteArrayOutputStream outStream = new ByteArrayOutputStream();
         mPlan.serialize(outStream);
-        TestPlan parsedPlan = new TestPlan("parsed");
+        TestPlan parsedPlan = new TestPlan("parsed", AbiUtils.getAbisSupportedByCts());
         parsedPlan.parse(getStringAsStream(outStream.toString()));
         // parsedPlan should contain same contents as TEST_DATA
         assertTestData(parsedPlan);
@@ -166,14 +196,19 @@
      * Test serializing and deserializing plan with multiple excluded tests
      */
     public void testSerialize_multiExclude() throws ParseException, IOException  {
-        mPlan.addPackage(TEST_URI1);
-        mPlan.addExcludedTest(TEST_URI1, new TestIdentifier(EXCLUDE_TEST_CLASS,
-                EXCLUDE_TEST_METHOD));
-        mPlan.addExcludedTest(TEST_URI1, new TestIdentifier(EXCLUDE_TEST_CLASS,
-                EXCLUDE_TEST_METHOD2));
+        Set<String> abis = AbiUtils.getAbisSupportedByCts();
+
+        for (String abi : abis) {
+            String test1Id = AbiUtils.createId(abi, TEST_NAME1);
+            mPlan.addPackage(test1Id);
+            mPlan.addExcludedTest(test1Id, new TestIdentifier(EXCLUDE_TEST_CLASS,
+                    EXCLUDE_TEST_METHOD));
+            mPlan.addExcludedTest(test1Id, new TestIdentifier(EXCLUDE_TEST_CLASS,
+                    EXCLUDE_TEST_METHOD2));
+        }
         ByteArrayOutputStream outStream = new ByteArrayOutputStream();
         mPlan.serialize(outStream);
-        TestPlan parsedPlan = new TestPlan("parsed");
+        TestPlan parsedPlan = new TestPlan("parsed", AbiUtils.getAbisSupportedByCts());
         parsedPlan.parse(getStringAsStream(outStream.toString()));
         // parsedPlan should contain same contents as TEST_DATA
         assertMultiExcluded(parsedPlan);
diff --git a/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java b/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
index 0307375..c772c33 100644
--- a/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
+++ b/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
@@ -223,10 +223,10 @@
       String cmd = String.format("ANDROID_DATA=%s dalvikvm|#ABI#| -Xmx512M -Xss32K " +
               "-Djava.io.tmpdir=%s -classpath %s %s", TARGET_JAR_ROOT_PATH, TARGET_JAR_ROOT_PATH,
               classpath, mainclass);
-      return "String cmd = AbiFormatter.formatCmdForAbi(\"" + cmd + "\", mAbi.getBitness());\n" +
-          "String res = getDevice().executeShellCommand(cmd);\n" +
-          "// A sucessful adb shell command returns an empty string.\n" +
-          "assertEquals(cmd, \"\", res);";
+      return "    String cmd = AbiFormatter.formatCmdForAbi(\"" + cmd + "\", mAbi.getBitness());\n" +
+              "    String res = getDevice().executeShellCommand(cmd);\n" +
+              "    // A sucessful adb shell command returns an empty string.\n" +
+              "    assertEquals(cmd, \"\", res);";
     }
 
     private String getWarningMessage() {
@@ -257,8 +257,8 @@
 
         //"dot.junit.opcodes.add_double_2addr.Main_testN2";
         String mainclass = pName + ".Main_" + method;
-        curJunitFileData += "    " + getShellExecJavaLine(cp, mainclass);
-        curJunitFileData += "}\n\n";
+        curJunitFileData += getShellExecJavaLine(cp, mainclass);
+        curJunitFileData += "\n}\n\n";
     }
 
     private void handleTests() throws IOException {