xref: /aosp_15_r20/tools/netsim/testing/mobly/ble_utils.py (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
1*cf78ab8cSAndroid Build Coastguard Worker"""BLE test utils for netsim."""
2*cf78ab8cSAndroid Build Coastguard Worker
3*cf78ab8cSAndroid Build Coastguard Workerimport logging
4*cf78ab8cSAndroid Build Coastguard Workerimport time
5*cf78ab8cSAndroid Build Coastguard Workerfrom typing import Any
6*cf78ab8cSAndroid Build Coastguard Worker
7*cf78ab8cSAndroid Build Coastguard Workerfrom mobly import asserts
8*cf78ab8cSAndroid Build Coastguard Workerfrom mobly import utils
9*cf78ab8cSAndroid Build Coastguard Workerfrom mobly.controllers import android_device
10*cf78ab8cSAndroid Build Coastguard Workerfrom mobly.snippet import callback_event
11*cf78ab8cSAndroid Build Coastguard Worker
12*cf78ab8cSAndroid Build Coastguard Worker
13*cf78ab8cSAndroid Build Coastguard Worker# Number of seconds for the target to stay BLE advertising.
14*cf78ab8cSAndroid Build Coastguard WorkerADVERTISING_TIME = 120
15*cf78ab8cSAndroid Build Coastguard Worker# Number of seconds for the target to start BLE advertising.
16*cf78ab8cSAndroid Build Coastguard WorkerADVERTISING_START_TIME = 30
17*cf78ab8cSAndroid Build Coastguard Worker# The number of seconds to wait for receiving scan results.
18*cf78ab8cSAndroid Build Coastguard WorkerSCAN_TIMEOUT = 20
19*cf78ab8cSAndroid Build Coastguard Worker# The number of seconds to wair for connection established.
20*cf78ab8cSAndroid Build Coastguard WorkerCONNECTION_TIMEOUT = 60
21*cf78ab8cSAndroid Build Coastguard Worker# The number of seconds to wait before cancel connection.
22*cf78ab8cSAndroid Build Coastguard WorkerCANCEL_CONNECTION_WAIT_TIME = 0.1
23*cf78ab8cSAndroid Build Coastguard Worker# UUID for test service.
24*cf78ab8cSAndroid Build Coastguard WorkerTEST_BLE_SERVICE_UUID = '0000fe23-0000-1000-8000-00805f9b34fb'
25*cf78ab8cSAndroid Build Coastguard Worker# UUID for write characteristic.
26*cf78ab8cSAndroid Build Coastguard WorkerTEST_WRITE_UUID = '0000e632-0000-1000-8000-00805f9b34fb'
27*cf78ab8cSAndroid Build Coastguard Worker# UUID for second write characteristic.
28*cf78ab8cSAndroid Build Coastguard WorkerTEST_SECOND_WRITE_UUID = '0000e633-0000-1000-8000-00805f9b34fb'
29*cf78ab8cSAndroid Build Coastguard Worker# UUID for read test.
30*cf78ab8cSAndroid Build Coastguard WorkerTEST_READ_UUID = '0000e631-0000-1000-8000-00805f9b34fb'
31*cf78ab8cSAndroid Build Coastguard Worker# UUID for second read characteristic.
32*cf78ab8cSAndroid Build Coastguard WorkerTEST_SECOND_READ_UUID = '0000e634-0000-1000-8000-00805f9b34fb'
33*cf78ab8cSAndroid Build Coastguard Worker# UUID for third read characteristic.
34*cf78ab8cSAndroid Build Coastguard WorkerTEST_THIRD_READ_UUID = '0000e635-0000-1000-8000-00805f9b34fb'
35*cf78ab8cSAndroid Build Coastguard Worker# UUID for scan response.
36*cf78ab8cSAndroid Build Coastguard WorkerTEST_SCAN_RESPONSE_UUID = '0000e639-0000-1000-8000-00805f9b34fb'
37*cf78ab8cSAndroid Build Coastguard Worker# Advertise settings in json format for Ble Advertise.
38*cf78ab8cSAndroid Build Coastguard WorkerADVERTISE_SETTINGS = {
39*cf78ab8cSAndroid Build Coastguard Worker    'AdvertiseMode': 'ADVERTISE_MODE_LOW_LATENCY',
40*cf78ab8cSAndroid Build Coastguard Worker    'Timeout': ADVERTISING_TIME * 1000,
41*cf78ab8cSAndroid Build Coastguard Worker    'Connectable': True,
42*cf78ab8cSAndroid Build Coastguard Worker    'TxPowerLevel': 'ADVERTISE_TX_POWER_ULTRA_LOW',
43*cf78ab8cSAndroid Build Coastguard Worker}
44*cf78ab8cSAndroid Build Coastguard Worker# Ramdom data to represent device stored in advertise data.
45*cf78ab8cSAndroid Build Coastguard WorkerDATA = utils.rand_ascii_str(16)
46*cf78ab8cSAndroid Build Coastguard Worker# Random data for scan response.
47*cf78ab8cSAndroid Build Coastguard WorkerSCAN_RESPONSE_DATA = utils.rand_ascii_str(16)
48*cf78ab8cSAndroid Build Coastguard Worker# Random data for read operation.
49*cf78ab8cSAndroid Build Coastguard WorkerREAD_DATA = utils.rand_ascii_str(8)
50*cf78ab8cSAndroid Build Coastguard Worker# Random data for second read operation.
51*cf78ab8cSAndroid Build Coastguard WorkerSECOND_READ_DATA = utils.rand_ascii_str(8)
52*cf78ab8cSAndroid Build Coastguard Worker# Random data for third read operation.
53*cf78ab8cSAndroid Build Coastguard WorkerTHIRD_READ_DATA = utils.rand_ascii_str(8)
54*cf78ab8cSAndroid Build Coastguard Worker# Random data for write operation.
55*cf78ab8cSAndroid Build Coastguard WorkerWRITE_DATA = utils.rand_ascii_str(8)
56*cf78ab8cSAndroid Build Coastguard Worker# Random data for second write operation.
57*cf78ab8cSAndroid Build Coastguard WorkerSECOND_WRITE_DATA = utils.rand_ascii_str(8)
58*cf78ab8cSAndroid Build Coastguard Worker# Advertise data in json format for BLE advertise.
59*cf78ab8cSAndroid Build Coastguard WorkerADVERTISE_DATA = {
60*cf78ab8cSAndroid Build Coastguard Worker    'IncludeDeviceName': False,
61*cf78ab8cSAndroid Build Coastguard Worker    'ServiceData': [{'UUID': TEST_BLE_SERVICE_UUID, 'Data': DATA}],
62*cf78ab8cSAndroid Build Coastguard Worker}
63*cf78ab8cSAndroid Build Coastguard Worker# Advertise data in json format representing scan response for BLE advertise.
64*cf78ab8cSAndroid Build Coastguard WorkerSCAN_RESPONSE = {
65*cf78ab8cSAndroid Build Coastguard Worker    'IncludeDeviceName': False,
66*cf78ab8cSAndroid Build Coastguard Worker    'ServiceData': [{
67*cf78ab8cSAndroid Build Coastguard Worker        'UUID': TEST_SCAN_RESPONSE_UUID,
68*cf78ab8cSAndroid Build Coastguard Worker        'Data': SCAN_RESPONSE_DATA,
69*cf78ab8cSAndroid Build Coastguard Worker    }],
70*cf78ab8cSAndroid Build Coastguard Worker}
71*cf78ab8cSAndroid Build Coastguard Worker# Scan filter in json format for BLE scan.
72*cf78ab8cSAndroid Build Coastguard WorkerSCAN_FILTER = {'ServiceUuid': TEST_BLE_SERVICE_UUID}
73*cf78ab8cSAndroid Build Coastguard Worker# Scan settings in json format for BLE scan.
74*cf78ab8cSAndroid Build Coastguard WorkerSCAN_SETTINGS = {'ScanMode': 'SCAN_MODE_LOW_LATENCY'}
75*cf78ab8cSAndroid Build Coastguard Worker# Characteristics for write in json format.
76*cf78ab8cSAndroid Build Coastguard WorkerWRITE_CHARACTERISTIC = {
77*cf78ab8cSAndroid Build Coastguard Worker    'UUID': TEST_WRITE_UUID,
78*cf78ab8cSAndroid Build Coastguard Worker    'Property': 'PROPERTY_WRITE',
79*cf78ab8cSAndroid Build Coastguard Worker    'Permission': 'PERMISSION_WRITE',
80*cf78ab8cSAndroid Build Coastguard Worker}
81*cf78ab8cSAndroid Build Coastguard WorkerSECOND_WRITE_CHARACTERISTIC = {
82*cf78ab8cSAndroid Build Coastguard Worker    'UUID': TEST_SECOND_WRITE_UUID,
83*cf78ab8cSAndroid Build Coastguard Worker    'Property': 'PROPERTY_WRITE',
84*cf78ab8cSAndroid Build Coastguard Worker    'Permission': 'PERMISSION_WRITE',
85*cf78ab8cSAndroid Build Coastguard Worker}
86*cf78ab8cSAndroid Build Coastguard Worker# Characteristics for read in json format.
87*cf78ab8cSAndroid Build Coastguard WorkerREAD_CHARACTERISTIC = {
88*cf78ab8cSAndroid Build Coastguard Worker    'UUID': TEST_READ_UUID,
89*cf78ab8cSAndroid Build Coastguard Worker    'Property': 'PROPERTY_READ',
90*cf78ab8cSAndroid Build Coastguard Worker    'Permission': 'PERMISSION_READ',
91*cf78ab8cSAndroid Build Coastguard Worker    'Data': READ_DATA,
92*cf78ab8cSAndroid Build Coastguard Worker}
93*cf78ab8cSAndroid Build Coastguard WorkerSECOND_READ_CHARACTERISTIC = {
94*cf78ab8cSAndroid Build Coastguard Worker    'UUID': TEST_SECOND_READ_UUID,
95*cf78ab8cSAndroid Build Coastguard Worker    'Property': 'PROPERTY_READ',
96*cf78ab8cSAndroid Build Coastguard Worker    'Permission': 'PERMISSION_READ',
97*cf78ab8cSAndroid Build Coastguard Worker    'Data': SECOND_READ_DATA,
98*cf78ab8cSAndroid Build Coastguard Worker}
99*cf78ab8cSAndroid Build Coastguard WorkerTHIRD_READ_CHARACTERISTIC = {
100*cf78ab8cSAndroid Build Coastguard Worker    'UUID': TEST_THIRD_READ_UUID,
101*cf78ab8cSAndroid Build Coastguard Worker    'Property': 'PROPERTY_READ',
102*cf78ab8cSAndroid Build Coastguard Worker    'Permission': 'PERMISSION_READ',
103*cf78ab8cSAndroid Build Coastguard Worker    'Data': THIRD_READ_DATA,
104*cf78ab8cSAndroid Build Coastguard Worker}
105*cf78ab8cSAndroid Build Coastguard Worker# Service data in json format for Ble Server.
106*cf78ab8cSAndroid Build Coastguard WorkerSERVICE = {
107*cf78ab8cSAndroid Build Coastguard Worker    'UUID': TEST_BLE_SERVICE_UUID,
108*cf78ab8cSAndroid Build Coastguard Worker    'Type': 'SERVICE_TYPE_PRIMARY',
109*cf78ab8cSAndroid Build Coastguard Worker    'Characteristics': [
110*cf78ab8cSAndroid Build Coastguard Worker        WRITE_CHARACTERISTIC,
111*cf78ab8cSAndroid Build Coastguard Worker        SECOND_WRITE_CHARACTERISTIC,
112*cf78ab8cSAndroid Build Coastguard Worker        READ_CHARACTERISTIC,
113*cf78ab8cSAndroid Build Coastguard Worker        SECOND_READ_CHARACTERISTIC,
114*cf78ab8cSAndroid Build Coastguard Worker        THIRD_READ_CHARACTERISTIC,
115*cf78ab8cSAndroid Build Coastguard Worker    ],
116*cf78ab8cSAndroid Build Coastguard Worker}
117*cf78ab8cSAndroid Build Coastguard Worker# Macros for literal string.
118*cf78ab8cSAndroid Build Coastguard WorkerUUID = 'UUID'
119*cf78ab8cSAndroid Build Coastguard WorkerGATT_SUCCESS = 'GATT_SUCCESS'
120*cf78ab8cSAndroid Build Coastguard WorkerSTATE = 'newState'
121*cf78ab8cSAndroid Build Coastguard WorkerSTATUS = 'status'
122*cf78ab8cSAndroid Build Coastguard Worker
123*cf78ab8cSAndroid Build Coastguard Worker
124*cf78ab8cSAndroid Build Coastguard Workerdef IsRequiredScanResult(scan_result: callback_event.CallbackEvent) -> bool:
125*cf78ab8cSAndroid Build Coastguard Worker  result = scan_result.data['result']
126*cf78ab8cSAndroid Build Coastguard Worker  for service in result['ScanRecord']['Services']:
127*cf78ab8cSAndroid Build Coastguard Worker    if service[UUID] == TEST_BLE_SERVICE_UUID and service['Data'] == DATA:
128*cf78ab8cSAndroid Build Coastguard Worker      return True
129*cf78ab8cSAndroid Build Coastguard Worker  return False
130*cf78ab8cSAndroid Build Coastguard Worker
131*cf78ab8cSAndroid Build Coastguard Worker
132*cf78ab8cSAndroid Build Coastguard Workerdef Discover(
133*cf78ab8cSAndroid Build Coastguard Worker    scanner: android_device.AndroidDevice,
134*cf78ab8cSAndroid Build Coastguard Worker    advertiser: android_device.AndroidDevice,
135*cf78ab8cSAndroid Build Coastguard Worker) -> dict[str, Any]:
136*cf78ab8cSAndroid Build Coastguard Worker  """Logic for BLE scan and advertising.
137*cf78ab8cSAndroid Build Coastguard Worker
138*cf78ab8cSAndroid Build Coastguard Worker  Steps:
139*cf78ab8cSAndroid Build Coastguard Worker    1. Advertiser starts advertising and gets a startSuccess callback.
140*cf78ab8cSAndroid Build Coastguard Worker    2. Scanner starts scanning and finds advertiser from scan results.
141*cf78ab8cSAndroid Build Coastguard Worker
142*cf78ab8cSAndroid Build Coastguard Worker  Verifies:
143*cf78ab8cSAndroid Build Coastguard Worker    Advertiser is discovered within 5s by scanner.
144*cf78ab8cSAndroid Build Coastguard Worker
145*cf78ab8cSAndroid Build Coastguard Worker  Args:
146*cf78ab8cSAndroid Build Coastguard Worker    scanner: AndroidDevice. The device that starts BLE scan to find target.
147*cf78ab8cSAndroid Build Coastguard Worker    advertiser: AndroidDevice. The device that keeps advertising so other
148*cf78ab8cSAndroid Build Coastguard Worker      devices acknowledge it.
149*cf78ab8cSAndroid Build Coastguard Worker
150*cf78ab8cSAndroid Build Coastguard Worker  Returns:
151*cf78ab8cSAndroid Build Coastguard Worker    dict. Scan results.
152*cf78ab8cSAndroid Build Coastguard Worker
153*cf78ab8cSAndroid Build Coastguard Worker  Raises:
154*cf78ab8cSAndroid Build Coastguard Worker    TimeoutError: The expected event does not occur within the time limit.
155*cf78ab8cSAndroid Build Coastguard Worker  """
156*cf78ab8cSAndroid Build Coastguard Worker  # Retry initial command in case command is lost after triggering a reset
157*cf78ab8cSAndroid Build Coastguard Worker  max_attempts = 2
158*cf78ab8cSAndroid Build Coastguard Worker  for attempt_num in range(max_attempts):
159*cf78ab8cSAndroid Build Coastguard Worker    advertiser.advertise_callback = advertiser.mbs.bleStartAdvertising(
160*cf78ab8cSAndroid Build Coastguard Worker        ADVERTISE_SETTINGS, ADVERTISE_DATA, SCAN_RESPONSE
161*cf78ab8cSAndroid Build Coastguard Worker    )
162*cf78ab8cSAndroid Build Coastguard Worker    scanner.scan_callback = scanner.mbs.bleStartScan(
163*cf78ab8cSAndroid Build Coastguard Worker        [SCAN_FILTER], SCAN_SETTINGS
164*cf78ab8cSAndroid Build Coastguard Worker    )
165*cf78ab8cSAndroid Build Coastguard Worker    success = False
166*cf78ab8cSAndroid Build Coastguard Worker    for _ in range(ADVERTISING_START_TIME):
167*cf78ab8cSAndroid Build Coastguard Worker      failure = advertiser.advertise_callback.getAll('onStartFailure')
168*cf78ab8cSAndroid Build Coastguard Worker      if failure:
169*cf78ab8cSAndroid Build Coastguard Worker        logging.warning(
170*cf78ab8cSAndroid Build Coastguard Worker            "'onStartFailure' event detected after bleStartAdvertising"
171*cf78ab8cSAndroid Build Coastguard Worker        )
172*cf78ab8cSAndroid Build Coastguard Worker      success = advertiser.advertise_callback.getAll('onStartSuccess')
173*cf78ab8cSAndroid Build Coastguard Worker      if success:
174*cf78ab8cSAndroid Build Coastguard Worker        break
175*cf78ab8cSAndroid Build Coastguard Worker      time.sleep(1)
176*cf78ab8cSAndroid Build Coastguard Worker    else:
177*cf78ab8cSAndroid Build Coastguard Worker      logging.error(
178*cf78ab8cSAndroid Build Coastguard Worker          'Timed out after %ss waiting for an "onStartSuccess" event ',
179*cf78ab8cSAndroid Build Coastguard Worker          ADVERTISING_START_TIME,
180*cf78ab8cSAndroid Build Coastguard Worker      )
181*cf78ab8cSAndroid Build Coastguard Worker    if not success:
182*cf78ab8cSAndroid Build Coastguard Worker      if attempt_num < max_attempts - 1:
183*cf78ab8cSAndroid Build Coastguard Worker        logging.warning(
184*cf78ab8cSAndroid Build Coastguard Worker            "'onStartSuccess' event was not received after "
185*cf78ab8cSAndroid Build Coastguard Worker            'bleStartAdvertising. Retrying... (%d)',
186*cf78ab8cSAndroid Build Coastguard Worker            attempt_num + 1,
187*cf78ab8cSAndroid Build Coastguard Worker        )
188*cf78ab8cSAndroid Build Coastguard Worker      else:
189*cf78ab8cSAndroid Build Coastguard Worker        raise TimeoutError(
190*cf78ab8cSAndroid Build Coastguard Worker            f'Timed out after {max_attempts} retries of '
191*cf78ab8cSAndroid Build Coastguard Worker            f'{ADVERTISING_START_TIME}s waiting for an '
192*cf78ab8cSAndroid Build Coastguard Worker            '"onStartSuccess" event '
193*cf78ab8cSAndroid Build Coastguard Worker        )
194*cf78ab8cSAndroid Build Coastguard Worker
195*cf78ab8cSAndroid Build Coastguard Worker  advertiser.log.info('BLE advertising started')
196*cf78ab8cSAndroid Build Coastguard Worker  time.sleep(SCAN_TIMEOUT)
197*cf78ab8cSAndroid Build Coastguard Worker  scan_result = scanner.scan_callback.waitForEvent(
198*cf78ab8cSAndroid Build Coastguard Worker      'onScanResult', IsRequiredScanResult, SCAN_TIMEOUT
199*cf78ab8cSAndroid Build Coastguard Worker  )
200*cf78ab8cSAndroid Build Coastguard Worker  scan_success = False
201*cf78ab8cSAndroid Build Coastguard Worker  scan_response_found = False
202*cf78ab8cSAndroid Build Coastguard Worker  result = scan_result.data['result']
203*cf78ab8cSAndroid Build Coastguard Worker  scan_start_to_result_time_ms = scan_result.data['StartToResultTimeDeltaMs']
204*cf78ab8cSAndroid Build Coastguard Worker  for service in result['ScanRecord']['Services']:
205*cf78ab8cSAndroid Build Coastguard Worker    if service[UUID] == TEST_BLE_SERVICE_UUID and service['Data'] == DATA:
206*cf78ab8cSAndroid Build Coastguard Worker      scanner.connect_to_address = result['Device']['Address']
207*cf78ab8cSAndroid Build Coastguard Worker      scan_success = True
208*cf78ab8cSAndroid Build Coastguard Worker    if (
209*cf78ab8cSAndroid Build Coastguard Worker        service[UUID] == TEST_SCAN_RESPONSE_UUID
210*cf78ab8cSAndroid Build Coastguard Worker        and service['Data'] == SCAN_RESPONSE_DATA
211*cf78ab8cSAndroid Build Coastguard Worker    ):
212*cf78ab8cSAndroid Build Coastguard Worker      scan_response_found = True
213*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_true(
214*cf78ab8cSAndroid Build Coastguard Worker      scan_success, 'Advertiser is not found inside %d seconds' % SCAN_TIMEOUT
215*cf78ab8cSAndroid Build Coastguard Worker  )
216*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_true(scan_response_found, 'Scan response is not found')
217*cf78ab8cSAndroid Build Coastguard Worker  logging.info('Discovery metrics: %d', scan_start_to_result_time_ms)
218*cf78ab8cSAndroid Build Coastguard Worker  return result
219*cf78ab8cSAndroid Build Coastguard Worker
220*cf78ab8cSAndroid Build Coastguard Worker
221*cf78ab8cSAndroid Build Coastguard Workerdef StartScanning(
222*cf78ab8cSAndroid Build Coastguard Worker    scanner: android_device.AndroidDevice, scan_duration: int
223*cf78ab8cSAndroid Build Coastguard Worker) -> list[dict[str, Any]]:
224*cf78ab8cSAndroid Build Coastguard Worker  """Logic for BLE scanning for advertisers.
225*cf78ab8cSAndroid Build Coastguard Worker
226*cf78ab8cSAndroid Build Coastguard Worker  Steps:
227*cf78ab8cSAndroid Build Coastguard Worker    1. Scanner starts scanning with retries
228*cf78ab8cSAndroid Build Coastguard Worker    2. Retrieves the ScanResult
229*cf78ab8cSAndroid Build Coastguard Worker
230*cf78ab8cSAndroid Build Coastguard Worker  Verifies:
231*cf78ab8cSAndroid Build Coastguard Worker    Advertiser is discovered within timeout by scanner.
232*cf78ab8cSAndroid Build Coastguard Worker
233*cf78ab8cSAndroid Build Coastguard Worker  Args:
234*cf78ab8cSAndroid Build Coastguard Worker    scanner: AndroidDevice. The device that starts BLE scan to find advertisers.
235*cf78ab8cSAndroid Build Coastguard Worker    scan_duration: Number of seconds for each scan attempt
236*cf78ab8cSAndroid Build Coastguard Worker
237*cf78ab8cSAndroid Build Coastguard Worker  Returns:
238*cf78ab8cSAndroid Build Coastguard Worker    List of dicts containing Scan results.
239*cf78ab8cSAndroid Build Coastguard Worker
240*cf78ab8cSAndroid Build Coastguard Worker  Raises:
241*cf78ab8cSAndroid Build Coastguard Worker    TimeoutError: The expected event does not occur within the time limit.
242*cf78ab8cSAndroid Build Coastguard Worker  """
243*cf78ab8cSAndroid Build Coastguard Worker  # Retry initial command in case command is lost after triggering a reset
244*cf78ab8cSAndroid Build Coastguard Worker  max_attempts = 3
245*cf78ab8cSAndroid Build Coastguard Worker  scan_success = False
246*cf78ab8cSAndroid Build Coastguard Worker  result = []
247*cf78ab8cSAndroid Build Coastguard Worker  scan_result = None
248*cf78ab8cSAndroid Build Coastguard Worker  for attempt_num in range(max_attempts):
249*cf78ab8cSAndroid Build Coastguard Worker    scanner.scan_callback = scanner.mbs.bleStartScan()
250*cf78ab8cSAndroid Build Coastguard Worker    scanner.log.info('BLE scanning started')
251*cf78ab8cSAndroid Build Coastguard Worker    failure = scanner.scan_callback.getAll('onScanFailed')
252*cf78ab8cSAndroid Build Coastguard Worker    if failure:
253*cf78ab8cSAndroid Build Coastguard Worker      logging.warning("'onScanFailed' event detected after bleStartScan")
254*cf78ab8cSAndroid Build Coastguard Worker      continue
255*cf78ab8cSAndroid Build Coastguard Worker    success = False
256*cf78ab8cSAndroid Build Coastguard Worker    for _ in range(int(SCAN_TIMEOUT / scan_duration)):
257*cf78ab8cSAndroid Build Coastguard Worker      time.sleep(scan_duration)
258*cf78ab8cSAndroid Build Coastguard Worker      scan_result = scanner.scan_callback.getAll('onScanResult')
259*cf78ab8cSAndroid Build Coastguard Worker      if scan_result:
260*cf78ab8cSAndroid Build Coastguard Worker        success = True
261*cf78ab8cSAndroid Build Coastguard Worker        break
262*cf78ab8cSAndroid Build Coastguard Worker    else:
263*cf78ab8cSAndroid Build Coastguard Worker      logging.error(
264*cf78ab8cSAndroid Build Coastguard Worker          'Timed out after %ss waiting for an "onScanResult" event ',
265*cf78ab8cSAndroid Build Coastguard Worker          SCAN_TIMEOUT,
266*cf78ab8cSAndroid Build Coastguard Worker      )
267*cf78ab8cSAndroid Build Coastguard Worker    if success:
268*cf78ab8cSAndroid Build Coastguard Worker      break
269*cf78ab8cSAndroid Build Coastguard Worker    if attempt_num < max_attempts - 1:
270*cf78ab8cSAndroid Build Coastguard Worker      logging.warning(
271*cf78ab8cSAndroid Build Coastguard Worker          "'onScanResult' event was not received after "
272*cf78ab8cSAndroid Build Coastguard Worker          'bleStartScan. Retrying... (%d)',
273*cf78ab8cSAndroid Build Coastguard Worker          attempt_num + 1,
274*cf78ab8cSAndroid Build Coastguard Worker      )
275*cf78ab8cSAndroid Build Coastguard Worker    else:
276*cf78ab8cSAndroid Build Coastguard Worker      raise TimeoutError(
277*cf78ab8cSAndroid Build Coastguard Worker          f'Timed out after {max_attempts} retries of '
278*cf78ab8cSAndroid Build Coastguard Worker          f'{SCAN_TIMEOUT}s waiting for an '
279*cf78ab8cSAndroid Build Coastguard Worker          '"onScanResult" event '
280*cf78ab8cSAndroid Build Coastguard Worker      )
281*cf78ab8cSAndroid Build Coastguard Worker
282*cf78ab8cSAndroid Build Coastguard Worker  if scan_result:
283*cf78ab8cSAndroid Build Coastguard Worker    scan_success = True
284*cf78ab8cSAndroid Build Coastguard Worker    result = [result.data['result'] for result in scan_result]
285*cf78ab8cSAndroid Build Coastguard Worker
286*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_true(
287*cf78ab8cSAndroid Build Coastguard Worker      scan_success, 'Advertiser is not found inside %d seconds' % SCAN_TIMEOUT
288*cf78ab8cSAndroid Build Coastguard Worker  )
289*cf78ab8cSAndroid Build Coastguard Worker  return result
290*cf78ab8cSAndroid Build Coastguard Worker
291*cf78ab8cSAndroid Build Coastguard Worker
292*cf78ab8cSAndroid Build Coastguard Workerdef StopDiscover(
293*cf78ab8cSAndroid Build Coastguard Worker    scanner: android_device.AndroidDevice,
294*cf78ab8cSAndroid Build Coastguard Worker    advertiser: android_device.AndroidDevice,
295*cf78ab8cSAndroid Build Coastguard Worker) -> None:
296*cf78ab8cSAndroid Build Coastguard Worker  """Logic for stopping BLE scan and advertising.
297*cf78ab8cSAndroid Build Coastguard Worker
298*cf78ab8cSAndroid Build Coastguard Worker  Steps:
299*cf78ab8cSAndroid Build Coastguard Worker    1. Scanner stops scanning.
300*cf78ab8cSAndroid Build Coastguard Worker    2. Advertiser stops advertising.
301*cf78ab8cSAndroid Build Coastguard Worker
302*cf78ab8cSAndroid Build Coastguard Worker  Args:
303*cf78ab8cSAndroid Build Coastguard Worker    scanner: AndroidDevice. The device that starts BLE scan to find target.
304*cf78ab8cSAndroid Build Coastguard Worker    advertiser: AndroidDevice. The device that keeps advertising so other
305*cf78ab8cSAndroid Build Coastguard Worker      devices acknowledge it.
306*cf78ab8cSAndroid Build Coastguard Worker  """
307*cf78ab8cSAndroid Build Coastguard Worker  scanner.mbs.bleStopScan(scanner.scan_callback.callback_id)
308*cf78ab8cSAndroid Build Coastguard Worker  scanner.log.info('BLE scanning stopped')
309*cf78ab8cSAndroid Build Coastguard Worker  advertiser.mbs.bleStopAdvertising(advertiser.advertise_callback.callback_id)
310*cf78ab8cSAndroid Build Coastguard Worker  advertiser.log.info('BLE advertising stopped')
311*cf78ab8cSAndroid Build Coastguard Worker
312*cf78ab8cSAndroid Build Coastguard Worker
313*cf78ab8cSAndroid Build Coastguard Workerdef StopScanning(scanner: android_device.AndroidDevice) -> None:
314*cf78ab8cSAndroid Build Coastguard Worker  """Logic for stopping BLE scan.
315*cf78ab8cSAndroid Build Coastguard Worker
316*cf78ab8cSAndroid Build Coastguard Worker  Steps:
317*cf78ab8cSAndroid Build Coastguard Worker    1. Scanner stops scanning.
318*cf78ab8cSAndroid Build Coastguard Worker
319*cf78ab8cSAndroid Build Coastguard Worker  Args:
320*cf78ab8cSAndroid Build Coastguard Worker    scanner: AndroidDevice. The device that starts BLE scan to find target.
321*cf78ab8cSAndroid Build Coastguard Worker  """
322*cf78ab8cSAndroid Build Coastguard Worker  scanner.mbs.bleStopScan(scanner.scan_callback.callback_id)
323*cf78ab8cSAndroid Build Coastguard Worker  scanner.log.info('BLE scanning stopped')
324*cf78ab8cSAndroid Build Coastguard Worker
325*cf78ab8cSAndroid Build Coastguard Worker
326*cf78ab8cSAndroid Build Coastguard Workerdef Connect(
327*cf78ab8cSAndroid Build Coastguard Worker    client: android_device.AndroidDevice, server: android_device.AndroidDevice
328*cf78ab8cSAndroid Build Coastguard Worker) -> None:
329*cf78ab8cSAndroid Build Coastguard Worker  """Logic for create a Gatt connection between a client and a server.
330*cf78ab8cSAndroid Build Coastguard Worker
331*cf78ab8cSAndroid Build Coastguard Worker  Steps:
332*cf78ab8cSAndroid Build Coastguard Worker    1. Server starts and service added properly.
333*cf78ab8cSAndroid Build Coastguard Worker    2. Client connects to server via Gatt, connection completes with
334*cf78ab8cSAndroid Build Coastguard Worker    GATT_SUCCESS within TIMEOUT, onConnectionStateChange/STATE_CONNECTED is
335*cf78ab8cSAndroid Build Coastguard Worker    called EXACTLY once.
336*cf78ab8cSAndroid Build Coastguard Worker
337*cf78ab8cSAndroid Build Coastguard Worker  Verifies:
338*cf78ab8cSAndroid Build Coastguard Worker    Both the client and the server consider themselves connected to each other.
339*cf78ab8cSAndroid Build Coastguard Worker
340*cf78ab8cSAndroid Build Coastguard Worker  Args:
341*cf78ab8cSAndroid Build Coastguard Worker    client: AndroidDevice. The device that behaves as GATT client.
342*cf78ab8cSAndroid Build Coastguard Worker    server: AndroidDevice. The device that behaves as GATT server.
343*cf78ab8cSAndroid Build Coastguard Worker  """
344*cf78ab8cSAndroid Build Coastguard Worker  server.server_callback = server.mbs.bleStartServer([SERVICE])
345*cf78ab8cSAndroid Build Coastguard Worker  start_server_result = server.server_callback.waitAndGet('onServiceAdded', 30)
346*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(start_server_result.data[STATUS], GATT_SUCCESS)
347*cf78ab8cSAndroid Build Coastguard Worker  uuids = [
348*cf78ab8cSAndroid Build Coastguard Worker      characteristic[UUID]
349*cf78ab8cSAndroid Build Coastguard Worker      for characteristic in start_server_result.data['Service'][
350*cf78ab8cSAndroid Build Coastguard Worker          'Characteristics'
351*cf78ab8cSAndroid Build Coastguard Worker      ]
352*cf78ab8cSAndroid Build Coastguard Worker  ]
353*cf78ab8cSAndroid Build Coastguard Worker  for uuid in [
354*cf78ab8cSAndroid Build Coastguard Worker      characteristic[UUID] for characteristic in SERVICE['Characteristics']
355*cf78ab8cSAndroid Build Coastguard Worker  ]:
356*cf78ab8cSAndroid Build Coastguard Worker    asserts.assert_true(uuid in uuids, 'Failed to find uuid %s.' % uuid)
357*cf78ab8cSAndroid Build Coastguard Worker  server.log.info('BLE server started')
358*cf78ab8cSAndroid Build Coastguard Worker  client.client_callback = client.mbs.bleConnectGatt(client.connect_to_address)
359*cf78ab8cSAndroid Build Coastguard Worker  start_client_result = client.client_callback.waitAndGet(
360*cf78ab8cSAndroid Build Coastguard Worker      'onConnectionStateChange', CONNECTION_TIMEOUT
361*cf78ab8cSAndroid Build Coastguard Worker  )
362*cf78ab8cSAndroid Build Coastguard Worker  extra_events = client.client_callback.getAll('onConnectionStateChange')
363*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_false(
364*cf78ab8cSAndroid Build Coastguard Worker      extra_events,
365*cf78ab8cSAndroid Build Coastguard Worker      'Got unexpected onConnectionStateChange events: %s',
366*cf78ab8cSAndroid Build Coastguard Worker      extra_events,
367*cf78ab8cSAndroid Build Coastguard Worker  )
368*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(start_client_result.data[STATUS], GATT_SUCCESS)
369*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(start_client_result.data[STATE], 'STATE_CONNECTED')
370*cf78ab8cSAndroid Build Coastguard Worker  client.log.info('BLE client connected')
371*cf78ab8cSAndroid Build Coastguard Worker  # Verify that the server side also considers itself connected.
372*cf78ab8cSAndroid Build Coastguard Worker  server_event = server.server_callback.waitAndGet('onConnectionStateChange')
373*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(server_event.data[STATUS], GATT_SUCCESS)
374*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(
375*cf78ab8cSAndroid Build Coastguard Worker      server_event.data[STATE],
376*cf78ab8cSAndroid Build Coastguard Worker      'STATE_CONNECTED',
377*cf78ab8cSAndroid Build Coastguard Worker      'The server side does not consider itself connected, error!',
378*cf78ab8cSAndroid Build Coastguard Worker  )
379*cf78ab8cSAndroid Build Coastguard Worker  logging.info('Gatt connection complete.')
380*cf78ab8cSAndroid Build Coastguard Worker  logging.info(
381*cf78ab8cSAndroid Build Coastguard Worker      'Connection metrics: %d', start_client_result.data['gattConnectionTimeMs']
382*cf78ab8cSAndroid Build Coastguard Worker  )
383*cf78ab8cSAndroid Build Coastguard Worker
384*cf78ab8cSAndroid Build Coastguard Worker
385*cf78ab8cSAndroid Build Coastguard Workerdef Disconnect(
386*cf78ab8cSAndroid Build Coastguard Worker    client: android_device.AndroidDevice, server: android_device.AndroidDevice
387*cf78ab8cSAndroid Build Coastguard Worker) -> None:
388*cf78ab8cSAndroid Build Coastguard Worker  """Logic for stopping BLE client and server.
389*cf78ab8cSAndroid Build Coastguard Worker
390*cf78ab8cSAndroid Build Coastguard Worker  Steps:
391*cf78ab8cSAndroid Build Coastguard Worker    1. Client calls disconnect, gets a callback with STATE_DISCONNECTED and
392*cf78ab8cSAndroid Build Coastguard Worker    GATT_SUCCESS.
393*cf78ab8cSAndroid Build Coastguard Worker    2. Server closes.
394*cf78ab8cSAndroid Build Coastguard Worker
395*cf78ab8cSAndroid Build Coastguard Worker  Verifies: Client gets corresponding callback.
396*cf78ab8cSAndroid Build Coastguard Worker
397*cf78ab8cSAndroid Build Coastguard Worker  Args:
398*cf78ab8cSAndroid Build Coastguard Worker    client: AndroidDevice. The device that behaves as GATT client.
399*cf78ab8cSAndroid Build Coastguard Worker    server: AndroidDevice. The device that behaves as GATT server.
400*cf78ab8cSAndroid Build Coastguard Worker  """
401*cf78ab8cSAndroid Build Coastguard Worker  client.mbs.bleDisconnect()
402*cf78ab8cSAndroid Build Coastguard Worker  stop_client_result = client.client_callback.waitAndGet(
403*cf78ab8cSAndroid Build Coastguard Worker      'onConnectionStateChange', 30
404*cf78ab8cSAndroid Build Coastguard Worker  )
405*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(stop_client_result.data[STATUS], GATT_SUCCESS)
406*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(stop_client_result.data[STATE], 'STATE_DISCONNECTED')
407*cf78ab8cSAndroid Build Coastguard Worker  client.log.info('BLE client disconnected')
408*cf78ab8cSAndroid Build Coastguard Worker  server.mbs.bleStopServer()
409*cf78ab8cSAndroid Build Coastguard Worker  server.log.info('BLE server stopped')
410*cf78ab8cSAndroid Build Coastguard Worker
411*cf78ab8cSAndroid Build Coastguard Worker
412*cf78ab8cSAndroid Build Coastguard Workerdef DiscoverServices(client: android_device.AndroidDevice) -> None:
413*cf78ab8cSAndroid Build Coastguard Worker  """Logic for BLE services discovery.
414*cf78ab8cSAndroid Build Coastguard Worker
415*cf78ab8cSAndroid Build Coastguard Worker  Steps:
416*cf78ab8cSAndroid Build Coastguard Worker    1. Client successfully completes service discovery & gets
417*cf78ab8cSAndroid Build Coastguard Worker    onServicesDiscovered callback within some TIMEOUT, onServicesDiscovered/
418*cf78ab8cSAndroid Build Coastguard Worker    GATT_SUCCESS is called EXACTLY once.
419*cf78ab8cSAndroid Build Coastguard Worker    2. Client discovers the readable and writable characteristics.
420*cf78ab8cSAndroid Build Coastguard Worker
421*cf78ab8cSAndroid Build Coastguard Worker  Verifies:
422*cf78ab8cSAndroid Build Coastguard Worker    Client gets corresponding callback.
423*cf78ab8cSAndroid Build Coastguard Worker
424*cf78ab8cSAndroid Build Coastguard Worker  Args:
425*cf78ab8cSAndroid Build Coastguard Worker    client: AndroidDevice. The device that behaves as GATT client.
426*cf78ab8cSAndroid Build Coastguard Worker  """
427*cf78ab8cSAndroid Build Coastguard Worker  client.mbs.bleDiscoverServices()
428*cf78ab8cSAndroid Build Coastguard Worker  time.sleep(CONNECTION_TIMEOUT)
429*cf78ab8cSAndroid Build Coastguard Worker  discover_services_results = client.client_callback.getAll(
430*cf78ab8cSAndroid Build Coastguard Worker      'onServiceDiscovered'
431*cf78ab8cSAndroid Build Coastguard Worker  )
432*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(len(discover_services_results), 1)
433*cf78ab8cSAndroid Build Coastguard Worker  service_discovered = False
434*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(discover_services_results[0].data[STATUS], GATT_SUCCESS)
435*cf78ab8cSAndroid Build Coastguard Worker  for service in discover_services_results[0].data['Services']:
436*cf78ab8cSAndroid Build Coastguard Worker    if service['UUID'] == TEST_BLE_SERVICE_UUID:
437*cf78ab8cSAndroid Build Coastguard Worker      service_discovered = True
438*cf78ab8cSAndroid Build Coastguard Worker      uuids = [
439*cf78ab8cSAndroid Build Coastguard Worker          characteristic[UUID] for characteristic in service['Characteristics']
440*cf78ab8cSAndroid Build Coastguard Worker      ]
441*cf78ab8cSAndroid Build Coastguard Worker      for uuid in [
442*cf78ab8cSAndroid Build Coastguard Worker          characteristic[UUID] for characteristic in SERVICE['Characteristics']
443*cf78ab8cSAndroid Build Coastguard Worker      ]:
444*cf78ab8cSAndroid Build Coastguard Worker        asserts.assert_true(uuid in uuids, 'Failed to find uuid %s.' % uuid)
445*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_true(
446*cf78ab8cSAndroid Build Coastguard Worker      service_discovered, 'Failed to discover the customize service'
447*cf78ab8cSAndroid Build Coastguard Worker  )
448*cf78ab8cSAndroid Build Coastguard Worker  client.log.info('BLE discover services finished')
449*cf78ab8cSAndroid Build Coastguard Worker
450*cf78ab8cSAndroid Build Coastguard Worker
451*cf78ab8cSAndroid Build Coastguard Workerdef ReadCharacteristic(client: android_device.AndroidDevice) -> None:
452*cf78ab8cSAndroid Build Coastguard Worker  """Logic for BLE characteristic retrieval.
453*cf78ab8cSAndroid Build Coastguard Worker
454*cf78ab8cSAndroid Build Coastguard Worker  Steps:
455*cf78ab8cSAndroid Build Coastguard Worker    1. Client reads a characteristic from server & gets true.
456*cf78ab8cSAndroid Build Coastguard Worker    2. Server calls sendResponse & client gets onCharacteristicRead.
457*cf78ab8cSAndroid Build Coastguard Worker
458*cf78ab8cSAndroid Build Coastguard Worker  Verifies:
459*cf78ab8cSAndroid Build Coastguard Worker    Client gets corresponding callback.
460*cf78ab8cSAndroid Build Coastguard Worker
461*cf78ab8cSAndroid Build Coastguard Worker  Args:
462*cf78ab8cSAndroid Build Coastguard Worker    client: AndroidDevice. The device that behaves as GATT client.
463*cf78ab8cSAndroid Build Coastguard Worker  """
464*cf78ab8cSAndroid Build Coastguard Worker  read_operation_result = client.mbs.bleReadOperation(
465*cf78ab8cSAndroid Build Coastguard Worker      TEST_BLE_SERVICE_UUID, TEST_READ_UUID
466*cf78ab8cSAndroid Build Coastguard Worker  )
467*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_true(
468*cf78ab8cSAndroid Build Coastguard Worker      read_operation_result, 'BLE read operation failed to start'
469*cf78ab8cSAndroid Build Coastguard Worker  )
470*cf78ab8cSAndroid Build Coastguard Worker  read_operation_result = client.client_callback.waitAndGet(
471*cf78ab8cSAndroid Build Coastguard Worker      'onCharacteristicRead', 30
472*cf78ab8cSAndroid Build Coastguard Worker  )
473*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(read_operation_result.data[STATUS], GATT_SUCCESS)
474*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(read_operation_result.data['Data'], READ_DATA)
475*cf78ab8cSAndroid Build Coastguard Worker  client.log.info('Read operation finished')
476*cf78ab8cSAndroid Build Coastguard Worker  read_operation_result = client.mbs.bleReadOperation(
477*cf78ab8cSAndroid Build Coastguard Worker      TEST_BLE_SERVICE_UUID, TEST_SECOND_READ_UUID
478*cf78ab8cSAndroid Build Coastguard Worker  )
479*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_true(
480*cf78ab8cSAndroid Build Coastguard Worker      read_operation_result, 'BLE read operation failed to start'
481*cf78ab8cSAndroid Build Coastguard Worker  )
482*cf78ab8cSAndroid Build Coastguard Worker  read_operation_result = client.client_callback.waitAndGet(
483*cf78ab8cSAndroid Build Coastguard Worker      'onCharacteristicRead', 30
484*cf78ab8cSAndroid Build Coastguard Worker  )
485*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(read_operation_result.data[STATUS], GATT_SUCCESS)
486*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(read_operation_result.data['Data'], SECOND_READ_DATA)
487*cf78ab8cSAndroid Build Coastguard Worker  client.log.info('Second read operation finished')
488*cf78ab8cSAndroid Build Coastguard Worker  read_operation_result = client.mbs.bleReadOperation(
489*cf78ab8cSAndroid Build Coastguard Worker      TEST_BLE_SERVICE_UUID, TEST_THIRD_READ_UUID
490*cf78ab8cSAndroid Build Coastguard Worker  )
491*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_true(
492*cf78ab8cSAndroid Build Coastguard Worker      read_operation_result, 'BLE read operation failed to start'
493*cf78ab8cSAndroid Build Coastguard Worker  )
494*cf78ab8cSAndroid Build Coastguard Worker  read_operation_result = client.client_callback.waitAndGet(
495*cf78ab8cSAndroid Build Coastguard Worker      'onCharacteristicRead', 30
496*cf78ab8cSAndroid Build Coastguard Worker  )
497*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(read_operation_result.data[STATUS], GATT_SUCCESS)
498*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(read_operation_result.data['Data'], THIRD_READ_DATA)
499*cf78ab8cSAndroid Build Coastguard Worker  client.log.info('Third read operation finished')
500*cf78ab8cSAndroid Build Coastguard Worker
501*cf78ab8cSAndroid Build Coastguard Worker
502*cf78ab8cSAndroid Build Coastguard Workerdef WriteCharacteristic(
503*cf78ab8cSAndroid Build Coastguard Worker    client: android_device.AndroidDevice, server: android_device.AndroidDevice
504*cf78ab8cSAndroid Build Coastguard Worker) -> None:
505*cf78ab8cSAndroid Build Coastguard Worker  """Logic for BLE characteristic write.
506*cf78ab8cSAndroid Build Coastguard Worker
507*cf78ab8cSAndroid Build Coastguard Worker  Steps:
508*cf78ab8cSAndroid Build Coastguard Worker    1. Client writes a characteristic to server & gets true.
509*cf78ab8cSAndroid Build Coastguard Worker    2. Server calls sendResponse & client gets onCharacteristicWrite.
510*cf78ab8cSAndroid Build Coastguard Worker
511*cf78ab8cSAndroid Build Coastguard Worker  Verifies:
512*cf78ab8cSAndroid Build Coastguard Worker    Client gets corresponding callback.
513*cf78ab8cSAndroid Build Coastguard Worker
514*cf78ab8cSAndroid Build Coastguard Worker  Args:
515*cf78ab8cSAndroid Build Coastguard Worker    client: AndroidDevice. The device that behaves as GATT client.
516*cf78ab8cSAndroid Build Coastguard Worker    server: AndroidDevice. The device that behaves as GATT server.
517*cf78ab8cSAndroid Build Coastguard Worker  """
518*cf78ab8cSAndroid Build Coastguard Worker  write_operation_result = client.mbs.bleWriteOperation(
519*cf78ab8cSAndroid Build Coastguard Worker      TEST_BLE_SERVICE_UUID, TEST_WRITE_UUID, WRITE_DATA
520*cf78ab8cSAndroid Build Coastguard Worker  )
521*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_true(
522*cf78ab8cSAndroid Build Coastguard Worker      write_operation_result, 'BLE write operation failed to start'
523*cf78ab8cSAndroid Build Coastguard Worker  )
524*cf78ab8cSAndroid Build Coastguard Worker  server_write_operation_result = server.server_callback.waitAndGet(
525*cf78ab8cSAndroid Build Coastguard Worker      'onCharacteristicWriteRequest', 30
526*cf78ab8cSAndroid Build Coastguard Worker  )
527*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(server_write_operation_result.data['Data'], WRITE_DATA)
528*cf78ab8cSAndroid Build Coastguard Worker  client.client_callback.waitAndGet('onCharacteristicWrite', 30)
529*cf78ab8cSAndroid Build Coastguard Worker  client.log.info('Write operation finished')
530*cf78ab8cSAndroid Build Coastguard Worker  write_operation_result = client.mbs.bleWriteOperation(
531*cf78ab8cSAndroid Build Coastguard Worker      TEST_BLE_SERVICE_UUID, TEST_SECOND_WRITE_UUID, SECOND_WRITE_DATA
532*cf78ab8cSAndroid Build Coastguard Worker  )
533*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_true(
534*cf78ab8cSAndroid Build Coastguard Worker      write_operation_result, 'BLE write operation failed to start'
535*cf78ab8cSAndroid Build Coastguard Worker  )
536*cf78ab8cSAndroid Build Coastguard Worker  server_write_operation_result = server.server_callback.waitAndGet(
537*cf78ab8cSAndroid Build Coastguard Worker      'onCharacteristicWriteRequest', 30
538*cf78ab8cSAndroid Build Coastguard Worker  )
539*cf78ab8cSAndroid Build Coastguard Worker  asserts.assert_equal(
540*cf78ab8cSAndroid Build Coastguard Worker      server_write_operation_result.data['Data'], SECOND_WRITE_DATA
541*cf78ab8cSAndroid Build Coastguard Worker  )
542*cf78ab8cSAndroid Build Coastguard Worker  client.client_callback.waitAndGet('onCharacteristicWrite', 30)
543*cf78ab8cSAndroid Build Coastguard Worker  client.log.info('Second write operation finished')
544