DO NOT MERGE Add preparer for cts extended device info

Change-Id: I38297f32749eeaa816332f7e3d8fba18c12c29c6
diff --git a/build/device_info_package.mk b/build/device_info_package.mk
index e0b2ad9..897d8dc 100644
--- a/build/device_info_package.mk
+++ b/build/device_info_package.mk
@@ -63,5 +63,4 @@
 
 LOCAL_SDK_VERSION := current
 
-include $(BUILD_PACKAGE)
-
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildInfo.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildInfo.java
new file mode 100644
index 0000000..fafdb91
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildInfo.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 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.build;
+
+import com.android.tradefed.build.FolderBuildInfo;
+
+import java.io.File;
+
+/**
+ * An extension of {@link FolderBuildInfo} that includes additional CTS build info.
+ */
+public class CtsBuildInfo extends FolderBuildInfo implements ICtsBuildInfo {
+
+    private File mResultDir = null;
+
+    /**
+     * Creates a {@link CtsBuildInfo}
+     *
+     * @param buildId
+     * @param testTarget
+     * @param buildName
+     */
+    public CtsBuildInfo(String buildId, String testTarget, String buildName) {
+        super(buildId, testTarget, buildName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public File getResultDir() {
+        return mResultDir;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setResultDir(File resultDir) {
+        mResultDir = resultDir;
+    }
+}
\ No newline at end of file
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
index a33fc01..6169e77 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
@@ -15,7 +15,6 @@
  */
 package com.android.cts.tradefed.build;
 
-import com.android.tradefed.build.FolderBuildInfo;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.build.IBuildProvider;
 import com.android.tradefed.build.IFolderBuildInfo;
@@ -42,7 +41,7 @@
         if (mCtsRootDirPath == null) {
             throw new IllegalArgumentException("Missing --cts-install-path");
         }
-        IFolderBuildInfo ctsBuild = new FolderBuildInfo(CTS_BUILD_VERSION, "cts", "cts");
+        CtsBuildInfo ctsBuild = new CtsBuildInfo(CTS_BUILD_VERSION, "cts", "cts");
         ctsBuild.setRootDir(new File(mCtsRootDirPath));
         return ctsBuild;
     }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/ICtsBuildInfo.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/ICtsBuildInfo.java
new file mode 100644
index 0000000..0b3ad65
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/ICtsBuildInfo.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 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.build;
+
+import com.android.tradefed.build.IFolderBuildInfo;
+
+import java.io.File;
+
+/**
+ * An {@link IFolderBuildInfo} that contains local CTS result directory.
+ */
+public interface ICtsBuildInfo extends IFolderBuildInfo{
+
+    /**
+     * Returns the local CTS result directory.
+     */
+    public File getResultDir();
+
+    /**
+     * Sets the local CTS result directory.
+     */
+    public void setResultDir(File resultDir);
+}
\ No newline at end of file
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java b/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java
index 733eb4f..7883fce 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/device/DeviceInfoCollector.java
@@ -15,8 +15,10 @@
  */
 package com.android.cts.tradefed.device;
 
+import com.android.cts.tradefed.build.ICtsBuildInfo;
 import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.Log;
+import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.result.ITestInvocationListener;
@@ -39,10 +41,21 @@
     private static final String APK_NAME = "TestDeviceSetup";
     public static final String APP_PACKAGE_NAME = "android.tests.devicesetup";
     private static final String INSTRUMENTATION_NAME = "android.tests.getinfo.DeviceInfoInstrument";
+
+    private static final String EXTENDED_APK_NAME = "CtsDeviceInfo";
+    public static final String EXTENDED_APP_PACKAGE_NAME =
+            "com.android.compatibility.common.deviceinfo";
+    private static final String EXTENDED_INSTRUMENTATION_NAME =
+            "com.android.compatibility.common.deviceinfo.DeviceInfoInstrument";
+    private static final String DEVICE_RESULT_DIR = "/sdcard/device-info-files";
+
     public static final Set<String> IDS = new HashSet<String>();
+    public static final Set<String> EXTENDED_IDS = new HashSet<String>();
+
     static {
         for (String abi : AbiUtils.getAbisSupportedByCts()) {
             IDS.add(AbiUtils.createId(abi, APP_PACKAGE_NAME));
+            EXTENDED_IDS.add(AbiUtils.createId(abi, EXTENDED_APP_PACKAGE_NAME));
         }
     }
 
@@ -56,7 +69,33 @@
      */
     public static void collectDeviceInfo(ITestDevice device, String abi, File testApkDir,
             ITestInvocationListener listener) throws DeviceNotAvailableException {
-        File apkFile = new File(testApkDir, String.format("%s.apk", APK_NAME));
+        runInstrumentation(device, abi, testApkDir, listener, APK_NAME, APP_PACKAGE_NAME,
+            INSTRUMENTATION_NAME);
+    }
+
+    /**
+     * Installs and runs the extended device info collector instrumentation, and forwards results
+     * to the listener.
+     *
+     * @param device
+     * @param listener
+     * @throws DeviceNotAvailableException
+     */
+    public static void collectExtendedDeviceInfo(ITestDevice device, String abi, File testApkDir,
+            ITestInvocationListener listener, IBuildInfo buildInfo)
+            throws DeviceNotAvailableException {
+        // Clear files in device test result directory
+        device.executeShellCommand(String.format("rm -rf %s", DEVICE_RESULT_DIR));
+        runInstrumentation(device, abi, testApkDir, listener, EXTENDED_APK_NAME,
+            EXTENDED_APP_PACKAGE_NAME, EXTENDED_INSTRUMENTATION_NAME);
+        // Copy files in remote result directory to local directory
+        pullExtendedDeviceInfoResults(device, buildInfo);
+    }
+
+    private static void runInstrumentation(ITestDevice device, String abi, File testApkDir,
+            ITestInvocationListener listener, String apkName, String packageName,
+            String instrumentName) throws DeviceNotAvailableException {
+        File apkFile = new File(testApkDir, String.format("%s.apk", apkName));
         if (!apkFile.exists()) {
             Log.e(LOG_TAG, String.format("Could not find %s", apkFile.getAbsolutePath()));
             return;
@@ -68,9 +107,39 @@
         instrTest.setInstallFile(apkFile);
         // no need to collect tests and re-run
         instrTest.setRerunMode(false);
-        instrTest.setPackageName(APP_PACKAGE_NAME);
-        instrTest.setRunName(AbiUtils.createId(abi, APP_PACKAGE_NAME));
-        instrTest.setRunnerName(INSTRUMENTATION_NAME);
+        instrTest.setPackageName(packageName);
+        instrTest.setRunName(AbiUtils.createId(abi, packageName));
+        instrTest.setRunnerName(instrumentName);
         instrTest.run(listener);
     }
+
+    private static void pullExtendedDeviceInfoResults(ITestDevice device, IBuildInfo buildInfo)
+            throws DeviceNotAvailableException {
+        if (!(buildInfo instanceof ICtsBuildInfo)) {
+            Log.e(LOG_TAG, "Invalid instance of buildInfo");
+            return;
+        }
+        ICtsBuildInfo ctsBuildInfo = (ICtsBuildInfo) buildInfo;
+        File localResultDir = ctsBuildInfo.getResultDir();
+        if (localResultDir == null || !localResultDir.isDirectory()) {
+            Log.e(LOG_TAG, "Local result directory is null or is not a directory");
+            return;
+        }
+        // Pull files from device result directory to local result directory
+        String command = String.format("adb -s %s pull %s %s", device.getSerialNumber(),
+                DEVICE_RESULT_DIR, localResultDir.getAbsolutePath());
+        if (!execute(command)) {
+            Log.e(LOG_TAG, String.format("Failed to run %s", command));
+        }
+    }
+
+    private static boolean execute(String command) {
+        try {
+            Process p = Runtime.getRuntime().exec(new String[] {"/bin/bash", "-c", command});
+            return (p.waitFor() == 0);
+        } catch (Exception e) {
+            Log.e(LOG_TAG, e);
+            return false;
+        }
+    }
 }
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 5029dfe..b7f064f 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
@@ -43,6 +43,7 @@
     private TestResults mResults = new TestResults();
     private TestPackageResult mCurrentPkgResult = null;
     private boolean mIsDeviceInfoRun = false;
+    private boolean mIsExtendedDeviceInfoRun = false;
 
     @Override
     public void invocationStarted(IBuildInfo buildInfo) {
@@ -63,8 +64,11 @@
             logCompleteRun(mCurrentPkgResult);
         }
         mIsDeviceInfoRun = DeviceInfoCollector.IDS.contains(id);
+        mIsExtendedDeviceInfoRun = DeviceInfoCollector.EXTENDED_IDS.contains(id);
         if (mIsDeviceInfoRun) {
             logResult("Collecting device info");
+        } else if (mIsExtendedDeviceInfoRun) {
+            logResult("Collecting extended device info");
         } else  {
             if (mCurrentPkgResult == null || !id.equals(mCurrentPkgResult.getId())) {
                 logResult("-----------------------------------------");
@@ -132,9 +136,13 @@
     }
 
     private void logCompleteRun(TestPackageResult pkgResult) {
-        if (pkgResult.getAppPackageName().equals(DeviceInfoCollector.APP_PACKAGE_NAME)) {
+        String appPackageName = pkgResult.getAppPackageName();
+        if (appPackageName.equals(DeviceInfoCollector.APP_PACKAGE_NAME)) {
             logResult("Device info collection complete");
             return;
+        } else if (appPackageName.equals(DeviceInfoCollector.EXTENDED_APP_PACKAGE_NAME)) {
+            logResult("Extended device info collection complete");
+            return;
         }
         logResult("%s package complete: Passed %d, Failed %d, Not Executed %d",
                 pkgResult.getId(), pkgResult.countTests(CtsTestStatus.PASS),
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 8cb4072..30e51b7 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
@@ -17,13 +17,13 @@
 package com.android.cts.tradefed.result;
 
 import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.cts.tradefed.build.ICtsBuildInfo;
 import com.android.cts.tradefed.device.DeviceInfoCollector;
 import com.android.cts.tradefed.testtype.CtsTest;
 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.build.IFolderBuildInfo;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.Option.Importance;
 import com.android.tradefed.log.LogUtil.CLog;
@@ -104,6 +104,7 @@
     private TestPackageResult mCurrentPkgResult = null;
     private Test mCurrentTest = null;
     private boolean mIsDeviceInfoRun = false;
+    private boolean mIsExtendedDeviceInfoRun = false;
     private ResultReporter mReporter;
     private File mLogDir;
     private String mSuiteName;
@@ -124,10 +125,10 @@
     @Override
     public void invocationStarted(IBuildInfo buildInfo) {
         mBuildInfo = buildInfo;
-        if (!(buildInfo instanceof IFolderBuildInfo)) {
-            throw new IllegalArgumentException("build info is not a IFolderBuildInfo");
+        if (!(buildInfo instanceof ICtsBuildInfo)) {
+            throw new IllegalArgumentException("build info is not a ICtsBuildInfo");
         }
-        IFolderBuildInfo ctsBuild = (IFolderBuildInfo)buildInfo;
+        ICtsBuildInfo ctsBuild = (ICtsBuildInfo)buildInfo;
         CtsBuildHelper ctsBuildHelper = getBuildHelper(ctsBuild);
         mDeviceSerial = buildInfo.getDeviceSerial() == null ? "unknown_device" :
             buildInfo.getDeviceSerial();
@@ -155,6 +156,8 @@
         mSuiteName = ctsBuildHelper.getSuiteName();
         mReporter = new ResultReporter(mResultServer, mSuiteName);
 
+        ctsBuild.setResultDir(mReportDir);
+
         // TODO: allow customization of log dir
         // create a unique directory for saving logs, with same name as result dir
         File rootLogDir = getBuildHelper(ctsBuild).getLogsDir();
@@ -199,7 +202,7 @@
      * Helper method to retrieve the {@link CtsBuildHelper}.
      * @param ctsBuild
      */
-    CtsBuildHelper getBuildHelper(IFolderBuildInfo ctsBuild) {
+    CtsBuildHelper getBuildHelper(ICtsBuildInfo ctsBuild) {
         CtsBuildHelper buildHelper = new CtsBuildHelper(ctsBuild.getRootDir());
         try {
             buildHelper.validateStructure();
@@ -255,7 +258,8 @@
     @Override
     public void testRunStarted(String id, int numTests) {
         mIsDeviceInfoRun = DeviceInfoCollector.IDS.contains(id);
-        if (!mIsDeviceInfoRun) {
+        mIsExtendedDeviceInfoRun = DeviceInfoCollector.EXTENDED_IDS.contains(id);
+        if (!mIsDeviceInfoRun && !mIsExtendedDeviceInfoRun) {
             mCurrentPkgResult = mResults.getOrCreatePackage(id);
             mCurrentPkgResult.setDeviceSerial(mDeviceSerial);
         }
@@ -266,7 +270,7 @@
      */
     @Override
     public void testStarted(TestIdentifier test) {
-        if (!mIsDeviceInfoRun) {
+        if (!mIsDeviceInfoRun && !mIsExtendedDeviceInfoRun) {
             mCurrentTest = mCurrentPkgResult.insertTest(test);
         }
     }
@@ -276,7 +280,7 @@
      */
     @Override
     public void testFailed(TestIdentifier test, String trace) {
-        if (!mIsDeviceInfoRun) {
+        if (!mIsDeviceInfoRun && !mIsExtendedDeviceInfoRun) {
             mCurrentPkgResult.reportTestFailure(test, CtsTestStatus.FAIL, trace);
         }
     }
@@ -287,7 +291,7 @@
     @Override
     public void testAssumptionFailure(TestIdentifier test, String trace) {
         // TODO: do something different here?
-        if (!mIsDeviceInfoRun) {
+        if (!mIsDeviceInfoRun && !mIsExtendedDeviceInfoRun) {
             mCurrentPkgResult.reportTestFailure(test, CtsTestStatus.FAIL, trace);
         }
     }
@@ -305,7 +309,7 @@
      */
     @Override
     public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
-        if (!mIsDeviceInfoRun) {
+        if (!mIsDeviceInfoRun && !mIsExtendedDeviceInfoRun) {
             mCurrentPkgResult.reportTestEnded(test, testMetrics);
         }
     }
@@ -317,11 +321,22 @@
     public void testRunEnded(long elapsedTime, Map<String, String> runMetrics) {
         if (mIsDeviceInfoRun) {
             mResults.populateDeviceInfoMetrics(runMetrics);
+        } else if (mIsExtendedDeviceInfoRun) {
+            checkExtendedDeviceInfoMetrics(runMetrics);
         } else {
             mCurrentPkgResult.populateMetrics(runMetrics);
         }
     }
 
+    private void checkExtendedDeviceInfoMetrics(Map<String, String> runMetrics) {
+        for (Map.Entry<String, String> metricEntry : runMetrics.entrySet()) {
+            String value = metricEntry.getValue();
+            if (!value.endsWith(".json")) {
+                CLog.e(String.format("%s failed: %s", metricEntry.getKey(), value));
+            }
+        }
+    }
+
     /**
      * {@inheritDoc}
      */
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 b3bc7da..01f148a 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
@@ -1058,6 +1058,8 @@
         if (!mSkipDeviceInfo) {
             String abi = AbiFormatter.getDefaultAbi(device, "");
             DeviceInfoCollector.collectDeviceInfo(device, abi, ctsBuild.getTestCasesDir(), listener);
+            DeviceInfoCollector.collectExtendedDeviceInfo(
+                device, abi, ctsBuild.getTestCasesDir(), listener, mBuildInfo);
         }
     }
 
diff --git a/tools/tradefed-host/tests/run_unit_func_tests.sh b/tools/tradefed-host/tests/run_unit_func_tests.sh
new file mode 100755
index 0000000..461a80d
--- /dev/null
+++ b/tools/tradefed-host/tests/run_unit_func_tests.sh
@@ -0,0 +1,48 @@
+#!/bin/bash
+
+# Copyright (C) 2015 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.
+
+# helper script for running the cts-tradefed unit tests
+
+checkFile() {
+    if [ ! -f "$1" ]; then
+        echo "Unable to locate $1"
+        exit
+    fi;
+}
+
+# check if in Android build env
+if [ ! -z ${ANDROID_BUILD_TOP} ]; then
+    HOST=`uname`
+    if [ "$HOST" == "Linux" ]; then
+        OS="linux-x86"
+    elif [ "$HOST" == "Darwin" ]; then
+        OS="darwin-x86"
+    else
+        echo "Unrecognized OS"
+        exit
+    fi;
+fi;
+
+JAR_DIR=${ANDROID_BUILD_TOP}/out/host/$OS/framework
+JARS="tradefed-prebuilt.jar hosttestlib.jar cts-tradefed.jar cts-tradefed-tests.jar"
+
+for JAR in $JARS; do
+    checkFile ${JAR_DIR}/${JAR}
+    JAR_PATH=${JAR_PATH}:${JAR_DIR}/${JAR}
+done
+
+java $RDBG_FLAG \
+  -cp ${JAR_PATH} com.android.tradefed.command.Console run singleCommand host --class com.android.cts.tradefed.FuncTests "$@"
diff --git a/tools/tradefed-host/tests/run_unit_tests.sh b/tools/tradefed-host/tests/run_unit_tests.sh
index 771dc75..d089c05 100755
--- a/tools/tradefed-host/tests/run_unit_tests.sh
+++ b/tools/tradefed-host/tests/run_unit_tests.sh
@@ -46,4 +46,3 @@
 
 java $RDBG_FLAG \
   -cp ${JAR_PATH} com.android.tradefed.command.Console run singleCommand host -n --class com.android.cts.tradefed.UnitTests "$@"
-
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/FuncTests.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/FuncTests.java
new file mode 100644
index 0000000..a9420d2
--- /dev/null
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/FuncTests.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015 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;
+
+import com.android.cts.tradefed.device.DeviceInfoCollectorFuncTest;
+import com.android.tradefed.testtype.DeviceTestSuite;
+
+import junit.framework.Test;
+
+/**
+ * A test suite for all cts-tradefed functional tests.
+ * <p/>
+ * Tests listed here should require a device.
+ */
+public class FuncTests extends DeviceTestSuite {
+
+    public FuncTests() {
+        super();
+
+        // device package
+        addTestSuite(DeviceInfoCollectorFuncTest.class);
+    }
+
+    public static Test suite() {
+        return new FuncTests();
+    }
+}
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 52a205b..f60dc03 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,33 +15,75 @@
  */
 package com.android.cts.tradefed.device;
 
+import com.android.ddmlib.Log.LogLevel;
+import com.android.cts.tradefed.build.ICtsBuildInfo;
 import com.android.cts.tradefed.UnitTests;
 import com.android.tradefed.build.BuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.CollectingTestListener;
 import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.util.FileUtil;
 
 import java.io.File;
 import java.util.Map;
 
+import org.easymock.EasyMock;
+
 /**
  * Functional test for {@link DeviceInfoCollector}.
  * <p/>
- * TODO: this test assumes the TestDeviceSetup apk is located in the "java.io.tmpdir"
+ * TODO: this test assumes the TestDeviceSetup and DeviceInfoCollector apks are located in the
+ * "java.io.tmpdir"
  */
 public class DeviceInfoCollectorFuncTest extends DeviceTestCase {
 
-    public void testCollectDeviceInfo() throws DeviceNotAvailableException {
-        CollectingTestListener testListener = new CollectingTestListener();
+    private CollectingTestListener testListener;
+    private BuildInfo buildInfo;
+    private File mResultDir;
+    private ICtsBuildInfo mMockCtsBuildInfo;
 
-        testListener.invocationStarted(new BuildInfo());
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        testListener = new CollectingTestListener();
+        buildInfo = new BuildInfo();
+        mResultDir = FileUtil.createTempDir("cts-result-dir");
+        mMockCtsBuildInfo = EasyMock.createMock(ICtsBuildInfo.class);
+        EasyMock.expect(mMockCtsBuildInfo.getResultDir()).andStubReturn(mResultDir);
+        EasyMock.replay(mMockCtsBuildInfo);
+
+        assertNotNull(getDevice().getSerialNumber());
+    }
+
+    public void testCollectDeviceInfo() throws DeviceNotAvailableException {
+        testListener.invocationStarted(buildInfo);
         DeviceInfoCollector.collectDeviceInfo(getDevice(), UnitTests.ABI.getName(), new File(
                 System.getProperty("java.io.tmpdir")), testListener);
         assertNotNull(testListener.getCurrentRunResults());
-        assertTrue(testListener.getCurrentRunResults().getRunMetrics().size() > 0);
-        for (Map.Entry<String, String> metricEntry : testListener.getCurrentRunResults().getRunMetrics().entrySet()) {
-            System.out.println(String.format("%s=%s", metricEntry.getKey(), metricEntry.getValue()));
-        }
+
+        Map<String, String> runMetrics = testListener.getCurrentRunResults().getRunMetrics();
+        assertTrue(runMetrics.size() > 0);
+        displayMetrics(runMetrics);
         testListener.invocationEnded(0);
     }
+
+    public void testExtendedDeviceInfo() throws DeviceNotAvailableException {
+        testListener.invocationStarted(buildInfo);
+        DeviceInfoCollector.collectExtendedDeviceInfo(getDevice(), UnitTests.ABI.getName(),
+                new File(System.getProperty("java.io.tmpdir")), testListener, mMockCtsBuildInfo);
+        assertNotNull(testListener.getCurrentRunResults());
+
+        Map<String, String> runMetrics = testListener.getCurrentRunResults().getRunMetrics();
+        assertTrue(runMetrics.size() > 0);
+        displayMetrics(runMetrics);
+        testListener.invocationEnded(0);
+    }
+
+    private void displayMetrics(Map<String, String> runMetrics) {
+        for (Map.Entry<String, String> metricEntry : runMetrics.entrySet()) {
+            CLog.logAndDisplay(LogLevel.INFO,
+                    String.format("%s=%s", metricEntry.getKey(), metricEntry.getValue()));
+        }
+    }
 }
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 ae4a41e..7ba1741 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,10 +17,10 @@
 
 import static com.android.cts.tradefed.result.CtsXmlResultReporter.CTS_RESULT_FILE_VERSION;
 
+import com.android.cts.tradefed.build.ICtsBuildInfo;
 import com.android.cts.tradefed.UnitTests;
 import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.testrunner.TestIdentifier;
-import com.android.tradefed.build.IFolderBuildInfo;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.result.LogFile;
@@ -53,7 +53,7 @@
     private ByteArrayOutputStream mOutputStream;
     private File mBuildDir;
     private File mReportDir;
-    private IFolderBuildInfo mMockBuild;
+    private ICtsBuildInfo mMockBuild;
 
     /**
      * {@inheritDoc}
@@ -84,9 +84,11 @@
         File plansDir = new File(repoDir, "plans");
         assertTrue(casesDir.mkdirs());
         assertTrue(plansDir.mkdirs());
-        mMockBuild = EasyMock.createNiceMock(IFolderBuildInfo.class);
+        mMockBuild = EasyMock.createMock(ICtsBuildInfo.class);
         EasyMock.expect(mMockBuild.getDeviceSerial()).andStubReturn(null);
         EasyMock.expect(mMockBuild.getRootDir()).andStubReturn(mBuildDir);
+        mMockBuild.setResultDir((File) EasyMock.anyObject());
+        EasyMock.expectLastCall();
         EasyMock.replay(mMockBuild);
     }
 
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 5dc2e5a..792b15e 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
@@ -23,6 +23,7 @@
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.targetprep.ITargetPreparer;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
@@ -286,6 +287,8 @@
         EasyMock.expect(mMockPackageDef.getAbi()).andReturn(UnitTests.ABI).atLeastOnce();
         EasyMock.expect(mMockPackageDef.getId()).andReturn(ID).atLeastOnce();
         EasyMock.expect(mMockPackageDef.getDigest()).andReturn("digest").atLeastOnce();
+        EasyMock.expect(mMockPackageDef.getPackagePreparers()).andReturn(
+                    new ArrayList<ITargetPreparer>()).atLeastOnce();
         mMockTest.run((ITestInvocationListener) EasyMock.anyObject());
     }