• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
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
18import time
19from acts import utils
20from acts.libs.proc import job
21from acts.controllers.ap_lib import bridge_interface as bi
22from acts_contrib.test_utils.wifi import wifi_test_utils as wutils
23from acts.controllers.adb_lib.error import AdbCommandError
24from acts.controllers.ap_lib import hostapd_security
25from acts.controllers.ap_lib import hostapd_ap_preset
26
27# http://www.secdev.org/projects/scapy/
28# On ubuntu, sudo pip3 install scapy
29import scapy.all as scapy
30
31GET_FROM_PHONE = 'get_from_dut'
32GET_FROM_AP = 'get_from_ap'
33ENABLED_MODULATED_DTIM = 'gEnableModulatedDTIM='
34MAX_MODULATED_DTIM = 'gMaxLIModulatedDTIM='
35
36
37def change_dtim(ad, gEnableModulatedDTIM, gMaxLIModulatedDTIM=10):
38    """Function to change the DTIM setting in the phone.
39
40    Args:
41        ad: the target android device, AndroidDevice object
42        gEnableModulatedDTIM: Modulated DTIM, int
43        gMaxLIModulatedDTIM: Maximum modulated DTIM, int
44    """
45    ad.log.info('Sets dtim to {}'.format(gEnableModulatedDTIM))
46
47    # In P21 the dtim setting method changed and an AdbCommandError will take
48    # place to get ini_file_phone. Thus add try/except block for the old method.
49    # If error occurs, use change_dtim_adb method later. Otherwise, first trying
50    # to find the ini file with DTIM settings
51    try:
52        ini_file_phone = ad.adb.shell('ls /vendor/firmware/wlan/*/*.ini')
53
54    except AdbCommandError as e:
55
56        # Gets AdbCommandError, change dtim later with change_dtim_adb merthod.
57        # change_dtim_adb requires that wifi connection is on.
58        ad.log.info('Gets AdbCommandError, change dtim with change_dtim_adb.')
59        change_dtim_adb(ad, gEnableModulatedDTIM)
60        return 0
61
62    ini_file_local = ini_file_phone.split('/')[-1]
63
64    # Pull the file and change the DTIM to desired value
65    ad.adb.pull('{} {}'.format(ini_file_phone, ini_file_local))
66
67    with open(ini_file_local, 'r') as fin:
68        for line in fin:
69            if ENABLED_MODULATED_DTIM in line:
70                gE_old = line.strip('\n')
71                gEDTIM_old = line.strip(ENABLED_MODULATED_DTIM).strip('\n')
72            if MAX_MODULATED_DTIM in line:
73                gM_old = line.strip('\n')
74                gMDTIM_old = line.strip(MAX_MODULATED_DTIM).strip('\n')
75    fin.close()
76    if int(gEDTIM_old) == gEnableModulatedDTIM and int(
77            gMDTIM_old) == gMaxLIModulatedDTIM:
78        ad.log.info('Current DTIM is already the desired value,'
79                    'no need to reset it')
80        return 0
81
82    gE_new = ENABLED_MODULATED_DTIM + str(gEnableModulatedDTIM)
83    gM_new = MAX_MODULATED_DTIM + str(gMaxLIModulatedDTIM)
84
85    sed_gE = 'sed -i \'s/{}/{}/g\' {}'.format(gE_old, gE_new, ini_file_local)
86    sed_gM = 'sed -i \'s/{}/{}/g\' {}'.format(gM_old, gM_new, ini_file_local)
87    job.run(sed_gE)
88    job.run(sed_gM)
89
90    # Push the file to the phone
91    push_file_to_phone(ad, ini_file_local, ini_file_phone)
92    ad.log.info('DTIM changes checked in and rebooting...')
93    ad.reboot()
94    # Wait for auto-wifi feature to start
95    time.sleep(20)
96    ad.adb.shell('dumpsys battery set level 100')
97    ad.log.info('DTIM updated and device back from reboot')
98    return 1
99
100def change_dtim_adb(ad, gEnableModulatedDTIM):
101    """Function to change the DTIM setting in the P21 phone.
102
103        This method should be run after connecting wifi.
104
105    Args:
106        ad: the target android device, AndroidDevice object
107        gEnableModulatedDTIM: Modulated DTIM, int
108    """
109    ad.log.info('Changes DTIM to {} with adb'.format(gEnableModulatedDTIM))
110    ad.adb.root()
111    screen_status = ad.adb.shell('dumpsys nfc | grep Screen')
112    screen_is_on = 'ON_UNLOCKED' in screen_status
113
114    # To read the dtim with 'adb shell wl bcn_li_dtim', the screen should be off
115    if screen_is_on:
116        ad.log.info('The screen is on. Set it to off before change dtim')
117        ad.droid.goToSleepNow()
118        time_limit_seconds = 60
119        _wait_screen_off(ad, time_limit_seconds)
120
121    old_dtim = ad.adb.shell('wl bcn_li_dtim')
122    ad.log.info('The dtim before change is {}'.format(old_dtim))
123    if int(old_dtim) == gEnableModulatedDTIM:
124        ad.log.info('Current DTIM is already the desired value,'
125                    'no need to reset it')
126        if screen_is_on:
127            ad.log.info('Changes the screen to the original on status')
128            ad.droid.wakeUpNow()
129        return
130    current_dtim = _set_dtim(ad, gEnableModulatedDTIM)
131    ad.log.info(
132        'Old DTIM is {}, current DTIM is {}'.format(old_dtim, current_dtim))
133    if screen_is_on:
134        ad.log.info('Changes the screen to the original on status')
135        ad.droid.wakeUpNow()
136
137def _set_dtim(ad, gEnableModulatedDTIM):
138    ad.adb.shell("halutil -dtim_config {}".format(gEnableModulatedDTIM))
139    return ad.adb.shell('wl bcn_li_dtim')
140
141
142def _wait_screen_off(ad, time_limit_seconds):
143    while time_limit_seconds > 0:
144        screen_status = ad.adb.shell('dumpsys nfc | grep Screen')
145        if 'OFF_UNLOCKED' in screen_status:
146            ad.log.info('The screen status is {}'.format(screen_status))
147            return
148        time.sleep(1)
149        time_limit_seconds -= 1
150    raise TimeoutError('Timed out while waiting the screen off after {} '
151                       'seconds.'.format(time_limit_seconds))
152
153
154def push_file_to_phone(ad, file_local, file_phone):
155    """Function to push local file to android phone.
156
157    Args:
158        ad: the target android device
159        file_local: the locla file to push
160        file_phone: the file/directory on the phone to be pushed
161    """
162    ad.adb.root()
163    cmd_out = ad.adb.remount()
164    if 'Permission denied' in cmd_out:
165        ad.log.info('Need to disable verity first and reboot')
166        ad.adb.disable_verity()
167        time.sleep(1)
168        ad.reboot()
169        ad.log.info('Verity disabled and device back from reboot')
170        ad.adb.root()
171        ad.adb.remount()
172    time.sleep(1)
173    ad.adb.push('{} {}'.format(file_local, file_phone))
174
175
176def ap_setup(ap, network, bandwidth=80, dtim_period=None):
177    """Set up the whirlwind AP with provided network info.
178
179    Args:
180        ap: access_point object of the AP
181        network: dict with information of the network, including ssid, password
182                 bssid, channel etc.
183        bandwidth: the operation bandwidth for the AP, default 80MHz
184        dtim_period: the dtim period of access point
185    Returns:
186        brconfigs: the bridge interface configs
187    """
188    log = logging.getLogger()
189    bss_settings = []
190    ssid = network[wutils.WifiEnums.SSID_KEY]
191    if "password" in network.keys():
192        password = network["password"]
193        security = hostapd_security.Security(
194            security_mode="wpa", password=password)
195    else:
196        security = hostapd_security.Security(security_mode=None, password=None)
197    channel = network["channel"]
198    config = hostapd_ap_preset.create_ap_preset(
199        channel=channel,
200        ssid=ssid,
201        dtim_period=dtim_period,
202        security=security,
203        bss_settings=bss_settings,
204        vht_bandwidth=bandwidth,
205        profile_name='whirlwind',
206        iface_wlan_2g=ap.wlan_2g,
207        iface_wlan_5g=ap.wlan_5g)
208    config_bridge = ap.generate_bridge_configs(channel)
209    brconfigs = bi.BridgeInterfaceConfigs(config_bridge[0], config_bridge[1],
210                                          config_bridge[2])
211    ap.bridge.startup(brconfigs)
212    ap.start_ap(config)
213    log.info("AP started on channel {} with SSID {}".format(channel, ssid))
214    return brconfigs
215
216
217def run_iperf_client_nonblocking(ad, server_host, extra_args=""):
218    """Start iperf client on the device with nohup.
219
220    Return status as true if iperf client start successfully.
221    And data flow information as results.
222
223    Args:
224        ad: the android device under test
225        server_host: Address of the iperf server.
226        extra_args: A string representing extra arguments for iperf client,
227            e.g. "-i 1 -t 30".
228
229    """
230    log = logging.getLogger()
231    ad.adb.shell_nb("nohup >/dev/null 2>&1 sh -c 'iperf3 -c {} {} &'".format(
232        server_host, extra_args))
233    log.info("IPerf client started")
234
235
236def get_wifi_rssi(ad):
237    """Get the RSSI of the device.
238
239    Args:
240        ad: the android device under test
241    Returns:
242        RSSI: the rssi level of the device
243    """
244    RSSI = ad.droid.wifiGetConnectionInfo()['rssi']
245    return RSSI
246
247
248def get_phone_ip(ad):
249    """Get the WiFi IP address of the phone.
250
251    Args:
252        ad: the android device under test
253    Returns:
254        IP: IP address of the phone for WiFi, as a string
255    """
256    IP = ad.droid.connectivityGetIPv4Addresses('wlan0')[0]
257
258    return IP
259
260
261def get_phone_mac(ad):
262    """Get the WiFi MAC address of the phone.
263
264    Args:
265        ad: the android device under test
266    Returns:
267        mac: MAC address of the phone for WiFi, as a string
268    """
269    mac = ad.droid.wifiGetConnectionInfo()["mac_address"]
270
271    return mac
272
273
274def get_phone_ipv6(ad):
275    """Get the WiFi IPV6 address of the phone.
276
277    Args:
278        ad: the android device under test
279    Returns:
280        IPv6: IPv6 address of the phone for WiFi, as a string
281    """
282    IPv6 = ad.droid.connectivityGetLinkLocalIpv6Address('wlan0')[:-6]
283
284    return IPv6
285
286
287def wait_for_dhcp(interface_name):
288    """Wait the DHCP address assigned to desired interface.
289
290    Getting DHCP address takes time and the wait time isn't constant. Utilizing
291    utils.timeout to keep trying until success
292
293    Args:
294        interface_name: desired interface name
295    Returns:
296        ip: ip address of the desired interface name
297    Raise:
298        TimeoutError: After timeout, if no DHCP assigned, raise
299    """
300    log = logging.getLogger()
301    reset_host_interface(interface_name)
302    start_time = time.time()
303    time_limit_seconds = 60
304    ip = '0.0.0.0'
305    while start_time + time_limit_seconds > time.time():
306        ip = scapy.get_if_addr(interface_name)
307        if ip == '0.0.0.0':
308            time.sleep(1)
309        else:
310            log.info(
311                'DHCP address assigned to %s as %s' % (interface_name, ip))
312            return ip
313    raise TimeoutError('Timed out while getting if_addr after %s seconds.' %
314                       time_limit_seconds)
315
316
317def reset_host_interface(intferface_name):
318    """Reset the host interface.
319
320    Args:
321        intferface_name: the desired interface to reset
322    """
323    log = logging.getLogger()
324    intf_down_cmd = 'ifconfig %s down' % intferface_name
325    intf_up_cmd = 'ifconfig %s up' % intferface_name
326    try:
327        job.run(intf_down_cmd)
328        time.sleep(10)
329        job.run(intf_up_cmd)
330        log.info('{} has been reset'.format(intferface_name))
331    except job.Error:
332        raise Exception('No such interface')
333
334
335def bringdown_host_interface(intferface_name):
336    """Reset the host interface.
337
338    Args:
339        intferface_name: the desired interface to reset
340    """
341    log = logging.getLogger()
342    intf_down_cmd = 'ifconfig %s down' % intferface_name
343    try:
344        job.run(intf_down_cmd)
345        time.sleep(2)
346        log.info('{} has been brought down'.format(intferface_name))
347    except job.Error:
348        raise Exception('No such interface')
349
350
351def create_pkt_config(test_class):
352    """Creates the config for generating multicast packets
353
354    Args:
355        test_class: object with all networking paramters
356
357    Returns:
358        Dictionary with the multicast packet config
359    """
360    addr_type = (scapy.IPV6_ADDR_LINKLOCAL
361                 if test_class.ipv6_src_type == 'LINK_LOCAL' else
362                 scapy.IPV6_ADDR_GLOBAL)
363
364    mac_dst = test_class.mac_dst
365    if GET_FROM_PHONE in test_class.mac_dst:
366        mac_dst = get_phone_mac(test_class.dut)
367
368    ipv4_dst = test_class.ipv4_dst
369    if GET_FROM_PHONE in test_class.ipv4_dst:
370        ipv4_dst = get_phone_ip(test_class.dut)
371
372    ipv6_dst = test_class.ipv6_dst
373    if GET_FROM_PHONE in test_class.ipv6_dst:
374        ipv6_dst = get_phone_ipv6(test_class.dut)
375
376    ipv4_gw = test_class.ipv4_gwt
377    if GET_FROM_AP in test_class.ipv4_gwt:
378        ipv4_gw = test_class.access_point.ssh_settings.hostname
379
380    pkt_gen_config = {
381        'interf': test_class.pkt_sender.interface,
382        'subnet_mask': test_class.sub_mask,
383        'src_mac': test_class.mac_src,
384        'dst_mac': mac_dst,
385        'src_ipv4': test_class.ipv4_src,
386        'dst_ipv4': ipv4_dst,
387        'src_ipv6': test_class.ipv6_src,
388        'src_ipv6_type': addr_type,
389        'dst_ipv6': ipv6_dst,
390        'gw_ipv4': ipv4_gw
391    }
392    return pkt_gen_config
393