Add an alternate detection technique for WiFi_Regulatory

In addition to testing for ping packet loss, also test for
reception of a DEAUTH frame at the AP.  Adherence in some
devices can only be detected by packet loss (they leave
the channel silently) so leave both detection methods in
parallel.

BUG=chrome-os-partner:20691
TEST=Run test on daisy, lumpy and a WP2 device

Change-Id: Id8610d5650c1dc0edf0c51933377bdb33f11c676
Reviewed-on: https://gerrit.chromium.org/gerrit/66015
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Commit-Queue: Paul Stewart <pstew@chromium.org>
Tested-by: Paul Stewart <pstew@chromium.org>
diff --git a/server/site_linux_router.py b/server/site_linux_router.py
index 6972425..827f2ab 100644
--- a/server/site_linux_router.py
+++ b/server/site_linux_router.py
@@ -785,6 +785,22 @@
         self._release_wlanif(interface)
 
 
+    def detect_client_deauth(self, client_mac, instance=0):
+        """Detects whether hostapd has logged a deauthentication from
+        |client_mac|.
+
+        @param client_mac string the MAC address of the client to detect.
+        @param instance int indicating which hostapd instance to query.
+
+        """
+        interface = self.hostapd_instances[instance]['interface']
+        deauth_msg = "%s: deauthentication: STA=%s" % (interface, client_mac)
+        log_file = self.hostapd_instances[instance]['log_file']
+        result = self.router.run("grep -qi '%s' %s" % (deauth_msg, log_file),
+                                 ignore_status=True)
+        return result.exit_status == 0
+
+
     def _pre_config_hook(self, config):
         """Hook for subclasses.
 
diff --git a/server/site_tests/network_WiFi_Regulatory/network_WiFi_Regulatory.py b/server/site_tests/network_WiFi_Regulatory/network_WiFi_Regulatory.py
index 072e0ad..981c670 100644
--- a/server/site_tests/network_WiFi_Regulatory/network_WiFi_Regulatory.py
+++ b/server/site_tests/network_WiFi_Regulatory/network_WiFi_Regulatory.py
@@ -10,8 +10,8 @@
 
 
 class network_WiFi_Regulatory(wifi_cell_test_base.WiFiCellTestBase):
-    """Test that the client vacates the channel and can no longer ping after
-    notification from the AP that it should switch channels."""
+    """Test that the client vacates the channel after notification
+    from the AP that it should switch channels."""
     version = 1
 
 
@@ -37,12 +37,20 @@
             self.context.assert_connect_wifi(assoc_params)
             ping_config = ping_runner.PingConfig(
                     self.context.get_wifi_addr(ap_num=0))
+            client_mac = self.context.client.wifi_mac
             for attempt in range(10):
+                # Since the client might be in power-save, we are not
+                # guaranteed it will hear this message the first time around.
                 self.context.router.send_management_frame(
                         'channel_switch:%d' % alternate_channel)
-                # This should fail at some point.  Since the client
-                # might be in power-save, we are not guaranteed it will hear
-                # this message the first time around.
+
+                # Test to see if the router received a deauth message from
+                # the client.
+                if self.context.router.detect_client_deauth(client_mac):
+                    break
+
+                # Otherwise detect the client leaving indirectly by measuring
+                # client pings.  This should fail at some point.
                 ping_config = ping_runner.PingConfig(
                         self.context.get_wifi_addr(ap_num=0),
                         count=3, ignore_status=True,