1#!/usr/bin/env python3
2#
3# Copyright (C) 2019 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"""
17Script for testing WiFi recovery after rebooting the AP.
18
19Override default number of iterations using the following
20parameter in the test config file.
21
22"beacon_loss_test_iterations": "5"
23"""
24
25import os
26import uuid
27import time
28
29from acts import asserts
30from acts import signals
31from acts import utils
32from acts.base_test import BaseTestClass
33from acts.controllers.ap_lib import hostapd_constants
34from acts.test_utils.abstract_devices.utils_lib.wlan_utils import disconnect
35from acts.test_utils.abstract_devices.utils_lib.wlan_utils import setup_ap
36from acts.test_utils.abstract_devices.utils_lib.wlan_utils import associate
37from acts.test_utils.abstract_devices.wlan_device import create_wlan_device
38from acts.test_utils.abstract_devices.wlan_device_lib.AbstractDeviceWlanDeviceBaseTest import AbstractDeviceWlanDeviceBaseTest
39from acts.utils import rand_ascii_str
40
41
42class BeaconLossTest(AbstractDeviceWlanDeviceBaseTest):
43    # Default number of test iterations here.
44    # Override using parameter in config file.
45    # Eg: "beacon_loss_test_iterations": "10"
46    num_of_iterations = 5
47
48    # Time to wait for AP to startup
49    wait_ap_startup_s = 15
50
51    # Default wait time in seconds for the AP radio to turn back on
52    wait_to_connect_after_ap_txon_s = 5
53
54    # Time to wait for device to disconnect after AP radio of
55    wait_after_ap_txoff_s = 15
56
57    # Time to wait for device to complete connection setup after
58    # given an associate command
59    wait_client_connection_setup_s = 15
60
61    def setup_class(self):
62        super().setup_class()
63        self.ssid = rand_ascii_str(10)
64        self.wlan_device = create_wlan_device(self.fuchsia_devices[0])
65        self.ap = self.access_points[0]
66        self.num_of_iterations = int(
67            self.user_params.get("beacon_loss_test_iterations",
68                                 self.num_of_iterations))
69        self.in_use_interface = None
70
71    def teardown_test(self):
72        disconnect(self.wlan_device)
73        self.wlan_device.reset_wifi()
74        # ensure radio is on, in case the test failed while the radio was off
75        self.ap.iwconfig.ap_iwconfig(self.in_use_interface, "txpower on")
76        self.ap.stop_all_aps()
77
78    def on_fail(self, test_name, begin_time):
79        super().on_fail(test_name, begin_time)
80        self.access_point.stop_all_aps()
81
82    def beacon_loss(self, channel):
83        setup_ap(access_point=self.ap,
84                 profile_name='whirlwind',
85                 channel=channel,
86                 ssid=self.ssid)
87        time.sleep(self.wait_ap_startup_s)
88        if channel > 14:
89            self.in_use_interface = self.ap.wlan_5g
90        else:
91            self.in_use_interface = self.ap.wlan_2g
92
93        # TODO(b/144505723): [ACTS] update BeaconLossTest.py to handle client
94        # roaming, saved networks, etc.
95        self.log.info("sending associate command for ssid %s", self.ssid)
96        associate(client=self.wlan_device, ssid=self.ssid)
97
98        asserts.assert_true(self.wlan_device.is_connected(),
99                            'Failed to connect.')
100
101        time.sleep(self.wait_client_connection_setup_s)
102
103        for _ in range(0, self.num_of_iterations):
104            # Turn off AP radio
105            self.log.info("turning off radio")
106            self.ap.iwconfig.ap_iwconfig(self.in_use_interface, "txpower off")
107            time.sleep(self.wait_after_ap_txoff_s)
108
109            # Did we disconnect from AP?
110            asserts.assert_false(self.wlan_device.is_connected(),
111                                 'Failed to disconnect.')
112
113            # Turn on AP radio
114            self.log.info("turning on radio")
115            self.ap.iwconfig.ap_iwconfig(self.in_use_interface, "txpower on")
116            time.sleep(self.wait_to_connect_after_ap_txon_s)
117
118            # Tell the client to connect
119            self.log.info("sending associate command for ssid %s" % self.ssid)
120            associate(client=self.wlan_device, ssid=self.ssid)
121            time.sleep(self.wait_client_connection_setup_s)
122
123            # Did we connect back to WiFi?
124            asserts.assert_true(self.wlan_device.is_connected(),
125                                'Failed to connect back.')
126
127        return True
128
129    def test_beacon_loss_2g(self):
130        self.beacon_loss(channel=hostapd_constants.AP_DEFAULT_CHANNEL_2G)
131
132    def test_beacon_loss_5g(self):
133        self.beacon_loss(channel=hostapd_constants.AP_DEFAULT_CHANNEL_5G)
134