blob: 2806feb44e31443eba595dc2252abb24fcd516e1 [file] [log] [blame]
#!/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