1#!/usr/bin/env python3
2#
3#   Copyright 2019 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of 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,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import inspect
18import logging
19
20import acts.test_utils.wifi.wifi_test_utils as awutils
21import acts.test_utils.abstract_devices.utils_lib.wlan_utils as fwutils
22from acts.utils import get_interface_ip_addresses
23from acts.utils import adb_shell_ping
24
25from acts import asserts
26from acts.controllers.fuchsia_device import FuchsiaDevice
27from acts.controllers.android_device import AndroidDevice
28
29
30def create_wlan_device(hardware_device):
31    """Creates a generic WLAN device based on type of device that is sent to
32    the functions.
33
34    Args:
35        hardware_device: A WLAN hardware device that is supported by ACTS.
36    """
37    if isinstance(hardware_device, FuchsiaDevice):
38        return FuchsiaWlanDevice(hardware_device)
39    elif isinstance(hardware_device, AndroidDevice):
40        return AndroidWlanDevice(hardware_device)
41    else:
42        raise ValueError('Unable to create WlanDevice for type %s' %
43                         type(hardware_device))
44
45
46class WlanDevice(object):
47    """Class representing a generic WLAN device.
48
49    Each object of this class represents a generic WLAN device.
50    Android device and Fuchsia devices are the currently supported devices/
51
52    Attributes:
53        device: A generic WLAN device.
54    """
55    def __init__(self, device):
56        self.device = device
57        self.log = logging
58
59    def wifi_toggle_state(self, state):
60        """Base generic WLAN interface.  Only called if not overridden by
61        another supported device.
62        """
63        raise NotImplementedError("{} must be defined.".format(
64            inspect.currentframe().f_code.co_name))
65
66    def reset_wifi(self):
67        """Base generic WLAN interface.  Only called if not overridden by
68        another supported device.
69        """
70        raise NotImplementedError("{} must be defined.".format(
71            inspect.currentframe().f_code.co_name))
72
73    def take_bug_report(self, test_name, begin_time):
74        """Base generic WLAN interface.  Only called if not overridden by
75        another supported device.
76        """
77        raise NotImplementedError("{} must be defined.".format(
78            inspect.currentframe().f_code.co_name))
79
80    def get_log(self, test_name, begin_time):
81        """Base generic WLAN interface.  Only called if not overridden by
82        another supported device.
83        """
84        raise NotImplementedError("{} must be defined.".format(
85            inspect.currentframe().f_code.co_name))
86
87    def turn_location_off_and_scan_toggle_off(self):
88        """Base generic WLAN interface.  Only called if not overridden by
89        another supported device.
90        """
91        raise NotImplementedError("{} must be defined.".format(
92            inspect.currentframe().f_code.co_name))
93
94    def associate(self,
95                  target_ssid,
96                  target_pwd=None,
97                  check_connectivity=True,
98                  hidden=False):
99        """Base generic WLAN interface.  Only called if not overriden by
100        another supported device.
101        """
102        raise NotImplementedError("{} must be defined.".format(
103            inspect.currentframe().f_code.co_name))
104
105    def disconnect(self):
106        """Base generic WLAN interface.  Only called if not overridden by
107        another supported device.
108        """
109        raise NotImplementedError("{} must be defined.".format(
110            inspect.currentframe().f_code.co_name))
111
112    def get_wlan_interface_id_list(self):
113        """Base generic WLAN interface.  Only called if not overridden by
114        another supported device.
115        """
116        raise NotImplementedError("{} must be defined.".format(
117            inspect.currentframe().f_code.co_name))
118
119    def destroy_wlan_interface(self, iface_id):
120        """Base generic WLAN interface.  Only called if not overridden by
121        another supported device.
122        """
123        raise NotImplementedError("{} must be defined.".format(
124            inspect.currentframe().f_code.co_name))
125
126    def send_command(self, command):
127        raise NotImplementedError("{} must be defined.".format(
128            inspect.currentframe().f_code.co_name))
129
130    def get_interface_ip_addresses(self, interface):
131        raise NotImplementedError("{} must be defined.".format(
132            inspect.currentframe().f_code.co_name))
133
134    def is_connected(self):
135        raise NotImplementedError("{} must be defined.".format(
136            inspect.currentframe().f_code.co_name))
137
138    def ping(self, dest_ip, count=3, interval=1000, timeout=1000, size=25):
139        raise NotImplementedError("{} must be defined.".format(
140            inspect.currentframe().f_code.co_name))
141
142
143class AndroidWlanDevice(WlanDevice):
144    """Class wrapper for an Android WLAN device.
145
146    Each object of this class represents a generic WLAN device.
147    Android device and Fuchsia devices are the currently supported devices/
148
149    Attributes:
150        android_device: An Android WLAN device.
151    """
152    def __init__(self, android_device):
153        super().__init__(android_device)
154
155    def wifi_toggle_state(self, state):
156        awutils.wifi_toggle_state(self.device, state)
157
158    def reset_wifi(self):
159        awutils.reset_wifi(self.device)
160
161    def take_bug_report(self, test_name, begin_time):
162        self.device.take_bug_report(test_name, begin_time)
163
164    def get_log(self, test_name, begin_time):
165        self.device.cat_adb_log(test_name, begin_time)
166
167    def turn_location_off_and_scan_toggle_off(self):
168        awutils.turn_location_off_and_scan_toggle_off(self.device)
169
170    def associate(self,
171                  target_ssid,
172                  target_pwd=None,
173                  key_mgmt=None,
174                  check_connectivity=True,
175                  hidden=False):
176        """Function to associate an Android WLAN device.
177
178        Args:
179            target_ssid: SSID to associate to.
180            target_pwd: Password for the SSID, if necessary.
181            key_mgmt: The hostapd wpa_key_mgmt value, distinguishes wpa3 from
182                wpa2 for android tests.
183            check_connectivity: Whether to check for internet connectivity.
184            hidden: Whether the network is hidden.
185        Returns:
186            True if successfully connected to WLAN, False if not.
187        """
188        network = {'SSID': target_ssid, 'hiddenSSID': hidden}
189        if target_pwd:
190            network['password'] = target_pwd
191        if key_mgmt:
192            network['security'] = key_mgmt
193        try:
194            awutils.connect_to_wifi_network(
195                self.device,
196                network,
197                check_connectivity=check_connectivity,
198                hidden=hidden)
199            return True
200        except Exception as e:
201            self.device.log.info('Failed to associated (%s)' % e)
202            return False
203
204    def disconnect(self):
205        awutils.turn_location_off_and_scan_toggle_off(self.device)
206
207    def get_wlan_interface_id_list(self):
208        pass
209
210    def destroy_wlan_interface(self, iface_id):
211        pass
212
213    def send_command(self, command):
214        return self.device.adb.shell(str(command))
215
216    def get_interface_ip_addresses(self, interface):
217        return get_interface_ip_addresses(self.device, interface)
218
219    def is_connected(self):
220        return 'BSSID' in self.device.droid.wifiGetConnectionInfo()
221
222    def ping(self, dest_ip, count=3, interval=1000, timeout=1000, size=25):
223        return adb_shell_ping(self.device,
224                              dest_ip=dest_ip,
225                              count=count,
226                              timeout=timeout)
227
228
229class FuchsiaWlanDevice(WlanDevice):
230    """Class wrapper for an Fuchsia WLAN device.
231
232    Each object of this class represents a generic WLAN device.
233    Android device and Fuchsia devices are the currently supported devices/
234
235    Attributes:
236        fuchsia_device: A Fuchsia WLAN device.
237    """
238    def __init__(self, fuchsia_device):
239        super().__init__(fuchsia_device)
240
241    def wifi_toggle_state(self, state):
242        """Stub for Fuchsia implementation."""
243        pass
244
245    def reset_wifi(self):
246        """Stub for Fuchsia implementation."""
247        pass
248
249    def take_bug_report(self, test_name, begin_time):
250        """Stub for Fuchsia implementation."""
251        pass
252
253    def get_log(self, test_name, begin_time):
254        """Stub for Fuchsia implementation."""
255        pass
256
257    def turn_location_off_and_scan_toggle_off(self):
258        """Stub for Fuchsia implementation."""
259        pass
260
261    def associate(self,
262                  target_ssid,
263                  target_pwd=None,
264                  key_mgmt=None,
265                  check_connectivity=True,
266                  hidden=False):
267        """Function to associate a Fuchsia WLAN device.
268
269        Args:
270            target_ssid: SSID to associate to.
271            target_pwd: Password for the SSID, if necessary.
272            key_mgmt: the hostapd wpa_key_mgmt, if specified.
273            check_connectivity: Whether to check for internet connectivity.
274            hidden: Whether the network is hidden.
275        Returns:
276            True if successfully connected to WLAN, False if not.
277        """
278        connection_response = self.device.wlan_lib.wlanConnectToNetwork(
279            target_ssid, target_pwd=target_pwd)
280
281        return self.device.check_connect_response(connection_response)
282
283    def disconnect(self):
284        """Function to disconnect from a Fuchsia WLAN device.
285           Asserts if disconnect was not successful.
286        """
287        disconnect_response = self.device.wlan_lib.wlanDisconnect()
288        asserts.assert_true(
289            self.device.check_disconnect_response(disconnect_response),
290            'Failed to disconnect.')
291
292    def status(self):
293        return self.device.wlan_lib.wlanStatus()
294
295    def ping(self, dest_ip, count=3, interval=1000, timeout=1000, size=25):
296        ping_result = self.device.ping(dest_ip,
297                                       count=count,
298                                       interval=interval,
299                                       timeout=timeout,
300                                       size=size)
301        return ping_result['status']
302
303    def get_wlan_interface_id_list(self):
304        """Function to list available WLAN interfaces.
305
306        Returns:
307            A list of wlan interface IDs.
308        """
309        return self.device.wlan_lib.wlanGetIfaceIdList().get('result')
310
311    def destroy_wlan_interface(self, iface_id):
312        """Function to associate a Fuchsia WLAN device.
313
314        Args:
315            target_ssid: SSID to associate to.
316            target_pwd: Password for the SSID, if necessary.
317            check_connectivity: Whether to check for internet connectivity.
318            hidden: Whether the network is hidden.
319        Returns:
320            True if successfully destroyed wlan interface, False if not.
321        """
322        result = self.device.wlan_lib.wlanDestroyIface(iface_id)
323        if result.get('error') is None:
324            return True
325        else:
326            self.log.error("Failed to destroy interface with: {}".format(
327                result.get('error')))
328            return False
329
330    def send_command(self, command):
331        return self.device.send_command_ssh(str(command)).stdout
332
333    def get_interface_ip_addresses(self, interface):
334        return get_interface_ip_addresses(self.device, interface)
335
336    def is_connected(self):
337        return fwutils.is_connected(self)
338