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 advanced functionality and try to create race
18conditions by executing actions quickly.
19"""
20
21import time
22
23from acts.test_decorators import test_tracker_info
24from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
25from acts.test_utils.bt.BluetoothCarHfpBaseTest import BluetoothCarHfpBaseTest
26from acts.test_utils.bt import BtEnum
27from acts.test_utils.bt import bt_test_utils
28from acts.test_utils.car import car_telecom_utils
29from acts.test_utils.tel import tel_defines
30
31STABILIZATION_DELAY_SEC = 5
32
33
34class BtCarHfpFuzzTest(BluetoothCarHfpBaseTest):
35    def setup_class(self):
36        if not super(BtCarHfpFuzzTest, self).setup_class():
37            return False
38
39        # Connect the devices now, try twice.
40        attempts = 2
41        connected = False
42        while attempts > 0 and not connected:
43            connected = bt_test_utils.connect_pri_to_sec(
44                self.hf, self.ag,
45                set([BtEnum.BluetoothProfile.HEADSET_CLIENT.value]))
46            self.log.info("Connected {}".format(connected))
47            attempts -= 1
48
49        if not connected:
50            self.log.error("Failed to connect")
51            return False
52
53        # Delay set contains the delay between dial and hangup for a call.
54        # We keep very small delays to significantly large ones to stress test
55        # various kind of timing issues.
56        self.delay_set = [
57            0.1,
58            0.2,
59            0.3,
60            0.4,
61            0.5,  # Very short delays
62            1.0,
63            2.0,
64            3.0,
65            4.0,
66            5.0,  # Med delays
67            10.0
68        ]  # Large delays
69
70    def dial_a_hangup_b_quick(self, a, b, delay=0, ph=""):
71        """
72        This test does a quick succession of dial and hangup. We make the test
73        case sleep for delay amount between each dial and hangup. This is
74        different from other test cases where we give enough time for devices
75        to get into a calling state before trying to tear down connection.
76        """
77        if ph == "": ph = self.re_phone_number
78
79        # Dial from A now.
80        self.log.info("Dialing at droid {}".format(a.droid.getBuildDisplay()))
81        a.droid.telecomCallTelUri(ph)
82
83        # Wait for delay millis.
84        time.sleep(delay)
85
86        # Cancel the call at B. Use end call in this scenario so that we do not
87        # wait for looking up the call!
88        self.log.info("Hanging up at droid {}".format(b.droid.getBuildDisplay(
89        )))
90        b.droid.telecomEndCall()
91
92        # Check/Wait that we are clear before executing the test.
93        for d in self.android_devices:
94            if not car_telecom_utils.wait_for_not_in_call(self.log, d):
95                self.log.warn(
96                    "dial_a_hangup_quick wait_for_not_in_call failed {}".
97                    format(d.serial))
98                return False
99
100        return True
101
102    def stabilize_and_check_sanity(self):
103        # Since we dial and hangup very very quickly we may end up in a state
104        # where we need to wait to see the results. For instance if the delay is
105        # 0.1 sec it may take upto 2 seconds for the platform to respond to a
106        # dial() and hence even if we hangup 0.1 sec later we will not see its
107        # result immidiately (this may be a false positive on test).
108        time.sleep(STABILIZATION_DELAY_SEC)
109
110        # First check if HF is in dialing state, we can send an actual hangup if
111        # that is the case and then wait for devices to come back to normal.
112        if self.hf.droid.telecomIsInCall():
113            self.log.info("HF still in call, send hangup")
114            self.hf.droid.telecomEndCall()
115
116        # Wait for devices to go back to normal.
117        for d in self.android_devices:
118            if not car_telecom_utils.wait_for_not_in_call(self.log, d):
119                self.log.warning(
120                    "stabilize_and_check_sanity wait_for_not_in_call failed {}".
121                    format(d.serial))
122                return False
123
124        return True
125
126    @test_tracker_info(uuid='32022c74-fdf3-44c4-9e82-e518bdcce667')
127    @BluetoothBaseTest.bt_test_wrap
128    def test_fuzz_outgoing_hf(self):
129        """
130        Test calling and hangup from HF with varied delays as defined in
131        self.delay_set
132
133        Precondition:
134        1. Devices are paired and connected
135
136        Steps:
137        For each delay do the following:
138        a) Call HF
139        b) Wait for delay seconds
140        c) Hangup HF
141        d) Check if all devices are in stable state, if not wait for stabilizing
142        e) If (d) fails then we fail otherwise we go back to (a)
143        f) Once all delays are done we do a final check for sanity as pass
144        scenario
145
146        Returns:
147          Pass if True
148          Fail if False
149
150        Priority: 1
151        """
152
153        for delay in self.delay_set:
154            self.log.info("test_fuzz outgoing_hf: {}".format(delay))
155            # Make the call and hangup, we do a light check inside to see if the
156            # phones are in a clean state -- if not, we let them stabilize
157            # before continuing.
158            if not self.dial_a_hangup_b_quick(self.hf, self.hf, delay):
159                if not self.stabilize_and_check_sanity():
160                    self.log.info("Devices not able to stabilize!")
161                    return False
162
163        # Final sanity check (if we never called stabilize_and_check_sanity
164        # above).
165        return self.stabilize_and_check_sanity()
166
167    @test_tracker_info(uuid='bc6d52b2-4acc-461e-ad55-fad5a5ecb091')
168    @BluetoothBaseTest.bt_test_wrap
169    def test_fuzz_outgoing_ag(self):
170        """
171        Test calling and hangup from AG with varied delays as defined in
172        self.delay_set
173
174        Precondition:
175        1. Devices are paired and connected
176
177        Steps:
178        For each delay do the following:
179        a) Call AG
180        b) Wait for delay seconds
181        c) Hangup AG
182        d) Check if all devices are in stable state, if not wait for stabilizing
183        e) If (d) fails then we fail otherwise we go back to (a)
184        f) Once all delays are done we do a final check for sanity as pass
185        scenario
186
187        Returns:
188          Pass if True
189          Fail if False
190
191        Priority: 1
192        """
193
194        for delay in self.delay_set:
195            self.log.info("test_fuzz outgoing_ag: {}".format(delay))
196            # Make the call and hangup, we do a light check inside to see if the
197            # phones are in a clean state -- if not, we let them stabilize
198            # before continuing.
199            if not self.dial_a_hangup_b_quick(self.ag, self.ag, delay):
200                if not self.stabilize_and_check_sanity():
201                    self.log.error("Devices not able to stabilize!")
202                    return False
203
204        # Final sanity check (if we never called stabilize_and_check_sanity
205        # above).
206        return self.stabilize_and_check_sanity()
207
208    @test_tracker_info(uuid='d834384a-38d5-4260-bfd5-98f8207c04f5')
209    @BluetoothBaseTest.bt_test_wrap
210    def test_fuzz_dial_hf_hangup_ag(self):
211        """
212        Test calling and hangup from HF and AG resp. with varied delays as defined in
213        self.delay_set
214
215        Precondition:
216        1. Devices are paired and connected
217
218        Steps:
219        For each delay do the following:
220        a) Call HF
221        b) Wait for delay seconds
222        c) Hangup AG
223        d) Check if all devices are in stable state, if not wait for stabilizing
224        e) If (d) fails then we fail otherwise we go back to (a)
225        f) Once all delays are done we do a final check for sanity as pass
226        scenario
227
228        Returns:
229          Pass if True
230          Fail if False
231
232        Priority: 1
233        """
234
235        for delay in self.delay_set:
236            self.log.info("test_fuzz dial_hf hangup_ag: {}".format(delay))
237            # Make the call and hangup, we do a light check inside to see if the
238            # phones are in a clean state -- if not, we let them stabilize
239            # before continuing.
240            if not self.dial_a_hangup_b_quick(self.hf, self.ag, delay):
241                if not self.stabilize_and_check_sanity():
242                    self.log.info("Devices not able to stabilize!")
243                    return False
244
245        # Final sanity check (if we never called stabilize_and_check_sanity
246        # above).
247        return self.stabilize_and_check_sanity()
248
249    @test_tracker_info(uuid='6de1a8ab-3cb0-4594-a9bb-d882a3414836')
250    @BluetoothBaseTest.bt_test_wrap
251    def test_fuzz_dial_ag_hangup_hf(self):
252        """
253        Test calling and hangup from HF and AG resp. with varied delays as defined in
254        self.delay_set
255
256        Precondition:
257        1. Devices are paired and connected
258
259        Steps:
260        For each delay do the following:
261        a) Call AG
262        b) Wait for delay seconds
263        c) Hangup HF
264        d) Check if all devices are in stable state, if not wait for stabilizing
265        e) If (d) fails then we fail otherwise we go back to (a)
266        f) Once all delays are done we do a final check for sanity as pass
267        scenario
268
269        Returns:
270          Pass if True
271          Fail if False
272
273        Priority: 1
274        """
275
276        for delay in self.delay_set:
277            self.log.info("test_fuzz dial_ag hangup_hf: {}".format(delay))
278            # Make the call and hangup, we do a light check inside to see if the
279            # phones are in a clean state -- if not, we let them stabilize
280            # before continuing.
281            if not self.dial_a_hangup_b_quick(self.ag, self.hf, delay):
282                if not self.stabilize_and_check_sanity():
283                    self.log.info("Devices not able to stabilize!")
284                    return False
285
286        # Final sanity check (if we never called stabilize_and_check_sanity
287        # above).
288        return self.stabilize_and_check_sanity()
289