1 # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 5 import cgi 6 import logging 7 8 from autotest_lib.client.bin import utils 9 from autotest_lib.client.common_lib import error 10 from autotest_lib.server.cros.bluetooth import bluetooth_test 11 12 13 class bluetooth_Sanity_LEDiscovery(bluetooth_test.BluetoothTest): 14 """ 15 Verify that the client can discover the tester. 16 """ 17 version = 1 18 19 # How long should the tester remain discoverable? 20 DISCOVERABLE_TIMEOUT=180 21 22 def find_device(self): 23 """Retrieve devices from client and look for tester. 24 25 @return True if device has been found, False otherwise. 26 27 """ 28 # Get the set of devices known to the DUT. 29 devices = self.device.get_devices() 30 if devices == False: 31 raise error.TestFail('Could not retrieve devices from DUT') 32 33 device_found = False 34 for device in devices: 35 if self.tester: 36 if self.address == device['Address']: 37 logging.info('Found tester with RSSI %d', 38 device.get('RSSI')) 39 # Check name as well; if the name and alias fields don't 40 # match, that means it hasn't been requested yet, so 41 # wait until next time. 42 if device.get('Name') != device['Alias']: 43 logging.info('Device name not yet received') 44 continue 45 if device['Alias'] not in self.name_list: 46 raise error.TestFail( 47 'Tester "%s" is not one of the expected names ' 48 '"%s"' % (device['Alias'], str(self.name_list))) 49 # Found the device 50 device_found = True 51 # Write out the RSSI now we've found it. 52 self.write_perf_keyval({'rssi': int(device.get('RSSI', 0))}) 53 self.output_perf_value('rssi', int(device.get('RSSI', 0)), 54 'dBm') 55 56 if self.interactive: 57 item_name = device['Address'].replace(':', '') 58 html = '%s %s' % (cgi.escape(device['Address']), 59 cgi.escape(device['Alias'])) 60 61 if device['Address'] in self.devices_discovered: 62 self.interactive.replace_list_item(item_name, html) 63 else: 64 self.interactive.append_list_item('devices', item_name, 65 html) 66 self.devices_discovered.append(device['Address']) 67 68 result = self.interactive.check_for_button() 69 if result == 0: 70 device_found = True 71 elif result != -1: 72 raise error.TestFail('User indicated test failed') 73 74 return device_found 75 76 77 def run_once(self): 78 """Running BLE test to discover a remote advertising device.""" 79 # Reset the adapter to the powered on state. 80 if not self.device.reset_on(): 81 raise error.TestFail('DUT could not be reset to initial state') 82 83 if self.tester: 84 # Setup the tester as a generic LE peripheral. 85 if not self.tester.setup('peripheral'): 86 raise error.TestFail('Tester could not be initialized') 87 # Enable general discoverable advertising on the tester 88 self.tester.set_discoverable(True) 89 self.tester.set_advertising(True) 90 # Read the tester information so we know what we're looking for. 91 ( address, bluetooth_version, manufacturer_id, 92 supported_settings, current_settings, class_of_device, 93 name, short_name ) = self.tester.read_info() 94 self.address = address 95 self.name = name 96 self.name_list = [name, short_name] 97 98 if self.interactive: 99 self.interactive.login() 100 101 if self.tester: 102 self.interactive.append_output( 103 '<p>The Tester is advertising as an LE peripheral. ' 104 '<p>The DUT is in the observer/discovery state. ' 105 '<p>Please verify that you can discover the tester ' + 106 ('<b>%s</b> with address <b>%s</b> from the device.' % 107 (cgi.escape(self.name), 108 cgi.escape(self.address)))) 109 else: 110 self.interactive.append_output( 111 '<p>The DUT is in the observer/discovery state. ' 112 '<p>Please verify that you can discover the device.') 113 114 self.interactive.append_output('<h2>Devices Found</h2>') 115 self.interactive.append_list('devices') 116 self.devices_discovered = [] 117 118 if self.tester: 119 self.interactive.append_buttons('Tester Found', 120 'Tester Not Found') 121 else: 122 self.interactive.append_buttons('Device Found', 123 'Device Not Found') 124 125 # Discover devices from the DUT. 126 for failed_attempts in range(0, 5): 127 if not self.device.start_discovery(): 128 raise error.TestFail('Could not start discovery on DUT') 129 try: 130 utils.poll_for_condition( 131 condition=self.find_device, 132 desc='Device discovered from DUT', 133 timeout=self.DISCOVERABLE_TIMEOUT) 134 # We only reach this if we find a device. Break out of the 135 # failed_attempts loop to bypass the "reached the end" 136 # condition. 137 break 138 except utils.TimeoutError: 139 # Capture the timeout error and try once more through the 140 # loop. 141 pass 142 finally: 143 if not self.device.stop_discovery(): 144 logging.warning('Failed to stop discovery on DUT') 145 else: 146 # We only reach this if we tried five times to find the device and 147 # failed. 148 raise error.TestFail('DUT could not discover device') 149 # Record how many attempts this took, hopefully we'll one day figure 150 # out a way to reduce this to zero and then the loop above can go 151 # away. 152 self.write_perf_keyval({'failed_attempts': failed_attempts }) 153 self.output_perf_value('failed_attempts', failed_attempts, 'attempts') 154 155 156 def cleanup(self): 157 """Set the tester back to not advertising.""" 158 if self.tester: 159 self.tester.set_advertising(False) 160 self.tester.set_discoverable(False) 161 162 super(bluetooth_Sanity_LEDiscovery, self).cleanup() 163