blob: b1565d2c41c944c1e3e51c6e56490e12b1ed451e [file] [log] [blame]
Mark De Ruyter1a7ae572018-03-02 15:35:36 -08001#!/usr/bin/env python3
Qi Jiang591437d2017-08-01 17:46:55 -07002#
3# Copyright 2017 Google, Inc.
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 logging
Qi Jiang591437d2017-08-01 17:46:55 -070018import time
19from acts import utils
Qi Jiangb4110312017-10-05 22:00:29 -070020from acts.libs.proc import job
Qi Jiang53ff16a2018-02-15 13:12:10 -080021from acts.controllers.ap_lib import bridge_interface as bi
Xianyuan Jia63751fb2020-11-17 00:07:40 +000022from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
Qi Jiang591437d2017-08-01 17:46:55 -070023from acts.controllers.ap_lib import hostapd_security
24from acts.controllers.ap_lib import hostapd_ap_preset
Daniel Barrosd1672712017-11-06 21:50:50 -080025
Daniel Barros1cefafd2017-08-04 15:36:40 -070026# http://www.secdev.org/projects/scapy/
Daniel Barros57c14d82019-01-17 19:03:05 -080027# On ubuntu, sudo pip3 install scapy
Daniel Barros1cefafd2017-08-04 15:36:40 -070028import scapy.all as scapy
Qi Jiang591437d2017-08-01 17:46:55 -070029
Daniel Barros32ed8472017-09-30 16:45:37 -070030GET_FROM_PHONE = 'get_from_dut'
31GET_FROM_AP = 'get_from_ap'
Qi Jiang6035b442018-04-20 15:38:35 -070032ENABLED_MODULATED_DTIM = 'gEnableModulatedDTIM='
33MAX_MODULATED_DTIM = 'gMaxLIModulatedDTIM='
Qi Jiang591437d2017-08-01 17:46:55 -070034
35
Qi Jiange26642e2017-11-03 14:57:25 -070036def change_dtim(ad, gEnableModulatedDTIM, gMaxLIModulatedDTIM=10):
Qi Jiang591437d2017-08-01 17:46:55 -070037 """Function to change the DTIM setting in the phone.
38
39 Args:
40 ad: the target android device, AndroidDevice object
41 gEnableModulatedDTIM: Modulated DTIM, int
42 gMaxLIModulatedDTIM: Maximum modulated DTIM, int
43 """
Qi Jiang6035b442018-04-20 15:38:35 -070044 # First trying to find the ini file with DTIM settings
45 ini_file_phone = ad.adb.shell('ls /vendor/firmware/wlan/*/*.ini')
46 ini_file_local = ini_file_phone.split('/')[-1]
47
48 # Pull the file and change the DTIM to desired value
49 ad.adb.pull('{} {}'.format(ini_file_phone, ini_file_local))
Qi Jiang591437d2017-08-01 17:46:55 -070050
51 with open(ini_file_local, 'r') as fin:
52 for line in fin:
Qi Jiang6035b442018-04-20 15:38:35 -070053 if ENABLED_MODULATED_DTIM in line:
54 gE_old = line.strip('\n')
55 gEDTIM_old = line.strip(ENABLED_MODULATED_DTIM).strip('\n')
56 if MAX_MODULATED_DTIM in line:
57 gM_old = line.strip('\n')
58 gMDTIM_old = line.strip(MAX_MODULATED_DTIM).strip('\n')
59 fin.close()
Qi Jiange26642e2017-11-03 14:57:25 -070060 if int(gEDTIM_old) == gEnableModulatedDTIM and int(
61 gMDTIM_old) == gMaxLIModulatedDTIM:
Qi Jiang591437d2017-08-01 17:46:55 -070062 ad.log.info('Current DTIM is already the desired value,'
63 'no need to reset it')
Qi Jiang1785a4a2018-05-20 17:20:32 -070064 return 0
Qi Jiang591437d2017-08-01 17:46:55 -070065
Qi Jiang6035b442018-04-20 15:38:35 -070066 gE_new = ENABLED_MODULATED_DTIM + str(gEnableModulatedDTIM)
67 gM_new = MAX_MODULATED_DTIM + str(gMaxLIModulatedDTIM)
Qi Jiang591437d2017-08-01 17:46:55 -070068
Qi Jiang6035b442018-04-20 15:38:35 -070069 sed_gE = 'sed -i \'s/{}/{}/g\' {}'.format(gE_old, gE_new, ini_file_local)
70 sed_gM = 'sed -i \'s/{}/{}/g\' {}'.format(gM_old, gM_new, ini_file_local)
71 job.run(sed_gE)
72 job.run(sed_gM)
Qi Jiang591437d2017-08-01 17:46:55 -070073
Qi Jiang6035b442018-04-20 15:38:35 -070074 # Push the file to the phone
75 push_file_to_phone(ad, ini_file_local, ini_file_phone)
76 ad.log.info('DTIM changes checked in and rebooting...')
77 ad.reboot()
Qi Jiang1785a4a2018-05-20 17:20:32 -070078 # Wait for auto-wifi feature to start
79 time.sleep(20)
Qi Jiang83f45882018-09-18 11:50:06 -070080 ad.adb.shell('dumpsys battery set level 100')
Qi Jiang6035b442018-04-20 15:38:35 -070081 ad.log.info('DTIM updated and device back from reboot')
Qi Jiang1785a4a2018-05-20 17:20:32 -070082 return 1
Qi Jiang6035b442018-04-20 15:38:35 -070083
84
85def push_file_to_phone(ad, file_local, file_phone):
86 """Function to push local file to android phone.
87
88 Args:
89 ad: the target android device
90 file_local: the locla file to push
91 file_phone: the file/directory on the phone to be pushed
92 """
93 ad.adb.root()
94 cmd_out = ad.adb.remount()
95 if 'Permission denied' in cmd_out:
Qi Jiang591437d2017-08-01 17:46:55 -070096 ad.log.info('Need to disable verity first and reboot')
Qi Jiang6035b442018-04-20 15:38:35 -070097 ad.adb.disable_verity()
Qi Jiang591437d2017-08-01 17:46:55 -070098 time.sleep(1)
99 ad.reboot()
100 ad.log.info('Verity disabled and device back from reboot')
Qi Jiang6035b442018-04-20 15:38:35 -0700101 ad.adb.root()
102 ad.adb.remount()
Qi Jiang591437d2017-08-01 17:46:55 -0700103 time.sleep(1)
Qi Jiang6035b442018-04-20 15:38:35 -0700104 ad.adb.push('{} {}'.format(file_local, file_phone))
Qi Jiang591437d2017-08-01 17:46:55 -0700105
106
Qi Jiangef7de182017-12-05 14:56:08 -0800107def ap_setup(ap, network, bandwidth=80):
Qi Jiang591437d2017-08-01 17:46:55 -0700108 """Set up the whirlwind AP with provided network info.
109
110 Args:
111 ap: access_point object of the AP
112 network: dict with information of the network, including ssid, password
113 bssid, channel etc.
Qi Jiangef7de182017-12-05 14:56:08 -0800114 bandwidth: the operation bandwidth for the AP, default 80MHz
Qi Jiang53ff16a2018-02-15 13:12:10 -0800115 Returns:
116 brconfigs: the bridge interface configs
Qi Jiang591437d2017-08-01 17:46:55 -0700117 """
Qi Jiang591437d2017-08-01 17:46:55 -0700118 log = logging.getLogger()
119 bss_settings = []
120 ssid = network[wutils.WifiEnums.SSID_KEY]
Qi Jiangb4110312017-10-05 22:00:29 -0700121 if "password" in network.keys():
122 password = network["password"]
123 security = hostapd_security.Security(
124 security_mode="wpa", password=password)
125 else:
126 security = hostapd_security.Security(security_mode=None, password=None)
Qi Jiang591437d2017-08-01 17:46:55 -0700127 channel = network["channel"]
Qi Jiang591437d2017-08-01 17:46:55 -0700128 config = hostapd_ap_preset.create_ap_preset(
129 channel=channel,
130 ssid=ssid,
131 security=security,
132 bss_settings=bss_settings,
Qi Jiangef7de182017-12-05 14:56:08 -0800133 vht_bandwidth=bandwidth,
134 profile_name='whirlwind',
135 iface_wlan_2g=ap.wlan_2g,
136 iface_wlan_5g=ap.wlan_5g)
Qi Jiang53ff16a2018-02-15 13:12:10 -0800137 config_bridge = ap.generate_bridge_configs(channel)
138 brconfigs = bi.BridgeInterfaceConfigs(config_bridge[0], config_bridge[1],
139 config_bridge[2])
140 ap.bridge.startup(brconfigs)
Qi Jiang591437d2017-08-01 17:46:55 -0700141 ap.start_ap(config)
142 log.info("AP started on channel {} with SSID {}".format(channel, ssid))
Qi Jiang53ff16a2018-02-15 13:12:10 -0800143 return brconfigs
Qi Jiang591437d2017-08-01 17:46:55 -0700144
145
Qi Jiang591437d2017-08-01 17:46:55 -0700146def run_iperf_client_nonblocking(ad, server_host, extra_args=""):
147 """Start iperf client on the device with nohup.
148
149 Return status as true if iperf client start successfully.
150 And data flow information as results.
151
152 Args:
153 ad: the android device under test
154 server_host: Address of the iperf server.
155 extra_args: A string representing extra arguments for iperf client,
156 e.g. "-i 1 -t 30".
157
158 """
159 log = logging.getLogger()
Kevin Klug8286e7b2019-03-20 11:58:35 -0700160 ad.adb.shell_nb("nohup >/dev/null 2>&1 sh -c 'iperf3 -c {} {} &'".format(
161 server_host, extra_args))
Qi Jiang591437d2017-08-01 17:46:55 -0700162 log.info("IPerf client started")
163
164
165def get_wifi_rssi(ad):
166 """Get the RSSI of the device.
167
168 Args:
169 ad: the android device under test
170 Returns:
171 RSSI: the rssi level of the device
172 """
173 RSSI = ad.droid.wifiGetConnectionInfo()['rssi']
174 return RSSI
Qi Jiang23842472017-08-08 12:58:40 -0700175
176
177def get_phone_ip(ad):
178 """Get the WiFi IP address of the phone.
179
180 Args:
181 ad: the android device under test
182 Returns:
183 IP: IP address of the phone for WiFi, as a string
184 """
185 IP = ad.droid.connectivityGetIPv4Addresses('wlan0')[0]
186
187 return IP
188
189
190def get_phone_mac(ad):
191 """Get the WiFi MAC address of the phone.
192
193 Args:
194 ad: the android device under test
195 Returns:
196 mac: MAC address of the phone for WiFi, as a string
197 """
198 mac = ad.droid.wifiGetConnectionInfo()["mac_address"]
199
200 return mac
201
202
203def get_phone_ipv6(ad):
204 """Get the WiFi IPV6 address of the phone.
205
206 Args:
207 ad: the android device under test
208 Returns:
209 IPv6: IPv6 address of the phone for WiFi, as a string
210 """
211 IPv6 = ad.droid.connectivityGetLinkLocalIpv6Address('wlan0')[:-6]
212
213 return IPv6
Daniel Barros1cefafd2017-08-04 15:36:40 -0700214
215
Qide56d672019-08-27 13:33:21 -0700216def wait_for_dhcp(interface_name):
Qi Jiangb4110312017-10-05 22:00:29 -0700217 """Wait the DHCP address assigned to desired interface.
218
219 Getting DHCP address takes time and the wait time isn't constant. Utilizing
220 utils.timeout to keep trying until success
221
222 Args:
Qide56d672019-08-27 13:33:21 -0700223 interface_name: desired interface name
Qi Jiangb4110312017-10-05 22:00:29 -0700224 Returns:
225 ip: ip address of the desired interface name
226 Raise:
227 TimeoutError: After timeout, if no DHCP assigned, raise
228 """
229 log = logging.getLogger()
Qide56d672019-08-27 13:33:21 -0700230 reset_host_interface(interface_name)
231 start_time = time.time()
232 time_limit_seconds = 60
Qi Jiangb4110312017-10-05 22:00:29 -0700233 ip = '0.0.0.0'
Qi6ca8a922019-09-03 12:32:25 -0700234 while start_time + time_limit_seconds > time.time():
Qide56d672019-08-27 13:33:21 -0700235 ip = scapy.get_if_addr(interface_name)
236 if ip == '0.0.0.0':
237 time.sleep(1)
238 else:
Qifb9140b682019-09-13 18:07:02 -0700239 log.info(
240 'DHCP address assigned to %s as %s' % (interface_name, ip))
Qide56d672019-08-27 13:33:21 -0700241 return ip
242 raise TimeoutError('Timed out while getting if_addr after %s seconds.' %
Qifb9140b682019-09-13 18:07:02 -0700243 time_limit_seconds)
244
Qi Jiangb4110312017-10-05 22:00:29 -0700245
Qide56d672019-08-27 13:33:21 -0700246def reset_host_interface(intferface_name):
Qi Jiangb4110312017-10-05 22:00:29 -0700247 """Reset the host interface.
248
249 Args:
Qide56d672019-08-27 13:33:21 -0700250 intferface_name: the desired interface to reset
Qi Jiangb4110312017-10-05 22:00:29 -0700251 """
252 log = logging.getLogger()
Qide56d672019-08-27 13:33:21 -0700253 intf_down_cmd = 'ifconfig %s down' % intferface_name
254 intf_up_cmd = 'ifconfig %s up' % intferface_name
Qi Jiangb4110312017-10-05 22:00:29 -0700255 try:
256 job.run(intf_down_cmd)
Qi Jiangb29f3912018-02-02 15:12:02 -0800257 time.sleep(10)
Qi Jiangb4110312017-10-05 22:00:29 -0700258 job.run(intf_up_cmd)
Qide56d672019-08-27 13:33:21 -0700259 log.info('{} has been reset'.format(intferface_name))
Qi Jiangb4110312017-10-05 22:00:29 -0700260 except job.Error:
261 raise Exception('No such interface')
262
Qifb9140b682019-09-13 18:07:02 -0700263
Qide56d672019-08-27 13:33:21 -0700264def bringdown_host_interface(intferface_name):
265 """Reset the host interface.
266
267 Args:
268 intferface_name: the desired interface to reset
269 """
270 log = logging.getLogger()
271 intf_down_cmd = 'ifconfig %s down' % intferface_name
272 try:
273 job.run(intf_down_cmd)
274 time.sleep(2)
275 log.info('{} has been brought down'.format(intferface_name))
276 except job.Error:
277 raise Exception('No such interface')
Qi Jiangb4110312017-10-05 22:00:29 -0700278
Qifb9140b682019-09-13 18:07:02 -0700279
Daniel Barros32ed8472017-09-30 16:45:37 -0700280def create_pkt_config(test_class):
281 """Creates the config for generating multicast packets
282
283 Args:
284 test_class: object with all networking paramters
285
286 Returns:
287 Dictionary with the multicast packet config
288 """
289 addr_type = (scapy.IPV6_ADDR_LINKLOCAL
290 if test_class.ipv6_src_type == 'LINK_LOCAL' else
291 scapy.IPV6_ADDR_GLOBAL)
292
293 mac_dst = test_class.mac_dst
294 if GET_FROM_PHONE in test_class.mac_dst:
295 mac_dst = get_phone_mac(test_class.dut)
296
297 ipv4_dst = test_class.ipv4_dst
298 if GET_FROM_PHONE in test_class.ipv4_dst:
299 ipv4_dst = get_phone_ip(test_class.dut)
300
301 ipv6_dst = test_class.ipv6_dst
302 if GET_FROM_PHONE in test_class.ipv6_dst:
303 ipv6_dst = get_phone_ipv6(test_class.dut)
304
305 ipv4_gw = test_class.ipv4_gwt
306 if GET_FROM_AP in test_class.ipv4_gwt:
307 ipv4_gw = test_class.access_point.ssh_settings.hostname
308
309 pkt_gen_config = {
310 'interf': test_class.pkt_sender.interface,
311 'subnet_mask': test_class.sub_mask,
312 'src_mac': test_class.mac_src,
313 'dst_mac': mac_dst,
314 'src_ipv4': test_class.ipv4_src,
315 'dst_ipv4': ipv4_dst,
316 'src_ipv6': test_class.ipv6_src,
317 'src_ipv6_type': addr_type,
318 'dst_ipv6': ipv6_dst,
319 'gw_ipv4': ipv4_gw
320 }
321 return pkt_gen_config