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 conference calling functionality.
18"""
19
20import time
21
22from acts.test_decorators import test_tracker_info
23from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
24from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
25from acts.test_utils.bt import BtEnum
26from acts.test_utils.bt import bt_test_utils
27from acts.test_utils.car import car_telecom_utils
28from acts.test_utils.tel import tel_defines
29from acts.test_utils.tel.tel_test_utils import hangup_call
30from acts.test_utils.tel.tel_test_utils import initiate_call
31from acts.test_utils.tel.tel_test_utils import wait_and_answer_call
32from acts.test_utils.tel.tel_test_utils import wait_for_ringing_call
33from acts.test_utils.tel.tel_voice_utils import get_audio_route
34from acts.test_utils.tel.tel_voice_utils import set_audio_route
35from acts.test_utils.tel.tel_voice_utils import swap_calls
36
37BLUETOOTH_PKG_NAME = "com.android.bluetooth"
38CALL_TYPE_OUTGOING = "CALL_TYPE_OUTGOING"
39CALL_TYPE_INCOMING = "CALL_TYPE_INCOMING"
40SHORT_TIMEOUT = 3
41
42
43class BtCarHfpConferenceTest(BluetoothCarHfpBaseTest):
44    def setup_class(self):
45        if not super(BtCarHfpConferenceTest, self).setup_class():
46            return False
47
48        # Connect the devices now, try twice.
49        attempts = 2
50        connected = False
51        while attempts > 0 and not connected:
52            connected = bt_test_utils.connect_pri_to_sec(
53                self.hf, self.ag,
54                set([BtEnum.BluetoothProfile.HEADSET_CLIENT.value]))
55            self.log.info("Connected {}".format(connected))
56            attempts -= 1
57        return connected
58
59    @test_tracker_info(uuid='a9657693-b534-4625-bf91-69a1d1b9a943')
60    @BluetoothBaseTest.bt_test_wrap
61    def test_multi_way_call_accept(self):
62        """
63        Tests if we can have a 3-way calling between re, RE2 and AG/HF.
64
65        Precondition:
66        1. Devices are connected over HFP.
67
68        Steps:
69        1. Make a call from AG to RE
70        2. Wait for dialing on re and ringing on RE/HF.
71        3. Accept the call on RE
72        4. Make a call from AG to RE2
73        5. Wait for dialing on re and ringing on RE2/HF.
74        6. Accept the call on RE2.
75        7. See that HF/AG have one active and one held call.
76        8. Merge the call on HF.
77        9. Verify that we have a conference call on HF/AG.
78        10. Hangup the call on HF.
79        11. Wait for all devices to go back into stable state.
80
81        Returns:
82          Pass if True
83          Fail if False
84
85        Priority: 0
86        """
87
88        # Dial RE from AG
89        if not initiate_call(self.log, self.ag, self.re_phone_number):
90            self.log.error("Failed to initiate call from re.")
91            return False
92
93        # Wait for dialing/ringing
94        ret = True
95        ret &= wait_for_ringing_call(self.log, self.re)
96        ret &= car_telecom_utils.wait_for_ringing(self.log, self.hf)
97
98        if not ret:
99            self.log.error("Failed to dial incoming number from")
100            return False
101
102        # Give time for state to update due to carrier limitations
103        time.sleep(SHORT_TIMEOUT)
104        # Extract the call.
105        call_1 = car_telecom_utils.get_calls_in_states(
106            self.log, self.hf, [tel_defines.CALL_STATE_DIALING])
107        if len(call_1) != 1:
108            self.hf.log.error("Call State in ringing failed {}".format(call_1))
109            return False
110
111        re_ringing_call_id = car_telecom_utils.get_calls_in_states(
112            self.log, self.re, [tel_defines.CALL_STATE_RINGING])
113
114        # Accept the call on RE
115        if not car_telecom_utils.accept_call(self.log, self.re, re_ringing_call_id[0]):
116            self.hf.log.error("Accepting call failed {}".format(
117                self.hf.serial))
118            return False
119
120        time.sleep(SHORT_TIMEOUT)
121        # Dial another call to RE2
122        if not initiate_call(self.log, self.ag, self.re2_phone_number):
123            self.re2.log.error("Failed to initiate call from re.")
124            return False
125
126        # Wait for dialing/ringing
127        ret &= wait_for_ringing_call(self.log, self.re2)
128        ret &= car_telecom_utils.wait_for_ringing(self.log, self.hf)
129
130        if not ret:
131            self.log.error("AG and HF not in ringing state.")
132            return False
133
134        # Give time for state to update due to carrier limitations
135        time.sleep(SHORT_TIMEOUT)
136        # Extract the call.
137        # input("Continue?")
138        call_2 = car_telecom_utils.get_calls_in_states(
139            self.log, self.hf, [tel_defines.CALL_STATE_DIALING])
140        if len(call_2) != 1:
141            self.hf.log.info("Call State in ringing failed {}".format(call_2))
142            return False
143
144        re2_ringing_call_id = car_telecom_utils.get_calls_in_states(
145            self.log, self.re2, [tel_defines.CALL_STATE_RINGING])
146
147        # Accept the call on HF
148        if not car_telecom_utils.accept_call(self.log, self.re2, re2_ringing_call_id[0]):
149            self.hf.log.info("Accepting call failed {}".format(self.hf.serial))
150            return False
151
152        # Give time before merge for state to update due to carrier limitations
153        time.sleep(SHORT_TIMEOUT)
154
155        # Merge the calls now.
156        self.hf.droid.telecomCallJoinCallsInConf(call_1[0], call_2[0])
157
158        # Check if we are in conference with call_1 and call_2
159        conf_call_id = car_telecom_utils.wait_for_conference(self.log, self.hf, participants=2)
160        if conf_call_id is None:
161            self.hf.log.error("Did not get the conference setup correctly")
162            return False
163
164        # Now hangup the conference call.
165        if not car_telecom_utils.hangup_conf(self.log, self.hf, conf_call_id):
166            self.hf.log.error("Could not hangup conference call {}!".format(conf_call_id))
167            return False
168
169        return True
170