Merge "Update eclipse"
diff --git a/invocation_interfaces/com/android/tradefed/invoker/TestInformation.java b/invocation_interfaces/com/android/tradefed/invoker/TestInformation.java
index 3c28a97..32f0f41 100644
--- a/invocation_interfaces/com/android/tradefed/invoker/TestInformation.java
+++ b/invocation_interfaces/com/android/tradefed/invoker/TestInformation.java
@@ -17,8 +17,11 @@
 
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.ExecutionFiles.FilesKey;
+import com.android.tradefed.util.FileUtil;
 
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.util.List;
 
 /**
@@ -143,4 +146,83 @@
             return this;
         }
     }
+
+    /**
+     * Search for a dependency/artifact file based on its name, and whether or not it's a target or
+     * host file (for quicker search).
+     *
+     * @param fileName The name of the file we are looking for.
+     * @param targetFirst whether or not we are favoring target-side files vs. host-side files for
+     *     the search.
+     * @return The found artifact file.
+     * @throws FileNotFoundException If the file is not found.
+     */
+    public File getDependencyFile(String fileName, boolean targetFirst)
+            throws FileNotFoundException {
+        File dependency = null;
+        dependency = getFromEnv(fileName, targetFirst);
+        if (dependency != null && dependency.isFile()) {
+            return dependency;
+        }
+        dependency = getFromTestsDir(fileName);
+        if (dependency != null && dependency.isFile()) {
+            return dependency;
+        }
+        dependency = getFile(fileName);
+        if (dependency != null && dependency.isFile()) {
+            return dependency;
+        }
+        dependency = getFromDependencyFolder(fileName);
+        if (dependency != null && dependency.isFile()) {
+            return dependency;
+        }
+        throw new FileNotFoundException(
+                String.format("Could not find an artifact file associated with %s", fileName));
+    }
+
+    private File getFromEnv(String fileName, boolean targetFirst) {
+        FilesKey hostOrTarget = FilesKey.HOST_TESTS_DIRECTORY;
+        if (targetFirst) {
+            hostOrTarget = FilesKey.TARGET_TESTS_DIRECTORY;
+        }
+        File testsDir = mExecutionFiles.get(hostOrTarget);
+        if (testsDir != null && testsDir.exists()) {
+            File file = FileUtil.findFile(testsDir, fileName);
+            if (file != null) {
+                return file;
+            }
+        }
+        return null;
+    }
+
+    private File getFromTestsDir(String fileName) {
+        File testsDir = mExecutionFiles.get(FilesKey.TESTS_DIRECTORY);
+        if (testsDir != null && testsDir.exists()) {
+            File file = FileUtil.findFile(testsDir, fileName);
+            if (file == null) {
+                // TODO(b/138416078): Once build dependency can be fixed and test required
+                // APKs are all under the test module directory, we can remove this fallback
+                // approach to do individual download from remote artifact.
+                // Try to stage the files from remote zip files.
+                file = getBuildInfo().stageRemoteFile(fileName, testsDir);
+            }
+            return file;
+        }
+        return null;
+    }
+
+    private File getFile(String fileName) {
+        return mExecutionFiles.get(fileName);
+    }
+
+    private File getFromDependencyFolder(String fileName) {
+        File testsDir = mDependenciesFolder;
+        if (testsDir != null && testsDir.exists()) {
+            File file = FileUtil.findFile(testsDir, fileName);
+            if (file != null) {
+                return file;
+            }
+        }
+        return null;
+    }
 }
diff --git a/invocation_interfaces/com/android/tradefed/testtype/IRemoteTest.java b/invocation_interfaces/com/android/tradefed/testtype/IRemoteTest.java
index 6df4fc4..527075a 100644
--- a/invocation_interfaces/com/android/tradefed/testtype/IRemoteTest.java
+++ b/invocation_interfaces/com/android/tradefed/testtype/IRemoteTest.java
@@ -42,7 +42,12 @@
      */
     @Deprecated
     public default void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
-        // Do nothing by default.
+        // Throw if not implemented: If the new interface is implemented this won't be called. If
+        // something is calling the old interface instead of new one, then it will throw and report
+        // the error.
+        throw new UnsupportedOperationException(
+                "run(ITestInvocationListener) is deprecated. You need to update to the new "
+                        + "run(TestInformation, ITestInvocationListener) interface.");
     }
 
     /**
diff --git a/src/com/android/tradefed/build/BootstrapBuildProvider.java b/src/com/android/tradefed/build/BootstrapBuildProvider.java
index 5927fff..919a26a 100644
--- a/src/com/android/tradefed/build/BootstrapBuildProvider.java
+++ b/src/com/android/tradefed/build/BootstrapBuildProvider.java
@@ -17,6 +17,7 @@
 package com.android.tradefed.build;
 
 import com.android.annotations.VisibleForTesting;
+import com.android.tradefed.build.IBuildInfo.BuildInfoProperties;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.OptionClass;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -78,8 +79,6 @@
     @Option(name="tests-dir", description="Path to top directory of expanded tests zip")
     private File mTestsDir = null;
 
-    private boolean mCreatedTestDir = false;
-
     @Override
     public IBuildInfo getBuild() throws BuildRetrievalError {
         throw new UnsupportedOperationException("Call getBuild(ITestDevice)");
@@ -87,16 +86,13 @@
 
     @Override
     public void cleanUp(IBuildInfo info) {
-        // If we created the tests dir, we delete it.
-        if (mCreatedTestDir) {
-            FileUtil.recursiveDelete(((IDeviceBuildInfo) info).getTestsDir());
-        }
     }
 
     @Override
     public IBuildInfo getBuild(ITestDevice device) throws BuildRetrievalError,
             DeviceNotAvailableException {
         IBuildInfo info = new DeviceBuildInfo(mBuildId, mBuildTargetName);
+        info.setProperties(BuildInfoProperties.DO_NOT_COPY_ON_SHARDING);
         if (!(device.getIDevice() instanceof StubDevice)) {
             if (!device.waitForDeviceShell(mShellAvailableTimeout * 1000)) {
                 throw new DeviceNotAvailableException(
@@ -120,8 +116,9 @@
             info.setFile("testsdir", mTestsDir, info.getBuildId());
         }
         // Avoid tests dir being null, by creating a temporary dir.
+        boolean createdTestDir = false;
         if (mTestsDir == null) {
-            mCreatedTestDir = true;
+            createdTestDir = true;
             try {
                 mTestsDir =
                         FileUtil.createTempDir(
@@ -137,7 +134,7 @@
                     .put(
                             FilesKey.TESTS_DIRECTORY,
                             mTestsDir,
-                            !mCreatedTestDir /* shouldNotDelete */);
+                            !createdTestDir /* shouldNotDelete */);
         }
         return info;
     }
diff --git a/src/com/android/tradefed/device/NativeDevice.java b/src/com/android/tradefed/device/NativeDevice.java
index 40ad670..ee6c71d 100644
--- a/src/com/android/tradefed/device/NativeDevice.java
+++ b/src/com/android/tradefed/device/NativeDevice.java
@@ -518,12 +518,14 @@
             CLog.e("setProperty requires adb root = true.");
             return false;
         }
-        CommandResult result =
-                executeShellV2Command(String.format("setprop \"%s\" \"%s\"", propKey, propValue));
+        String setPropCmd = String.format("\"setprop %s '%s'\"", propKey, propValue);
+        CommandResult result = executeShellV2Command(setPropCmd);
         if (CommandStatus.SUCCESS.equals(result.getStatus())) {
             return true;
         }
-        CLog.e("Something went wrong went setting property %s: %s", propKey, result.getStderr());
+        CLog.e(
+                "Something went wrong went setting property %s (command: %s): %s",
+                propKey, setPropCmd, result.getStderr());
         return false;
     }
 
