1#!/usr/bin/env python3 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""" 17Test the HFP profile for basic calling functionality. 18""" 19 20import time 21from acts.test_decorators import test_tracker_info 22from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest 23from acts_contrib.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest 24from acts_contrib.test_utils.bt import BtEnum 25from acts_contrib.test_utils.bt import bt_test_utils 26from acts_contrib.test_utils.car import car_telecom_utils 27from acts_contrib.test_utils.car import tel_telecom_utils 28from acts_contrib.test_utils.tel import tel_defines 29 30BLUETOOTH_PKG_NAME = "com.android.bluetooth" 31CALL_TYPE_OUTGOING = "CALL_TYPE_OUTGOING" 32CALL_TYPE_INCOMING = "CALL_TYPE_INCOMING" 33AUDIO_STATE_DISCONNECTED = 0 34AUDIO_STATE_ROUTED = 2 35SHORT_TIMEOUT = 5 36 37 38class BtCarHfpTest(BluetoothCarHfpBaseTest): 39 def setup_class(self): 40 if not super(BtCarHfpTest, self).setup_class(): 41 return False 42 # Disable the A2DP profile. 43 bt_test_utils.set_profile_priority(self.hf, self.ag, [ 44 BtEnum.BluetoothProfile.PBAP_CLIENT.value, 45 BtEnum.BluetoothProfile.A2DP_SINK.value 46 ], BtEnum.BluetoothPriorityLevel.PRIORITY_OFF) 47 bt_test_utils.set_profile_priority( 48 self.hf, self.ag, [BtEnum.BluetoothProfile.HEADSET_CLIENT.value], 49 BtEnum.BluetoothPriorityLevel.PRIORITY_ON) 50 51 if not bt_test_utils.connect_pri_to_sec( 52 self.hf, self.ag, 53 set([BtEnum.BluetoothProfile.HEADSET_CLIENT.value])): 54 self.log.error("Failed to connect.") 55 return False 56 return True 57 58 @test_tracker_info(uuid='4ce2195a-b70a-4584-912e-cbd20d20e19d') 59 @BluetoothBaseTest.bt_test_wrap 60 def test_default_calling_account(self): 61 """ 62 Tests if the default calling account is coming from the 63 bluetooth pacakge. 64 65 Precondition: 66 1. Devices are connected. 67 68 Steps: 69 1. Check if the default calling account is via Bluetooth package. 70 71 Returns: 72 Pass if True 73 Fail if False 74 75 Priority: 0 76 """ 77 selected_acc = \ 78 self.hf.droid.telecomGetUserSelectedOutgoingPhoneAccount() 79 if not selected_acc: 80 self.hf.log.error("No default account found.") 81 return False 82 83 # Check if the default account is from the Bluetooth package. This is a 84 # light weight check. 85 try: 86 acc_component_id = selected_acc['ComponentName'] 87 except KeyError: 88 self.hf.log.error("No component name for account {}".format( 89 selected_acc)) 90 return False 91 if not acc_component_id.startswith(BLUETOOTH_PKG_NAME): 92 self.hf.log.error("Component name does not start with pkg name {}". 93 format(selected_acc)) 94 return False 95 return True 96 97 @test_tracker_info(uuid='e579009d-05f3-4236-a698-5de8c11d73a9') 98 @BluetoothBaseTest.bt_test_wrap 99 def test_outgoing_call_hf(self): 100 """ 101 Tests if we can make a phone call from HF role and disconnect from HF 102 role. 103 104 Precondition: 105 1. Devices are connected. 106 107 Steps: 108 1. Make a call from HF role. 109 2. Wait for the HF, AG to be dialing and RE to see the call ringing. 110 3. Hangup the call on HF role. 111 4. Wait for all devices to hangup the call. 112 113 Returns: 114 Pass if True 115 Fail if False 116 117 Priority: 0 118 """ 119 return self.dial_a_hangup_b(self.hf, self.hf) 120 121 @test_tracker_info(uuid='c9d5f9cd-f275-4adf-b212-c2e9a70d4cac') 122 @BluetoothBaseTest.bt_test_wrap 123 def test_outgoing_call_ag(self): 124 """ 125 Tests if we can make a phone call from AG role and disconnect from AG 126 role. 127 128 Precondition: 129 1. Devices are connected. 130 131 Steps: 132 1. Make a call from AG role. 133 2. Wait for the HF, AG to be in dialing and RE to see the call ringing. 134 3. Hangup the call on AG role. 135 4. Wait for all devices to hangup the call. 136 137 Returns: 138 Pass if True 139 Fail if False 140 141 Priority: 0 142 """ 143 return self.dial_a_hangup_b(self.ag, self.ag) 144 145 @test_tracker_info(uuid='908c199b-ca65-4694-821d-1b864ee3fe69') 146 @BluetoothBaseTest.bt_test_wrap 147 def test_outgoing_dial_ag_hangup_hf(self): 148 """ 149 Tests if we can make a phone call from AG role and disconnect from HF 150 role. 151 152 Precondition: 153 1. Devices are connected. 154 155 Steps: 156 1. Make a call from AG role. 157 2. Wait for the HF, AG to show dialing and RE to see the call ringing. 158 3. Hangup the call on HF role. 159 4. Wait for all devices to hangup the call. 160 161 Returns: 162 Pass if True 163 Fail if False 164 165 Priority: 0 166 """ 167 return self.dial_a_hangup_b(self.ag, self.hf) 168 169 @test_tracker_info(uuid='5d1d52c7-51d8-4c82-b437-2e91a6220db3') 170 @BluetoothBaseTest.bt_test_wrap 171 def test_outgoing_dial_hf_hangup_ag(self): 172 """ 173 Tests if we can make a phone call from HF role and disconnect from AG 174 role. 175 176 Precondition: 177 1. Devices are connected. 178 179 Steps: 180 1. Make a call from HF role. 181 2. Wait for the HF, AG to show dialing and RE to see the call ringing. 182 3. Hangup the call on AG role. 183 4. Wait for all devices to hangup the call. 184 185 Returns: 186 Pass if True 187 Fail if False 188 189 Priority: 0 190 """ 191 return self.dial_a_hangup_b(self.hf, self.ag) 192 193 @test_tracker_info(uuid='a718e238-7e31-40c9-a45b-72081210cc73') 194 @BluetoothBaseTest.bt_test_wrap 195 def test_incoming_dial_re_hangup_re(self): 196 """ 197 Tests if we can make a phone call from remote and disconnect from 198 remote. 199 200 Precondition: 201 1. Devices are connected. 202 203 Steps: 204 1. Make a call from RE role. 205 2. Wait for the HF, AG to show ringing and RE to see the call dialing. 206 3. Hangup the call on RE role. 207 4. Wait for all devices to hangup the call. 208 209 Returns: 210 Pass if True 211 Fail if False 212 213 Priority: 0 214 """ 215 return self.dial_a_hangup_b(self.re, self.re, self.ag_phone_number) 216 217 def test_bluetooth_voice_recognition_assistant(self): 218 """ 219 Tests if we can initate a remote Voice Recognition session. 220 221 Precondition: 222 1. Devices are connected. 223 224 Steps: 225 1. Verify that audio isn't routed between the HF and AG. 226 2. From the HF send a BVRA command. 227 3. Verify that audio is routed from the HF to AG. 228 4. From the HF send a BVRA command to stop the session. 229 5. Verify that audio is no longer routed from the HF to AG. 230 231 Returns: 232 Pass if True 233 Fail if False 234 235 Priority: 0 236 """ 237 audio_state = self.hf.droid.bluetoothHfpClientGetAudioState( 238 self.ag.droid.bluetoothGetLocalAddress()) 239 if (audio_state != AUDIO_STATE_DISCONNECTED): 240 self.log.info( 241 "Audio connected before test started, current state {}.". 242 format(str(audio_state))) 243 return False 244 bvra_started = self.hf.droid.bluetoothHfpClientStartVoiceRecognition( 245 self.ag.droid.bluetoothGetLocalAddress()) 246 if (bvra_started != True): 247 self.log.info("BVRA Failed to start.") 248 return False 249 time.sleep(SHORT_TIMEOUT) 250 audio_state = self.hf.droid.bluetoothHfpClientGetAudioState( 251 self.ag.droid.bluetoothGetLocalAddress()) 252 if (audio_state != AUDIO_STATE_ROUTED): 253 self.log.info("Audio didn't route, current state {}.".format( 254 str(audio_state))) 255 return False 256 bvra_stopped = self.hf.droid.bluetoothHfpClientStopVoiceRecognition( 257 self.ag.droid.bluetoothGetLocalAddress()) 258 if (bvra_stopped != True): 259 self.log.info("BVRA Failed to stop.") 260 return False 261 time.sleep(SHORT_TIMEOUT) 262 audio_state = self.hf.droid.bluetoothHfpClientGetAudioState( 263 self.ag.droid.bluetoothGetLocalAddress()) 264 if (audio_state != AUDIO_STATE_DISCONNECTED): 265 self.log.info("Audio didn't cleanup, current state {}.".format( 266 str(audio_state))) 267 return False 268 return True 269 270 def dial_a_hangup_b(self, caller, callee, ph=""): 271 """ 272 a, b and c can be either of AG, HF or Remote. 273 1. Make a call from 'a' on a fixed number. 274 2. Wait for the call to get connected (check on both 'a' and 'b') 275 Check that 'c' is in ringing state. 276 3. Hangup the call on 'b'. 277 4. Wait for call to get completely disconnected 278 (check on both 'a' and 'b') 279 It is assumed that scenarios will not go into voice mail. 280 """ 281 if ph == "": ph = self.re_phone_number 282 283 # Determine if this is outgoing or incoming call. 284 call_type = None 285 if caller == self.ag or caller == self.hf: 286 call_type = CALL_TYPE_OUTGOING 287 if callee != self.ag and callee != self.hf: 288 self.log.info("outgoing call should terminate at AG or HF") 289 return False 290 elif caller == self.re: 291 call_type = CALL_TYPE_INCOMING 292 if callee != self.re: 293 self.log.info("Incoming call should terminate at Re") 294 return False 295 296 self.log.info("Call type is {}".format(call_type)) 297 298 # make a call on 'caller' 299 if not tel_telecom_utils.dial_number(self.log, caller, ph): 300 return False 301 302 # Give time for state to update due to carrier limitations 303 time.sleep(SHORT_TIMEOUT) 304 # Check that everyone is in dialing/ringing state. 305 ret = True 306 if call_type == CALL_TYPE_OUTGOING: 307 ret &= tel_telecom_utils.wait_for_dialing(self.log, self.hf) 308 ret &= tel_telecom_utils.wait_for_dialing(self.log, self.ag) 309 ret &= tel_telecom_utils.wait_for_ringing(self.log, self.re) 310 else: 311 ret &= tel_telecom_utils.wait_for_ringing(self.log, self.hf) 312 ret &= tel_telecom_utils.wait_for_ringing(self.log, self.ag) 313 ret &= tel_telecom_utils.wait_for_dialing(self.log, self.re) 314 if not ret: 315 return False 316 317 # Give time for state to update due to carrier limitations 318 time.sleep(SHORT_TIMEOUT) 319 # Check if we have any calls with dialing or active state on 'b'. 320 # We assume we never disconnect from 'ringing' state since it will lead 321 # to voicemail. 322 call_state_dialing_or_active = \ 323 [tel_defines.CALL_STATE_CONNECTING, 324 tel_defines.CALL_STATE_DIALING, 325 tel_defines.CALL_STATE_ACTIVE] 326 327 calls_in_dialing_or_active = tel_telecom_utils.get_calls_in_states( 328 self.log, callee, call_state_dialing_or_active) 329 330 # Make sure there is only one! 331 if len(calls_in_dialing_or_active) != 1: 332 self.log.info("Call State in dialing or active failed {}".format( 333 calls_in_dialing_or_active)) 334 return False 335 336 # Hangup the *only* call on 'callee' 337 if not car_telecom_utils.hangup_call(self.log, callee, 338 calls_in_dialing_or_active[0]): 339 return False 340 341 time.sleep(SHORT_TIMEOUT) 342 # Make sure everyone got out of in call state. 343 for d in self.android_devices: 344 ret &= tel_telecom_utils.wait_for_not_in_call(self.log, d) 345 return ret 346