bluetooth: restart raspi bt peer at start of test

Currently raspi bt stack restarts between tests, but non-quicksanity
tests in lab do not clear state after running. Here we ensure we
always clear state before and after all quicksanity tests

TEST=quicksanity.stress on raspi and fizz
BUG=b:144117600

Change-Id: Iea9e97fdd58eecb673af004e090811a23f41293b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/1966574
Commit-Queue: Daniel Winkler <danielwinkler@google.com>
Tested-by: Daniel Winkler <danielwinkler@google.com>
Reviewed-by: Yun-Hao Chung <howardchung@google.com>
diff --git a/server/cros/bluetooth/bluetooth_adapter_quick_tests.py b/server/cros/bluetooth/bluetooth_adapter_quick_tests.py
index a408496..bf2718f 100644
--- a/server/cros/bluetooth/bluetooth_adapter_quick_tests.py
+++ b/server/cros/bluetooth/bluetooth_adapter_quick_tests.py
@@ -62,7 +62,7 @@
             for device in device_list:
                 if device is not None:
                     logging.info('Restarting %s', device_type)
-                    self.get_device(device_type)
+                    self.get_device(device_type, on_start=False)
 
 
     def start_peers(self, devices):
@@ -240,6 +240,10 @@
             logging.info('Cleanning up and restarting towards next test...')
 
         self.bluetooth_facade.stop_discovery()
+
+        # Store a copy of active devices for raspi reset in the final step
+        self.active_test_devices = self.devices
+
         # Disconnect devices used in the test, and remove the pairing.
         for device_list in self.devices.values():
             for device in device_list:
@@ -379,6 +383,13 @@
 
     def quick_test_cleanup(self):
         """ Cleanup any state test server and all device"""
+
+        # Clear any raspi devices at very end of test
+        for device_list in self.active_test_devices.values():
+            for device in device_list:
+                if device is not None:
+                    self.clear_raspi_device(device)
+
         # Reset the adapter
         self.test_reset_on_adapter()
         # Initialize bluetooth_adapter_tests class (also clears self.fails)
diff --git a/server/cros/bluetooth/bluetooth_adapter_tests.py b/server/cros/bluetooth/bluetooth_adapter_tests.py
index 9bbdd78..5b538ca 100644
--- a/server/cros/bluetooth/bluetooth_adapter_tests.py
+++ b/server/cros/bluetooth/bluetooth_adapter_tests.py
@@ -578,11 +578,45 @@
             if len(self.chameleon_group[device_type]) == 0:
                 logging.error('No device is detected on %d-th chameleon', idx)
 
-    def get_device_rasp(self, device_num):
+
+    def clear_raspi_device(self, device):
+        """Clears a device on a raspi chameleon by resetting bluetooth stack
+
+        @param device: proxy object of peripheral device
+        """
+
+        try:
+            device.ResetStack()
+
+        except SocketError as e:
+            # Ignore conn reset, expected during stack reset
+            if e.errno != errno.ECONNRESET:
+                raise
+
+        except httplib.BadStatusLine as e:
+            # BadStatusLine occurs occasionally when chameleon
+            # is restarted. We ignore it here
+            logging.error('Ignoring badstatusline exception')
+            pass
+
+        # Catch generic Fault exception by rpc server, ignore
+        # method not available as it indicates platform didn't
+        # support method and that's ok
+        except Exception, e:
+            if not (e.__class__.__name__ == 'Fault' and
+                'is not supported' in str(e)):
+                raise
+
+
+    def get_device_rasp(self, device_num, on_start=True):
         """Get all bluetooth device objects from chameleons.
         This method should be called only after group_chameleons_type
         @param device_num : dict of {device_type:number}, to specify the number
                             of device needed for each device_type.
+
+        @param on_start: boolean describing whether the requested clear is for a
+                            new test, or in the middle of a current one
+
         @returns: True if Success.
         """
 
@@ -596,6 +630,10 @@
             for chameleon in self.chameleon_group[device_type][:number]:
                 device = get_bluetooth_emulated_device(chameleon, device_type)
 
+                # Re-fresh device to clean state if test is starting
+                if on_start:
+                    self.clear_raspi_device(device)
+
                 try:
                     # Tell generic chameleon to bind to this device type
                     device.SpecifyDeviceType(device_type)
@@ -619,17 +657,24 @@
         return True
 
 
-    def get_device(self, device_type):
+    def get_device(self, device_type, on_start=True):
         """Get the bluetooth device object.
 
         @param device_type : the bluetooth device type, e.g., 'MOUSE'
 
+        @param on_start: boolean describing whether the requested clear is for a
+                            new test, or in the middle of a current one
+
         @returns: the bluetooth device object
 
         """
         self.devices[device_type].append(get_bluetooth_emulated_device(\
                                     self.host.chameleon, device_type))
 
+        # Re-fresh device to clean state if test is starting
+        if on_start:
+            self.clear_raspi_device(self.devices[device_type][-1])
+
         try:
             # Tell generic chameleon to bind to this device type
             self.devices[device_type][-1].SpecifyDeviceType(device_type)
@@ -2757,32 +2802,8 @@
                 if device is not None:
                     device.Close()
 
-                    # If module has a reset feature, use it
-                    if on_start:
-                        try:
-                            device.ResetStack()
-
-                        except SocketError as e:
-                            # Ignore conn reset, expected during stack reset
-                            if e.errno != errno.ECONNRESET:
-                                raise
-
-                        except httplib.BadStatusLine as e:
-                            # BadStatusLine occurs occasionally when chameleon
-                            # is restarted. We ignore it here
-                            logging.error('Ignoring badstatusline exception')
-                            pass
-
-                        # Catch generic Fault exception by rpc server, ignore
-                        # method not available as it indicates platform didn't
-                        # support method and that's ok
-                        except Exception, e:
-                            if not (e.__class__.__name__ == 'Fault' and
-                                'is not supported' in str(e)):
-                                raise
-
-                    else:
-                        # If we are doing a reset action, powercycle device
+                    # Power cycle BT device if we're in the middle of a test
+                    if not on_start:
                         device.PowerCycle()
 
         self.devices = dict()