autotest: Only expect BSS's on current SSID from background scans

johannes.berg@intel.com notes that background scans are used in the
context of roaming in wpa_supplicant, and results from background scans
are restricted to BSSes on the SSID we're currently associated with.
Fix our tests to reflect this by putting the BSS we're checking for
on the same SSID as the BSS we're associated with.

While here, refactor the code for retrieving hostapd MAC addresses so
that it uses shared ip addr parsing code.

BUG=chromium:332250
TEST=This is a test that continues to pass on ath9k parts, and now seems
to pass against mwifiex parts.

Change-Id: I7069a5caf29248cca766eed0bdcb186c2e95b26d
Reviewed-on: https://chromium-review.googlesource.com/181813
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Commit-Queue: Christopher Wiley <wiley@chromium.org>
Tested-by: Christopher Wiley <wiley@chromium.org>
diff --git a/client/common_lib/cros/network/iw_runner.py b/client/common_lib/cros/network/iw_runner.py
index 817646d..e0c3b7b 100644
--- a/client/common_lib/cros/network/iw_runner.py
+++ b/client/common_lib/cros/network/iw_runner.py
@@ -60,6 +60,50 @@
         self._command_iw = command_iw
 
 
+    def _parse_scan_results(self, output):
+        """Parse the output of the 'scan' and 'scan dump' commands.
+
+        @param output: string command output.
+
+        @returns a list of IwBss namedtuples; None if the scan fails
+
+        """
+        bss = None
+        frequency = None
+        ssid = None
+        ht = None
+        security = None
+        supported_securities = []
+        bss_list = []
+        for line in output.splitlines():
+            line = line.strip()
+            if line.startswith('BSS'):
+                if bss != None:
+                    security = self.determine_security(supported_securities)
+                    iwbss = IwBss(bss, frequency, ssid, security, ht)
+                    bss_list.append(iwbss)
+                    bss = frequency = ssid = security = ht = None
+                    supported_securities = []
+                bss = line.split()[1]
+            if line.startswith('freq:'):
+                frequency = int(line.split()[1])
+            if line.startswith('SSID:'):
+                ssid = line.split()
+                if len(ssid) > 1:
+                    ssid = ssid[1]
+                else:
+                    ssid = None
+            if line.startswith('* secondary channel offset'):
+                ht = HT_TABLE[line.split(':')[1].strip()]
+            if line.startswith('WPA'):
+               supported_securities.append(SECURITY_WPA)
+            if line.startswith('RSN'):
+               supported_securities.append(SECURITY_WPA2)
+        security = self.determine_security(supported_securities)
+        bss_list.append(IwBss(bss, frequency, ssid, security, ht))
+        return bss_list
+
+
     def add_interface(self, phy, interface, interface_type):
         """
         Add an interface to a WiFi PHY.
@@ -283,7 +327,7 @@
         @param frequencies: list of int frequencies in Mhz to scan.
         @param ssids: list of string SSIDs to send probe requests for.
 
-        @returns a list of IwBss collections; None if the scan fails
+        @returns a list of IwBss namedtuples; None if the scan fails
 
         """
         freq_param = ''
@@ -300,42 +344,23 @@
             # The device was busy
            return None
 
-        bss = None
-        frequency = None
-        ssid = None
-        ht = None
-        security = None
+        return self._parse_scan_results(scan.stdout)
 
-        supported_securities = []
-        bss_list = []
 
-        for line in scan.stdout.splitlines():
-            line = line.strip()
-            if line.startswith('BSS'):
-                if bss != None:
-                    security = self.determine_security(supported_securities)
-                    iwbss = IwBss(bss, frequency, ssid, security, ht)
-                    bss_list.append(iwbss)
-                    bss = frequency = ssid = security = ht = None
-                    supported_securities = []
-                bss = line.split()[1]
-            if line.startswith('freq:'):
-                frequency = int(line.split()[1])
-            if line.startswith('SSID:'):
-                ssid = line.split()
-                if len(ssid) > 1:
-                    ssid = ssid[1]
-                else:
-                    ssid = None
-            if line.startswith('* secondary channel offset'):
-                ht = HT_TABLE[line.split(':')[1].strip()]
-            if line.startswith('WPA'):
-               supported_securities.append(SECURITY_WPA)
-            if line.startswith('RSN'):
-               supported_securities.append(SECURITY_WPA2)
-        security = self.determine_security(supported_securities)
-        bss_list.append(IwBss(bss, frequency, ssid, security, ht))
-        return bss_list
+    def scan_dump(self, interface):
+        """Dump the contents of the scan cache.
+
+        Note that this does not trigger a scan.  Instead, it returns
+        the kernel's idea of what BSS's are currently visible.
+
+        @param interface: the interface to run the iw command against
+
+        @returns a list of IwBss namedtuples; None if the scan fails
+
+        """
+        result = self._run('%s dev %s scan dump' % (self._command_iw,
+                                                    interface))
+        return self._parse_scan_results(result.stdout)
 
 
     def set_tx_power(self, interface, power):
