Merge "/bin/wifi:  Save options per-interface rather than per-band."
diff --git a/wifi/iw.py b/wifi/iw.py
index c0bbf57..f16c50b 100644
--- a/wifi/iw.py
+++ b/wifi/iw.py
@@ -267,10 +267,10 @@
 
 
 def find_all_interfaces_from_band(band, interface_type=None):
-  """Finds the names of all interface on a given band.
+  """Finds the names of all interfaces on a given band.
 
   Args:
-    band: The band for which you want the interface.
+    band: The band for which you want the interface(s).
     interface_type: An INTERFACE_TYPE value (optional).
 
   Returns:
@@ -283,6 +283,32 @@
   return find_all_interfaces_from_phy(phy, interface_type)
 
 
+def find_interfaces_from_band_and_suffix(band, suffix, interface_type=None):
+  """Finds the names of interfaces on a given band.
+
+  Args:
+    band: The band for which you want the interface(s).
+    suffix: The interface suffix.  'ALL' is a special value that matches all
+            suffixes.
+    interface_type: An INTERFACE_TYPE value (optional).
+
+  Returns:
+    A list of all interfaces found.
+  """
+  interfaces = set()
+  if suffix == 'ALL':
+    interfaces = find_all_interfaces_from_band(band, interface_type)
+  else:
+    interface_types = interface_type or (INTERFACE_TYPE.ap,
+                                         INTERFACE_TYPE.client)
+    for ifc_type in interface_types:
+      interface = find_interface_from_band(band, ifc_type, suffix)
+      if interface:
+        interfaces.add(interface)
+
+  return interfaces
+
+
 def find_width_and_channel(interface):
   """Finds the width and channel being used by a given interface.
 
diff --git a/wifi/iw_test.py b/wifi/iw_test.py
index 2293954..b7ebb3a 100755
--- a/wifi/iw_test.py
+++ b/wifi/iw_test.py
@@ -555,6 +555,28 @@
 
 
 @wvtest.wvtest
+def find_interfaces_from_band_and_suffix_test():
+  """Test find_interfaces_from_band_and_suffix."""
+  wvtest.WVPASSEQ(set(['wlan0', 'wlan0_portal', 'wcli0']),
+                  iw.find_interfaces_from_band_and_suffix('2.4', 'ALL'))
+  wvtest.WVPASSEQ(set(['wlan0', 'wcli0']),
+                  iw.find_interfaces_from_band_and_suffix('2.4', ''))
+  wvtest.WVPASSEQ(set(['wlan0_portal']),
+                  iw.find_interfaces_from_band_and_suffix('2.4', '_portal'))
+  wvtest.WVPASSEQ(set([]),
+                  iw.find_interfaces_from_band_and_suffix('2.4', 'fake_suffix'))
+
+  wvtest.WVPASSEQ(set(['wlan0', 'wlan0_portal']),
+                  iw.find_interfaces_from_band_and_suffix('2.4', 'ALL',
+                                                          iw.INTERFACE_TYPE.ap))
+  wvtest.WVPASSEQ(set(['wcli0']),
+                  iw.find_interfaces_from_band_and_suffix(
+                      '2.4', 'ALL', iw.INTERFACE_TYPE.client))
+  wvtest.WVPASSEQ(set(['wlan1', 'wlan1_portal']),
+                  iw.find_interfaces_from_band_and_suffix('5', 'ALL'))
+
+
+@wvtest.wvtest
 def info_parsed_test():
   wvtest.WVPASSEQ({
       'wdev': '0x3',
diff --git a/wifi/persist.py b/wifi/persist.py
index 827c2a1..3290698 100644
--- a/wifi/persist.py
+++ b/wifi/persist.py
@@ -8,8 +8,11 @@
 
 import utils
 
+# TODO(rofrankel):  Figure out the right way to delete old-style persisted
+# options (e.g. /config/wifi/hostapd.opts.5).
 
-def save_options(program, band, argv, tmp=False):
+
+def save_options(program, interface, argv, tmp=False):
   """Saves program options.
 
   Persistence options are stripped before saving to prevent rewriting identical
