Merge Android 12 QPR 3

Bug: 236045730
Merged-In: I3f8d9e7075b084890c5c4dda409df3e8c6034d23
Change-Id: Ifffe05f36e31b03833d4e070819dbb697b251fca
diff --git a/report/src/com/android/catbox/result/ResultReportCollector.java b/report/src/com/android/catbox/result/ResultReportCollector.java
new file mode 100644
index 0000000..85517b7
--- /dev/null
+++ b/report/src/com/android/catbox/result/ResultReportCollector.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.catbox.result;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.util.CollectorUtil;
+
+import com.android.ddmlib.Log.LogLevel;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+import com.android.tradefed.log.LogUtil.CLog;
+
+import com.android.tradefed.targetprep.BuildError;
+import com.android.tradefed.targetprep.ITargetPreparer;
+import com.android.tradefed.targetprep.TargetSetupError;
+
+import com.android.tradefed.util.CommandResult;
+import com.android.tradefed.util.CommandStatus;
+
+import com.google.common.base.Strings;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * ResultReportCollector is an {@link ITargetPreparer} that pulls the test result reports from the
+ * device and adds it to the Results.
+ */
+@OptionClass(alias = "result-report-collector")
+public class ResultReportCollector implements ITargetPreparer {
+    @Option(
+            name = "pull-file-content-uri",
+            description = "Copy the src files from device to destination using content uri")
+    private Map<String, String> mFileContentUriMap = new HashMap<String, String>();
+
+    @Option(
+            name = "pull-file-path",
+            description = "Copy the src files from device to destination using file path")
+    private Map<String, String> mFilePathMap = new HashMap<String, String>();
+
+    @Option(
+            name = "pull-dir-path",
+            description = "Copy the src directory from device to destination using directory path")
+    private Map<String, String> mDirPathMap = new HashMap<String, String>();
+
+    private static final String NO_RESULTS_STRING = "No result found.";
+    private static final String ERROR_MESSAGE_TAG = "[ERROR]";
+
+    @Override
+    public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
+            BuildError, DeviceNotAvailableException {
+        // Nothing To Do
+    }
+
+    @Override
+    public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
+            throws DeviceNotAvailableException {
+        // Pull files from device using Content URI
+        pullFilesUsingContentUri(device, buildInfo, mFileContentUriMap);
+
+        // Pull files from device using File Paths
+        pullFilesUsingFilePaths(device, buildInfo, mFilePathMap);
+
+        // Pull directories from device using Directory Paths
+        pullDirUsingFilePaths(device, buildInfo, mDirPathMap);
+    }
+
+    /** Pull files from the device using Content URI */
+    private void pullFilesUsingContentUri(ITestDevice device, IBuildInfo buildInfo,
+            Map<String, String> contentUriMap) throws DeviceNotAvailableException {
+        if (contentUriMap.isEmpty()) {
+            // No Content URI provided
+            return;
+        }
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                "Started pulling file using Content URI.");
+
+        // Iterate over given Content URIs
+        for (Map.Entry<String, String> entry: contentUriMap.entrySet()) {
+            // Pull file using Content URI
+            pullFileUsingContentUri(device, buildInfo, entry.getKey(), entry.getValue());
+        }
+
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                "Completed pulling file using Content URI.");
+    }
+
+    private void pullFileUsingContentUri(ITestDevice device, IBuildInfo buildInfo,
+            String fileContentUri, String destFilePath) throws DeviceNotAvailableException {
+        // Validate Source and Destination File Path
+        if (Strings.isNullOrEmpty(fileContentUri) || Strings.isNullOrEmpty(destFilePath) ||
+                !fileContentUri.startsWith("content://")) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Either Src or Dest Path is invalid. Source: %s, Destination: %s",
+                            fileContentUri, destFilePath));
+            return;
+        }
+
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                String.format("Started pulling file. Source File: %s, Destination File: %s",
+                        fileContentUri, destFilePath));
+
+        // Check if file exist on the device
+        if (!doesFileExist(device, fileContentUri)) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("File %s does not exist on the device.",
+                            fileContentUri));
+            return;
+        }
+
+        // Get Destination File using filepath
+        File destinationFile = getDestinationFile(buildInfo, destFilePath);
+
+        if (destinationFile == null) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Unable to get destination file path for %s.",
+                            destFilePath));
+            return;
+        }
+
+        // Create the result directory if it does not exist
+        // isDestFile=true : Since destination is a file
+        if (!createResultDir(buildInfo, destinationFile, true /* isDestFile */)) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Unable to create results directory %s.",
+                            destFilePath));
+            return;
+        }
+
+        // Create Pull Command
+        String pullCommand =
+                String.format("content read --user %d --uri %s",
+                        device.getCurrentUser(),
+                        fileContentUri);
+
+        // Open the output stream to the local file.
+        // try-with-resource should close the stream once the try block completes
+        // so we don't need a finally block to close the stream
+        try (OutputStream localFileStream = new FileOutputStream(destinationFile)) {
+            CommandResult pullResult = device.executeShellV2Command(pullCommand,
+                    localFileStream);
+            if (!isSuccessful(pullResult)) {
+                String stderr = pullResult.getStderr();
+                CLog.logAndDisplay(
+                        LogLevel.ERROR,
+                        String.format(
+                                "Failed to pull a file at '%s' to %s. Error: '%s'",
+                                fileContentUri, destFilePath, stderr));
+            }
+        } catch(IOException ex) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Failed to open OutputStream to the local file %s. Error: %s",
+                            destFilePath, ex.getMessage()));
+            return;
+        }
+
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                String.format("Completed pulling file. Source File: %s, Destination File: %s",
+                        fileContentUri, destFilePath));
+    }
+
+    /** Verify if file exists  */
+    private boolean doesFileExist(ITestDevice device, String contentUri)
+            throws DeviceNotAvailableException {
+        String queryContentCommand =
+                String.format(
+                        "content query --user %d --uri %s", device.getCurrentUser(), contentUri);
+
+        String listCommandResult = device.executeShellCommand(queryContentCommand);
+
+        return (!NO_RESULTS_STRING.equals(listCommandResult.trim()));
+    }
+
+    /** Verify Command Result for success  */
+    private boolean isSuccessful(CommandResult result) {
+        if (!CommandStatus.SUCCESS.equals(result.getStatus())) {
+            return false;
+        }
+        String stdout = result.getStdout();
+        if (stdout.contains(ERROR_MESSAGE_TAG)) {
+            return false;
+        }
+        return Strings.isNullOrEmpty(result.getStderr());
+    }
+
+    /** Get Destination File using file path */
+    private File getDestinationFile(IBuildInfo buildInfo, String destPath) {
+        File destinationFile = null;
+        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(buildInfo);
+        try {
+            // get results directory
+            destinationFile = buildHelper.getResultDir();
+            // get destination file using path
+            destinationFile = new File(destinationFile, destPath);
+        } catch (IOException ex) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Unable to get destination file %s: Error: %s.",
+                            destPath, ex.getMessage()));
+            return null;
+        }
+        return destinationFile;
+    }
+
+    /** Create directory for results */
+    private boolean createResultDir(IBuildInfo buildInfo, File destination, boolean isDestFile) {
+        File resultDir = destination;
+        if (isDestFile) {
+            // if filepath, get the parent for creating the directory
+            resultDir = destination.getParentFile();
+        }
+        if (!resultDir.exists() && !resultDir.mkdirs()) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Unable to create %s directory", resultDir.getAbsolutePath()));
+            return false;
+        }
+        if (!resultDir.isDirectory()) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("%s is not a directory", resultDir.getAbsolutePath()));
+            return false;
+        }
+        return true;
+    }
+
+    /** Pull files from the device using File Path */
+    private void pullFilesUsingFilePaths(ITestDevice device, IBuildInfo buildInfo,
+            Map<String, String> filePathMap) throws DeviceNotAvailableException {
+        if (filePathMap.isEmpty()) {
+            // No File Paths provided
+            return;
+        }
+
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                "Started pulling file using file path.");
+
+        // Iterate over given Paths
+        for (Map.Entry<String, String> entry: filePathMap.entrySet()) {
+            // Pull file using File Path
+            pullFileUsingFilePath(device, buildInfo, entry.getKey(), entry.getValue());
+        }
+
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                "Completed pulling file using file path.");
+    }
+
+    /** Pull file using File Path */
+    private void pullFileUsingFilePath(ITestDevice device, IBuildInfo buildInfo, String filePath,
+            String destPath) throws DeviceNotAvailableException {
+        // Validate Source and Destination File Path
+        if (Strings.isNullOrEmpty(filePath) || Strings.isNullOrEmpty(destPath)) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Either Src or Dest Path is invalid. Source: %s, Destination: %s",
+                            filePath, destPath));
+            return;
+        }
+
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                String.format("Started pulling file. Source File: %s, Destination File: %s",
+                        filePath, destPath));
+
+        // Check if file exist on the device
+        if (!device.doesFileExist(filePath)) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("File %s does not exist on the device.",
+                            filePath));
+            return;
+        }
+
+        // Get Destination File using filepath
+        File destinationFile = getDestinationFile(buildInfo, destPath);
+
+        if (destinationFile == null) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Unable to get destination file path for %s.",
+                            destPath));
+            return;
+        }
+
+        // Create the result directory if it does not exist
+        // isDestFile=true : Since destination is a file
+        if (!createResultDir(buildInfo, destinationFile, true /* isDestFile */)) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Unable to create results directory %s.",
+                            destPath));
+            return;
+        }
+
+        // Pull File
+        boolean isSuccess = device.pullFile(filePath, destinationFile);
+
+        if (!isSuccess) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Failed to pull the file. Source File: %s, Destination File: %s",
+                            filePath, destPath));
+            return;
+        }
+
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                String.format("Completed pulling file. Source File: %s, Destination File: %s",
+                        filePath, destPath));
+    }
+
+    /** Pull directories from the device using File Path */
+    private void pullDirUsingFilePaths(ITestDevice device, IBuildInfo buildInfo,
+            Map<String, String> dirPathMap) throws DeviceNotAvailableException {
+        if (dirPathMap.isEmpty())  {
+            // No Dir Paths provided
+            return;
+        }
+
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                "Started pulling directory using file path.");
+
+        // Iterate over given Paths
+        for (Map.Entry<String, String> entry: dirPathMap.entrySet()) {
+            // Pull directory using Dir Path
+            pullDirectoryUsingDirPath(device, buildInfo, entry.getKey(), entry.getValue());
+        }
+
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                "Completed pulling directory using file path.");
+    }
+
+    /** Pull Dir using Dir Path */
+    private void pullDirectoryUsingDirPath(ITestDevice device, IBuildInfo buildInfo, String dirPath,
+            String destPath) throws DeviceNotAvailableException {
+        // Validate Source and Destination File Path
+        if (Strings.isNullOrEmpty(dirPath) || Strings.isNullOrEmpty(destPath)) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Either Src or Dest Path is invalid. Source: %s, Destination: %s",
+                            dirPath, destPath));
+            return;
+        }
+
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                String.format("Started pulling directory. Source File: %s, Destination File: %s",
+                        dirPath, destPath));
+
+        // Check if directory exist on the device
+        if (!device.doesFileExist(dirPath)) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("File %s does not exist on the device.",
+                            dirPath));
+            return;
+        }
+
+        // Get Destination Dir using filepath
+        File destinationDir = getDestinationFile(buildInfo, destPath);
+
+        if (destinationDir == null) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Unable to get destination file path for %s.",
+                            destPath));
+            return;
+        }
+
+        // Create the result directory if it does not exist
+        // isDestFile=false : Since destination is a directory
+        if (!createResultDir(buildInfo, destinationDir, false /* isDestFile */)) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Unable to create results directory %s.",
+                            destPath));
+            return;
+        }
+
+        // Pull Dir
+        boolean isSuccess = device.pullDir(dirPath, destinationDir);
+
+        if (!isSuccess) {
+            CLog.logAndDisplay(
+                    LogLevel.ERROR,
+                    String.format("Failed to pull directory. Source Dir: %s, Destination File: %s",
+                            dirPath, destPath));
+            return;
+        }
+
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                String.format("Completed pulling directory. Source File: %s, Destination File: %s",
+                        dirPath, destPath));
+    }
+}
diff --git a/target_preparers/Android.bp b/target_preparers/Android.bp
new file mode 100644
index 0000000..5c99a74
--- /dev/null
+++ b/target_preparers/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library_host {
+    name: "catbox-preparer-lib",
+    srcs: ["src/**/*.java"],
+    libs: [
+        "tradefed",
+        "loganalysis",
+        "tools-common-prebuilt",
+        "compatibility-tradefed",
+        "compatibility-host-util",
+    ]
+}
\ No newline at end of file
diff --git a/target_preparers/src/com/android/catbox/targetpreparer/SetDevicePropertyPreparer.java b/target_preparers/src/com/android/catbox/targetpreparer/SetDevicePropertyPreparer.java
new file mode 100644
index 0000000..ffe15ba
--- /dev/null
+++ b/target_preparers/src/com/android/catbox/targetpreparer/SetDevicePropertyPreparer.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.catbox.targetpreparer;
+
+import com.android.ddmlib.Log.LogLevel;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionClass;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+import com.android.tradefed.log.LogUtil.CLog;
+
+import com.android.tradefed.targetprep.BuildError;
+import com.android.tradefed.targetprep.ITargetPreparer;
+import com.android.tradefed.targetprep.TargetSetupError;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * SetPropPreparer is an {@link ITargetPreparer} that sets properties on the device
+ */
+@OptionClass(alias = "set-device-property")
+public class SetDevicePropertyPreparer implements ITargetPreparer {
+
+    /* Allow user to pass in property and value to be set */
+    @Option(name = "set-property", description = "Map of properties and values to set and restored")
+    private Map<String, String> setPropertyMap = new HashMap<String, String>();
+
+    /* Assign the property value to class variable */
+    private static Map<String, String> sharedPropValues = new HashMap<String, String>();
+    private final String EMPTY_STRING = "''";
+
+    @Override
+    public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
+            BuildError, DeviceNotAvailableException {
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                "Starting set property target preparer"
+        );
+
+        /* If no properties or values supplied, return immediately */
+        if (setPropertyMap.isEmpty()) {
+            CLog.logAndDisplay(
+                    LogLevel.INFO,
+                    "No properties to set. Skipping target preparer"
+            );
+            return;
+        }
+
+        for (Map.Entry<String, String> entry : setPropertyMap.entrySet()) {
+
+            /* Get the existing values for the properties */
+            sharedPropValues.put(entry.getKey(),
+                    device.executeShellCommand(String.format("getprop %s", entry.getKey())).trim());
+
+            /* Set the property to the value provided */
+            device.executeShellCommand(
+                    String.format("setprop %s %s", entry.getKey(), entry.getValue())
+            );
+        }
+    }
+
+    @Override
+    public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
+            throws DeviceNotAvailableException {
+        CLog.logAndDisplay(
+                LogLevel.INFO,
+                "Starting Device Property teardown - resetting device properties"
+        );
+
+        /* Reset the properties back to their original values */
+        for (Map.Entry<String, String> entry : sharedPropValues.entrySet()) {
+            CLog.logAndDisplay(
+                    LogLevel.DEBUG,
+                    String.format("Resetting prop %s to %s", entry.getKey(), entry.getValue())
+            );
+
+            /* If property did not exist before, set to empty string */
+            if (!entry.getValue().isEmpty()) {
+                device.executeShellCommand(
+                        String.format("setprop %s %s", entry.getKey(), entry.getValue())
+                );
+            } else {
+                device.executeShellCommand(
+                        String.format("setprop %s %s", entry.getKey(), EMPTY_STRING)
+                );
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tools/catbox-common/res/config/catbox-common.xml b/tools/catbox-common/res/config/catbox-common.xml
index 51a9cee..3c3db25 100644
--- a/tools/catbox-common/res/config/catbox-common.xml
+++ b/tools/catbox-common/res/config/catbox-common.xml
@@ -35,6 +35,9 @@
   <!-- Template for Metadata Reporters -->
   <template-include name="metadata-reporters" default="empty" />
 
+  <!-- Run As Root -->
+  <option name="enable-root" value="true" />
+
   <!-- Default ABI -->
   <option name="compatibility:primary-abi-only" value="true" />
 
diff --git a/tools/catbox-common/res/config/catbox-performance-base.xml b/tools/catbox-common/res/config/catbox-performance-base.xml
index 17bd214..6109552 100644
--- a/tools/catbox-common/res/config/catbox-performance-base.xml
+++ b/tools/catbox-common/res/config/catbox-performance-base.xml
@@ -31,7 +31,6 @@
   <option name="test-tag-suffix" value="performance" />
 
   <!-- Performance Specific Target Preparers -->
-  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer" />
   <target_preparer class="com.android.tradefed.targetprep.PerfettoPreparer" />
   <target_preparer class="com.android.tradefed.targetprep.TimeWaster" />
   <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ReportLogCollector">
diff --git a/tools/catbox-common/res/config/catbox-preparer.xml b/tools/catbox-common/res/config/catbox-preparer.xml
index 62c7c23..db9aaf7 100644
--- a/tools/catbox-common/res/config/catbox-preparer.xml
+++ b/tools/catbox-common/res/config/catbox-preparer.xml
@@ -14,14 +14,6 @@
      limitations under the License.
 -->
 <configuration description="CATBOX Target Preparer Configuration">
-  <!-- Target Preparers - Add System User and Disable By Default -->
-  <target_preparer class="com.android.tradefed.targetprep.RunOnSystemUserTargetPreparer">
-    <option name="disable" value="true" />
-  </target_preparer>
-
-  <!-- Target Preparers - Disable SELinux before Test Execution -->
-  <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" />
-
   <!-- Target Preparers - Clean the Device after Test Execution -->
   <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
     <option name="disable" value="true" />
@@ -42,14 +34,50 @@
     <option name="set-secure-setting" key="location_mode" value="1" />
   </target_preparer>
 
+  <!-- Target Preparers - ADB Root -->
+  <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+  <!-- Target Preparers - Add System User and Disable By Default -->
+  <target_preparer class="com.android.tradefed.targetprep.RunOnSystemUserTargetPreparer">
+    <option name="disable" value="true" />
+  </target_preparer>
+
+  <!-- Target Preparers - Disable SELinux before Test Execution -->
+  <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" />
+
   <!-- Target Preparers - Install Apps -->
   <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup" >
     <option name="cleanup-apks" value="true" />
+    <option name="install-arg" value="-d" />
+    <option name="install-arg" value="-r" />
+    <option name="install-arg" value="-g" />
   </target_preparer>
 
+  <!-- Target Preparers - Install Apps from Suite Testcases directory -->
+  <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller" >
+    <option name="cleanup-apks" value="true" />
+    <option name="install-arg" value="-d" />
+    <option name="install-arg" value="-r" />
+    <option name="install-arg" value="-g" />
+  </target_preparer>
+
+  <!-- Target Preparers - To Push Files On The Device -->
+  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer" />
+
+  <!-- Target Preparers - To Push Files On The Device from Suite Testcases directory -->
+  <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher" />
+
   <!-- Target Preparers - Run Shell Commands -->
   <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer" />
 
+  <!-- Target Preparers - Set Properties -->
+  <target_preparer class="com.android.catbox.targetpreparer.SetDevicePropertyPreparer" />
+
+  <!-- Target Preparers - WiFi Preparer -->
+  <target_preparer class="com.android.tradefed.targetprep.WifiPreparer" >
+    <option name="skip" value="true" />
+  </target_preparer>
+
   <!-- Template for Account Log In -->
   <template-include name="account-preparer" default="empty" />
 </configuration>
diff --git a/tools/catbox-tradefed/Android.bp b/tools/catbox-tradefed/Android.bp
index 3fc7e80..493e19c 100644
--- a/tools/catbox-tradefed/Android.bp
+++ b/tools/catbox-tradefed/Android.bp
@@ -16,6 +16,19 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+java_library_host {
+    name: "catbox-tradefed-lib",
+    java_resource_dirs: ["res"],
+    static_libs: [
+        "cts-tradefed-harness",
+        "catbox-common",
+        "catbox-preparer-lib",
+        "catbox-report-lib"
+    ],
+    required: ["compatibility-host-util"],
+}
+
+
 tradefed_binary_host {
     name: "catbox-tradefed",
     java_resource_dirs: ["res"],
@@ -23,6 +36,13 @@
     short_name: "CATBOX",
     full_name: "Complete Automotive Test in a Box",
     version: "1.0",
-    static_libs: ["cts-tradefed-harness", "catbox-common"],
-    required: ["compatibility-host-util", "catbox-report-lib"],
+    static_libs: [
+        "cts-tradefed-harness",
+        "catbox-common",
+        "catbox-preparer-lib"
+    ],
+    required: [
+        "compatibility-host-util",
+        "catbox-report-lib"
+    ],
 }
diff --git a/tools/catbox-tradefed/res/config/catbox-functional-bluetooth-audio.xml b/tools/catbox-tradefed/res/config/catbox-functional-bluetooth-audio.xml
index cd2d0d1..bc9f879 100644
--- a/tools/catbox-tradefed/res/config/catbox-functional-bluetooth-audio.xml
+++ b/tools/catbox-tradefed/res/config/catbox-functional-bluetooth-audio.xml
@@ -29,5 +29,5 @@
   <option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:no-rerun:true" />
 
   <!-- Tests -->
-  <option name="compatibility:include-filter" value="AndroidAutomotiveMediaCenterTests" />
+  <option name="compatibility:include-filter" value="AndroidAutomotiveMediaCenterTests android.platform.tests.BluetoothAudioTest" />
 </configuration>
\ No newline at end of file