Only install necessary prequisite apks.

Bug 5513483

Change-Id: Ic93592799108300d71677d7a317dd6e926502159
diff --git a/tools/tradefed-host/res/config/cts.xml b/tools/tradefed-host/res/config/cts.xml
index 922c22b..3a35a7f 100644
--- a/tools/tradefed-host/res/config/cts.xml
+++ b/tools/tradefed-host/res/config/cts.xml
@@ -20,7 +20,6 @@
     <option name="disable-keyguard" value="false" />
     <build_provider class="com.android.cts.tradefed.build.CtsBuildProvider" />
     <device_recovery class="com.android.tradefed.device.WaitDeviceRecovery" />
-    <target_preparer class="com.android.cts.tradefed.targetprep.CtsSetup" />
     <test class="com.android.cts.tradefed.testtype.CtsTest" />
     <logger class="com.android.tradefed.log.FileLogger" />
     <result_reporter class="com.android.cts.tradefed.result.CtsXmlResultReporter" />
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/CtsSetup.java b/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/CtsSetup.java
deleted file mode 100644
index 9ae2197..0000000
--- a/tools/tradefed-host/src/com/android/cts/tradefed/targetprep/CtsSetup.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2010 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.targetprep;
-
-import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.build.IFolderBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.targetprep.BuildError;
-import com.android.tradefed.targetprep.ITargetPreparer;
-import com.android.tradefed.targetprep.TargetSetupError;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-
-/**
- * A {@link ITargetPreparer} that sets up a device for CTS testing.
- * <p/>
- * All the actions performed in this class must work on a production device.
- */
-public class CtsSetup implements ITargetPreparer {
-
-    private static final String RUNNER_APK_NAME = "android.core.tests.runner.apk";
-    // TODO: read this from configuration file rather than hardcoding
-    private static final String TEST_STUBS_APK = "CtsTestStubs.apk";
-    private static final String ACCELERATION_TEST_STUBS_APK = "CtsAccelerationTestStubs.apk";
-
-    /**
-     * Factory method to create a {@link CtsBuildHelper}.
-     * <p/>
-     * Exposed for unit testing.
-     */
-    CtsBuildHelper createBuildHelper(File rootDir) throws FileNotFoundException {
-        return new CtsBuildHelper(rootDir);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
-            BuildError, DeviceNotAvailableException {
-        if (!(buildInfo instanceof IFolderBuildInfo)) {
-            throw new IllegalArgumentException("Provided buildInfo is not a IFolderBuildInfo");
-        }
-        IFolderBuildInfo ctsBuildInfo = (IFolderBuildInfo)buildInfo;
-        try {
-            CtsBuildHelper buildHelper = createBuildHelper(ctsBuildInfo.getRootDir());
-            installCtsPrereqs(device, buildHelper);
-        } catch (FileNotFoundException e) {
-            throw new TargetSetupError("Invalid CTS installation", e);
-        }
-    }
-
-    /**
-     * Installs an apkFile on device.
-     *
-     * @param device the {@link ITestDevice}
-     * @param apkFile the apk {@link File}
-     * @throws DeviceNotAvailableException
-     * @throws TargetSetupError if apk cannot be installed successfully
-     */
-    void installApk(ITestDevice device, File apkFile)
-            throws DeviceNotAvailableException, TargetSetupError {
-        String errorCode = device.installPackage(apkFile, true);
-        if (errorCode != null) {
-            // TODO: retry ?
-            throw new TargetSetupError(String.format(
-                    "Failed to install %s on device %s. Reason: %s", apkFile.getName(),
-                    device.getSerialNumber(), errorCode));
-        }
-    }
-
-    /**
-     * Install pre-requisite apks for running tests
-     *
-     * @throws TargetSetupError if the pre-requisite apks fail to install
-     * @throws DeviceNotAvailableException
-     * @throws FileNotFoundException
-     */
-    private void installCtsPrereqs(ITestDevice device, CtsBuildHelper ctsBuild)
-            throws DeviceNotAvailableException, TargetSetupError, FileNotFoundException {
-        installApk(device, ctsBuild.getTestApp(TEST_STUBS_APK));
-        installApk(device, ctsBuild.getTestApp(ACCELERATION_TEST_STUBS_APK));
-        installApk(device, ctsBuild.getTestApp(RUNNER_APK_NAME));
-    }
-}
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 37a61d1..4db3e48 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
@@ -50,6 +50,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -279,9 +280,15 @@
             mRemainingTestPkgs = buildTestsToRun();
         }
 
