conman:  Fallback to wpa_cli when control interface fails.

For as yet unknown reasons, requests to the the wpa_supplicant control
interface sometimes fail.  When that happens, we can fail over to
using a subprocess call to the wpa_cli.

The unreliable code is left in place for two reasons:

- Recently added code may help debug it.
- It's slightly more efficient to avoid unnecessary subprocess calls.
- We want to use the control interface anyway in order to receive
  events.  (It's unclear whether the request bugs also affect this,
  which is further motivation to debug that problem.)

Seperately, the Frenzy test was actually previously incorrect
(simulating the request failure, which could not occur on Frenzy
because we don't use wpactrl on Frenzy), and has been fixed.

BUG=30638247

Change-Id: Ic5d9bf597129810a3690a0bd2bffdebb4dd4d59b
diff --git a/conman/interface.py b/conman/interface.py
index cdeaf67..b20709a 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -500,6 +500,7 @@
       except wpactrl.error as e:
         logging.error('wpa_control STATUS request failed %s args %s',
                       e.message, e.args)
+        lines = self.wpa_cli_status().splitlines()
       for line in lines:
         if '=' not in line:
           continue
@@ -553,6 +554,15 @@
     return (self.wpa_status().get('wpa_state', None) == 'COMPLETED' and
             self.wpa_status().get('key_mgmt', None) == 'NONE')
 
+  # TODO(rofrankel):  Remove this if and when the wpactrl failures are fixed.
+  def wpa_cli_status(self):
+    """Fallback for wpa_supplicant control interface status requests."""
+    try:
+      return subprocess.check_output(['wpa_cli', '-i', self.name, 'status'])
+    except subprocess.CalledProcessError:
+      logging.error('wpa_cli status request failed')
+      return ''
+
 
 class FrenzyWPACtrl(object):
   """A WPACtrl for Frenzy devices.
diff --git a/conman/interface_test.py b/conman/interface_test.py
index dd5e037..e8f0876 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -164,12 +164,7 @@
     if request_type == 'STATUS':
       if self.request_status_fails:
         raise wpactrl.error('test error')
-      if self.connected:
-        return ('foo\nwpa_state=COMPLETED\nssid=%s\nkey_mgmt=%s\nbar' %
-                (self.ssid_testonly,
-                 'WPA2-PSK' if self.secure_testonly else 'NONE'))
-      else:
-        return 'wpa_state=SCANNING\naddress=12:34:56:78:90:ab'
+      return self.wpa_cli_status_testonly()
     else:
       raise ValueError('Invalid request_type %s' % request_type)
 
@@ -198,6 +193,14 @@
     if not os.path.exists(self._socket):
       raise wpactrl.error(msg)
 
+  def wpa_cli_status_testonly(self):
+    if self.connected:
+      return ('foo\nwpa_state=COMPLETED\nssid=%s\nkey_mgmt=%s\nbar' %
+              (self.ssid_testonly,
+               'WPA2-PSK' if self.secure_testonly else 'NONE'))
+    else:
+      return 'wpa_state=SCANNING\naddress=12:34:56:78:90:ab'
+
 
 class Wifi(FakeInterfaceMixin, interface.Wifi):
   """Fake Wifi for testing."""
@@ -247,6 +250,11 @@
     self._secure_testonly = False
     super(Wifi, self).detach_wpa_control()
 
+  def wpa_cli_status(self):
+    # This is just a convenient way of keeping things dry; the actual wpa_cli
+    # status makes a subprocess call which returns the same string.
+    return self._wpa_control.wpa_cli_status_testonly()
+
   def start_wpa_supplicant_testonly(self, path):
     wpa_socket = os.path.join(path, self.name)
     logging.debug('Starting fake wpa_supplicant for %s: %s',
@@ -296,12 +304,6 @@
     self.add_terminating_event()
     super(FrenzyWPACtrl, self).detach()
 
-  def request(self, request_type):
-    if request_type == 'STATUS' and self.request_status_fails:
-      raise wpactrl.error('test error')
-
-    return super(FrenzyWPACtrl, self).request(request_type)
-
 
 class FrenzyWifi(FakeInterfaceMixin, interface.FrenzyWifi):
   WPACtrl = FrenzyWPACtrl
@@ -520,7 +522,7 @@
 
   wvtest.WVPASSNE(w.wpa_status(), {})
   w._wpa_control.request_status_fails = True
-  wvtest.WVPASSEQ(w.wpa_status(), {})
+  wvtest.WVPASSNE(w.wpa_status(), {})
 
   # The wpa_supplicant process disconnects and terminates.
   wpa_control.add_disconnected_event()