Adding two prerequisite checks to CTS and GTS tests

1) Bluetooth:

Affected packages:
CtsBluetoothTestCases
CtsStatsdHostTestCases
GtsGmscoreHostTestCases

2) Region check:

Affected packages:
CtsWebkitTestCases
CtsContentTestCases
CtsAppSecurityHostTestCases
CtsThemeHostTestCases

TEST=None
BUG=b:131440102

Change-Id: I9b4aad2df9dd649621223e1bcc78c85983e23b1a
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1999482
Reviewed-by: Kazuhiro Inaba <kinaba@chromium.org>
Commit-Queue: Ian Lee <ianrlee@google.com>
Tested-by: Ian Lee <ianrlee@google.com>
diff --git a/server/cros/tradefed/generate_controlfiles_CTS_P.py b/server/cros/tradefed/generate_controlfiles_CTS_P.py
index 7b6ba00..b456b81 100755
--- a/server/cros/tradefed/generate_controlfiles_CTS_P.py
+++ b/server/cros/tradefed/generate_controlfiles_CTS_P.py
@@ -603,6 +603,18 @@
     'CtsThemeHostTestCases': ["/tmp/diff_*.png"],
 }
 
+_PREREQUISITE_BLUETOOTH = 'bluetooth'
+_PREREQUISITE_REGION_US = 'region_us'
+
+CONFIG['PREREQUISITES'] = {
+    'CtsBluetoothTestCases': [_PREREQUISITE_BLUETOOTH],
+    'CtsStatsdHostTestCases': [_PREREQUISITE_BLUETOOTH],
+    'CtsWebkitTestCases': [_PREREQUISITE_REGION_US],
+    'CtsContentTestCases': [_PREREQUISITE_REGION_US],
+    'CtsAppSecurityTestCases': [_PREREQUISITE_REGION_US],
+    'CtsThemeHostTestCases': [_PREREQUISITE_REGION_US],
+}
+
 from generate_controlfiles_common import main
 
 if __name__ == '__main__':
diff --git a/server/cros/tradefed/generate_controlfiles_GTS.py b/server/cros/tradefed/generate_controlfiles_GTS.py
index bb1e422..6142d50 100755
--- a/server/cros/tradefed/generate_controlfiles_GTS.py
+++ b/server/cros/tradefed/generate_controlfiles_GTS.py
@@ -146,6 +146,9 @@
 }
 CONFIG['EXTRA_ARTIFACTS'] = {}
 
+CONFIG['PREREQUISITES'] = {
+    'GtsGmscoreHostTestCases': ['bluetooth'],
+}
 
 from generate_controlfiles_common import main
 
diff --git a/server/cros/tradefed/generate_controlfiles_common.py b/server/cros/tradefed/generate_controlfiles_common.py
index 4252d16..7446817 100755
--- a/server/cros/tradefed/generate_controlfiles_common.py
+++ b/server/cros/tradefed/generate_controlfiles_common.py
@@ -435,6 +435,7 @@
     extra_args = set()
     preconditions = []
     login_preconditions = []
+    prerequisites = []
     for module in modules:
         if is_public:
             extra_args.add('warn_on_test_retry=False')
@@ -444,6 +445,8 @@
             preconditions.extend(CONFIG['PRECONDITION'].get(module, []))
             login_preconditions.extend(
                 CONFIG['LOGIN_PRECONDITION'].get(module, []))
+        prerequisites.extend(CONFIG['PREREQUISITES'].get(module,[]))
+
     # Notice: we are just squishing the preconditions for all modules together
     # with duplicated command removed. This may not always be correct.
     # In such a case one should split the bookmarks in a way that the modules
@@ -459,6 +462,9 @@
     if login_preconditions:
         extra_args.add('login_precondition_commands=[%s]' % ', '.join(
             deduped(login_preconditions)))
+    if prerequisites:
+        extra_args.add("prerequisites=['%s']" % "', '".join(
+            deduped(prerequisites)))
     return sorted(list(extra_args))
 
 