diff --git a/server/site_linux_router.py b/server/site_linux_router.py
index 9ca886f..9defce5 100644
--- a/server/site_linux_router.py
+++ b/server/site_linux_router.py
@@ -537,17 +537,13 @@
         @return string MAC address like 00:11:22:33:44:55.
 
         """
+        if not self.local_servers:
+            raise error.TestFail('Cannot retrieve MAC: '
+                                 'no AP instances configured.')
+
         instance = self.hostapd_instances[ap_num]
-        interface = instance['interface']
-        result = self.router.run('%s addr show %s' % (self.cmd_ip, interface))
-        # Example response:
-        #   1: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 UP qlen 1000
-        #   link/ether 99:88:77:66:55:44 brd ff:ff:ff:ff:ff:ff
-        #   inet 10.0.0.1/8 brd 10.255.255.255 scope global eth0
-        #   inet6 fe80::6a7f:74ff:fe66:5544/64 scope link
-        # we want the MAC address after the "link/ether" above.
-        parts = result.stdout.split(' ')
-        return parts[parts.index('link/ether') + 1]
+        ap_interface = interface.Interface(instance['interface'], self.host)
+        return ap_interface.mac_address
 
 
     def deconfig(self):
diff --git a/server/site_tests/network_WiFi_BgscanBackoff/network_WiFi_BgscanBackoff.py b/server/site_tests/network_WiFi_BgscanBackoff/network_WiFi_BgscanBackoff.py
index 817b871..d6533e1 100644
--- a/server/site_tests/network_WiFi_BgscanBackoff/network_WiFi_BgscanBackoff.py
+++ b/server/site_tests/network_WiFi_BgscanBackoff/network_WiFi_BgscanBackoff.py
@@ -6,6 +6,7 @@
 import time
 
 from autotest_lib.client.common_lib import error
+from autotest_lib.client.common_lib.cros.network import iw_runner
 from autotest_lib.client.common_lib.cros.network import ping_runner
 from autotest_lib.client.common_lib.cros.network import xmlrpc_datatypes
 from autotest_lib.server.cros.network import hostap_config
@@ -45,17 +46,25 @@
                 get_ping_config(self.BGSCAN_SAMPLE_PERIOD_SECONDS))
         logging.info('Ping statistics with bgscan: %r', result_bgscan)
         # Bring up a second AP, make sure that it shows up in bgscans.
-        self.context.configure(hostap_config.HostapConfig(channel=11),
-                               multi_interface=True)
+        self.context.configure(
+                hostap_config.HostapConfig(channel=11,
+                                           ssid=self.context.router.get_ssid()),
+                multi_interface=True)
         logging.info('Without a ping running, ensure that bgscans succeed.')
+        ap_mac = self.context.router.get_hostapd_mac(ap_num=1)
+        logging.debug('Looking for BSS %s', ap_mac)
+        iw = iw_runner.IwRunner(remote_host=self.context.client.host)
         start_time = time.time()
         while time.time() - start_time < self.BGSCAN_SAMPLE_PERIOD_SECONDS:
-            if (self.context.router.get_ssid(instance=1) in
-                    self.context.client.get_active_wifi_SSIDs()):
+            bsses = iw.scan_dump(self.context.client.wifi_if)
+            logging.debug('Found BSSes: %r', bsses)
+            if filter(lambda bss: bss.bss == ap_mac, bsses):
                 break
+
             time.sleep(1)
         else:
-            raise error.TestFail('Background scans should detect other SSIDs.')
+            raise error.TestFail('Background scans should detect new BSSes '
+                                 'within an associated ESS.')
 
         self.context.client.shill.disconnect(
                 self.context.router.get_ssid(instance=0))