@@ -4508,7 +4510,7 @@
         long utcEpochTimeSec = TimeUnit.SECONDS.convert(utcEpochTime, timeUnit);
         Map<Long, String> bootHistory = new LinkedHashMap<Long, String>();
         for (Map.Entry<Long, String> entry : getBootHistory().entrySet()) {
-            if (entry.getKey() > utcEpochTimeSec) {
+            if (entry.getKey() >= utcEpochTimeSec) {
                 bootHistory.put(entry.getKey(), entry.getValue());
             }
         }
@@ -4768,6 +4770,9 @@
         if (getIDevice() instanceof StubDevice) {
             return null;
         }
+        if (TestDeviceState.FASTBOOT.equals(getDeviceState())) {
+            return null;
+        }
         try {
             // Use default 5 minutes freshness
             Future<Integer> batteryFuture = getIDevice().getBattery();
diff --git a/src/com/android/tradefed/invoker/InvocationExecution.java b/src/com/android/tradefed/invoker/InvocationExecution.java
index f6efeee..8553b1e 100644
--- a/src/com/android/tradefed/invoker/InvocationExecution.java
+++ b/src/com/android/tradefed/invoker/InvocationExecution.java
@@ -732,27 +732,32 @@
         }
         // Load environment tests dir.
         if (info instanceof IDeviceBuildInfo) {
+            // TODO: Use tests directory from TestInformation instead.
             File testsDir = ((IDeviceBuildInfo) info).getTestsDir();
             if (testsDir != null && testsDir.exists()) {
-                File targetTestCases =
-                        handleLinkingExternalDirs(
-                                (IDeviceBuildInfo) info,
-                                testsDir,
-                                EnvVariable.ANDROID_TARGET_OUT_TESTCASES,
-                                BuildInfoFileKey.TARGET_LINKED_DIR.getFileKey());
-                if (targetTestCases != null) {
-                    testInfo.executionFiles()
-                            .put(FilesKey.TARGET_TESTS_DIRECTORY, targetTestCases, true);
+                if (testInfo.executionFiles().get(FilesKey.TARGET_TESTS_DIRECTORY) == null) {
+                    File targetTestCases =
+                            handleLinkingExternalDirs(
+                                    (IDeviceBuildInfo) info,
+                                    testsDir,
+                                    EnvVariable.ANDROID_TARGET_OUT_TESTCASES,
+                                    BuildInfoFileKey.TARGET_LINKED_DIR.getFileKey());
+                    if (targetTestCases != null) {
+                        testInfo.executionFiles()
+                                .put(FilesKey.TARGET_TESTS_DIRECTORY, targetTestCases, true);
+                    }
                 }
-                File hostTestCases =
-                        handleLinkingExternalDirs(
-                                (IDeviceBuildInfo) info,
-                                testsDir,
-                                EnvVariable.ANDROID_HOST_OUT_TESTCASES,
-                                BuildInfoFileKey.HOST_LINKED_DIR.getFileKey());
-                if (hostTestCases != null) {
-                    testInfo.executionFiles()
-                            .put(FilesKey.HOST_TESTS_DIRECTORY, hostTestCases, true);
+                if (testInfo.executionFiles().get(FilesKey.HOST_TESTS_DIRECTORY) == null) {
+                    File hostTestCases =
+                            handleLinkingExternalDirs(
+                                    (IDeviceBuildInfo) info,
+                                    testsDir,
+                                    EnvVariable.ANDROID_HOST_OUT_TESTCASES,
+                                    BuildInfoFileKey.HOST_LINKED_DIR.getFileKey());
+                    if (hostTestCases != null) {
+                        testInfo.executionFiles()
+                                .put(FilesKey.HOST_TESTS_DIRECTORY, hostTestCases, true);
+                    }
                 }
             }
         }
diff --git a/src/com/android/tradefed/targetprep/DeviceFlashPreparer.java b/src/com/android/tradefed/targetprep/DeviceFlashPreparer.java
index 6bfcacb..086454c 100644
--- a/src/com/android/tradefed/targetprep/DeviceFlashPreparer.java
+++ b/src/com/android/tradefed/targetprep/DeviceFlashPreparer.java
@@ -26,6 +26,7 @@
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.ITestDevice.RecoveryMode;
 import com.android.tradefed.host.IHostOptions;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.targetprep.IDeviceFlasher.UserDataFlashOption;
 import com.android.tradefed.util.CommandStatus;
@@ -170,16 +171,16 @@
         mUserDataFlashOption = flashOption;
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
-    public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
-            DeviceNotAvailableException, BuildError {
+    public void setUp(TestInformation testInfo)
+            throws TargetSetupError, DeviceNotAvailableException, BuildError {
         if (isDisabled()) {
             CLog.i("Skipping device flashing.");
             return;
         }
+        ITestDevice device = testInfo.getDevice();
+        IBuildInfo buildInfo = testInfo.getBuildInfo();
         CLog.i("Performing setup on %s", device.getSerialNumber());
         if (!(buildInfo instanceof IDeviceBuildInfo)) {
             throw new IllegalArgumentException("Provided buildInfo is not a IDeviceBuildInfo");
@@ -428,12 +429,12 @@
     }
 
     @Override
-    public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
-            throws DeviceNotAvailableException {
+    public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
         if (isDisabled()) {
             CLog.i("Skipping device flashing tearDown.");
             return;
         }
+        ITestDevice device = testInfo.getDevice();
         if (mEncryptUserData == EncryptionOptions.ENCRYPT
                 && mUserDataFlashOption != UserDataFlashOption.RETAIN) {
             if (e instanceof DeviceNotAvailableException) {
diff --git a/src/com/android/tradefed/targetprep/DeviceUpdateTargetPreparer.java b/src/com/android/tradefed/targetprep/DeviceUpdateTargetPreparer.java
index 9c81fb1..92ca140 100644
--- a/src/com/android/tradefed/targetprep/DeviceUpdateTargetPreparer.java
+++ b/src/com/android/tradefed/targetprep/DeviceUpdateTargetPreparer.java
@@ -16,7 +16,6 @@
 
 package com.android.tradefed.targetprep;
 
-import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.GlobalConfiguration;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -24,6 +23,7 @@
 import com.android.tradefed.device.IDeviceManager;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.ITestDevice.RecoveryMode;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.log.LogUtil.CLog;
 
 import java.io.File;
@@ -53,8 +53,9 @@
 
     /** {@inheritDoc} */
     @Override
-    public void setUp(ITestDevice device, IBuildInfo buildInfo)
+    public void setUp(TestInformation testInfo)
             throws TargetSetupError, BuildError, DeviceNotAvailableException {
+        ITestDevice device = testInfo.getDevice();
         File deviceUpdateImage = getDeviceUpdateImage();
         if (deviceUpdateImage == null) {
             CLog.i("No device image zip file provided, assuming no-op; skipping ...");
@@ -108,7 +109,7 @@
         CLog.i("Device update completed on %s", device.getDeviceDescriptor());
         // calling this last because we want to inject device side build info after device boots up
         if (mBootStrapBuildInfo) {
-            super.setUp(device, buildInfo);
+            super.setUp(testInfo);
         }
     }
 
diff --git a/src/com/android/tradefed/targetprep/FastbootUpdateBootstrapPreparer.java b/src/com/android/tradefed/targetprep/FastbootUpdateBootstrapPreparer.java
index f6990d9..eccfba6 100644
--- a/src/com/android/tradefed/targetprep/FastbootUpdateBootstrapPreparer.java
+++ b/src/com/android/tradefed/targetprep/FastbootUpdateBootstrapPreparer.java
@@ -21,6 +21,7 @@
 import com.android.tradefed.config.Option;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.targetprep.IDeviceFlasher.UserDataFlashOption;
 import com.android.tradefed.util.BuildInfoUtil;
 
@@ -73,8 +74,10 @@
     private String mOverrideDeviceBuildBranch = null;
 
     @Override
-    public void setUp(ITestDevice device, IBuildInfo buildInfo)
+    public void setUp(TestInformation testInfo)
             throws TargetSetupError, BuildError, DeviceNotAvailableException {
+        ITestDevice device = testInfo.getDevice();
+        IBuildInfo buildInfo = testInfo.getBuildInfo();
         if (!(buildInfo instanceof IDeviceBuildInfo)) {
             throw new IllegalArgumentException("Provided build info must be a IDeviceBuildInfo");
         }
@@ -87,7 +90,7 @@
         setSkipPostFlashBuildIdCheck(true);
         setSkipPostFlashFlavorCheck(true);
         // performs the actual flashing
-        super.setUp(device, buildInfo);
+        super.setUp(testInfo);
 
         if (mBootStrapBuildInfo) {
             BuildInfoUtil.bootstrapDeviceBuildAttributes(
diff --git a/src/com/android/tradefed/targetprep/ITargetPreparer.java b/src/com/android/tradefed/targetprep/ITargetPreparer.java
index b77736b..41dc9c0 100644
--- a/src/com/android/tradefed/targetprep/ITargetPreparer.java
+++ b/src/com/android/tradefed/targetprep/ITargetPreparer.java
@@ -39,13 +39,19 @@
      * @param device the {@link ITestDevice} to prepare.
      * @param buildInfo data about the build under test.
      * @throws TargetSetupError if fatal error occurred setting up environment
+     * @throws BuildError If an error related to the BuildInfo occurs
      * @throws DeviceNotAvailableException if device became unresponsive
      * @deprecated Use {@link #setUp(TestInformation)} instead
      */
     @Deprecated
     public default void setUp(ITestDevice device, IBuildInfo buildInfo)
             throws TargetSetupError, BuildError, DeviceNotAvailableException {
-        // Do nothing by default.
+        // Throw if not implemented: If the new interface is implemented this won't be called. If
+        // something is calling the old interface instead of new one, then it will throw and report
+        // the error.
+        throw new UnsupportedOperationException(
+                "setUp(ITestDevice, IBuildInfo) is deprecated. You need to update to the "
+                        + "new setUp(TestInformation).");
     }
 
     /**
diff --git a/src/com/android/tradefed/targetprep/TestAppInstallSetup.java b/src/com/android/tradefed/targetprep/TestAppInstallSetup.java
index d5704fb..f557b4b 100644
--- a/src/com/android/tradefed/targetprep/TestAppInstallSetup.java
+++ b/src/com/android/tradefed/targetprep/TestAppInstallSetup.java
@@ -134,6 +134,10 @@
     private List<String> mPackagesInstalled = null;
     private TestInformation mTestInfo;
 
+    protected void setTestInformation(TestInformation testInfo) {
+        mTestInfo = testInfo;
+    }
+
     /**
      * Adds a file name to the list of apks to installed
      *
@@ -182,25 +186,28 @@
     /**
      * Resolve the actual apk path based on testing artifact information inside build info.
      *
-     * @param buildInfo build artifact information
+     * @param testInfo The {@link TestInformation} for the invocation.
      * @param apkFileName filename of the apk to install
-     * @param device the {@link ITestDevice} being prepared
      * @return a {@link File} representing the physical apk file on host or {@code null} if the file
      *     does not exist.
      */
-    protected File getLocalPathForFilename(
-            IBuildInfo buildInfo, String apkFileName, ITestDevice device) throws TargetSetupError {
+    protected File getLocalPathForFilename(TestInformation testInfo, String apkFileName)
+            throws TargetSetupError {
         try {
-            return BuildTestsZipUtils.getApkFile(buildInfo, apkFileName, mAltDirs, mAltDirBehavior,
+            return BuildTestsZipUtils.getApkFile(
+                    testInfo.getBuildInfo(),
+                    apkFileName,
+                    mAltDirs,
+                    mAltDirBehavior,
                     false /* use resource as fallback */,
                     null /* device signing key */);
         } catch (IOException ioe) {
             throw new TargetSetupError(
                     String.format(
                             "failed to resolve apk path for apk %s in build %s",
-                            apkFileName, buildInfo.toString()),
+                            apkFileName, testInfo.getBuildInfo().toString()),
                     ioe,
-                    device.getDeviceDescriptor());
+                    testInfo.getDevice().getDeviceDescriptor());
         }
     }
 
@@ -259,15 +266,12 @@
         }
 
         for (String testAppName : mTestFileNames) {
-            installer(
-                    getDevice(),
-                    testInfo.getBuildInfo(),
-                    Arrays.asList(new String[] {testAppName}));
+            installer(testInfo, Arrays.asList(new String[] {testAppName}));
         }
 
         for (String testAppNames : mSplitApkFileNames) {
             List<String> apkNames = Arrays.asList(testAppNames.split(","));
-            installer(getDevice(), testInfo.getBuildInfo(), apkNames);
+            installer(testInfo, apkNames);
         }
     }
 
@@ -344,21 +348,21 @@
     /**
      * Attempt to install an package or split package on the device.
      *
-     * @param device the {@link ITestDevice} to install package
-     * @param buildInfo build artifact information
+     * @param testInfo the {@link TestInformation} for the invocation
      * @param apkNames List of String. The application file base names to be installed. If apkNames
      *     contains only one apk name, the apk will be installed as single package. If apkNames
      *     contains more than one name, the apks will be installed as split apks.
      */
-    protected void installer(ITestDevice device, IBuildInfo buildInfo, List<String> apkNames)
+    protected void installer(TestInformation testInfo, List<String> apkNames)
             throws TargetSetupError, DeviceNotAvailableException {
         List<File> appFiles = new ArrayList<File>();
         List<String> packageNames = new ArrayList<String>();
+        ITestDevice device = testInfo.getDevice();
         for (String name : apkNames) {
             if (name == null || name.trim().isEmpty()) {
                 continue;
             }
-            File testAppFile = getLocalPathForFilename(buildInfo, name, device);
+            File testAppFile = getLocalPathForFilename(testInfo, name);
             if (testAppFile == null) {
                 if (mThrowIfNoFile) {
                     throw new TargetSetupError(
diff --git a/src/com/android/tradefed/testtype/FakeTest.java b/src/com/android/tradefed/testtype/FakeTest.java
index de24a8a..11bdcc2 100644
--- a/src/com/android/tradefed/testtype/FakeTest.java
+++ b/src/com/android/tradefed/testtype/FakeTest.java
@@ -20,6 +20,7 @@
 import com.android.tradefed.config.OptionClass;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
 import com.android.tradefed.result.FileInputStreamSource;
 import com.android.tradefed.result.ITestInvocationListener;
@@ -234,11 +235,10 @@
         listener.testRunEnded(0, EMPTY_MAP);
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
-    public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+    public void run(TestInformation testInfo, ITestInvocationListener listener)
+            throws DeviceNotAvailableException {
         for (Map.Entry<String, String> run : mRuns.entrySet()) {
             final String name = run.getKey();
             final String testSpec = decode(run.getValue());
diff --git a/src/com/android/tradefed/testtype/NoisyDryRunTest.java b/src/com/android/tradefed/testtype/NoisyDryRunTest.java
index 9d034c7..3e17cdf 100644
--- a/src/com/android/tradefed/testtype/NoisyDryRunTest.java
+++ b/src/com/android/tradefed/testtype/NoisyDryRunTest.java
@@ -26,6 +26,7 @@
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.SandboxConfigurationFactory;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
 import com.android.tradefed.result.ITestInvocationListener;
@@ -60,7 +61,8 @@
     private long mTimeoutMilliSec = 0;
 
     @Override
-    public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+    public void run(TestInformation testInfo, ITestInvocationListener listener)
+            throws DeviceNotAvailableException {
         List<CommandLine> commands = testCommandFile(listener, mCmdfile);
         if (commands != null) {
             testCommandLines(listener, commands);
diff --git a/src/com/android/tradefed/testtype/StubTest.java b/src/com/android/tradefed/testtype/StubTest.java
index 0143170..e93cc43 100644
--- a/src/com/android/tradefed/testtype/StubTest.java
+++ b/src/com/android/tradefed/testtype/StubTest.java
@@ -22,6 +22,7 @@
 import com.android.tradefed.config.Option;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.DeviceUnresponsiveException;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
 import com.android.tradefed.result.ITestInvocationListener;
@@ -80,11 +81,10 @@
         return mRunTest;
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     @Override
-    public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+    public void run(TestInformation testInfo, ITestInvocationListener listener)
+            throws DeviceNotAvailableException {
         if (mThrowRuntime) {
             throw new RuntimeException("StubTest RuntimeException");
         }
diff --git a/src/com/android/tradefed/testtype/SubprocessTfLauncher.java b/src/com/android/tradefed/testtype/SubprocessTfLauncher.java
index fd5d7dd..db99b82 100644
--- a/src/com/android/tradefed/testtype/SubprocessTfLauncher.java
+++ b/src/com/android/tradefed/testtype/SubprocessTfLauncher.java
@@ -23,6 +23,7 @@
 import com.android.tradefed.config.IConfigurationReceiver;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
 import com.android.tradefed.result.FileInputStreamSource;
@@ -311,7 +312,7 @@
 
     /** {@inheritDoc} */
     @Override
-    public void run(ITestInvocationListener listener) {
+    public void run(TestInformation testInfo, ITestInvocationListener listener) {
         preRun();
         addInvocationData();
 
diff --git a/test_framework/com/android/tradefed/targetprep/InstallApexModuleTargetPreparer.java b/test_framework/com/android/tradefed/targetprep/InstallApexModuleTargetPreparer.java
index e18197b..2239bf8 100644
--- a/test_framework/com/android/tradefed/targetprep/InstallApexModuleTargetPreparer.java
+++ b/test_framework/com/android/tradefed/targetprep/InstallApexModuleTargetPreparer.java
@@ -16,7 +16,6 @@
 
 package com.android.tradefed.targetprep;
 
-import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.command.remote.DeviceDescriptor;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.OptionClass;
@@ -24,6 +23,7 @@
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.ITestDevice.ApexInfo;
 import com.android.tradefed.device.PackageInfo;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.targetprep.suite.SuiteApkInstaller;
 import com.android.tradefed.util.AaptParser;
@@ -31,6 +31,7 @@
 import com.android.tradefed.util.RunUtil;
 
 import com.google.common.annotations.VisibleForTesting;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -74,8 +75,10 @@
     private long mApexStagingWaitTime = 1 * 60 * 1000;
 
     @Override
-    public void setUp(ITestDevice device, IBuildInfo buildInfo)
-            throws TargetSetupError, DeviceNotAvailableException {
+    public void setUp(TestInformation testInfo)
+            throws TargetSetupError, BuildError, DeviceNotAvailableException {
+        setTestInformation(testInfo);
+        ITestDevice device = testInfo.getDevice();
 
         if (getTestsFileName().isEmpty()) {
             CLog.i("No apk/apex module file to install. Skipping.");
@@ -84,13 +87,13 @@
 
         cleanUpStagedAndActiveSession(device);
 
-        List<String> testAppFileNames = getModulesToInstall(buildInfo, device);
+        List<String> testAppFileNames = getModulesToInstall(testInfo);
         if (testAppFileNames.isEmpty()) {
             CLog.i("No modules are preloaded on the device, so no modules will be installed.");
             return;
         }
         if (containsApks(testAppFileNames)) {
-            installUsingBundleTool(buildInfo, device);
+            installUsingBundleTool(testInfo);
             if (mTestApexInfoList.isEmpty()) {
                 CLog.i("No Apex module in the train. Skipping reboot.");
                 return;
@@ -99,9 +102,9 @@
                 device.reboot();
             }
         } else {
-            installer(device, buildInfo, testAppFileNames);
+            installer(testInfo, testAppFileNames);
             if (containsApex(testAppFileNames)
-                    || containsPersistentApk(testAppFileNames, device, buildInfo)) {
+                    || containsPersistentApk(testAppFileNames, testInfo)) {
                 device.reboot();
             }
             if (mTestApexInfoList.isEmpty()) {
@@ -143,14 +146,14 @@
     }
 
     @Override
-    public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
-            throws DeviceNotAvailableException {
+    public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
+        ITestDevice device = testInfo.getDevice();
         if (e instanceof DeviceNotAvailableException) {
             CLog.e("Device %s is not available. Teardown() skipped.", device.getSerialNumber());
             return;
         } else {
             if (mTestApexInfoList.isEmpty() && getApkInstalled().isEmpty()) {
-                super.tearDown(device, buildInfo, e);
+                super.tearDown(testInfo, e);
             } else {
                 for (String apkPkgName : getApkInstalled()) {
                     super.uninstallPackage(device, apkPkgName);
@@ -165,20 +168,18 @@
     /**
      * Initializes the bundletool util for this class.
      *
-     * @param buildInfo the {@link IBuildInfo} for the artifacts.
-     * @param device the {@link ITestDevice} to install the train.
+     * @param testInfo the {@link TestInformation} for the invocation.
      * @throws TargetSetupError if bundletool cannot be found.
      */
-    private void initBundletoolUtil(IBuildInfo buildInfo, ITestDevice device)
-            throws TargetSetupError {
+    private void initBundletoolUtil(TestInformation testInfo) throws TargetSetupError {
         if (mBundletoolUtil != null) {
             return;
         }
-        File bundletoolJar = getLocalPathForFilename(buildInfo, getBundletoolFileName(), device);
+        File bundletoolJar = getLocalPathForFilename(testInfo, getBundletoolFileName());
         if (bundletoolJar == null) {
             throw new TargetSetupError(
                     String.format("Failed to find bundletool jar %s.", getBundletoolFileName()),
-                    device.getDeviceDescriptor());
+                    testInfo.getDevice().getDeviceDescriptor());
         }
         mBundletoolUtil = new BundletoolUtil(bundletoolJar);
     }
@@ -208,20 +209,23 @@
     /**
      * Extracts and returns splits for the specified apks.
      *
-     * @param buildInfo the {@link IBuildInfo} for the artifacts.
-     * @param device the {@link ITestDevice} to install the train.
+     * @param testInfo the {@link TestInformation}
      * @param apksName The name of the apks file to extract splits from.
      * @return a File[] containing the splits.
      * @throws TargetSetupError if bundletool cannot be found or device spec file fails to generate.
      */
-    private File[] getSplitsForApks(IBuildInfo buildInfo, ITestDevice device, String apksName)
+    private File[] getSplitsForApks(TestInformation testInfo, String apksName)
             throws TargetSetupError {
-        initBundletoolUtil(buildInfo, device);
-        initDeviceSpecFilePath(device);
-        File moduleFile = getLocalPathForFilename(buildInfo, apksName, device);
+        initBundletoolUtil(testInfo);
+        initDeviceSpecFilePath(testInfo.getDevice());
+        File moduleFile = getLocalPathForFilename(testInfo, apksName);
         File splitsDir =
                 getBundletoolUtil()
-                        .extractSplitsFromApks(moduleFile, mDeviceSpecFilePath, device, buildInfo);
+                        .extractSplitsFromApks(
+                                moduleFile,
+                                mDeviceSpecFilePath,
+                                testInfo.getDevice(),
+                                testInfo.getBuildInfo());
         return splitsDir.listFiles();
     }
 
@@ -229,16 +233,16 @@
      * Gets the modules that should be installed on the train, based on the modules preloaded on the
      * device. Modules that are not preloaded will not be installed.
      *
-     * @param buildInfo the {@link IBuildInfo} for the artifacts.
-     * @param device the {@link ITestDevice} to install the train.
+     * @param testInfo the {@link TestInformation}
      * @return List<String> of the modules that should be installed on the device.
      * @throws DeviceNotAvailableException when device is not available.
      * @throws TargetSetupError when mandatory modules are not installed, or module cannot be
      *     installed.
      */
-    public List<String> getModulesToInstall(IBuildInfo buildInfo, ITestDevice device)
+    public List<String> getModulesToInstall(TestInformation testInfo)
             throws DeviceNotAvailableException, TargetSetupError {
         // Get all preloaded modules for the device.
+        ITestDevice device = testInfo.getDevice();
         Set<String> installedPackages = new HashSet<>(device.getInstalledPackageNames());
         Set<ApexInfo> installedApexes = new HashSet<>(device.getActiveApexes());
         for (ApexInfo installedApex : installedApexes) {
@@ -247,7 +251,7 @@
         List<String> moduleFileNames = getTestsFileName();
         List<String> moduleNamesToInstall = new ArrayList<>();
         for (String moduleFileName : moduleFileNames) {
-            File moduleFile = getLocalPathForFilename(buildInfo, moduleFileName, device);
+            File moduleFile = getLocalPathForFilename(testInfo, moduleFileName);
             if (moduleFile == null) {
                 throw new TargetSetupError(
                         String.format("%s not found.", moduleFileName),
@@ -255,7 +259,7 @@
             }
             String modulePackageName = "";
             if (moduleFile.getName().endsWith(SPLIT_APKS_SUFFIX)) {
-                File[] splits = getSplitsForApks(buildInfo, device, moduleFileName);
+                File[] splits = getSplitsForApks(testInfo, moduleFileName);
                 modulePackageName = parsePackageName(splits[0], device.getDeviceDescriptor());
             } else {
                 modulePackageName = parsePackageName(moduleFile, device.getDeviceDescriptor());
@@ -281,43 +285,38 @@
         return moduleNamesToInstall;
     }
 
-
     // TODO(b/124461631): Remove after ddmlib supports install-multi-package.
     @Override
-    protected void installer(
-            ITestDevice device, IBuildInfo buildInfo, List<String> testAppFileNames)
+    protected void installer(TestInformation testInfo, List<String> testAppFileNames)
             throws TargetSetupError, DeviceNotAvailableException {
         if (containsApex(testAppFileNames)) {
-            mTestApexInfoList = collectApexInfoFromApexModules(testAppFileNames, device, buildInfo);
+            mTestApexInfoList = collectApexInfoFromApexModules(testAppFileNames, testInfo);
         }
-        if (containsPersistentApk(testAppFileNames, device, buildInfo)) {
+        if (containsPersistentApk(testAppFileNames, testInfo)) {
             // When there is a persistent apk in the train, use '--staged' to install full train
             // Otherwise, do normal install without '--staged'
-            installTrain(device, buildInfo, testAppFileNames, new String[] {"--staged"});
+            installTrain(testInfo, testAppFileNames, new String[] {"--staged"});
             return;
         }
-        installTrain(device, buildInfo, testAppFileNames, new String[] {});
+        installTrain(testInfo, testAppFileNames, new String[] {});
     }
 
     /**
      * Attempts to install a mainline train containing apex on the device.
      *
-     * @param device the {@link ITestDevice} to install the train
-     * @param buildInfo build artifact information
+     * @param testInfo the {@link TestInformation}
      * @param moduleFilenames List of String. The list of filenames of the mainline modules to be
      *     installed.
      */
     protected void installTrain(
-            ITestDevice device,
-            IBuildInfo buildInfo,
-            List<String> moduleFilenames,
-            final String[] extraArgs)
+            TestInformation testInfo, List<String> moduleFilenames, final String[] extraArgs)
             throws TargetSetupError, DeviceNotAvailableException {
         // TODO(b/137883918):remove after new adb is released, which supports installing
         // single apk/apex using 'install-multi-package'
+        ITestDevice device = testInfo.getDevice();
         if (moduleFilenames.size() == 1) {
             String moduleFileName = moduleFilenames.get(0);
-            File module = getLocalPathForFilename(buildInfo, moduleFileName, device);
+            File module = getLocalPathForFilename(testInfo, moduleFileName);
             device.installPackage(module, true, extraArgs);
             if (moduleFileName.endsWith(APK_SUFFIX)) {
                 String packageName = parsePackageName(module, device.getDeviceDescriptor());
@@ -337,7 +336,7 @@
         }
 
         for (String fileName : moduleFilenames) {
-            File moduleFile = getLocalPathForFilename(buildInfo, fileName, device);
+            File moduleFile = getLocalPathForFilename(testInfo, fileName);
             if (moduleFile == null) {
                 throw new TargetSetupError(
                         String.format("File %s not found.", fileName),
@@ -372,20 +371,19 @@
     /**
      * Attempts to install mainline module(s) using bundletool.
      *
-     * @param device the {@link ITestDevice} to install the train
-     * @param buildInfo build artifact information
+     * @param testInfo the {@link TestInformation}
      */
-    protected void installUsingBundleTool(IBuildInfo buildInfo, ITestDevice device)
+    protected void installUsingBundleTool(TestInformation testInfo)
             throws TargetSetupError, DeviceNotAvailableException {
-        initBundletoolUtil(buildInfo, device);
-        initDeviceSpecFilePath(device);
+        initBundletoolUtil(testInfo);
+        initDeviceSpecFilePath(testInfo.getDevice());
 
         if (getTestsFileName().size() == 1) {
             // Installs single .apks module.
             installSingleModuleUsingBundletool(
-                    device, buildInfo, mDeviceSpecFilePath, getTestsFileName().get(0));
+                    testInfo, mDeviceSpecFilePath, getTestsFileName().get(0));
         } else {
-            installMultipleModuleUsingBundletool(device, buildInfo, mDeviceSpecFilePath);
+            installMultipleModuleUsingBundletool(testInfo, mDeviceSpecFilePath);
         }
 
         mApkInstalled.addAll(mApkToInstall);
@@ -394,18 +392,17 @@
     /**
      * Attempts to install a single mainline module(.apks) using bundletool.
      *
-     * @param device the {@link ITestDevice} to install the train
-     * @param buildInfo build artifact information
+     * @param testInfo the {@link TestInformation}
      * @param deviceSpecFilePath the spec file of the test device
      * @param apksName the file name of the .apks
      */
     private void installSingleModuleUsingBundletool(
-            ITestDevice device, IBuildInfo buildInfo, String deviceSpecFilePath, String apksName)
+            TestInformation testInfo, String deviceSpecFilePath, String apksName)
             throws TargetSetupError, DeviceNotAvailableException {
-        File apks = getLocalPathForFilename(buildInfo, apksName, device);
+        File apks = getLocalPathForFilename(testInfo, apksName);
         // Rename the extracted files and add the file to filename list.
-        File[] splits = getSplitsForApks(buildInfo, device, apks.getName());
-
+        File[] splits = getSplitsForApks(testInfo, apks.getName());
+        ITestDevice device = testInfo.getDevice();
         if (splits.length == 0) {
             throw new TargetSetupError(
                     String.format("Extraction for %s failed. No apk/apex is extracted.", apksName),
@@ -414,7 +411,7 @@
         String splitFileName = splits[0].getName();
         // Install .apks that contain apex module.
         if (containsApex(Arrays.asList(splitFileName))) {
-            super.installer(device, buildInfo, Arrays.asList(splitFileName));
+            super.installer(testInfo, Arrays.asList(splitFileName));
         } else {
             // Install .apks that contain apk module.
             getBundletoolUtil().installApks(apks, device);
@@ -427,17 +424,17 @@
      * Attempts to install multiple mainline modules using bundletool. Modules can be any
      * combination of .apk, .apex or .apks.
      *
-     * @param device the {@link ITestDevice} to install the train
-     * @param buildInfo build artifact information
+     * @param testInfo the {@link TestInformation}
      * @param deviceSpecFilePath the spec file of the test device
      */
     private void installMultipleModuleUsingBundletool(
-            ITestDevice device, IBuildInfo buildInfo, String deviceSpecFilePath)
+            TestInformation testInfo, String deviceSpecFilePath)
             throws TargetSetupError, DeviceNotAvailableException {
+        ITestDevice device = testInfo.getDevice();
         for (String moduleFileName : getTestsFileName()) {
-            File moduleFile = getLocalPathForFilename(buildInfo, moduleFileName, device);
+            File moduleFile = getLocalPathForFilename(testInfo, moduleFileName);
             if (moduleFileName.endsWith(SPLIT_APKS_SUFFIX)) {
-                File[] splits = getSplitsForApks(buildInfo, device, moduleFileName);
+                File[] splits = getSplitsForApks(testInfo, moduleFileName);
                 String splitsArgs = createInstallArgsForSplit(splits, device);
                 mSplitsInstallArgs.add(splitsArgs);
             } else {
@@ -603,15 +600,13 @@
      * Checks if the input files contain any persistent apk.
      *
      * @param testAppFileNames The list of the file names of the modules to install
-     * @param device The test device
-     * @param buildInfo build artifact information
+     * @param testInfo The {@link TestInformation}
      * @return <code>true</code> if the input files contains a persistent apk module.
      */
-    protected boolean containsPersistentApk(
-            List<String> testAppFileNames, ITestDevice device, IBuildInfo buildInfo)
+    protected boolean containsPersistentApk(List<String> testAppFileNames, TestInformation testInfo)
             throws TargetSetupError, DeviceNotAvailableException {
         for (String moduleFileName : testAppFileNames) {
-            if (isPersistentApk(moduleFileName, device, buildInfo)) {
+            if (isPersistentApk(moduleFileName, testInfo)) {
                 return true;
             }
         }
@@ -622,18 +617,20 @@
      * Checks if an apk is a persistent apk.
      *
      * @param filename The apk module file to check
-     * @param device The test device
-     * @param buildInfo build artifact information
+     * @param testInfo The {@link TestInformation}
      * @return <code>true</code> if this is a persistent apk module.
      */
-    protected boolean isPersistentApk(String filename, ITestDevice device, IBuildInfo buildInfo)
+    protected boolean isPersistentApk(String filename, TestInformation testInfo)
             throws TargetSetupError, DeviceNotAvailableException {
         if (!filename.endsWith(APK_SUFFIX)) {
             return false;
         }
-        File moduleFile = getLocalPathForFilename(buildInfo, filename, device);
+        File moduleFile = getLocalPathForFilename(testInfo, filename);
         PackageInfo pkgInfo =
-            device.getAppPackageInfo(parsePackageName(moduleFile, device.getDeviceDescriptor()));
+                testInfo.getDevice()
+                        .getAppPackageInfo(
+                                parsePackageName(
+                                        moduleFile, testInfo.getDevice().getDeviceDescriptor()));
         return pkgInfo.isPersistentApp();
     }
 
@@ -641,18 +638,17 @@
      * Collects apex info from the apex modules for activation check.
      *
      * @param testAppFileNames The list of the file names of the modules to install
-     * @param device The test device
-     * @param buildInfo build artifact information
+     * @param testInfo The {@link TestInformation}
      * @return a list containing the apexinfo of the apex modules in the input file lists
      */
     protected List<ApexInfo> collectApexInfoFromApexModules(
-            List<String> testAppFileNames, ITestDevice device, IBuildInfo buildInfo)
-            throws TargetSetupError {
+            List<String> testAppFileNames, TestInformation testInfo) throws TargetSetupError {
         List<ApexInfo> apexInfoList = new ArrayList<>();
         for (String appFilename : getTestsFileName()) {
-            File appFile = getLocalPathForFilename(buildInfo, appFilename, device);
+            File appFile = getLocalPathForFilename(testInfo, appFilename);
             if (isApex(appFile)) {
-                ApexInfo apexInfo = retrieveApexInfo(appFile, device.getDeviceDescriptor());
+                ApexInfo apexInfo =
+                        retrieveApexInfo(appFile, testInfo.getDevice().getDeviceDescriptor());
                 apexInfoList.add(apexInfo);
             }
         }
diff --git a/test_framework/com/android/tradefed/targetprep/suite/SuiteApkInstaller.java b/test_framework/com/android/tradefed/targetprep/suite/SuiteApkInstaller.java
index d77eeca..66dcda9 100644
--- a/test_framework/com/android/tradefed/targetprep/suite/SuiteApkInstaller.java
+++ b/test_framework/com/android/tradefed/targetprep/suite/SuiteApkInstaller.java
@@ -16,16 +16,10 @@
 
 package com.android.tradefed.targetprep.suite;
 
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.build.IDeviceBuildInfo;
 import com.android.tradefed.config.OptionClass;
-import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.targetprep.TargetSetupError;
 import com.android.tradefed.targetprep.TestAppInstallSetup;
-import com.android.tradefed.util.FileUtil;
-
-import com.google.common.annotations.VisibleForTesting;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -37,106 +31,18 @@
 @OptionClass(alias = "apk-installer")
 public class SuiteApkInstaller extends TestAppInstallSetup {
 
-    private static final String ANDROID_TARGET_TESTCASES = "ANDROID_TARGET_OUT_TESTCASES";
-    private static final String ROOT_DIR = "ROOT_DIR";
-
-    @VisibleForTesting
-    String getEnvVariable() {
-        return System.getenv(ANDROID_TARGET_TESTCASES);
-    }
-
-    /**
-     * Try to find a path for the base root tests directory.
-     *
-     * @param buildInfo the {@link IBuildInfo} describing the build.
-     * @return a {@link File} pointing to the directory of the root tests dir.
-     * @throws FileNotFoundException if no root dir is defined.
-     */
-    @VisibleForTesting
-    protected File getRootDir(IBuildInfo buildInfo) throws FileNotFoundException {
-        if (buildInfo.getBuildAttributes().get(ROOT_DIR) != null) {
-            return new File(buildInfo.getBuildAttributes().get(ROOT_DIR));
-        }
-        throw new FileNotFoundException(String.format("%s was found.", ROOT_DIR));
-    }
-
-    /** Check within $ANDROID_TARGET_OUT_TESTCASES if the apk exists. */
-    private File getApkFromVariableTestsDir(String apkFileName) {
-        String testcasesPath = getEnvVariable();
-        if (testcasesPath != null) {
-            File testCasesFile = new File(testcasesPath);
-            // Only return the variable directory if it exists
-            if (testCasesFile.isDirectory()) {
-                return FileUtil.findFile(testCasesFile, apkFileName);
-            }
-        }
-        return null;
-    }
-
-    /** Check within {@link IDeviceBuildInfo#getTestsDir()} if the apk exists. */
-    private File getApkFromBuildTestsDir(IBuildInfo buildInfo, String apkFileName) {
-        if (buildInfo instanceof IDeviceBuildInfo) {
-            IDeviceBuildInfo deviceBuildInfo = (IDeviceBuildInfo) buildInfo;
-            File testDir = deviceBuildInfo.getTestsDir();
-            if (testDir != null && testDir.isDirectory()) {
-                File apkFile = FileUtil.findFile(testDir, apkFileName);
-                if (apkFile == null) {
-                    // TODO(b/138416078): Once build dependency can be fixed and test required
-                    // APKs are all under the test module directory, we can remove this fallback
-                    // approach to do individual download from remote artifact.
-                    // Try to stage the files from remote zip files.
-                    apkFile = buildInfo.stageRemoteFile(apkFileName, testDir);
-                }
-                return apkFile;
-            }
-        }
-        return null;
-    }
-
-    private File getApkFromDependencies(TestInformation testInfo, String apkFileName) {
-        if (testInfo != null && testInfo.dependenciesFolder() != null) {
-            return FileUtil.findFile(testInfo.dependenciesFolder(), apkFileName);
-        }
-        return null;
-    }
-
     /** {@inheritDoc} */
     @Override
-    protected File getLocalPathForFilename(
-            IBuildInfo buildInfo, String apkFileName, ITestDevice device) throws TargetSetupError {
+    protected File getLocalPathForFilename(TestInformation testInfo, String apkFileName)
+            throws TargetSetupError {
         File apkFile = null;
         try {
-            // check in ANDROID_TARGET_OUT_TESTCASES first.
-            apkFile = getApkFromVariableTestsDir(apkFileName);
-            if (apkFile != null && apkFile.isFile()) {
-                return apkFile;
-            }
-
-            // check from IDeviceBuildInfo.
-            apkFile = getApkFromBuildTestsDir(buildInfo, apkFileName);
-            if (apkFile != null && apkFile.isFile()) {
-                return apkFile;
-            }
-
-            // Check build info directly
-            apkFile = buildInfo.getFile(apkFileName);
-            if (apkFile != null && apkFile.isFile()) {
-                return apkFile;
-            }
-
-            apkFile = getApkFromDependencies(getTestInfo(), apkFileName);
-            if (apkFile != null && apkFile.isFile()) {
-                return apkFile;
-            }
-
-            // check ROOT_DIR
-            apkFile = FileUtil.findFile(getRootDir(buildInfo), apkFileName);
-            if (apkFile == null || !apkFile.isFile()) {
-                throw new FileNotFoundException();
-            }
+            apkFile = testInfo.getDependencyFile(apkFileName, true);
         } catch (FileNotFoundException e) {
             throw new TargetSetupError(
-                    String.format("%s not found", apkFileName), e, device.getDeviceDescriptor());
+                    String.format("%s not found", apkFileName),
+                    e,
+                    testInfo.getDevice().getDeviceDescriptor());
         }
         return apkFile;
     }
diff --git a/test_framework/com/android/tradefed/testtype/HostTest.java b/test_framework/com/android/tradefed/testtype/HostTest.java
index 88bd0e8..880f499 100644
--- a/test_framework/com/android/tradefed/testtype/HostTest.java
+++ b/test_framework/com/android/tradefed/testtype/HostTest.java
@@ -191,6 +191,10 @@
                 mIncludeAnnotations, mExcludeAnnotations);
     }
 
+    public void setTestInformation(TestInformation testInfo) {
+        mTestInfo = testInfo;
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/tests/src/com/android/tradefed/config/ConfigurationTest.java b/tests/src/com/android/tradefed/config/ConfigurationTest.java
index e0c3952..e2eec9e 100644
--- a/tests/src/com/android/tradefed/config/ConfigurationTest.java
+++ b/tests/src/com/android/tradefed/config/ConfigurationTest.java
@@ -25,6 +25,7 @@
 import com.android.tradefed.device.IDeviceSelection;
 import com.android.tradefed.device.TestDeviceOptions;
 import com.android.tradefed.invoker.InvocationContext;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.log.ILeveledLogOutput;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.result.TextResultReporter;
@@ -245,7 +246,9 @@
      */
     public void testGetTests() throws DeviceNotAvailableException {
         // check that the default test is present and doesn't blow up
-        mConfig.getTests().get(0).run(new TextResultReporter());
+        mConfig.getTests()
+                .get(0)
+                .run(TestInformation.newBuilder().build(), new TextResultReporter());
         IRemoteTest test1 = EasyMock.createMock(IRemoteTest.class);
         mConfig.setTest(test1);
         assertEquals(test1, mConfig.getTests().get(0));
diff --git a/tests/src/com/android/tradefed/device/NativeDeviceTest.java b/tests/src/com/android/tradefed/device/NativeDeviceTest.java
index c15837f..c45e163 100644
--- a/tests/src/com/android/tradefed/device/NativeDeviceTest.java
+++ b/tests/src/com/android/tradefed/device/NativeDeviceTest.java
@@ -2368,7 +2368,22 @@
         Map<Long, String> history = new LinkedHashMap<Long, String>();
         history.put(1556587278L, "kernel_panic");
         EasyMock.replay(mMockIDevice);
-        assertEquals(history, spy.getBootHistorySince(1556238008L, TimeUnit.SECONDS));
+        assertEquals(history, spy.getBootHistorySince(1556238009L, TimeUnit.SECONDS));
+        EasyMock.verify(mMockIDevice);
+    }
+
+    /** Test {@link NativeDevice#getBootHistorySince(long, TimeUnit)} on an edge condition. */
+    @Test
+    public void testGetBootHistorySince_limit() throws Exception {
+        TestableAndroidNativeDevice spy = Mockito.spy(mTestDevice);
+        doReturn("reboot,1579678463\n" + "        reboot,,1579678339\n")
+                .when(spy)
+                .getProperty(DeviceProperties.BOOT_REASON_HISTORY);
+        Map<Long, String> history = new LinkedHashMap<Long, String>();
+        history.put(1579678463L, "reboot");
+        EasyMock.replay(mMockIDevice);
+        // For the same value we should expect it to be part of the reboot.
+        assertEquals(history, spy.getBootHistorySince(1579678463, TimeUnit.SECONDS));
         EasyMock.verify(mMockIDevice);
     }
 
@@ -2386,7 +2401,7 @@
         Map<Long, String> history = new LinkedHashMap<Long, String>();
         history.put(1556587278L, "kernel_panic");
         EasyMock.replay(mMockIDevice);
-        assertEquals(history, spy.getBootHistorySince(1556238008000L, TimeUnit.MILLISECONDS));
+        assertEquals(history, spy.getBootHistorySince(1556238009000L, TimeUnit.MILLISECONDS));
         EasyMock.verify(mMockIDevice);
     }
 
@@ -2891,8 +2906,14 @@
         res.setStatus(CommandStatus.SUCCESS);
         EasyMock.expect(
                         mMockRunUtil.runTimedCmd(
-                                120000, stdout, stderr, "adb", "-s", "serial", "shell", "setprop",
-                                "test", "value"))
+                                120000,
+                                stdout,
+                                stderr,
+                                "adb",
+                                "-s",
+                                "serial",
+                                "shell",
+                                "setprop test 'value'"))
                 .andReturn(res);
         EasyMock.replay(mMockRunUtil, mMockIDevice);
         assertTrue(mTestDevice.setProperty("test", "value"));
diff --git a/tests/src/com/android/tradefed/targetprep/DeviceFlashPreparerTest.java b/tests/src/com/android/tradefed/targetprep/DeviceFlashPreparerTest.java
index 03b5aa1..ece027f 100644
--- a/tests/src/com/android/tradefed/targetprep/DeviceFlashPreparerTest.java
+++ b/tests/src/com/android/tradefed/targetprep/DeviceFlashPreparerTest.java
@@ -32,6 +32,9 @@
 import com.android.tradefed.device.ITestDevice.RecoveryMode;
 import com.android.tradefed.device.TestDeviceOptions;
 import com.android.tradefed.host.IHostOptions;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.InvocationContext;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.targetprep.IDeviceFlasher.UserDataFlashOption;
 import com.android.tradefed.util.CommandStatus;
 import com.android.tradefed.util.FileUtil;
@@ -58,6 +61,7 @@
     private IHostOptions mMockHostOptions;
     private File mTmpDir;
     private boolean mFlashingMetricsReported;
+    private TestInformation mTestInfo;
 
     @Before
     public void setUp() throws Exception {
@@ -104,6 +108,10 @@
         // expect this call
         mMockFlasher.setUserDataFlashOption(UserDataFlashOption.FLASH);
         mTmpDir = FileUtil.createTempDir("tmp");
+        IInvocationContext context = new InvocationContext();
+        context.addAllocatedDevice("device", mMockDevice);
+        context.addDeviceBuildInfo("device", mMockBuildInfo);
+        mTestInfo = TestInformation.newBuilder().setInvocationContext(context).build();
     }
 
     @After
@@ -111,7 +119,7 @@
         FileUtil.recursiveDelete(mTmpDir);
     }
 
-    /** Simple normal case test for {@link DeviceFlashPreparer#setUp(ITestDevice, IBuildInfo)}. */
+    /** Simple normal case test for {@link DeviceFlashPreparer#setUp(TestInformation)}. */
     @Test
     public void testSetup() throws Exception {
         doSetupExpectations();
@@ -120,7 +128,7 @@
         EasyMock.expect(mMockFlasher.getSystemFlashingStatus())
             .andReturn(CommandStatus.SUCCESS).anyTimes();
         EasyMock.replay(mMockFlasher, mMockDevice);
-        mDeviceFlashPreparer.setUp(mMockDevice, mMockBuildInfo);
+        mDeviceFlashPreparer.setUp(mTestInfo);
         EasyMock.verify(mMockFlasher, mMockDevice);
         assertTrue("should report flashing metrics in normal case", mFlashingMetricsReported);
     }
@@ -149,13 +157,16 @@
     }
 
     /**
-     * Test {@link DeviceFlashPreparer#setUp(ITestDevice, IBuildInfo)} when a non IDeviceBuildInfo
-     * type is provided.
+     * Test {@link DeviceFlashPreparer#setUp(TestInformation)} when a non IDeviceBuildInfo type is
+     * provided.
      */
     @Test
     public void testSetUp_nonDevice() throws Exception {
         try {
-            mDeviceFlashPreparer.setUp(mMockDevice, EasyMock.createMock(IBuildInfo.class));
+            mTestInfo
+                    .getContext()
+                    .addDeviceBuildInfo("device", EasyMock.createMock(IBuildInfo.class));
+            mDeviceFlashPreparer.setUp(mTestInfo);
             fail("IllegalArgumentException not thrown");
         } catch (IllegalArgumentException e) {
             // expected
@@ -163,21 +174,21 @@
     }
 
     /**
-     * Test {@link DeviceFlashPreparer#setUp(ITestDevice, IBuildInfo)} when ramdisk flashing is
-     * required via parameter but not provided in build info
+     * Test {@link DeviceFlashPreparer#setUp(TestInformation)} when ramdisk flashing is required via
+     * parameter but not provided in build info
      */
     @Test
     public void testSetUp_noRamdisk() throws Exception {
         try {
             mDeviceFlashPreparer.setShouldFlashRamdisk(true);
-            mDeviceFlashPreparer.setUp(mMockDevice, mMockBuildInfo);
+            mDeviceFlashPreparer.setUp(mTestInfo);
             fail("IllegalArgumentException not thrown");
         } catch (IllegalArgumentException e) {
             // expected
         }
     }
 
-    /** Test {@link DeviceFlashPreparer#setUp(ITestDevice, IBuildInfo)} when build does not boot. */
+    /** Test {@link DeviceFlashPreparer#setUp(TestInformation)} when build does not boot. */
     @Test
     public void testSetup_buildError() throws Exception {
         mMockDevice.setRecoveryMode(RecoveryMode.ONLINE);
@@ -206,7 +217,7 @@
             .andReturn(CommandStatus.SUCCESS).anyTimes();
         EasyMock.replay(mMockFlasher, mMockDevice);
         try {
-            mDeviceFlashPreparer.setUp(mMockDevice, mMockBuildInfo);
+            mDeviceFlashPreparer.setUp(mTestInfo);
             fail("DeviceFlashPreparerTest not thrown");
         } catch (BuildError e) {
             // expected; use the general version to make absolutely sure that
@@ -219,9 +230,9 @@
     }
 
     /**
-     * Test {@link DeviceFlashPreparer#setUp(ITestDevice, IBuildInfo)} when flashing step hits
-     * device failure.
-     **/
+     * Test {@link DeviceFlashPreparer#setUp(TestInformation)} when flashing step hits device
+     * failure.
+     */
     @Test
     public void testSetup_flashException() throws Exception {
         mMockDevice.setRecoveryMode(RecoveryMode.ONLINE);
@@ -237,7 +248,7 @@
             .andReturn(CommandStatus.EXCEPTION).anyTimes();
         EasyMock.replay(mMockFlasher, mMockDevice);
         try {
-            mDeviceFlashPreparer.setUp(mMockDevice, mMockBuildInfo);
+            mDeviceFlashPreparer.setUp(mTestInfo);
             fail("DeviceNotAvailableException not thrown");
         } catch (DeviceNotAvailableException e) {
             // expected
@@ -248,9 +259,9 @@
     }
 
     /**
-     * Test {@link DeviceFlashPreparer#setUp(ITestDevice, IBuildInfo)} when flashing of system
-     * partitions are skipped.
-     **/
+     * Test {@link DeviceFlashPreparer#setUp(TestInformation)} when flashing of system partitions
+     * are skipped.
+     */
     @Test
     public void testSetup_flashSkipped() throws Exception {
         doSetupExpectations();
@@ -258,7 +269,7 @@
         // report flashing status as null (for not flashing system partitions)
         EasyMock.expect(mMockFlasher.getSystemFlashingStatus()).andReturn(null).anyTimes();
         EasyMock.replay(mMockFlasher, mMockDevice);
-        mDeviceFlashPreparer.setUp(mMockDevice, mMockBuildInfo);
+        mDeviceFlashPreparer.setUp(mTestInfo);
         EasyMock.verify(mMockFlasher, mMockDevice);
         assertFalse("should not report flashing metrics in normal case", mFlashingMetricsReported);
     }
@@ -280,7 +291,7 @@
         mMockFlasher.setShouldFlashRamdisk(true);
         EasyMock.expectLastCall();
         EasyMock.replay(mMockFlasher, mMockDevice);
-        mDeviceFlashPreparer.setUp(mMockDevice, mMockBuildInfo);
+        mDeviceFlashPreparer.setUp(mTestInfo);
         EasyMock.verify(mMockFlasher, mMockDevice);
     }
 }
diff --git a/tests/src/com/android/tradefed/targetprep/InstallApexModuleTargetPreparerTest.java b/tests/src/com/android/tradefed/targetprep/InstallApexModuleTargetPreparerTest.java
index 60702a7..c8d3624 100644
--- a/tests/src/com/android/tradefed/targetprep/InstallApexModuleTargetPreparerTest.java
+++ b/tests/src/com/android/tradefed/targetprep/InstallApexModuleTargetPreparerTest.java
@@ -18,30 +18,35 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.when;
 
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.command.remote.DeviceDescriptor;
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.ITestDevice.ApexInfo;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.InvocationContext;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.util.BundletoolUtil;
 import com.android.tradefed.util.CommandResult;
 import com.android.tradefed.util.FileUtil;
+
 import com.google.common.collect.ImmutableSet;
-import com.android.tradefed.util.BundletoolUtil;
-import java.util.ArrayList;
-import java.util.List;
+
 import org.easymock.EasyMock;
-import org.mockito.Mockito;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.Mockito;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 /** Unit test for {@link InstallApexModuleTargetPreparer} */
@@ -52,6 +57,7 @@
     private InstallApexModuleTargetPreparer mInstallApexModuleTargetPreparer;
     private IBuildInfo mMockBuildInfo;
     private ITestDevice mMockDevice;
+    private TestInformation mTestInfo;
     private BundletoolUtil mMockBundletoolUtil;
     private File mFakeApex;
     private File mFakeApk;
@@ -94,6 +100,10 @@
         mMockBundletoolUtil = Mockito.mock(BundletoolUtil.class);
         EasyMock.expect(mMockDevice.getSerialNumber()).andStubReturn(SERIAL);
         EasyMock.expect(mMockDevice.getDeviceDescriptor()).andStubReturn(null);
+        IInvocationContext context = new InvocationContext();
+        context.addAllocatedDevice("device", mMockDevice);
+        context.addDeviceBuildInfo("device", mMockBuildInfo);
+        mTestInfo = TestInformation.newBuilder().setInvocationContext(context).build();
 
         mInstallApexModuleTargetPreparer =
                 new InstallApexModuleTargetPreparer() {
@@ -114,8 +124,7 @@
 
                     @Override
                     protected File getLocalPathForFilename(
-                            IBuildInfo buildInfo, String appFileName, ITestDevice device)
-                            throws TargetSetupError {
+                            TestInformation testInfo, String appFileName) throws TargetSetupError {
                         if (appFileName.endsWith(".apex")) {
                             return mFakeApex;
                         }
@@ -182,8 +191,7 @@
                     }
 
                     @Override
-                    protected boolean isPersistentApk(
-                            String filename, ITestDevice device, IBuildInfo buildInfo)
+                    protected boolean isPersistentApk(String filename, TestInformation testInfo)
                             throws TargetSetupError {
                         if (filename.contains("Persistent")) {
                             return true;
@@ -230,7 +238,7 @@
         EasyMock.expect(mMockDevice.getInstalledPackageNames()).andReturn(installableModules);
 
         EasyMock.replay(mMockBuildInfo, mMockDevice);
-        mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
+        mInstallApexModuleTargetPreparer.setUp(mTestInfo);
         EasyMock.verify(mMockBuildInfo, mMockDevice);
     }
 
@@ -250,7 +258,7 @@
         installableModules.add(APEX_PACKAGE_NAME);
         EasyMock.expect(mMockDevice.getInstalledPackageNames()).andReturn(installableModules);
         EasyMock.replay(mMockBuildInfo, mMockDevice);
-        mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
+        mInstallApexModuleTargetPreparer.setUp(mTestInfo);
         EasyMock.verify(mMockBuildInfo, mMockDevice);
     }
 
@@ -279,7 +287,7 @@
         EasyMock.expect(mMockDevice.getInstalledPackageNames()).andReturn(installableModules);
 
         EasyMock.replay(mMockBuildInfo, mMockDevice);
-        mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
+        mInstallApexModuleTargetPreparer.setUp(mTestInfo);
         EasyMock.verify(mMockBuildInfo, mMockDevice);
     }
 
@@ -307,7 +315,7 @@
 
         try {
             EasyMock.replay(mMockBuildInfo, mMockDevice);
-            mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
+            mInstallApexModuleTargetPreparer.setUp(mTestInfo);
             fail("Should have thrown a TargetSetupError.");
         } catch (TargetSetupError expected) {
             assertTrue(
@@ -344,7 +352,7 @@
 
         try {
             EasyMock.replay(mMockBuildInfo, mMockDevice);
-            mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
+            mInstallApexModuleTargetPreparer.setUp(mTestInfo);
             fail("Should have thrown a TargetSetupError.");
         } catch (TargetSetupError expected) {
             String failureMsg =
@@ -389,8 +397,8 @@
         EasyMock.expect(mMockDevice.getActiveApexes()).andReturn(ImmutableSet.of());
 
         EasyMock.replay(mMockBuildInfo, mMockDevice);
-        mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
-        mInstallApexModuleTargetPreparer.tearDown(mMockDevice, mMockBuildInfo, null);
+        mInstallApexModuleTargetPreparer.setUp(mTestInfo);
+        mInstallApexModuleTargetPreparer.tearDown(mTestInfo, null);
         EasyMock.verify(mMockBuildInfo, mMockDevice);
     }
 
@@ -424,8 +432,8 @@
         EasyMock.expect(mMockDevice.getActiveApexes()).andReturn(ImmutableSet.of());
 
         EasyMock.replay(mMockBuildInfo, mMockDevice);
-        mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
-        mInstallApexModuleTargetPreparer.tearDown(mMockDevice, mMockBuildInfo, null);
+        mInstallApexModuleTargetPreparer.setUp(mTestInfo);
+        mInstallApexModuleTargetPreparer.tearDown(mTestInfo, null);
         EasyMock.verify(mMockBuildInfo, mMockDevice);
     }
 
@@ -469,8 +477,8 @@
         EasyMock.expect(mMockDevice.getInstalledPackageNames()).andReturn(installableModules);
         EasyMock.expect(mMockDevice.getActiveApexes()).andReturn(ImmutableSet.of());
         EasyMock.replay(mMockBuildInfo, mMockDevice);
-        mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
-        mInstallApexModuleTargetPreparer.tearDown(mMockDevice, mMockBuildInfo, null);
+        mInstallApexModuleTargetPreparer.setUp(mTestInfo);
+        mInstallApexModuleTargetPreparer.tearDown(mTestInfo, null);
         EasyMock.verify(mMockBuildInfo, mMockDevice);
     }
 
@@ -546,8 +554,8 @@
             EasyMock.expect(mMockDevice.getActiveApexes()).andReturn(ImmutableSet.of());
 
             EasyMock.replay(mMockBuildInfo, mMockDevice);
-            mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
-            mInstallApexModuleTargetPreparer.tearDown(mMockDevice, mMockBuildInfo, null);
+            mInstallApexModuleTargetPreparer.setUp(mTestInfo);
+            mInstallApexModuleTargetPreparer.tearDown(mTestInfo, null);
             Mockito.verify(mMockBundletoolUtil, times(1))
                 .generateDeviceSpecFile(Mockito.any(ITestDevice.class));
             // Extract splits 1 time to get the package name for the module, and again during
@@ -597,8 +605,8 @@
 
 
         EasyMock.replay(mMockBuildInfo, mMockDevice);
-        mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
-        mInstallApexModuleTargetPreparer.tearDown(mMockDevice, mMockBuildInfo, null);
+        mInstallApexModuleTargetPreparer.setUp(mTestInfo);
+        mInstallApexModuleTargetPreparer.tearDown(mTestInfo, null);
         EasyMock.verify(mMockBuildInfo, mMockDevice);
     }
 
@@ -650,8 +658,8 @@
                 .once();
 
         EasyMock.replay(mMockBuildInfo, mMockDevice);
-        mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
-        mInstallApexModuleTargetPreparer.tearDown(mMockDevice, mMockBuildInfo, null);
+        mInstallApexModuleTargetPreparer.setUp(mTestInfo);
+        mInstallApexModuleTargetPreparer.tearDown(mTestInfo, null);
         EasyMock.verify(mMockBuildInfo, mMockDevice);
     }
 
@@ -746,8 +754,8 @@
             EasyMock.expect(mMockDevice.getInstalledPackageNames()).andReturn(installableModules);
 
             EasyMock.replay(mMockBuildInfo, mMockDevice);
-            mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
-            mInstallApexModuleTargetPreparer.tearDown(mMockDevice, mMockBuildInfo, null);
+            mInstallApexModuleTargetPreparer.setUp(mTestInfo);
+            mInstallApexModuleTargetPreparer.tearDown(mTestInfo, null);
             Mockito.verify(mMockBundletoolUtil, times(1))
                     .generateDeviceSpecFile(Mockito.any(ITestDevice.class));
             // Extract splits 1 time to get the package name for the module, and again during
@@ -780,7 +788,7 @@
     @Test
     public void testTearDown() throws Exception {
         EasyMock.replay(mMockBuildInfo, mMockDevice);
-        mInstallApexModuleTargetPreparer.tearDown(mMockDevice, mMockBuildInfo, null);
+        mInstallApexModuleTargetPreparer.tearDown(mTestInfo, null);
         EasyMock.verify(mMockBuildInfo, mMockDevice);
     }
 
@@ -843,8 +851,8 @@
         EasyMock.expectLastCall();
 
         EasyMock.replay(mMockBuildInfo, mMockDevice);
-        mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
-        mInstallApexModuleTargetPreparer.tearDown(mMockDevice, mMockBuildInfo, null);
+        mInstallApexModuleTargetPreparer.setUp(mTestInfo);
+        mInstallApexModuleTargetPreparer.tearDown(mTestInfo, null);
         EasyMock.verify(mMockBuildInfo, mMockDevice);
     }
 
@@ -881,8 +889,8 @@
                 .once();
 
         EasyMock.replay(mMockBuildInfo, mMockDevice);
-        mInstallApexModuleTargetPreparer.setUp(mMockDevice, mMockBuildInfo);
-        mInstallApexModuleTargetPreparer.tearDown(mMockDevice, mMockBuildInfo, null);
+        mInstallApexModuleTargetPreparer.setUp(mTestInfo);
+        mInstallApexModuleTargetPreparer.tearDown(mTestInfo, null);
         EasyMock.verify(mMockBuildInfo, mMockDevice);
     }
 }
diff --git a/tests/src/com/android/tradefed/targetprep/TestAppInstallSetupTest.java b/tests/src/com/android/tradefed/targetprep/TestAppInstallSetupTest.java
index c8b0479..ecd64da 100644
--- a/tests/src/com/android/tradefed/targetprep/TestAppInstallSetupTest.java
+++ b/tests/src/com/android/tradefed/targetprep/TestAppInstallSetupTest.java
@@ -20,7 +20,6 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.build.IDeviceBuildInfo;
 import com.android.tradefed.command.remote.DeviceDescriptor;
 import com.android.tradefed.config.OptionSetter;
@@ -84,8 +83,7 @@
 
                     @Override
                     protected File getLocalPathForFilename(
-                            IBuildInfo buildInfo, String apkFileName, ITestDevice device)
-                            throws TargetSetupError {
+                            TestInformation testInfo, String apkFileName) throws TargetSetupError {
                         return fakeApk;
                     }
                 };
@@ -139,8 +137,7 @@
 
                     @Override
                     protected File getLocalPathForFilename(
-                            IBuildInfo buildInfo, String apkFileName, ITestDevice device)
-                            throws TargetSetupError {
+                            TestInformation testInfo, String apkFileName) throws TargetSetupError {
                         return fakeApk;
                     }
                 };
@@ -166,8 +163,7 @@
 
                     @Override
                     protected File getLocalPathForFilename(
-                            IBuildInfo buildInfo, String apkFileName, ITestDevice device)
-                            throws TargetSetupError {
+                            TestInformation testInfo, String apkFileName) throws TargetSetupError {
                         return fakeApk;
                     }
                 };
diff --git a/tests/src/com/android/tradefed/targetprep/suite/SuiteApkInstallerTest.java b/tests/src/com/android/tradefed/targetprep/suite/SuiteApkInstallerTest.java
index 4e23f1f..c845e5c 100644
--- a/tests/src/com/android/tradefed/targetprep/suite/SuiteApkInstallerTest.java
+++ b/tests/src/com/android/tradefed/targetprep/suite/SuiteApkInstallerTest.java
@@ -18,18 +18,21 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.build.IDeviceBuildInfo;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.ExecutionFiles.FilesKey;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.InvocationContext;
 import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.targetprep.TargetSetupError;
 import com.android.tradefed.util.FileUtil;
 
 import org.easymock.EasyMock;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,9 +40,7 @@
 import org.mockito.Mockito;
 
 import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.HashMap;
-import java.util.Map;
+import java.io.IOException;
 
 /** Unit test for {@link SuiteApkInstaller} */
 @RunWith(JUnit4.class)
@@ -48,60 +49,28 @@
     private SuiteApkInstaller mPreparer;
     private IBuildInfo mMockBuildInfo;
     private ITestDevice mMockDevice;
+    private TestInformation mTestInfo;
+    private File mTmpDepDir;
 
     @Before
-    public void setUp() {
+    public void setUp() throws IOException {
         mPreparer = new SuiteApkInstaller();
         mMockBuildInfo = Mockito.mock(IBuildInfo.class);
         mMockDevice = Mockito.mock(ITestDevice.class);
+        IInvocationContext context = new InvocationContext();
+        context.addAllocatedDevice("device", mMockDevice);
+        context.addDeviceBuildInfo("device", mMockBuildInfo);
+        mTmpDepDir = FileUtil.createTempDir("suite-apk-installer-dep");
+        mTestInfo =
+                TestInformation.newBuilder()
+                        .setInvocationContext(context)
+                        .setDependenciesFolder(mTmpDepDir)
+                        .build();
     }
 
-    /**
-     * Test that when there is no $ANDROID_TARGET_OUT_TESTCASES defined and no ROOT_DIR, we throw an
-     * exception because we have nowhere to look for the files.
-     */
-    @Test
-    public void testGetTestsDir_noVar_noRootDir() {
-        mPreparer =
-                new SuiteApkInstaller() {
-                    @Override
-                    String getEnvVariable() {
-                        return null;
-                    }
-                };
-        doReturn(new HashMap<String, String>()).when(mMockBuildInfo).getBuildAttributes();
-        try {
-            mPreparer.getRootDir(mMockBuildInfo);
-            fail("Should have thrown an exception.");
-        } catch (FileNotFoundException expected) {
-            // expected
-        }
-    }
-
-    /**
-     * Test that when there is no $ANDROID_TARGET_OUT_TESTCASES defined but a ROOT_DIR is defined,
-     * we return the ROOT_DIR location.
-     */
-    @Test
-    public void testGetTestsDir_noVar() throws Exception {
-        mPreparer =
-                new SuiteApkInstaller() {
-                    @Override
-                    String getEnvVariable() {
-                        return null;
-                    }
-                };
-        File tmpDir = FileUtil.createTempDir("suite-apk-installer");
-        try {
-            Map<String, String> attributes = new HashMap<>();
-            attributes.put("ROOT_DIR", tmpDir.getAbsolutePath());
-            doReturn(attributes).when(mMockBuildInfo).getBuildAttributes();
-            File res = mPreparer.getRootDir(mMockBuildInfo);
-            assertNotNull(res);
-            assertEquals(tmpDir.getAbsolutePath(), res.getAbsolutePath());
-        } finally {
-            FileUtil.recursiveDelete(tmpDir);
-        }
+    @After
+    public void tearDown() {
+        FileUtil.recursiveDelete(mTmpDepDir);
     }
 
     /**
@@ -110,24 +79,11 @@
      */
     @Test
     public void testGetLocalPathForFilename_withVariable() throws Exception {
-        File varDir = FileUtil.createTempDir("suite-apk-installer-var");
-        File apk = FileUtil.createTempFile("testapk", ".apk", varDir);
-        try {
-            mPreparer =
-                    new SuiteApkInstaller() {
-                        @Override
-                        String getEnvVariable() {
-                            return varDir.getAbsolutePath();
-                        }
-                    };
-            File res =
-                    mPreparer.getLocalPathForFilename(mMockBuildInfo, apk.getName(), mMockDevice);
-            verify(mMockBuildInfo, times(0)).getBuildAttributes();
-            assertNotNull(res);
-            assertEquals(apk.getAbsolutePath(), res.getAbsolutePath());
-        } finally {
-            FileUtil.recursiveDelete(varDir);
-        }
+        File apk = FileUtil.createTempFile("testapk", ".apk", mTmpDepDir);
+        File res = mPreparer.getLocalPathForFilename(mTestInfo, apk.getName());
+        verify(mMockBuildInfo, times(0)).getBuildAttributes();
+        assertNotNull(res);
+        assertEquals(apk.getAbsolutePath(), res.getAbsolutePath());
     }
 
     /**
@@ -137,23 +93,12 @@
     @Test
     public void testGetTestsDir_notDir() throws Exception {
         File varDir = FileUtil.createTempFile("suite-apk-installer-var", ".txt");
+        mTestInfo.executionFiles().put(FilesKey.TARGET_TESTS_DIRECTORY, varDir);
         File tmpDir = FileUtil.createTempDir("suite-apk-installer");
+        mTestInfo.executionFiles().put(FilesKey.TESTS_DIRECTORY, tmpDir);
         File apkFile = FileUtil.createTempFile("apk-test", ".apk", tmpDir);
         try {
-            mPreparer =
-                    new SuiteApkInstaller() {
-                        @Override
-                        String getEnvVariable() {
-                            return varDir.getAbsolutePath();
-                        }
-                    };
-
-            Map<String, String> attributes = new HashMap<>();
-            attributes.put("ROOT_DIR", tmpDir.getAbsolutePath());
-            doReturn(attributes).when(mMockBuildInfo).getBuildAttributes();
-            File res =
-                    mPreparer.getLocalPathForFilename(
-                            mMockBuildInfo, apkFile.getName(), mMockDevice);
+            File res = mPreparer.getLocalPathForFilename(mTestInfo, apkFile.getName());
             assertNotNull(res);
             assertEquals(apkFile.getAbsolutePath(), res.getAbsolutePath());
         } finally {
@@ -163,23 +108,15 @@
     }
 
     /**
-     * Test that {@link SuiteApkInstaller#getLocalPathForFilename(IBuildInfo, String, ITestDevice)}
-     * returns the apk file when found.
+     * Test that {@link SuiteApkInstaller#getLocalPathForFilename(TestInformation, String)} returns
+     * the apk file when found.
      */
     @Test
     public void testGetLocalPathForFileName() throws Exception {
         File tmpApk = FileUtil.createTempFile("suite-apk-installer", ".apk");
-        mPreparer =
-                new SuiteApkInstaller() {
-                    @Override
-                    protected File getRootDir(IBuildInfo buildInfo) throws FileNotFoundException {
-                        return tmpApk.getParentFile();
-                    }
-                };
+        mTestInfo.executionFiles().put(tmpApk.getName(), tmpApk);
         try {
-            File apk =
-                    mPreparer.getLocalPathForFilename(
-                            mMockBuildInfo, tmpApk.getName(), mMockDevice);
+            File apk = mPreparer.getLocalPathForFilename(mTestInfo, tmpApk.getName());
             assertEquals(tmpApk.getAbsolutePath(), apk.getAbsolutePath());
         } finally {
             FileUtil.deleteFile(tmpApk);
@@ -187,21 +124,14 @@
     }
 
     /**
-     * Test that {@link SuiteApkInstaller#getLocalPathForFilename(IBuildInfo, String, ITestDevice)}
-     * throws an exception when the apk file is not found.
+     * Test that {@link SuiteApkInstaller#getLocalPathForFilename(TestInformation, String)} throws
+     * an exception when the apk file is not found.
      */
     @Test
     public void testGetLocalPathForFileName_noFound() throws Exception {
         File tmpApk = FileUtil.createTempFile("suite-apk-installer", ".apk");
-        mPreparer =
-                new SuiteApkInstaller() {
-                    @Override
-                    protected File getRootDir(IBuildInfo buildInfo) throws FileNotFoundException {
-                        return tmpApk.getParentFile();
-                    }
-                };
         try {
-            mPreparer.getLocalPathForFilename(mMockBuildInfo, "no_exist", mMockDevice);
+            mPreparer.getLocalPathForFilename(mTestInfo, "no_exist");
             fail("Should have thrown an exception.");
         } catch (TargetSetupError expected) {
             // expected
@@ -211,28 +141,20 @@
     }
 
     /**
-     * Test that {@link SuiteApkInstaller#getLocalPathForFilename(IBuildInfo, String, ITestDevice)}
-     * returns the apk file located in IDeviceBuildInfo.getTestsDir().
+     * Test that {@link SuiteApkInstaller#getLocalPathForFilename(TestInformation, String)} returns
+     * the apk file located in IDeviceBuildInfo.getTestsDir().
      */
     @Test
     public void testGetLocalPathForFileName_testsDir() throws Exception {
-        mPreparer =
-                new SuiteApkInstaller() {
-                    @Override
-                    protected File getRootDir(IBuildInfo buildInfo) throws FileNotFoundException {
-                        return null;
-                    }
-                };
         IDeviceBuildInfo deviceBuildInfo = EasyMock.createMock(IDeviceBuildInfo.class);
         File tmpDir = null;
         try {
             tmpDir = FileUtil.createTempDir("test");
+            mTestInfo.executionFiles().put(FilesKey.TESTS_DIRECTORY, tmpDir);
             File tmpApk = FileUtil.createTempFile("suite-apk-installer", ".apk", tmpDir);
-            EasyMock.expect(deviceBuildInfo.getTestsDir()).andReturn(tmpDir);
             EasyMock.replay(deviceBuildInfo);
-            File apk =
-                    mPreparer.getLocalPathForFilename(
-                            deviceBuildInfo, tmpApk.getName(), mMockDevice);
+            mTestInfo.getContext().addDeviceBuildInfo("device", deviceBuildInfo);
+            File apk = mPreparer.getLocalPathForFilename(mTestInfo, tmpApk.getName());
             assertEquals(tmpApk.getAbsolutePath(), apk.getAbsolutePath());
             EasyMock.verify(deviceBuildInfo);
         } finally {
@@ -244,16 +166,9 @@
     @Test
     public void testGetLocalPathForFileName_inBuildKey() throws Exception {
         File tmpApk = FileUtil.createTempFile("suite-apk-installer", ".apk");
-        mPreparer =
-                new SuiteApkInstaller() {
-                    @Override
-                    protected File getRootDir(IBuildInfo buildInfo) throws FileNotFoundException {
-                        return null;
-                    }
-                };
-        Mockito.doReturn(tmpApk).when(mMockBuildInfo).getFile("foo.apk");
+        mTestInfo.executionFiles().put("foo.apk", tmpApk);
         try {
-            File apk = mPreparer.getLocalPathForFilename(mMockBuildInfo, "foo.apk", mMockDevice);
+            File apk = mPreparer.getLocalPathForFilename(mTestInfo, "foo.apk");
             assertEquals(tmpApk.getAbsolutePath(), apk.getAbsolutePath());
         } finally {
             FileUtil.deleteFile(tmpApk);
@@ -261,34 +176,25 @@
     }
 
     /**
-     * Test that {@link SuiteApkInstaller#getLocalPathForFilename(IBuildInfo, String, ITestDevice)}
-     * returns the apk file retrieved from remote artifacts.
+     * Test that {@link SuiteApkInstaller#getLocalPathForFilename(TestInformation, String)} returns
+     * the apk file retrieved from remote artifacts.
      */
     @Test
     public void testGetLocalPathForFileName_remoteZip() throws Exception {
-        mPreparer =
-                new SuiteApkInstaller() {
-                    @Override
-                    protected File getRootDir(IBuildInfo buildInfo) throws FileNotFoundException {
-                        return null;
-                    }
-                };
         IDeviceBuildInfo deviceBuildInfo = EasyMock.createMock(IDeviceBuildInfo.class);
         File tmpDir = null;
         try {
             tmpDir = FileUtil.createTempDir("test");
-            Mockito.doReturn(null).when(mMockBuildInfo).getFile("foo.apk");
-            EasyMock.expect(deviceBuildInfo.getTestsDir()).andReturn(tmpDir);
+            mTestInfo.executionFiles().put(FilesKey.TESTS_DIRECTORY, tmpDir);
             // Change the name so direct file search will return null.
             File tmpApk = FileUtil.createTempFile("suite-apk-installer-2", ".apk", tmpDir);
             EasyMock.expect(
                             deviceBuildInfo.stageRemoteFile(
                                     EasyMock.eq("suite-apk-installer.apk"), EasyMock.eq(tmpDir)))
                     .andReturn(tmpApk);
+            mTestInfo.getContext().addDeviceBuildInfo("device", deviceBuildInfo);
             EasyMock.replay(deviceBuildInfo);
-            File apk =
-                    mPreparer.getLocalPathForFilename(
-                            deviceBuildInfo, "suite-apk-installer.apk", mMockDevice);
+            File apk = mPreparer.getLocalPathForFilename(mTestInfo, "suite-apk-installer.apk");
             assertEquals(tmpApk.getAbsolutePath(), apk.getAbsolutePath());
             EasyMock.verify(deviceBuildInfo);
         } finally {
@@ -299,28 +205,12 @@
     /** If the file is found in the build shared resources directory, use it. */
     @Test
     public void testGetLocalPathForFileName_inDependenciesDir() throws Exception {
-        File tmpDir = FileUtil.createTempDir("suite-apk-installer");
-        File tmpApk = FileUtil.createTempFile("suite-apk-installer", ".apk", tmpDir);
-        mPreparer =
-                new SuiteApkInstaller() {
-                    @Override
-                    protected File getRootDir(IBuildInfo buildInfo) throws FileNotFoundException {
-                        return null;
-                    }
-
-                    @Override
-                    public TestInformation getTestInfo() {
-                        return TestInformation.newBuilder().setDependenciesFolder(tmpDir).build();
-                    }
-                };
+        File tmpApk = FileUtil.createTempFile("suite-apk-installer", ".apk", mTmpDepDir);
         try {
-            File apk =
-                    mPreparer.getLocalPathForFilename(
-                            mMockBuildInfo, tmpApk.getName(), mMockDevice);
+            File apk = mPreparer.getLocalPathForFilename(mTestInfo, tmpApk.getName());
             assertEquals(tmpApk.getAbsolutePath(), apk.getAbsolutePath());
         } finally {
             FileUtil.deleteFile(tmpApk);
-            FileUtil.recursiveDelete(tmpDir);
         }
     }
 }
diff --git a/tests/src/com/android/tradefed/testtype/FakeTestTest.java b/tests/src/com/android/tradefed/testtype/FakeTestTest.java
index abcf3f4..5fe20b5 100644
--- a/tests/src/com/android/tradefed/testtype/FakeTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/FakeTestTest.java
@@ -20,6 +20,7 @@
 import static org.junit.Assert.fail;
 
 import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.result.LogDataType;
@@ -43,6 +44,7 @@
     private FakeTest mTest = null;
     private ITestInvocationListener mListener = null;
     private OptionSetter mOption = null;
+    private TestInformation mTestInfo;
 
     @Rule public TemporaryFolder mTempFolder = new TemporaryFolder();
 
@@ -51,6 +53,7 @@
         mTest = new FakeTest();
         mOption = new OptionSetter(mTest);
         mListener = EasyMock.createStrictMock(ITestInvocationListener.class);
+        mTestInfo = TestInformation.newBuilder().build();
     }
 
     @Test
@@ -148,7 +151,7 @@
 
         EasyMock.replay(mListener);
         mOption.setOptionValue("run", name, "");
-        mTest.run(mListener);
+        mTest.run(mTestInfo, mListener);
         EasyMock.verify(mListener);
     }
 
@@ -161,7 +164,7 @@
 
         EasyMock.replay(mListener);
         mOption.setOptionValue("run", name, "P");
-        mTest.run(mListener);
+        mTest.run(mTestInfo, mListener);
         EasyMock.verify(mListener);
     }
 
@@ -174,7 +177,7 @@
 
         EasyMock.replay(mListener);
         mOption.setOptionValue("run", name, "F");
-        mTest.run(mListener);
+        mTest.run(mTestInfo, mListener);
         EasyMock.verify(mListener);
     }
 
@@ -192,7 +195,7 @@
 
         EasyMock.replay(mListener);
         mOption.setOptionValue("run", name, "PFPAI");
-        mTest.run(mListener);
+        mTest.run(mTestInfo, mListener);
         EasyMock.verify(mListener);
     }
 
@@ -221,7 +224,7 @@
         mOption.setOptionValue("test-log", testLog.getAbsolutePath());
         mOption.setOptionValue("test-run-log", testRunLog.getAbsolutePath());
         mOption.setOptionValue("test-invocation-log", invocationLog.getAbsolutePath());
-        mTest.run(mListener);
+        mTest.run(mTestInfo, mListener);
         EasyMock.verify(mListener);
     }
 
@@ -238,7 +241,7 @@
 
         EasyMock.replay(mListener);
         mOption.setOptionValue("run", name, "(PF)2");
-        mTest.run(mListener);
+        mTest.run(mTestInfo, mListener);
         EasyMock.verify(mListener);
     }
 
@@ -263,7 +266,7 @@
 
         EasyMock.replay(mListener);
         mOption.setOptionValue("run", name, "((PF)2)2");
-        mTest.run(mListener);
+        mTest.run(mTestInfo, mListener);
         EasyMock.verify(mListener);
     }
 
@@ -293,7 +296,7 @@
         mOption.setOptionValue("run", name1, "PF");
         mOption.setOptionValue("run", name2, "FP");
         mOption.setOptionValue("run", name3, "");
-        mTest.run(mListener);
+        mTest.run(mTestInfo, mListener);
         EasyMock.verify(mListener);
     }
 
diff --git a/tests/src/com/android/tradefed/testtype/NoisyDryRunTestTest.java b/tests/src/com/android/tradefed/testtype/NoisyDryRunTestTest.java
index 080b8f6..a277317 100644
--- a/tests/src/com/android/tradefed/testtype/NoisyDryRunTestTest.java
+++ b/tests/src/com/android/tradefed/testtype/NoisyDryRunTestTest.java
@@ -26,6 +26,7 @@
 
 import com.android.tradefed.config.GlobalConfiguration;
 import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.sandbox.SandboxConfigDump;
@@ -57,6 +58,7 @@
     private File mFile;
     private ITestInvocationListener mMockListener;
     private IRunUtil mMockRunUtil;
+    private TestInformation mTestInfo;
 
     @BeforeClass
     public static void setUpClass() throws Exception {
@@ -73,6 +75,7 @@
         mFile = FileUtil.createTempFile("NoisyDryRunTestTest", "tmpFile");
         mMockListener = EasyMock.createMock(ITestInvocationListener.class);
         mMockRunUtil = EasyMock.createMock(IRunUtil.class);
+        mTestInfo = TestInformation.newBuilder().build();
     }
 
     @After
@@ -101,7 +104,7 @@
         NoisyDryRunTest noisyDryRunTest = new NoisyDryRunTest();
         OptionSetter setter = new OptionSetter(noisyDryRunTest);
         setter.setOptionValue("cmdfile", mFile.getAbsolutePath());
-        noisyDryRunTest.run(mMockListener);
+        noisyDryRunTest.run(mTestInfo, mMockListener);
         verifyMocks();
     }
 
@@ -128,7 +131,7 @@
         NoisyDryRunTest noisyDryRunTest = new NoisyDryRunTest();
         OptionSetter setter = new OptionSetter(noisyDryRunTest);
         setter.setOptionValue("cmdfile", mFile.getAbsolutePath());
-        noisyDryRunTest.run(mMockListener);
+        noisyDryRunTest.run(mTestInfo, mMockListener);
         verifyMocks();
     }
 
@@ -145,7 +148,7 @@
         NoisyDryRunTest noisyDryRunTest = new NoisyDryRunTest();
         OptionSetter setter = new OptionSetter(noisyDryRunTest);
         setter.setOptionValue("cmdfile", mFile.getAbsolutePath());
-        noisyDryRunTest.run(mMockListener);
+        noisyDryRunTest.run(mTestInfo, mMockListener);
         verifyMocks();
     }
 
@@ -171,7 +174,7 @@
         NoisyDryRunTest noisyDryRunTest = new NoisyDryRunTest();
         OptionSetter setter = new OptionSetter(noisyDryRunTest);
         setter.setOptionValue("cmdfile", mFile.getAbsolutePath());
-        noisyDryRunTest.run(mMockListener);
+        noisyDryRunTest.run(mTestInfo, mMockListener);
         verifyMocks();
     }
 
@@ -304,7 +307,7 @@
                 };
         OptionSetter setter = new OptionSetter(noisyDryRunTest);
         setter.setOptionValue("cmdfile", mFile.getAbsolutePath());
-        noisyDryRunTest.run(mMockListener);
+        noisyDryRunTest.run(mTestInfo, mMockListener);
         verifyMocks();
     }
 
@@ -359,7 +362,7 @@
                 };
         OptionSetter setter = new OptionSetter(noisyDryRunTest);
         setter.setOptionValue("cmdfile", mFile.getAbsolutePath());
-        noisyDryRunTest.run(mMockListener);
+        noisyDryRunTest.run(mTestInfo, mMockListener);
         verifyMocks();
     }
 
diff --git a/tests/src/com/android/tradefed/testtype/TfTestLauncherTest.java b/tests/src/com/android/tradefed/testtype/TfTestLauncherTest.java
index cd2e913..8c1bad4 100644
--- a/tests/src/com/android/tradefed/testtype/TfTestLauncherTest.java
+++ b/tests/src/com/android/tradefed/testtype/TfTestLauncherTest.java
@@ -22,6 +22,9 @@
 import com.android.tradefed.config.GlobalConfiguration;
 import com.android.tradefed.config.IConfiguration;
 import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.InvocationContext;
+import com.android.tradefed.invoker.TestInformation;
 import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
 import com.android.tradefed.result.FileInputStreamSource;
 import com.android.tradefed.result.ITestInvocationListener;
@@ -61,6 +64,7 @@
     private IRunUtil mMockRunUtil;
     private IFolderBuildInfo mMockBuildInfo;
     private IConfiguration mMockConfig;
+    private TestInformation mTestInfo;
 
     @Before
     public void setUp() throws Exception {
@@ -75,6 +79,10 @@
         mTfTestLauncher.setEventStreaming(false);
         mTfTestLauncher.setConfiguration(mMockConfig);
 
+        IInvocationContext context = new InvocationContext();
+        context.addDeviceBuildInfo("device", mMockBuildInfo);
+        mTestInfo = TestInformation.newBuilder().setInvocationContext(context).build();
+
         EasyMock.expect(mMockConfig.getCommandOptions()).andStubReturn(new CommandOptions());
 
         OptionSetter setter = new OptionSetter(mTfTestLauncher);
@@ -82,7 +90,7 @@
         setter.setOptionValue("sub-global-config", SUB_GLOBAL_CONFIG);
     }
 
-    /** Test {@link TfTestLauncher#run(ITestInvocationListener)} */
+    /** Test {@link TfTestLauncher#run(TestInformation, ITestInvocationListener)} */
     @Test
     public void testRun() {
         CommandResult cr = new CommandResult(CommandStatus.SUCCESS);
@@ -154,7 +162,7 @@
                 EasyMock.anyLong(), EasyMock.<HashMap<String, Metric>>anyObject());
 
         EasyMock.replay(mMockBuildInfo, mMockRunUtil, mMockListener, mMockConfig);
-        mTfTestLauncher.run(mMockListener);
+        mTfTestLauncher.run(mTestInfo, mMockListener);
         EasyMock.verify(mMockBuildInfo, mMockRunUtil, mMockListener, mMockConfig);
     }
 
diff --git a/tests/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4TestTest.java b/tests/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4TestTest.java
index fa6d0a5..244b052 100644
--- a/tests/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4TestTest.java
+++ b/tests/src/com/android/tradefed/testtype/junit4/BaseHostJUnit4TestTest.java
@@ -29,6 +29,7 @@
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.StubDevice;
+import com.android.tradefed.invoker.ExecutionFiles.FilesKey;
 import com.android.tradefed.invoker.IInvocationContext;
 import com.android.tradefed.invoker.InvocationContext;
 import com.android.tradefed.invoker.TestInformation;
@@ -58,7 +59,6 @@
 import java.io.File;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.Map;
 
 /** Unit tests for {@link BaseHostJUnit4Test}. */
 @RunWith(JUnit4.class)
@@ -394,6 +394,7 @@
     @Test
     public void testInstallUninstall() throws Exception {
         File fakeTestsDir = FileUtil.createTempDir("fake-base-host-dir");
+        mTestInfo.executionFiles().put(FilesKey.TESTS_DIRECTORY, fakeTestsDir);
         try {
             File apk = new File(fakeTestsDir, "apkFileName");
             apk.createNewFile();
@@ -408,10 +409,6 @@
             TestDescription description =
                     new TestDescription(InstallApkHostJUnit4Test.class.getName(), "testInstall");
             mMockListener.testStarted(description);
-            Map<String, String> properties = new HashMap<>();
-            properties.put("ROOT_DIR", fakeTestsDir.getAbsolutePath());
-            EasyMock.expect(mMockBuild.getFile("apkFileName")).andReturn(null);
-            EasyMock.expect(mMockBuild.getBuildAttributes()).andReturn(properties).times(2);
             EasyMock.expect(mMockDevice.getDeviceDescriptor()).andReturn(null);
 
             EasyMock.expect(mMockDevice.installPackage(apk, true)).andReturn(null);