blob: 7b3d4476c8eea127ccfcf828d49183b7d064cb2b [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
7
Hsinyu Chaoe0b08e62015-08-11 10:50:37 +00008from autotest_lib.client.cros import constants
Christopher Wiley6b1d9e72014-12-13 18:07:41 -08009from autotest_lib.server import autotest
10from autotest_lib.server import hosts
11from autotest_lib.server.cros import dnsname_mangler
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
Katherine Threlkeld88aa1432015-07-30 11:44:35 -070024 XMLRPC_LOG_PATH = '/var/log/bluetooth_xmlrpc_tester.log'
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070025
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070026 def __init__(self, tester_host):
27 """Construct a BluetoothTester.
28
Scott James Remnant1c72d7a2013-07-29 15:00:04 -070029 @param tester_host: host object representing a remote host.
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070030
31 """
Scott James Remnant1c72d7a2013-07-29 15:00:04 -070032 self.host = tester_host
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070033 # Make sure the client library is on the device so that the proxy code
34 # is there when we try to call it.
35 client_at = autotest.Autotest(self.host)
36 client_at.install()
37 # Start up the XML-RPC proxy on the tester.
Miao Chouedee53a2015-11-17 14:36:52 -080038 self._proxy = self.host.rpc_server_tracker.xmlrpc_connect(
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070039 constants.BLUETOOTH_TESTER_XMLRPC_SERVER_COMMAND,
40 constants.BLUETOOTH_TESTER_XMLRPC_SERVER_PORT,
41 command_name=
42 constants.BLUETOOTH_TESTER_XMLRPC_SERVER_CLEANUP_PATTERN,
43 ready_test_name=
44 constants.BLUETOOTH_TESTER_XMLRPC_SERVER_READY_METHOD,
Katherine Threlkeld88aa1432015-07-30 11:44:35 -070045 timeout_seconds=self.XMLRPC_BRINGUP_TIMEOUT_SECONDS,
46 logfile=self.XMLRPC_LOG_PATH)
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070047
Scott James Remnant1c72d7a2013-07-29 15:00:04 -070048
Scott James Remnantda9f43c2013-08-07 17:53:14 -070049 def setup(self, profile):
50 """Set up the tester with the given profile.
51
52 @param profile: Profile to use for this test, valid values are:
53 computer - a standard computer profile
54
55 @return True on success, False otherwise.
56
57 """
58 return self._proxy.setup(profile)
59
60
Scott James Remnantaec4edd2013-08-26 18:47:11 -070061 def set_discoverable(self, discoverable, timeout=0):
62 """Set the discoverable state of the controller.
63
64 @param discoverable: Whether controller should be discoverable.
65 @param timeout: Timeout in seconds before disabling discovery again,
66 ignored when discoverable is False, must not be zero when
67 discoverable is True.
68
69 @return True on success, False otherwise.
70
71 """
72 return self._proxy.set_discoverable(discoverable, timeout)
73
74
75 def read_info(self):
76 """Read the adapter information from the Kernel.
77
78 @return the information as a JSON-encoded tuple of:
79 ( address, bluetooth_version, manufacturer_id,
80 supported_settings, current_settings, class_of_device,
81 name, short_name )
82
83 """
84 return json.loads(self._proxy.read_info())
85
86
Scott James Remnante686dcc2014-12-05 17:14:08 -080087 def set_advertising(self, advertising):
88 """Set the whether the controller is advertising via LE.
89
90 @param advertising: Whether controller should advertise via LE.
91
92 @return True on success, False otherwise.
93
94 """
95 return self._proxy.set_advertising(advertising)
96
97
Scott James Remnantda9f43c2013-08-07 17:53:14 -070098 def discover_devices(self, br_edr=True, le_public=True, le_random=True):
99 """Discover remote devices.
100
101 Activates device discovery and collects the set of devices found,
102 returning them as a list.
103
104 @param br_edr: Whether to detect BR/EDR devices.
105 @param le_public: Whether to detect LE Public Address devices.
106 @param le_random: Whether to detect LE Random Address devices.
107
108 @return List of devices found as tuples with the format
109 (address, address_type, rssi, flags, base64-encoded eirdata),
110 or False if discovery could not be started.
111
112 """
113 devices = self._proxy.discover_devices(br_edr, le_public, le_random)
114 if devices == False:
115 return False
116
117 return (
118 (address, address_type, rssi, flags,
119 base64.decodestring(eirdata))
120 for address, address_type, rssi, flags, eirdata
121 in json.loads(devices)
122 )
123
124
Katherine Threlkeld88aa1432015-07-30 11:44:35 -0700125 def copy_logs(self, destination):
126 """Copy the logs generated by this tester to a given location.
127
128 @param destination: destination directory for the logs.
129
130 """
131 self.host.collect_logs(self.XMLRPC_LOG_PATH, destination)
132
133
Scott James Remnant4dcd73f2013-07-22 15:00:24 -0700134 def close(self):
135 """Tear down state associated with the client."""
136 # This kills the RPC server.
Scott James Remnant1c72d7a2013-07-29 15:00:04 -0700137 self.host.close()
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700138
139
Artem Rakhov448d52c2013-12-03 16:38:36 -0800140 def connect(self, address):
141 """Connect to device with the given address
142
143 @param address: Bluetooth address.
144
145 """
146 self._proxy.connect(address)
147
148
Artem Rakhovba130382014-02-26 17:39:57 -0800149 def service_search_request(self, uuids, max_rec_cnt, preferred_size=32,
Artem Rakhovf42dc832014-03-03 12:19:13 -0800150 forced_pdu_size=None, invalid_request=False):
Artem Rakhov448d52c2013-12-03 16:38:36 -0800151 """Send a Service Search Request
152
Artem Rakhovba130382014-02-26 17:39:57 -0800153 @param uuids: List of UUIDs (as integers) to look for.
Artem Rakhov448d52c2013-12-03 16:38:36 -0800154 @param max_rec_cnt: Maximum count of returned service records.
Artem Rakhovca443b52014-01-10 17:18:24 -0800155 @param preferred_size: Preffered size of UUIDs in bits (16, 32, or 128).
Artem Rakhovba130382014-02-26 17:39:57 -0800156 @param forced_pdu_size: Use certain PDU size parameter instead of
157 calculating actual length of sequence.
Artem Rakhovf42dc832014-03-03 12:19:13 -0800158 @param invalid_request: Whether to send request with intentionally
159 invalid syntax for testing purposes (bool flag).
Artem Rakhov448d52c2013-12-03 16:38:36 -0800160
Artem Rakhovba130382014-02-26 17:39:57 -0800161 @return list of found services' service record handles or Error Code
Artem Rakhov448d52c2013-12-03 16:38:36 -0800162
163 """
Artem Rakhov275ecaf2015-01-07 17:46:14 -0800164 return json.loads(
165 self._proxy.service_search_request(
166 uuids, max_rec_cnt, preferred_size, forced_pdu_size,
167 invalid_request)
168 )
Artem Rakhov448d52c2013-12-03 16:38:36 -0800169
170
Artem Rakhovaf4e4a62014-06-11 20:33:50 -0700171 def service_attribute_request(self, handle, max_attr_byte_count, attr_ids,
172 forced_pdu_size=None, invalid_request=None):
Artem Rakhovebf3df32014-04-01 14:52:25 -0700173 """Send a Service Attribute Request
174
175 @param handle: service record from which attribute values are to be
176 retrieved.
177 @param max_attr_byte_count: maximum number of bytes of attribute data to
178 be returned in the response to this request.
179 @param attr_ids: a list, where each element is either an attribute ID
180 or a range of attribute IDs.
Artem Rakhovaf4e4a62014-06-11 20:33:50 -0700181 @param forced_pdu_size: Use certain PDU size parameter instead of
182 calculating actual length of sequence.
183 @param invalid_request: Whether to send request with intentionally
184 invalid syntax for testing purposes (string with raw request).
Artem Rakhovebf3df32014-04-01 14:52:25 -0700185
186 @return list of found attributes IDs and their values or Error Code
187
188 """
Artem Rakhov275ecaf2015-01-07 17:46:14 -0800189 return json.loads(
190 self._proxy.service_attribute_request(
191 handle, max_attr_byte_count, attr_ids, forced_pdu_size,
192 invalid_request)
193 )
Artem Rakhovebf3df32014-04-01 14:52:25 -0700194
195
Artem Rakhovf53eeec2014-09-24 21:15:58 -0700196 def service_search_attribute_request(self, uuids, max_attr_byte_count,
Artem Rakhovef5a4582014-10-29 16:02:55 -0700197 attr_ids, preferred_size=32,
198 forced_pdu_size=None,
199 invalid_request=None):
Artem Rakhovf53eeec2014-09-24 21:15:58 -0700200 """Send a Service Search Attribute Request
201
202 @param uuids: list of UUIDs (as integers) to look for.
203 @param max_attr_byte_count: maximum number of bytes of attribute data to
204 be returned in the response to this request.
205 @param attr_ids: a list, where each element is either an attribute ID
206 or a range of attribute IDs.
207 @param preferred_size: Preffered size of UUIDs in bits (16, 32, or 128).
Artem Rakhovef5a4582014-10-29 16:02:55 -0700208 @param forced_pdu_size: Use certain PDU size parameter instead of
209 calculating actual length of sequence.
210 @param invalid_request: Whether to send request with intentionally
211 invalid syntax for testing purposes (string to be prepended
212 to correct request).
Artem Rakhovf53eeec2014-09-24 21:15:58 -0700213
214 @return list of found attributes IDs and their values or Error Code
215
216 """
Artem Rakhov275ecaf2015-01-07 17:46:14 -0800217 return json.loads(
218 self._proxy.service_search_attribute_request(
219 uuids, max_attr_byte_count, attr_ids, preferred_size,
220 forced_pdu_size, invalid_request)
221 )
Artem Rakhovf53eeec2014-09-24 21:15:58 -0700222
223
Scott James Remnant25622042014-12-05 11:20:37 -0800224def create_host_from(device_host, args=None):
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700225 """Creates a host object for the Tester associated with a DUT.
226
Scott James Remnant25622042014-12-05 11:20:37 -0800227 The IP address or the hostname can be specified in the 'tester' member of
228 the argument dictionary. When not present it is derived from the hostname
229 of the DUT by appending '-router' to the first part.
230
231 Will raise an exception if there isn't a tester for the DUT, or if the DUT
232 is specified as an IP address and thus the hostname cannot be derived.
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700233
Scott James Remnant8d2cbf32013-11-12 11:00:25 -0800234 @param device_host: Autotest host object for the DUT.
Scott James Remnant25622042014-12-05 11:20:37 -0800235 @param args: Dictionary of arguments passed to the test.
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700236
237 @return Autotest host object for the Tester.
238
239 """
Christopher Wiley6b1d9e72014-12-13 18:07:41 -0800240 cmdline_override = args.get('tester', None)
241 hostname = dnsname_mangler.get_tester_addr(
242 device_host.hostname,
243 cmdline_override=cmdline_override)
244 return hosts.create_host(hostname)