diff --git a/server/cros/tradefed/tradefed_prerequisite.py b/server/cros/tradefed/tradefed_prerequisite.py
new file mode 100644
index 0000000..5c32a60
--- /dev/null
+++ b/server/cros/tradefed/tradefed_prerequisite.py
@@ -0,0 +1,46 @@
+# Copyright 2019 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+
+def bluetooth(hosts):
+    """Check for missing bluetooth hardware.
+    """
+    for host in hosts:
+        output = host.run('hcitool dev').stdout
+        lines = output.splitlines()
+        if len(lines) < 2 or not lines[0].startswith('Devices:'):
+            return False, 'Failed: Bluetooth device is missing.'\
+                          'Stdout of the command "hcitool dev1"'\
+                          'on host %s was %s' % (host, output)
+    return True, ''
+
+
+def region_us(hosts):
+    """Check that region is set to "us".
+    """
+    for host in hosts:
+        output = host.run('vpd -g region').stdout
+        if out != 'us':
+            return False, 'Failed: Region is not "us".'\
+                          'Stdout of the command "vpd -l'\
+                          '| grep region" on host %s was %s'\
+                          % (host, output)
+    return True, ''
+
+prerequisite_map = {
+    'bluetooth': bluetooth,
+    'region_us': region_us,
+}
+
+def check(prereq, hosts):
+    """Execute the prerequisite check.
+
+    @return boolean indicating if check passes.
+    @return string error message if check fails.
+    """
+    if prereq not in prerequisite_map:
+        logging.info('%s is not a valid prerequisite.', prereq)
+        return True, ''
+    return prerequisite_map[prereq](hosts)
diff --git a/server/cros/tradefed/tradefed_test.py b/server/cros/tradefed/tradefed_test.py
index c8d3ced..3becc9b 100644
--- a/server/cros/tradefed/tradefed_test.py
+++ b/server/cros/tradefed/tradefed_test.py
@@ -37,6 +37,7 @@
 from autotest_lib.server.cros.tradefed import tradefed_chromelogin as login
 from autotest_lib.server.cros.tradefed import tradefed_constants as constants
 from autotest_lib.server.cros.tradefed import tradefed_utils
+from autotest_lib.server.cros.tradefed import tradefed_prerequisite
 
 # TODO(kinaba): Move to tradefed_utils together with the setup/cleanup methods.
 MediaAsset = namedtuple('MediaAssetInfo', ['uri', 'localpath'])
@@ -1112,12 +1113,18 @@
                                    extra_artifacts_host=[],
                                    cts_uri=None,
                                    login_precondition_commands=[],
-                                   precondition_commands=[]):
+                                   precondition_commands=[],
+                                   prerequisites=[]):
         """Run CTS/GTS with retry logic.
 
         We first kick off the specified module. Then rerun just the failures
         on the next MAX_RETRY iterations.
         """
+        for prereq in prerequisites:
+            result = tradefed_prerequisite.check(prereq, self._hosts)
+            if not result[0]:
+                raise error.TestError(result[1])
+
         # On dev and beta channels timeouts are sharp, lenient on stable.
         self._timeout = timeout
         if (self._get_release_branch_number() >=
diff --git a/server/site_tests/cheets_CTS_P/cheets_CTS_P.py b/server/site_tests/cheets_CTS_P/cheets_CTS_P.py
index 1464728..7644e37 100644
--- a/server/site_tests/cheets_CTS_P/cheets_CTS_P.py
+++ b/server/site_tests/cheets_CTS_P/cheets_CTS_P.py
@@ -78,23 +78,24 @@
 
     def initialize_camerabox(self, camera_facing, cmdline_args):
         """Configure DUT and chart running in camerabox environment.
+
         @param camera_facing: the facing of the DUT used in testing
                               (e.g. 'front', 'back').
         """
         chart_address = camerabox_utils.get_chart_address(
-                [h.hostname for h in self._hosts], cmdline_args)
+            [h.hostname for h in self._hosts], cmdline_args)
         if chart_address is None:
             raise error.TestFail(
-                    'Error: missing option --args="chart=<CHART IP>"')
+                'Error: missing option --args="chart=<CHART IP>"')
         chart_hosts = [hosts.create_host(ip) for ip in chart_address]
 
         self.chart_fixtures = [
-                camerabox_utils.ChartFixture(h, self._SCENE_URI)
-                for h in chart_hosts
+            camerabox_utils.ChartFixture(h, self._SCENE_URI)
+            for h in chart_hosts
         ]
         self.dut_fixtures = [
-                camerabox_utils.DUTFixture(self, h, camera_facing)
-                for h in self._hosts
+            camerabox_utils.DUTFixture(self, h, camera_facing)
+            for h in self._hosts
         ]
 
         for chart in self.chart_fixtures:
@@ -119,15 +120,15 @@
                    cmdline_args=None,
                    hard_reboot_on_failure=False):
         super(cheets_CTS_P, self).initialize(
-                bundle=bundle,
-                uri=uri,
-                host=host,
-                hosts=hosts,
-                max_retry=max_retry,
-                load_waivers=load_waivers,
-                retry_manual_tests=retry_manual_tests,
-                warn_on_test_retry=warn_on_test_retry,
-                hard_reboot_on_failure=hard_reboot_on_failure)
+            bundle=bundle,
+            uri=uri,
+            host=host,
+            hosts=hosts,
+            max_retry=max_retry,
+            load_waivers=load_waivers,
+            retry_manual_tests=retry_manual_tests,
+            warn_on_test_retry=warn_on_test_retry,
+            hard_reboot_on_failure=hard_reboot_on_failure)
         if camera_facing:
             self.initialize_camerabox(camera_facing, cmdline_args)
 
@@ -147,6 +148,7 @@
                  extra_artifacts_host=[],
                  precondition_commands=[],
                  login_precondition_commands=[],
+                 prerequisites=[],
                  timeout=_CTS_TIMEOUT_SECONDS):
         """Runs the specified CTS once, but with several retries.
 
@@ -171,6 +173,7 @@
         dut before the test is run, the scripts must already be installed.
         @param login_precondition_commands: a list of scripts to be run on the
         dut before the log-in for the test is performed.
+        @param prerequisites: a list of prerequisites that identify rogue DUTs.
         @param timeout: time after which tradefed can be interrupted.
         """
         self._run_tradefed_with_retries(
@@ -190,10 +193,12 @@
             extra_artifacts_host=extra_artifacts_host,
             cts_uri=_CTS_URI,
             login_precondition_commands=login_precondition_commands,
-            precondition_commands=precondition_commands)
+            precondition_commands=precondition_commands,
+            prerequisites=prerequisites)
 
     def cleanup_camerabox(self):
         """Cleanup configuration on DUT and chart tablet for running in
+
         camerabox environment.
         """
         for dut in self.dut_fixtures:
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsBluetoothTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsBluetoothTestCases
index fac6fac..3476a03 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsBluetoothTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsBluetoothTestCases
@@ -41,6 +41,7 @@
         target_plan=None,
         bundle='arm',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-arm.zip',
+        prerequisites=['bluetooth'],
         hard_reboot_on_failure=True,
         timeout=3600)
 
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsContentTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsContentTestCases
index 8d6aa6e..a295544 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsContentTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsContentTestCases
@@ -29,6 +29,7 @@
         target_plan=None,
         bundle='arm',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-arm.zip',
+        prerequisites=['region_us'],
         timeout=3600)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsStatsdHostTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsStatsdHostTestCases
index 2526bfa..43e1c24 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsStatsdHostTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsStatsdHostTestCases
@@ -28,6 +28,7 @@
         target_plan=None,
         bundle='arm',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-arm.zip',
+        prerequisites=['bluetooth'],
         timeout=3600)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsTheme b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsTheme
index a2c6ad6..d8759bc 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsTheme
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsTheme
@@ -30,6 +30,7 @@
         bundle='arm',
         extra_artifacts_host=['/tmp/diff_*.png'],
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-arm.zip',
+        prerequisites=['region_us'],
         timeout=5400)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsWebkitTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsWebkitTestCases
index d061a64..464a726 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsWebkitTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.CtsWebkitTestCases
@@ -28,6 +28,7 @@
         target_plan=None,
         bundle='arm',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-arm.zip',
+        prerequisites=['region_us'],
         timeout=3600)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsAdminPackageInstallerTestCases_-_CtsDebugTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsAdminPackageInstallerTestCases_-_CtsDebugTestCases