+        // collect and install the prerequisiteApks first, to save time when multiple test
+        // 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);
 
         try {
+            installPrerequisiteApks(prerequisiteApks);
+
             // always collect the device info, even for resumed runs, since test will likely be
             // running on a different device
             collectDeviceInfo(getDevice(), mCtsBuild, listener);
@@ -310,6 +317,9 @@
                     screenshotSource.cancel();
                 }
             }
+
+            uninstallPrequisiteApks(uninstallPackages);
+
         } finally {
             filter.reportUnexecutedTests();
         }
@@ -427,6 +437,71 @@
     }
 
     /**
+     * Return the list of unique prerequisite Android package names
+     * @param testPackages
+     * @return
+     */
+    private Collection<String> getPrerequisitePackageNames(List<TestPackage> testPackages) {
+        Set<String> pkgNames = new HashSet<String>();
+        for (TestPackage testPkg : testPackages) {
+            String pkgName = testPkg.mPackageDef.getTargetPackageName();
+            if (pkgName != null) {
+                pkgNames.add(pkgName);
+            }
+        }
+        return pkgNames;
+    }
+
+    /**
+     * Return the list of unique prerequisite apks to install
+     * @param testPackages
+     * @return
+     */
+    private Collection<String> getPrerequisiteApks(List<TestPackage> testPackages) {
+        Set<String> apkNames = new HashSet<String>();
+        for (TestPackage testPkg : testPackages) {
+            String apkName = testPkg.mPackageDef.getTargetApkName();
+            if (apkName != null) {
+                apkNames.add(apkName);
+            }
+        }
+        return apkNames;
+    }
+
+    /**
+     * Install the collection of test apk file names
+     *
+     * @param prerequisiteApks
+     * @throws DeviceNotAvailableException
+     */
+    private void installPrerequisiteApks(Collection<String> prerequisiteApks)
+            throws DeviceNotAvailableException {
+        for (String apkName : prerequisiteApks) {
+            try {
+                File apkFile = mCtsBuild.getTestApp(apkName);
+                String errorCode = getDevice().installPackage(apkFile, true);
+                if (errorCode != null) {
+                    CLog.e("Failed to install %s. Reason: %s", apkName, errorCode);
+                }
+            } catch (FileNotFoundException e) {
+                CLog.e("Could not find test apk %s", apkName);
+            }
+        }
+    }
+
+    /**
+     * Uninstalls the collection of android package names from device.
+     *
+     * @param uninstallPackages
+     */
+    private void uninstallPrequisiteApks(Collection<String> uninstallPackages)
+            throws DeviceNotAvailableException {
+        for (String pkgName : uninstallPackages) {
+            getDevice().uninstallPackage(pkgName);
+        }
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
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 1893a93..68d6743 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
@@ -100,4 +100,17 @@
      */
     public void setClassName(String className, String methodName);
 
+    /**
+     * Return the file name of this package's instrumentation target apk.
+     *
+     * @return the file name or <code>null</code> if not applicable.
+     */
+    public String getTargetApkName();
+
+    /**
+     * Return the Android package name of this package's instrumentation target, or
+     * <code>null</code> if not applicable.
+     */
+    public String getTargetPackageName();
+
 }
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 8f0ed2d..8844667 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
@@ -68,6 +68,8 @@
     private String mClassName;
     private String mMethodName;
     private TestFilter mExcludedTestFilter = new TestFilter();
+    private String mTargetBinaryName;
+    private String mTargetNameSpace;
 
     void setUri(String uri) {
         mUri = uri;
@@ -162,6 +164,30 @@
         mApkToTestName = apkName;
     }
 
+    void setTargetBinaryName(String targetBinaryName) {
+        mTargetBinaryName = targetBinaryName;
+    }
+
+    void setTargetNameSpace(String targetNameSpace) {
+        mTargetNameSpace = targetNameSpace;
+    }
+
+    @Override
+    public String getTargetApkName() {
+       if (mTargetBinaryName != null && !mTargetBinaryName.isEmpty()) {
+           return String.format("%s.apk", mTargetBinaryName);
+       }
+       return null;
+    }
+
+    @Override
+    public String getTargetPackageName() {
+        if (mTargetNameSpace != null && mTargetNameSpace.isEmpty()) {
+            return null;
+        }
+        return mTargetNameSpace;
+    }
+
     /**
      * {@inheritDoc}
      */
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 fa2423f..64706a2 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
@@ -74,6 +74,8 @@
                 final String apkToTest = attributes.getValue("apkToTestName");
                 final String packageToTest = attributes.getValue("packageToTest");
                 final String javaPackageFilter = attributes.getValue("javaPackageFilter");
