| #!/usr/bin/env python3 |
| # |
| # Copyright (C) 2016 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """ |
| Test the HFP profile for basic calling functionality. |
| """ |
| |
| import time |
| from acts.test_decorators import test_tracker_info |
| from acts_contrib.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest |
| from acts_contrib.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest |
| from acts_contrib.test_utils.bt import BtEnum |
| from acts_contrib.test_utils.bt import bt_test_utils |
| from acts_contrib.test_utils.car import car_telecom_utils |
| from acts_contrib.test_utils.car import tel_telecom_utils |
| from acts_contrib.test_utils.tel import tel_defines |
| |
| BLUETOOTH_PKG_NAME = "com.android.bluetooth" |
| CALL_TYPE_OUTGOING = "CALL_TYPE_OUTGOING" |
| CALL_TYPE_INCOMING = "CALL_TYPE_INCOMING" |
| AUDIO_STATE_DISCONNECTED = 0 |
| AUDIO_STATE_ROUTED = 2 |
| SHORT_TIMEOUT = 5 |
| |
| |
| class BtCarHfpTest(BluetoothCarHfpBaseTest): |
| def setup_class(self): |
| if not super(BtCarHfpTest, self).setup_class(): |
| return False |
| # Disable the A2DP profile. |
| bt_test_utils.set_profile_priority(self.hf, self.ag, [ |
| BtEnum.BluetoothProfile.PBAP_CLIENT.value, |
| BtEnum.BluetoothProfile.A2DP_SINK.value |
| ], BtEnum.BluetoothPriorityLevel.PRIORITY_OFF) |
| bt_test_utils.set_profile_priority( |
| self.hf, self.ag, [BtEnum.BluetoothProfile.HEADSET_CLIENT.value], |
| BtEnum.BluetoothPriorityLevel.PRIORITY_ON) |
| |
| if not bt_test_utils.connect_pri_to_sec( |
| self.hf, self.ag, |
| set([BtEnum.BluetoothProfile.HEADSET_CLIENT.value])): |
| self.log.error("Failed to connect.") |
| return False |
| return True |
| |
| @test_tracker_info(uuid='4ce2195a-b70a-4584-912e-cbd20d20e19d') |
| @BluetoothBaseTest.bt_test_wrap |
| def test_default_calling_account(self): |
| """ |
| Tests if the default calling account is coming from the |
| bluetooth pacakge. |
| |
| Precondition: |
| 1. Devices are connected. |
| |
| Steps: |
| 1. Check if the default calling account is via Bluetooth package. |
| |
| Returns: |
| Pass if True |
| Fail if False |
| |
| Priority: 0 |
| """ |
| selected_acc = \ |
| self.hf.droid.telecomGetUserSelectedOutgoingPhoneAccount() |
| if not selected_acc: |
| self.hf.log.error("No default account found.") |
| return False |
| |
| # Check if the default account is from the Bluetooth package. This is a |
| # light weight check. |
| try: |
| acc_component_id = selected_acc['ComponentName'] |
| except KeyError: |
| self.hf.log.error("No component name for account {}".format( |
| selected_acc)) |
| return False |
| if not acc_component_id.startswith(BLUETOOTH_PKG_NAME): |
| self.hf.log.error("Component name does not start with pkg name {}". |
| format(selected_acc)) |
| return False |
| return True |
| |
| @test_tracker_info(uuid='e579009d-05f3-4236-a698-5de8c11d73a9') |
| @BluetoothBaseTest.bt_test_wrap |
| def test_outgoing_call_hf(self): |
| """ |
| Tests if we can make a phone call from HF role and disconnect from HF |
| role. |
| |
| Precondition: |
| 1. Devices are connected. |
| |
| Steps: |
| 1. Make a call from HF role. |
| 2. Wait for the HF, AG to be dialing and RE to see the call ringing. |
| 3. Hangup the call on HF role. |
| 4. Wait for all devices to hangup the call. |
| |
| Returns: |
| Pass if True |
| Fail if False |
| |
| Priority: 0 |
| """ |
| return self.dial_a_hangup_b(self.hf, self.hf) |
| |
| @test_tracker_info(uuid='c9d5f9cd-f275-4adf-b212-c2e9a70d4cac') |
| @BluetoothBaseTest.bt_test_wrap |
| def test_outgoing_call_ag(self): |
| """ |
| Tests if we can make a phone call from AG role and disconnect from AG |
| role. |
| |
| Precondition: |
| 1. Devices are connected. |
| |
| Steps: |
| 1. Make a call from AG role. |
| 2. Wait for the HF, AG to be in dialing and RE to see the call ringing. |
| 3. Hangup the call on AG role. |
| 4. Wait for all devices to hangup the call. |
| |
| Returns: |
| Pass if True |
| Fail if False |
| |
| Priority: 0 |
| """ |
| return self.dial_a_hangup_b(self.ag, self.ag) |
| |
| @test_tracker_info(uuid='908c199b-ca65-4694-821d-1b864ee3fe69') |
| @BluetoothBaseTest.bt_test_wrap |
| def test_outgoing_dial_ag_hangup_hf(self): |
| """ |
| Tests if we can make a phone call from AG role and disconnect from HF |
| role. |
| |
| Precondition: |
| 1. Devices are connected. |
| |
| Steps: |
| 1. Make a call from AG role. |
| 2. Wait for the HF, AG to show dialing and RE to see the call ringing. |
| 3. Hangup the call on HF role. |
| 4. Wait for all devices to hangup the call. |
| |
| Returns: |
| Pass if True |
| Fail if False |
| |
| Priority: 0 |
| """ |
| return self.dial_a_hangup_b(self.ag, self.hf) |
| |
| @test_tracker_info(uuid='5d1d52c7-51d8-4c82-b437-2e91a6220db3') |
| @BluetoothBaseTest.bt_test_wrap |
| def test_outgoing_dial_hf_hangup_ag(self): |
| """ |
| Tests if we can make a phone call from HF role and disconnect from AG |
| role. |
| |
| Precondition: |
| 1. Devices are connected. |
| |
| Steps: |
| 1. Make a call from HF role. |
| 2. Wait for the HF, AG to show dialing and RE to see the call ringing. |
| 3. Hangup the call on AG role. |
| 4. Wait for all devices to hangup the call. |
| |
| Returns: |
| Pass if True |
| Fail if False |
| |
| Priority: 0 |
| """ |
| return self.dial_a_hangup_b(self.hf, self.ag) |
| |
| @test_tracker_info(uuid='a718e238-7e31-40c9-a45b-72081210cc73') |
| @BluetoothBaseTest.bt_test_wrap |
| def test_incoming_dial_re_hangup_re(self): |
| """ |
| Tests if we can make a phone call from remote and disconnect from |
| remote. |
| |
| Precondition: |
| 1. Devices are connected. |
| |
| Steps: |
| 1. Make a call from RE role. |
| 2. Wait for the HF, AG to show ringing and RE to see the call dialing. |
| 3. Hangup the call on RE role. |
| 4. Wait for all devices to hangup the call. |
| |
| Returns: |
| Pass if True |
| Fail if False |
| |
| Priority: 0 |
| """ |
| return self.dial_a_hangup_b(self.re, self.re, self.ag_phone_number) |
| |
| def test_bluetooth_voice_recognition_assistant(self): |
| """ |
| Tests if we can initate a remote Voice Recognition session. |
| |
| Precondition: |
| 1. Devices are connected. |
| |
| Steps: |
| 1. Verify that audio isn't routed between the HF and AG. |
| 2. From the HF send a BVRA command. |
| 3. Verify that audio is routed from the HF to AG. |
| 4. From the HF send a BVRA command to stop the session. |
| 5. Verify that audio is no longer routed from the HF to AG. |
| |
| Returns: |
| Pass if True |
| Fail if False |
| |
| Priority: 0 |
| """ |
| audio_state = self.hf.droid.bluetoothHfpClientGetAudioState( |
| self.ag.droid.bluetoothGetLocalAddress()) |
| if (audio_state != AUDIO_STATE_DISCONNECTED): |
| self.log.info( |
| "Audio connected before test started, current state {}.". |
| format(str(audio_state))) |
| return False |
| bvra_started = self.hf.droid.bluetoothHfpClientStartVoiceRecognition( |
| self.ag.droid.bluetoothGetLocalAddress()) |
| if (bvra_started != True): |
| self.log.info("BVRA Failed to start.") |
| return False |
| time.sleep(SHORT_TIMEOUT) |
| audio_state = self.hf.droid.bluetoothHfpClientGetAudioState( |
| self.ag.droid.bluetoothGetLocalAddress()) |
| if (audio_state != AUDIO_STATE_ROUTED): |
| self.log.info("Audio didn't route, current state {}.".format( |
| str(audio_state))) |
| return False |
| bvra_stopped = self.hf.droid.bluetoothHfpClientStopVoiceRecognition( |
| self.ag.droid.bluetoothGetLocalAddress()) |
| if (bvra_stopped != True): |
| self.log.info("BVRA Failed to stop.") |
| return False |
| time.sleep(SHORT_TIMEOUT) |
| audio_state = self.hf.droid.bluetoothHfpClientGetAudioState( |
| self.ag.droid.bluetoothGetLocalAddress()) |
| if (audio_state != AUDIO_STATE_DISCONNECTED): |
| self.log.info("Audio didn't cleanup, current state {}.".format( |
| str(audio_state))) |
| return False |
| return True |
| |
| def dial_a_hangup_b(self, caller, callee, ph=""): |
| """ |
| a, b and c can be either of AG, HF or Remote. |
| 1. Make a call from 'a' on a fixed number. |
| 2. Wait for the call to get connected (check on both 'a' and 'b') |
| Check that 'c' is in ringing state. |
| 3. Hangup the call on 'b'. |
| 4. Wait for call to get completely disconnected |
| (check on both 'a' and 'b') |
| It is assumed that scenarios will not go into voice mail. |
| """ |
| if ph == "": ph = self.re_phone_number |
| |
| # Determine if this is outgoing or incoming call. |
| call_type = None |
| if caller == self.ag or caller == self.hf: |
| call_type = CALL_TYPE_OUTGOING |
| if callee != self.ag and callee != self.hf: |
| self.log.info("outgoing call should terminate at AG or HF") |
| return False |
| elif caller == self.re: |
| call_type = CALL_TYPE_INCOMING |
| if callee != self.re: |
| self.log.info("Incoming call should terminate at Re") |
| return False |
| |
| self.log.info("Call type is {}".format(call_type)) |
| |
| # make a call on 'caller' |
| if not tel_telecom_utils.dial_number(self.log, caller, ph): |
| return False |
| |
| # Give time for state to update due to carrier limitations |
| time.sleep(SHORT_TIMEOUT) |
| # Check that everyone is in dialing/ringing state. |
| ret = True |
| if call_type == CALL_TYPE_OUTGOING: |
| ret &= tel_telecom_utils.wait_for_dialing(self.log, self.hf) |
| ret &= tel_telecom_utils.wait_for_dialing(self.log, self.ag) |
| ret &= tel_telecom_utils.wait_for_ringing(self.log, self.re) |
| else: |
| ret &= tel_telecom_utils.wait_for_ringing(self.log, self.hf) |
| ret &= tel_telecom_utils.wait_for_ringing(self.log, self.ag) |
| ret &= tel_telecom_utils.wait_for_dialing(self.log, self.re) |
| if not ret: |
| return False |
| |
| # Give time for state to update due to carrier limitations |
| time.sleep(SHORT_TIMEOUT) |
| # Check if we have any calls with dialing or active state on 'b'. |
| # We assume we never disconnect from 'ringing' state since it will lead |
| # to voicemail. |
| call_state_dialing_or_active = \ |
| [tel_defines.CALL_STATE_CONNECTING, |
| tel_defines.CALL_STATE_DIALING, |
| tel_defines.CALL_STATE_ACTIVE] |
| |
| calls_in_dialing_or_active = tel_telecom_utils.get_calls_in_states( |
| self.log, callee, call_state_dialing_or_active) |
| |
| # Make sure there is only one! |
| if len(calls_in_dialing_or_active) != 1: |
| self.log.info("Call State in dialing or active failed {}".format( |
| calls_in_dialing_or_active)) |
| return False |
| |
| # Hangup the *only* call on 'callee' |
| if not car_telecom_utils.hangup_call(self.log, callee, |
| calls_in_dialing_or_active[0]): |
| return False |
| |
| time.sleep(SHORT_TIMEOUT) |
| # Make sure everyone got out of in call state. |
| for d in self.android_devices: |
| ret &= tel_telecom_utils.wait_for_not_in_call(self.log, d) |
| return ret |