1 #/usr/bin/env python3.4 2 # 3 # Copyright (C) 2016 The Android Open Source Project 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 # use this file except in compliance with the License. You may obtain a copy of 7 # 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, WITHOUT 13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 # License for the specific language governing permissions and limitations under 15 # the License. 16 """ 17 Ble libraries 18 """ 19 20 from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseMode 21 from acts.test_utils.bt.BleEnum import AdvertiseSettingsAdvertiseTxPower 22 from acts.test_utils.bt.BleEnum import ScanSettingsScanMode 23 from acts.test_utils.bt.bt_test_utils import TIMEOUT_SMALL 24 from acts.test_utils.bt.bt_test_utils import adv_fail 25 from acts.test_utils.bt.bt_test_utils import adv_succ 26 from acts.test_utils.bt.bt_test_utils import advertising_set_on_own_address_read 27 from acts.test_utils.bt.bt_test_utils import advertising_set_started 28 from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects 29 30 import time 31 import os 32 33 34 class BleLib(): 35 def __init__(self, log, mac_addr, dut): 36 self.advertisement_list = [] 37 self.dut = dut 38 self.log = log 39 self.mac_addr = mac_addr 40 self.default_timeout = 5 41 self.set_advertisement_list = [] 42 self.generic_uuid = "0000{}-0000-1000-8000-00805f9b34fb" 43 44 def _verify_ble_adv_started(self, advertise_callback): 45 """Helper for verifying if an advertisment started or not""" 46 regex = "({}|{})".format( 47 adv_succ.format(advertise_callback), 48 adv_fail.format(advertise_callback)) 49 try: 50 event = self.dut.ed.pop_events(regex, 5, TIMEOUT_SMALL) 51 except Empty: 52 self.dut.log.error("Failed to get success or failed event.") 53 return 54 if event[0]["name"] == adv_succ.format(advertise_callback): 55 self.dut.log.info("Advertisement started successfully.") 56 return True 57 else: 58 self.dut.log.info("Advertisement failed to start.") 59 return False 60 61 def start_generic_connectable_advertisement(self, line): 62 """Start a connectable LE advertisement""" 63 scan_response = None 64 if line: 65 scan_response = bool(line) 66 self.dut.droid.bleSetAdvertiseSettingsAdvertiseMode( 67 AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value) 68 self.dut.droid.bleSetAdvertiseSettingsIsConnectable(True) 69 advertise_callback, advertise_data, advertise_settings = ( 70 generate_ble_advertise_objects(self.dut.droid)) 71 if scan_response: 72 self.dut.droid.bleStartBleAdvertisingWithScanResponse( 73 advertise_callback, advertise_data, advertise_settings, 74 advertise_data) 75 else: 76 self.dut.droid.bleStartBleAdvertising( 77 advertise_callback, advertise_data, advertise_settings) 78 if self._verify_ble_adv_started(advertise_callback): 79 self.log.info( 80 "Tracking Callback ID: {}".format(advertise_callback)) 81 self.advertisement_list.append(advertise_callback) 82 self.log.info(self.advertisement_list) 83 84 def start_connectable_advertisement_set(self, line): 85 """Start Connectable Advertisement Set""" 86 adv_callback = self.dut.droid.bleAdvSetGenCallback() 87 adv_data = { 88 "includeDeviceName": True, 89 } 90 self.dut.droid.bleAdvSetStartAdvertisingSet({ 91 "connectable": 92 True, 93 "legacyMode": 94 False, 95 "primaryPhy": 96 "PHY_LE_1M", 97 "secondaryPhy": 98 "PHY_LE_1M", 99 "interval": 100 320 101 }, adv_data, None, None, None, 0, 0, adv_callback) 102 evt = self.dut.ed.pop_event( 103 advertising_set_started.format(adv_callback), self.default_timeout) 104 set_id = evt['data']['setId'] 105 self.log.error("did not receive the set started event!") 106 evt = self.dut.ed.pop_event( 107 advertising_set_on_own_address_read.format(set_id), 108 self.default_timeout) 109 address = evt['data']['address'] 110 self.log.info("Advertiser address is: {}".format(str(address))) 111 self.set_advertisement_list.append(adv_callback) 112 113 def stop_all_advertisement_set(self, line): 114 """Stop all Advertisement Sets""" 115 for adv in self.set_advertisement_list: 116 try: 117 self.dut.droid.bleAdvSetStopAdvertisingSet(adv) 118 except Exception as err: 119 self.log.error("Failed to stop advertisement: {}".format(err)) 120 121 def adv_add_service_uuid_list(self, line): 122 """Add service UUID to the LE advertisement inputs: 123 [uuid1 uuid2 ... uuidN]""" 124 uuids = line.split() 125 uuid_list = [] 126 for uuid in uuids: 127 if len(uuid) == 4: 128 uuid = self.generic_uuid.format(line) 129 uuid_list.append(uuid) 130 self.dut.droid.bleSetAdvertiseDataSetServiceUuids(uuid_list) 131 132 def adv_data_include_local_name(self, is_included): 133 """Include local name in the advertisement. inputs: [true|false]""" 134 self.dut.droid.bleSetAdvertiseDataIncludeDeviceName(bool(is_included)) 135 136 def adv_data_include_tx_power_level(self, is_included): 137 """Include tx power level in the advertisement. inputs: [true|false]""" 138 self.dut.droid.bleSetAdvertiseDataIncludeTxPowerLevel( 139 bool(is_included)) 140 141 def adv_data_add_manufacturer_data(self, line): 142 """Include manufacturer id and data to the advertisment: 143 [id data1 data2 ... dataN]""" 144 info = line.split() 145 manu_id = int(info[0]) 146 manu_data = [] 147 for data in info[1:]: 148 manu_data.append(int(data)) 149 self.dut.droid.bleAddAdvertiseDataManufacturerId(manu_id, manu_data) 150 151 def start_generic_nonconnectable_advertisement(self, line): 152 """Start a nonconnectable LE advertisement""" 153 self.dut.droid.bleSetAdvertiseSettingsAdvertiseMode( 154 AdvertiseSettingsAdvertiseMode.ADVERTISE_MODE_LOW_LATENCY.value) 155 self.dut.droid.bleSetAdvertiseSettingsIsConnectable(False) 156 advertise_callback, advertise_data, advertise_settings = ( 157 generate_ble_advertise_objects(self.dut.droid)) 158 self.dut.droid.bleStartBleAdvertising( 159 advertise_callback, advertise_data, advertise_settings) 160 if self._verify_ble_adv_started(advertise_callback): 161 self.log.info( 162 "Tracking Callback ID: {}".format(advertise_callback)) 163 self.advertisement_list.append(advertise_callback) 164 self.log.info(self.advertisement_list) 165 166 def stop_all_advertisements(self, line): 167 """Stop all LE advertisements""" 168 for callback_id in self.advertisement_list: 169 self.log.info("Stopping Advertisement {}".format(callback_id)) 170 self.dut.droid.bleStopBleAdvertising(callback_id) 171 time.sleep(1) 172 self.advertisement_list = [] 173 174 def do_ble_stop_advertisement(self, callback_id): 175 """Stop an LE advertisement""" 176 if not callback_id: 177 self.log.info("Need a callback ID") 178 return 179 callback_id = int(callback_id) 180 if callback_id not in self.advertisement_list: 181 self.log.info("Callback not in list of advertisements.") 182 return 183 self.dut.droid.bleStopBleAdvertising(callback_id) 184 self.advertisement_list.remove(callback_id) 185