index 8e358be..a6b31d8 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsAdminPackageInstallerTestCases_-_CtsDebugTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsAdminPackageInstallerTestCases_-_CtsDebugTestCases
@@ -31,6 +31,7 @@
         bundle='arm',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-arm.zip',
         login_precondition_commands=['lsblk -do NAME,RM | sed -n s/1$//p | xargs -n1 eject'],
+        prerequisites=['region_us', 'bluetooth'],
         timeout=172800)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsShortcutHostTestCases_-_CtsVideoTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsShortcutHostTestCases_-_CtsVideoTestCases
index e9fdab7..38a0bdb 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsShortcutHostTestCases_-_CtsVideoTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsShortcutHostTestCases_-_CtsVideoTestCases
@@ -30,6 +30,7 @@
         bundle='arm',
         extra_artifacts_host=['/tmp/diff_*.png'],
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-arm.zip',
+        prerequisites=['bluetooth', 'region_us'],
         timeout=172800)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsVmTestCases_-_vm-tests-tf b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsVmTestCases_-_vm-tests-tf
index 38392f1..ba7e7b1 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsVmTestCases_-_vm-tests-tf
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.arm.all.CtsVmTestCases_-_vm-tests-tf
@@ -29,6 +29,7 @@
         target_plan=None,
         bundle='arm',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-arm.zip',
+        prerequisites=['region_us'],
         timeout=172800)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsBluetoothTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsBluetoothTestCases
index 840f861..23b29f5 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsBluetoothTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsBluetoothTestCases
@@ -41,6 +41,7 @@
         target_plan=None,
         bundle='x86',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-x86.zip',
+        prerequisites=['bluetooth'],
         hard_reboot_on_failure=True,
         timeout=3600)
 
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsContentTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsContentTestCases
index cb188c3..d618169 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsContentTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsContentTestCases
@@ -29,6 +29,7 @@
         target_plan=None,
         bundle='x86',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-x86.zip',
+        prerequisites=['region_us'],
         timeout=3600)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsStatsdHostTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsStatsdHostTestCases
index af791fc..1e120e5 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsStatsdHostTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsStatsdHostTestCases
@@ -28,6 +28,7 @@
         target_plan=None,
         bundle='x86',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-x86.zip',
+        prerequisites=['bluetooth'],
         timeout=3600)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsTheme b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsTheme
index 0775523..231d23b 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsTheme
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsTheme
@@ -29,6 +29,7 @@
         bundle='x86',
         extra_artifacts_host=['/tmp/diff_*.png'],
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-x86.zip',
+        prerequisites=['region_us'],
         timeout=5400)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsWebkitTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsWebkitTestCases
index 4f37006..3a29c69 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsWebkitTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.CtsWebkitTestCases
@@ -28,6 +28,7 @@
         target_plan=None,
         bundle='x86',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-x86.zip',
+        prerequisites=['region_us'],
         timeout=3600)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsAdminPackageInstallerTestCases_-_CtsDebugTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsAdminPackageInstallerTestCases_-_CtsDebugTestCases
index ff8634b..1bae274 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsAdminPackageInstallerTestCases_-_CtsDebugTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsAdminPackageInstallerTestCases_-_CtsDebugTestCases
@@ -31,6 +31,7 @@
         bundle='x86',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-x86.zip',
         login_precondition_commands=['lsblk -do NAME,RM | sed -n s/1$//p | xargs -n1 eject'],
+        prerequisites=['region_us', 'bluetooth'],
         timeout=172800)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsShortcutHostTestCases_-_CtsVideoTestCases b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsShortcutHostTestCases_-_CtsVideoTestCases
index b5550de..a333d81 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsShortcutHostTestCases_-_CtsVideoTestCases
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsShortcutHostTestCases_-_CtsVideoTestCases
@@ -30,6 +30,7 @@
         bundle='x86',
         extra_artifacts_host=['/tmp/diff_*.png'],
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-x86.zip',
+        prerequisites=['bluetooth', 'region_us'],
         timeout=172800)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsVmTestCases_-_vm-tests-tf b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsVmTestCases_-_vm-tests-tf
