blob: 1fdffd3e1e38ac1cbdc7013240e86aca7d31fcad [file] [log] [blame]
Scott James Remnant4dcd73f2013-07-22 15:00:24 -07001# 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
Scott James Remnantda9f43c2013-08-07 17:53:14 -07005import base64
6import json
Scott James Remnant25622042014-12-05 11:20:37 -08007import socket
Scott James Remnantda9f43c2013-08-07 17:53:14 -07008
Scott James Remnant25622042014-12-05 11:20:37 -08009from autotest_lib.client.common_lib import error
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070010from autotest_lib.client.cros import constants
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -070011from autotest_lib.server import autotest, hosts
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070012
13
14class BluetoothTester(object):
Scott James Remnant1c72d7a2013-07-29 15:00:04 -070015 """BluetoothTester is a thin layer of logic over a remote tester.
16
17 The Autotest host object representing the remote tester, passed to this
18 class on initialization, can be accessed from its host property.
19
20 """
21
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070022
23 XMLRPC_BRINGUP_TIMEOUT_SECONDS = 60
24
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070025 def __init__(self, tester_host):
26 """Construct a BluetoothTester.
27
Scott James Remnant1c72d7a2013-07-29 15:00:04 -070028 @param tester_host: host object representing a remote host.
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070029
30 """
Scott James Remnant1c72d7a2013-07-29 15:00:04 -070031 self.host = tester_host
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070032 # Make sure the client library is on the device so that the proxy code
33 # is there when we try to call it.
34 client_at = autotest.Autotest(self.host)
35 client_at.install()
36 # Start up the XML-RPC proxy on the tester.
37 self._proxy = self.host.xmlrpc_connect(
38 constants.BLUETOOTH_TESTER_XMLRPC_SERVER_COMMAND,
39 constants.BLUETOOTH_TESTER_XMLRPC_SERVER_PORT,
40 command_name=
41 constants.BLUETOOTH_TESTER_XMLRPC_SERVER_CLEANUP_PATTERN,
42 ready_test_name=
43 constants.BLUETOOTH_TESTER_XMLRPC_SERVER_READY_METHOD,
44 timeout_seconds=self.XMLRPC_BRINGUP_TIMEOUT_SECONDS)
45
Scott James Remnant1c72d7a2013-07-29 15:00:04 -070046
Scott James Remnantda9f43c2013-08-07 17:53:14 -070047 def setup(self, profile):
48 """Set up the tester with the given profile.
49
50 @param profile: Profile to use for this test, valid values are:
51 computer - a standard computer profile
52
53 @return True on success, False otherwise.
54
55 """
56 return self._proxy.setup(profile)
57
58
Scott James Remnantaec4edd2013-08-26 18:47:11 -070059 def set_discoverable(self, discoverable, timeout=0):
60 """Set the discoverable state of the controller.
61
62 @param discoverable: Whether controller should be discoverable.
63 @param timeout: Timeout in seconds before disabling discovery again,
64 ignored when discoverable is False, must not be zero when
65 discoverable is True.
66
67 @return True on success, False otherwise.
68
69 """
70 return self._proxy.set_discoverable(discoverable, timeout)
71
72
73 def read_info(self):
74 """Read the adapter information from the Kernel.
75
76 @return the information as a JSON-encoded tuple of:
77 ( address, bluetooth_version, manufacturer_id,
78 supported_settings, current_settings, class_of_device,
79 name, short_name )
80
81 """
82 return json.loads(self._proxy.read_info())
83
84
Scott James Remnantda9f43c2013-08-07 17:53:14 -070085 def discover_devices(self, br_edr=True, le_public=True, le_random=True):
86 """Discover remote devices.
87
88 Activates device discovery and collects the set of devices found,
89 returning them as a list.
90
91 @param br_edr: Whether to detect BR/EDR devices.
92 @param le_public: Whether to detect LE Public Address devices.
93 @param le_random: Whether to detect LE Random Address devices.
94
95 @return List of devices found as tuples with the format
96 (address, address_type, rssi, flags, base64-encoded eirdata),
97 or False if discovery could not be started.
98
99 """
100 devices = self._proxy.discover_devices(br_edr, le_public, le_random)
101 if devices == False:
102 return False
103
104 return (
105 (address, address_type, rssi, flags,
106 base64.decodestring(eirdata))
107 for address, address_type, rssi, flags, eirdata
108 in json.loads(devices)
109 )
110
111
Scott James Remnant4dcd73f2013-07-22 15:00:24 -0700112 def close(self):
113 """Tear down state associated with the client."""
114 # This kills the RPC server.
Scott James Remnant1c72d7a2013-07-29 15:00:04 -0700115 self.host.close()
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700116
117
Artem Rakhov448d52c2013-12-03 16:38:36 -0800118 def connect(self, address):
119 """Connect to device with the given address
120
121 @param address: Bluetooth address.
122
123 """
124 self._proxy.connect(address)
125
126
Artem Rakhovba130382014-02-26 17:39:57 -0800127 def service_search_request(self, uuids, max_rec_cnt, preferred_size=32,
Artem Rakhovf42dc832014-03-03 12:19:13 -0800128 forced_pdu_size=None, invalid_request=False):
Artem Rakhov448d52c2013-12-03 16:38:36 -0800129 """Send a Service Search Request
130
Artem Rakhovba130382014-02-26 17:39:57 -0800131 @param uuids: List of UUIDs (as integers) to look for.
Artem Rakhov448d52c2013-12-03 16:38:36 -0800132 @param max_rec_cnt: Maximum count of returned service records.
Artem Rakhovca443b52014-01-10 17:18:24 -0800133 @param preferred_size: Preffered size of UUIDs in bits (16, 32, or 128).
Artem Rakhovba130382014-02-26 17:39:57 -0800134 @param forced_pdu_size: Use certain PDU size parameter instead of
135 calculating actual length of sequence.
Artem Rakhovf42dc832014-03-03 12:19:13 -0800136 @param invalid_request: Whether to send request with intentionally
137 invalid syntax for testing purposes (bool flag).
Artem Rakhov448d52c2013-12-03 16:38:36 -0800138
Artem Rakhovba130382014-02-26 17:39:57 -0800139 @return list of found services' service record handles or Error Code
Artem Rakhov448d52c2013-12-03 16:38:36 -0800140
141 """
Artem Rakhovca443b52014-01-10 17:18:24 -0800142 return self._proxy.service_search_request(uuids, max_rec_cnt,
Artem Rakhovba130382014-02-26 17:39:57 -0800143 preferred_size,
Artem Rakhovf42dc832014-03-03 12:19:13 -0800144 forced_pdu_size,
145 invalid_request)
Artem Rakhov448d52c2013-12-03 16:38:36 -0800146
147
Artem Rakhovaf4e4a62014-06-11 20:33:50 -0700148 def service_attribute_request(self, handle, max_attr_byte_count, attr_ids,
149 forced_pdu_size=None, invalid_request=None):
Artem Rakhovebf3df32014-04-01 14:52:25 -0700150 """Send a Service Attribute Request
151
152 @param handle: service record from which attribute values are to be
153 retrieved.
154 @param max_attr_byte_count: maximum number of bytes of attribute data to
155 be returned in the response to this request.
156 @param attr_ids: a list, where each element is either an attribute ID
157 or a range of attribute IDs.
Artem Rakhovaf4e4a62014-06-11 20:33:50 -0700158 @param forced_pdu_size: Use certain PDU size parameter instead of
159 calculating actual length of sequence.
160 @param invalid_request: Whether to send request with intentionally
161 invalid syntax for testing purposes (string with raw request).
Artem Rakhovebf3df32014-04-01 14:52:25 -0700162
163 @return list of found attributes IDs and their values or Error Code
164
165 """
166 return self._proxy.service_attribute_request(handle,
167 max_attr_byte_count,
Artem Rakhovaf4e4a62014-06-11 20:33:50 -0700168 attr_ids,
169 forced_pdu_size,
170 invalid_request)
Artem Rakhovebf3df32014-04-01 14:52:25 -0700171
172
Artem Rakhovf53eeec2014-09-24 21:15:58 -0700173 def service_search_attribute_request(self, uuids, max_attr_byte_count,
Artem Rakhovef5a4582014-10-29 16:02:55 -0700174 attr_ids, preferred_size=32,
175 forced_pdu_size=None,
176 invalid_request=None):
Artem Rakhovf53eeec2014-09-24 21:15:58 -0700177 """Send a Service Search Attribute Request
178
179 @param uuids: list of UUIDs (as integers) to look for.
180 @param max_attr_byte_count: maximum number of bytes of attribute data to
181 be returned in the response to this request.
182 @param attr_ids: a list, where each element is either an attribute ID
183 or a range of attribute IDs.
184 @param preferred_size: Preffered size of UUIDs in bits (16, 32, or 128).
Artem Rakhovef5a4582014-10-29 16:02:55 -0700185 @param forced_pdu_size: Use certain PDU size parameter instead of
186 calculating actual length of sequence.
187 @param invalid_request: Whether to send request with intentionally
188 invalid syntax for testing purposes (string to be prepended
189 to correct request).
Artem Rakhovf53eeec2014-09-24 21:15:58 -0700190
191 @return list of found attributes IDs and their values or Error Code
192
193 """
194 return self._proxy.service_search_attribute_request(uuids,
195 max_attr_byte_count,
196 attr_ids,
Artem Rakhovef5a4582014-10-29 16:02:55 -0700197 preferred_size,
198 forced_pdu_size,
199 invalid_request)
Artem Rakhovf53eeec2014-09-24 21:15:58 -0700200
201
Scott James Remnant25622042014-12-05 11:20:37 -0800202def create_host_from(device_host, args=None):
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700203 """Creates a host object for the Tester associated with a DUT.
204
Scott James Remnant25622042014-12-05 11:20:37 -0800205 The IP address or the hostname can be specified in the 'tester' member of
206 the argument dictionary. When not present it is derived from the hostname
207 of the DUT by appending '-router' to the first part.
208
209 Will raise an exception if there isn't a tester for the DUT, or if the DUT
210 is specified as an IP address and thus the hostname cannot be derived.
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700211
Scott James Remnant8d2cbf32013-11-12 11:00:25 -0800212 @param device_host: Autotest host object for the DUT.
Scott James Remnant25622042014-12-05 11:20:37 -0800213 @param args: Dictionary of arguments passed to the test.
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700214
215 @return Autotest host object for the Tester.
216
217 """
218
Scott James Remnant25622042014-12-05 11:20:37 -0800219 def is_ip_address(hostname):
220 try:
221 socket.inet_aton(hostname)
222 return True
223 except socket.error:
224 return False
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700225
Scott James Remnant25622042014-12-05 11:20:37 -0800226 try:
227 tester_hostname = args['tester']
228 except KeyError:
229 device_hostname = device_host.hostname
230 if is_ip_address(device_hostname):
231 raise error.TestError("Remote host cannot be an IP address unless "
232 "tester specified with --args tester=IP")
233
234 parts = device_hostname.split('.')
235 parts[0] = parts[0] + '-router'
236 tester_hostname = '.'.join(parts)
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700237
238 return hosts.create_host(tester_hostname)