+                final String targetBinaryName = attributes.getValue("targetBinaryName");
+                final String targetNameSpace = attributes.getValue("targetNameSpace");
 
                 mPackageDef = new TestPackageDef();
                 mPackageDef.setUri(entryUriValue);
@@ -88,6 +90,8 @@
                 mPackageDef.setApkToTest(apkToTest);
                 mPackageDef.setPackageToTest(packageToTest);
                 mPackageDef.setTestPackageName(javaPackageFilter);
+                mPackageDef.setTargetBinaryName(targetBinaryName);
+                mPackageDef.setTargetNameSpace(targetNameSpace);
 
                 // reset the class name
                 mClassNameStack = new Stack<String>();
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 432c125..9a62af3 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
@@ -16,7 +16,6 @@
 package com.android.cts.tradefed;
 
 import com.android.cts.tradefed.result.CtsXmlResultReporterTest;
-import com.android.cts.tradefed.targetprep.CtsSetupTest;
 import com.android.cts.tradefed.testtype.CtsTestTest;
 import com.android.cts.tradefed.testtype.JarHostTestTest;
 import com.android.cts.tradefed.testtype.TestPackageDefTest;
@@ -37,7 +36,6 @@
     public UnitTests() {
         super();
         addTestSuite(CtsXmlResultReporterTest.class);
-        addTestSuite(CtsSetupTest.class);
         addTestSuite(JarHostTestTest.class);
         addTestSuite(CtsTestTest.class);
         addTestSuite(TestPlanTest.class);
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetprep/CtsSetupTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetprep/CtsSetupTest.java
deleted file mode 100644
index 949cfd5..0000000
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/targetprep/CtsSetupTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2010 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.targetprep;
-
-import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.cts.tradefed.build.StubCtsBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.build.IFolderBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.targetprep.BuildError;
-import com.android.tradefed.targetprep.TargetSetupError;
-
-import org.easymock.EasyMock;
-
-import java.io.File;
-
-import junit.framework.TestCase;
-
-/**
- * Unit tests for {@link CtsSetup}.
- */
-public class CtsSetupTest extends TestCase {
-
-    private CtsSetup mSetup;
-    private ITestDevice mMockDevice;
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mSetup = new CtsSetup() {
-            @Override
-            CtsBuildHelper createBuildHelper(File rootDir) {
-                return new StubCtsBuildHelper();
-            }
-        };
-        mMockDevice = EasyMock.createMock(ITestDevice.class);
-    }
-
-    /**
-     * Test {@link CtsSetup#setUp(ITestDevice, IBuildInfo)} when provided buildInfo is the incorrect
-     * type
-     */
-    public void testSetUp_wrongBuildInfo() throws TargetSetupError, BuildError,
-            DeviceNotAvailableException {
-        try {
-            mSetup.setUp(mMockDevice, EasyMock.createMock(IBuildInfo.class));
-            fail("IllegalArgumentException not thrown");
-        } catch (IllegalArgumentException e) {
-            // expected
-        }
-    }
-
-    /**
-     * Test normal case for {@link CtsSetup#setUp(ITestDevice, IBuildInfo)}
-     */
-    public void testSetUp() throws TargetSetupError, BuildError, DeviceNotAvailableException {
-        IFolderBuildInfo ctsBuild = EasyMock.createMock(IFolderBuildInfo.class);
-        EasyMock.expect(ctsBuild.getRootDir()).andReturn(
-                new File("tmp")).anyTimes();
-        EasyMock.expect(ctsBuild.getBuildId()).andStubReturn("0");
-        EasyMock.expect(
-                mMockDevice.installPackage((File)EasyMock.anyObject(), EasyMock.anyBoolean()))
-                .andReturn(null)
-                .anyTimes();
-        EasyMock.replay(ctsBuild, mMockDevice);
-        mSetup.setUp(mMockDevice, ctsBuild);
-    }
-}
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 1311c05..02311b0 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
@@ -93,6 +93,8 @@
         mCtsTest.setBuildHelper(mStubBuildHelper);
         // turn off device collection for simplicity
         mCtsTest.setCollectDeviceInfo(false);
+        EasyMock.expect(mMockPackageDef.getTargetApkName()).andStubReturn(null);
+        EasyMock.expect(mMockPackageDef.getTargetPackageName()).andStubReturn(null);
 
 
     }