index 3d18b88..a8cac3e 100644
--- a/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsVmTestCases_-_vm-tests-tf
+++ b/server/site_tests/cheets_CTS_P/control.9.0_r10.x86.all.CtsVmTestCases_-_vm-tests-tf
@@ -29,6 +29,7 @@
         target_plan=None,
         bundle='x86',
         uri='gs://chromeos-arc-images/cts/bundle/P/android-cts-9.0_r10-linux_x86-x86.zip',
+        prerequisites=['region_us'],
         timeout=172800)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_GTS/cheets_GTS.py b/server/site_tests/cheets_GTS/cheets_GTS.py
index 28aed19..577670b 100644
--- a/server/site_tests/cheets_GTS/cheets_GTS.py
+++ b/server/site_tests/cheets_GTS/cheets_GTS.py
@@ -26,7 +26,7 @@
 _PARTNER_GTS_LOCATION = _PARTNER_GTS_BUCKET + 'gts-7_r3-6045416.zip'
 _PARTNER_GTS_AUTHKEY = _PARTNER_GTS_BUCKET + 'gts-arc.json'
 _GTS_MEDIA_URI = ('https://storage.googleapis.com/youtube-test-media/gts/' +
-    'GtsYouTubeTestCases-media-1.2.zip')
+                  'GtsYouTubeTestCases-media-1.2.zip')
 _GTS_MEDIA_LOCALPATH = '/tmp/android-gts-media/GtsYouTubeTestCases'
 
 
@@ -82,6 +82,7 @@
                  precondition_commands=[],
                  login_precondition_commands=[],
                  authkey=None,
+                 prerequisites=[],
                  timeout=_GTS_TIMEOUT_SECONDS):
         """Runs the specified GTS once, but with several retries.
 
@@ -105,6 +106,7 @@
         dut before the test is run, the scripts must already be installed.
         @param login_precondition_commands: a list of scripts to be run on the
         dut before the log-in for the test is performed.
+        @param prerequisites: a list of prerequisites that identify rogue DUTs.
         """
         # Download the GTS auth key to the local temp directory.
         tmpdir = tempfile.mkdtemp()
@@ -124,6 +126,7 @@
                     _GTS_MEDIA_LOCALPATH),
                 enable_default_apps=enable_default_apps,
                 login_precondition_commands=login_precondition_commands,
-                precondition_commands=precondition_commands)
+                precondition_commands=precondition_commands,
+                prerequisites=prerequisites)
         finally:
             shutil.rmtree(tmpdir)
diff --git a/server/site_tests/cheets_GTS/control.7.0_r3.GtsGmscoreHostTestCases b/server/site_tests/cheets_GTS/control.7.0_r3.GtsGmscoreHostTestCases
index 948b228..c64a7a4 100644
--- a/server/site_tests/cheets_GTS/control.7.0_r3.GtsGmscoreHostTestCases
+++ b/server/site_tests/cheets_GTS/control.7.0_r3.GtsGmscoreHostTestCases
@@ -28,6 +28,7 @@
         target_module='GtsGmscoreHostTestCases',
         target_plan=None,
         uri='gs://chromeos-arc-images/cts/bundle/android-gts-7_r3-6045416.zip',
+        prerequisites=['bluetooth'],
         timeout=3600)
 
 parallel_simple(run_TS, machines)
diff --git a/server/site_tests/cheets_GTS/control.7.0_r3.all.GtsCameraTestCases_-_GtsYouTubeTestCases b/server/site_tests/cheets_GTS/control.7.0_r3.all.GtsCameraTestCases_-_GtsYouTubeTestCases
index 795c8d6..489e03f 100644
--- a/server/site_tests/cheets_GTS/control.7.0_r3.all.GtsCameraTestCases_-_GtsYouTubeTestCases
+++ b/server/site_tests/cheets_GTS/control.7.0_r3.all.GtsCameraTestCases_-_GtsYouTubeTestCases
@@ -30,6 +30,7 @@
         target_module='all.GtsCameraTestCases_-_GtsYouTubeTestCases',
         target_plan=None,
         uri='gs://chromeos-arc-images/cts/bundle/android-gts-7_r3-6045416.zip',
+        prerequisites=['bluetooth'],
         timeout=86400)
 
 parallel_simple(run_TS, machines)