1#!/usr/bin/env python3
2#
3# Copyright 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import logging
18import time
19import statistics
20from queue import Empty
21from concurrent.futures import ThreadPoolExecutor
22
23from acts.test_utils.bt.bt_gatt_utils import close_gatt_client
24from acts.test_utils.bt.bt_coc_test_utils import do_multi_connection_throughput
25from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
26from acts.test_utils.bt.bt_constants import gatt_cb_err
27from acts.test_utils.bt.bt_constants import gatt_cb_strings
28from acts.test_utils.bt.bt_constants import l2cap_coc_header_size
29from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
30from acts.test_utils.bt.bt_coc_test_utils import orchestrate_coc_connection
31from acts.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
32
33default_event_timeout = 10
34rssi_read_duration = 25
35
36
37def establish_ble_connection(client_ad, server_ad):
38    """Function to establish BLE connection between two BLE devices.
39
40    Args:
41        client_ad: the Android device performing the connection.
42        server_ad: the Android device accepting the connection.
43    Returns:
44        bluetooth_gatt: GATT object
45        gatt_callback: Gatt callback object
46        adv_callback: advertisement callback object
47        gatt_server: the gatt server
48    """
49    gatt_server_cb = server_ad.droid.gattServerCreateGattServerCallback()
50    gatt_server = server_ad.droid.gattServerOpenGattServer(gatt_server_cb)
51    try:
52        bluetooth_gatt, gatt_callback, adv_callback = (
53            orchestrate_gatt_connection(client_ad, server_ad))
54    except GattTestUtilsError as err:
55        logging.error(err)
56        return False
57    return bluetooth_gatt, gatt_callback, adv_callback, gatt_server
58
59
60def read_ble_rssi(client_ad, gatt_server, gatt_callback):
61    """Function to Read BLE RSSI of the remote BLE device.
62    Args:
63        client_ad: the Android device performing the connection.
64        gatt_server: the gatt server
65        gatt_callback:the gatt connection call back object
66    Returns:
67      ble_rssi: RSSI value of the remote BLE device
68    """
69    AVG_RSSI = []
70    end_time = time.time() + rssi_read_duration
71    logging.info("Reading BLE RSSI for {} sec".format(rssi_read_duration))
72    while time.time() < end_time:
73        expected_event = gatt_cb_strings['rd_remote_rssi'].format(
74            gatt_callback)
75        read_rssi = client_ad.droid.gattClientReadRSSI(gatt_server)
76        if read_rssi:
77            try:
78                event = client_ad.ed.pop_event(expected_event,
79                                               default_event_timeout)
80            except Empty:
81                logging.error(
82                    gatt_cb_err['rd_remote_rssi_err'].format(expected_event))
83                return False
84        rssi_value = event['data']['Rssi']
85        AVG_RSSI.append(rssi_value)
86    logging.debug("First & Last reading of RSSI :{:03d} & {:03d}".format(
87        AVG_RSSI[0], AVG_RSSI[-1]))
88    ble_rssi = statistics.mean(AVG_RSSI)
89    ble_rssi = round(ble_rssi, 2)
90
91    return ble_rssi
92
93
94def ble_coc_connection(client_ad, server_ad):
95    """Sets up the CoC connection between two Android devices.
96
97    Args:
98        client_ad: the Android device performing the connection.
99        server_ad: the Android device accepting the connection.
100
101    Returns:
102        True if connection was successful or false if unsuccessful,
103        gatt_callback: GATT callback object
104        client connection ID: Client connection ID
105        and server connection ID : server connection ID
106    """
107    #secured_conn: True if using secured connection
108    #le_connection_interval: LE Connection interval. 0 means use default.
109    #buffer_size : is the number of bytes per L2CAP data buffer
110    #le_tx_data_length: LE Data Length used by BT Controller to transmit.
111    is_secured = False
112    le_connection_interval = 30
113    buffer_size = 240
114    le_tx_data_length = buffer_size + l2cap_coc_header_size
115    gatt_server_cb = server_ad.droid.gattServerCreateGattServerCallback()
116    gatt_server = server_ad.droid.gattServerOpenGattServer(gatt_server_cb)
117
118    logging.info(
119        "orchestrate_ble_coc_connection. is_secured={}, Connection Interval={}msec, "
120        "buffer_size={}bytes".format(is_secured, le_connection_interval,
121                                     buffer_size))
122    try:
123        status, client_conn_id, server_conn_id, bluetooth_gatt, gatt_callback = orchestrate_coc_connection(
124            client_ad,
125            server_ad,
126            True,
127            is_secured,
128            le_connection_interval,
129            le_tx_data_length,
130            gatt_disconnection=False)
131    except Exception as err:
132        logging.info("Failed to esatablish COC connection".format(err))
133        return 0
134    return True, gatt_callback, gatt_server, bluetooth_gatt, client_conn_id
135
136
137def run_ble_throughput(server_ad, client_conn_id, client_ad,
138                       num_iterations=30):
139    """Function to measure Throughput from one client to one-or-many servers
140
141    Args:
142        server_ad: the Android device accepting the connection.
143        client_conn_id: the client connection ID.
144        client_ad: the Android device performing the connection.
145        num_iterations: The num_iterations is that number of repetitions of each
146        set of buffers r/w.
147    Returns:
148      data_rate: Throughput in terms of bytes per second, 0 if test failed.
149    """
150    # number_buffers is the total number of data buffers to transmit per
151    # set of buffers r/w.
152    # buffer_size is the number of bytes per L2CAP data buffer.
153    number_buffers = 100
154    buffer_size = 240
155    list_server_ad = [server_ad]
156    list_client_conn_id = [client_conn_id]
157    data_rate = do_multi_connection_throughput(client_ad, list_server_ad,
158                                               list_client_conn_id,
159                                               num_iterations, number_buffers,
160                                               buffer_size)
161    if data_rate <= 0:
162        return False
163    data_rate = data_rate * 8
164    logging.info(
165        "run_ble_coc_connection_throughput: throughput=%d bites per sec",
166        data_rate)
167    return data_rate
168
169
170def run_ble_throughput_and_read_rssi(client_ad, server_ad, client_conn_id,
171                                     gatt_server, gatt_callback):
172    """Function to measure ble rssi while sendinng data from client to server
173
174    Args:
175        client_ad: the Android device performing the connection.
176        server_ad: the Android device accepting the connection.
177        client_conn_id: the client connection ID.
178        gatt_server: the gatt server
179        gatt_callback: Gatt callback object
180    Returns:
181      ble_rssi: RSSI value of the remote BLE device.
182    """
183    executor = ThreadPoolExecutor(2)
184    ble_throughput = executor.submit(run_ble_throughput, client_ad,
185                                     client_conn_id, server_ad)
186    ble_rssi = executor.submit(read_ble_rssi, server_ad, gatt_server,
187                               gatt_callback)
188    logging.info("BLE RSSI is:{} dBm with data rate={} bites per sec ".format(
189        ble_rssi.result(), ble_throughput.result()))
190    return ble_rssi.result()
191
192
193def ble_gatt_disconnection(client_ad, bluetooth_gatt, gatt_callback):
194    """Function to disconnect GATT connection between client and server.
195
196    Args:
197        client_ad: the Android device performing the connection.
198        bluetooth_gatt: GATT object
199        gatt_callback:the gatt connection call back object
200    Returns:
201      ble_rssi: RSSI value of the remote BLE device
202    """
203    logging.info("Disconnecting from peripheral device.")
204    try:
205        disconnect_gatt_connection(client_ad, bluetooth_gatt, gatt_callback)
206        close_gatt_client(client_ad, bluetooth_gatt)
207    except GattTestUtilsError as err:
208        logging.error(err)
209        return False
210    return True
211