@@ -17,7 +20,7 @@
 
   Args:
     program: The program for which to save options.
-    band: The band for which to save options.
+    interface: The interface for which to save options.
     argv: The options to save.
     tmp: Whether to save options to /tmp or _CONFIG_DIR.
   """
@@ -35,16 +38,17 @@
         os.environ['WIFI_CLIENT_PSK'])
 
   utils.atomic_write(
-      utils.get_filename(program, utils.FILENAME_KIND.options, band, tmp=tmp),
+      utils.get_filename(program, utils.FILENAME_KIND.options, interface,
+                         tmp=tmp),
       repr(to_save))
 
 
-def load_options(program, band, tmp):
+def load_options(program, interface, tmp):
   """Loads program options, if any have been saved.
 
   Args:
     program: The program for which to load options.
-    band: The band for which to load options.
+    interface: The interface for which to load options.
     tmp: Whether to load options from /tmp (i.e. what is currently running) or
       _CONFIG_DIR (the options from last time --persist was set).
 
@@ -52,7 +56,7 @@
     The stored options (which can be passed to wifi._run), or None if the file
     cannot be opened.
   """
-  filename = utils.get_filename(program, utils.FILENAME_KIND.options, band,
+  filename = utils.get_filename(program, utils.FILENAME_KIND.options, interface,
                                 tmp=tmp)
   try:
     with open(filename) as options_file:
@@ -65,26 +69,26 @@
     raise
 
 
-def delete_options(program, band):
+def delete_options(program, interface):
   """Deletes persisted program options from _CONFIG_DIR.
 
   Args:
     program: The program for which to delete options.
-    band: The band for which to delete options.
+    interface: The interface for which to delete options.
 
   Returns:
     Whether deletion succeeded.
   """
-  filename = utils.get_filename(program, utils.FILENAME_KIND.options, band)
+  filename = utils.get_filename(program, utils.FILENAME_KIND.options, interface)
   if os.path.exists(filename):
     try:
       os.remove(filename)
-      utils.log('Removed persisted options for %s GHz %s.', band, program)
+      utils.log('Removed persisted options for %s %s.', interface, program)
     except OSError:
-      utils.log('Failed to remove persisted options for %s GHz %s.',
-                band, program)
+      utils.log('Failed to remove persisted options for %s %s.',
+                interface, program)
       return False
   else:
-    utils.log('No persisted options to remove for %s GHz %s.', band, program)
+    utils.log('No persisted options to remove for %s %s.', interface, program)
 
   return True
diff --git a/wifi/wifi.py b/wifi/wifi.py
index 76a8446..56d742d 100755
--- a/wifi/wifi.py
+++ b/wifi/wifi.py
@@ -402,7 +402,7 @@
     for interface in interfaces:
       if _stop_hostapd(interface):
         if opt.persist:
-          persist.delete_options('hostapd', band)
+          persist.delete_options('hostapd', interface)
       else:
         utils.log('Failed to stop hostapd on interface %s', interface)
         success = False
@@ -411,29 +411,29 @@
 
 
 @iw.requires_iw
-def _restore_wifi(band, program):
+def _restore_wifi(interface, program):
   """Restore a program from persisted settings.
 
   Args:
-    band: The band on which to restore program.
+    interface: The interface on which to restore program.
     program: The program to restore (wpa_supplicant or hostapd).
 
   Returns:
-    Whether whether restoring succeeded, but may die.
+    Whether restoring succeeded.
   """
-  argv = persist.load_options(program, band, False)
+  argv = persist.load_options(program, interface, False)
   if argv is None:
-    utils.log('No persisted options for %s GHz %s, not restoring',
-              band, program)
+    utils.log('No persisted options for %s %s, not restoring',
+              interface, program)
     return False
 
-  utils.log('Loaded persisted options for %s GHz %s', band, program)
+  utils.log('Loaded persisted options for %s %s', interface, program)
 
   if _run(argv):
-    utils.log('Restored %s for %s GHz', program, band)
+    utils.log('Restored %s for %s', program, interface)
     return True
 
-  utils.log('Failed to restore %s for %s GHz', program, band)
+  utils.log('Failed to restore %s for %s', program, interface)
   return False
 
 
@@ -450,11 +450,19 @@
   """
   # If both bands are specified, restore 5 GHz first so that STAs are more
   # likely to join it.
+  restored = set()
   for band in sorted(opt.band.split(),
                      reverse=not experiment.enabled('WifiReverseBandsteering')):
-    _restore_wifi(band, 'wpa_supplicant')
-    _restore_wifi(band, 'hostapd')
-
+    client_interface = iw.find_interface_from_band(band,
+                                                   iw.INTERFACE_TYPE.client,
+                                                   opt.interface_suffix)
+    ap_interface = iw.find_interface_from_band(band, iw.INTERFACE_TYPE.ap,
+                                               opt.interface_suffix)
+    for interface, program in ((client_interface, 'wpa_supplicant'),
+                               (ap_interface, 'hostapd')):
+      if interface and interface not in restored:
+        restored.add(interface)
+        _restore_wifi(interface, program)
   return True
 
 
@@ -834,13 +842,14 @@
   return True
 
 
-def _restart_hostapd(band):
+def _restart_hostapd(interface, *overrides):
   """Restart hostapd from previous options.
 
   Only used by _set_wpa_supplicant_config, to restart hostapd after stopping it.
 
   Args:
-    band: The band on which to restart hostapd.
+    interface: The interface on which to restart hostapd.
+    *overrides:  A list of options to override the pre-existing ones.
 
   Returns:
     Whether hostapd was successfully restarted.
@@ -848,7 +857,7 @@
   Raises:
     BinWifiException: If reading previous settings fails.
   """
-  argv = persist.load_options('hostapd', band, True)
+  argv = persist.load_options('hostapd', interface, True) + list(overrides)
 
   if argv is None:
     raise utils.BinWifiException('Failed to read previous hostapd config')
@@ -930,7 +939,7 @@
         'details.')
 
   if restart_hostapd:
-    _restart_hostapd(band)
+    _restart_hostapd(ap_interface)
 
   return True
 
@@ -1024,15 +1033,8 @@
     if band == '5' and quantenna.stop_client_wifi(opt):
       continue
 
-    interfaces = []
-    if opt.interface_suffix == 'ALL':
-      interfaces = iw.find_all_interfaces_from_band(
-          band, iw.INTERFACE_TYPE.client)
-    else:
-      interface = iw.find_interface_from_band(
-          band, iw.INTERFACE_TYPE.client, opt.interface_suffix)
-      if interface:
-        interfaces = [interface]
+    interfaces = iw.find_interfaces_from_band_and_suffix(
+        band, opt.interface_suffix, iw.INTERFACE_TYPE.client)
     if not interfaces:
       utils.log('No client interfaces for %s GHz; nothing to stop', band)
       continue
@@ -1040,7 +1042,7 @@
     for interface in interfaces:
       if _stop_wpa_supplicant(interface):
         if opt.persist:
-          persist.delete_options('wpa_supplicant', band)
+          persist.delete_options('wpa_supplicant', interface)
       else:
         utils.log('Failed to stop wpa_supplicant on interface %s', interface)
         success = False
@@ -1126,14 +1128,19 @@
 
   if success:
     if command in ('set', 'setclient'):
-      program = 'hostapd' if command == 'set' else 'wpa_supplicant'
+      if command == 'set':
+        program = 'hostapd'
+        interface_type = iw.INTERFACE_TYPE.ap
+      else:
+        program = 'wpa_supplicant'
+        interface_type = iw.INTERFACE_TYPE.client
+      interface = iw.find_interface_from_band(opt.band, interface_type,
+                                              opt.interface_suffix)
       if opt.persist:
-        phy = iw.find_phy(opt.band, opt.channel)
-        for band in iw.phy_bands(phy):
-          if band != opt.band:
-            persist.delete_options(program, band)
-        persist.save_options(program, opt.band, argv)
-      persist.save_options(program, opt.band, argv, tmp=True)
+        # Save in /config.
+        persist.save_options(program, interface, argv, False)
+      # Save in /tmp.
+      persist.save_options(program, interface, argv, True)
 
   return success