Merge "/bin/wifi:  Delete old band's options when switching bands on Camaro."
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index 93d1bef..829fda4 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -97,7 +97,7 @@
     self.stop_client()
 
     try:
-      subprocess.check_call(self.command, stderr=subprocess.STDOUT)
+      subprocess.check_output(self.command, stderr=subprocess.STDOUT)
       self.access_point_up = True
       logging.debug('Started %s GHz AP', self.band)
     except subprocess.CalledProcessError as e:
@@ -112,7 +112,7 @@
       command += ['--interface_suffix', self.interface_suffix]
 
     try:
-      subprocess.check_call(command, stderr=subprocess.STDOUT)
+      subprocess.check_output(command, stderr=subprocess.STDOUT)
       self.access_point_up = False
       logging.debug('Stopped %s GHz AP', self.band)
     except subprocess.CalledProcessError as e:
@@ -132,7 +132,7 @@
     if self.passphrase:
       env['WIFI_CLIENT_PSK'] = self.passphrase
     try:
-      subprocess.check_call(command, stderr=subprocess.STDOUT, env=env)
+      subprocess.check_output(command, stderr=subprocess.STDOUT, env=env)
       self.client_up = True
       logging.info('Started wifi client on %s GHz', self.band)
     except subprocess.CalledProcessError as e:
@@ -146,7 +146,7 @@
     self.wifi.detach_wpa_control()
 
     try:
-      subprocess.check_call(self.WIFI_STOPCLIENT + ['-b', self.band],
+      subprocess.check_output(self.WIFI_STOPCLIENT + ['-b', self.band],
                             stderr=subprocess.STDOUT)
       self.client_up = False
       logging.debug('Stopped wifi client on %s GHz', self.band)
@@ -221,23 +221,6 @@
         os.makedirs(directory)
         logging.info('Created monitored directory: %s', directory)
 
-    # It is very important that we know whether ethernet is up.  So if the
-    # ethernet file doesn't exist for any reason when conman starts, check
-    # explicitly.
-    if os.path.exists(os.path.join(self._interface_status_dir,
-                                   self.ETHERNET_STATUS_FILE)):
-      self._process_file(self._interface_status_dir, self.ETHERNET_STATUS_FILE)
-    else:
-      ethernet_up = self.is_ethernet_up()
-      self.bridge.ethernet = ethernet_up
-      self.ifplugd_action('eth0', ethernet_up)
-
-    for path, prefix in ((self._moca_status_dir, self.MOCA_NODE_FILE_PREFIX),
-                         (self._status_dir, self.COMMAND_FILE_PREFIX),
-                         (self._status_dir, self.GATEWAY_FILE_PREFIX)):
-      for filepath in glob.glob(os.path.join(path, prefix + '*')):
-        self._process_file(path, os.path.split(filepath)[-1])
-
     wm = pyinotify.WatchManager()
     wm.add_watch(self._status_dir,
                  pyinotify.IN_CLOSE_WRITE | pyinotify.IN_MOVED_TO |
@@ -248,6 +231,27 @@
                  pyinotify.IN_CLOSE_WRITE | pyinotify.IN_MOVED_TO)
     self.notifier = pyinotify.Notifier(wm, FileChangeHandler(self), timeout=0)
 
+    # If the ethernet file doesn't exist for any reason when conman starts,
+    # check explicitly and run ifplugd.action to create the file.
+    if not os.path.exists(os.path.join(self._interface_status_dir,
+                                       self.ETHERNET_STATUS_FILE)):
+      ethernet_up = self.is_ethernet_up()
+      self.bridge.ethernet = ethernet_up
+      self.ifplugd_action('eth0', ethernet_up)
+
+    for path, prefix in ((self._status_dir, self.GATEWAY_FILE_PREFIX),
+                         (self._interface_status_dir, ''),
+                         (self._moca_status_dir, self.MOCA_NODE_FILE_PREFIX),
+                         (self._status_dir, self.COMMAND_FILE_PREFIX)):
+      for filepath in glob.glob(os.path.join(path, prefix + '*')):
+        self._process_file(path, os.path.split(filepath)[-1])
+
+    # Now that we've ready any existing state, it's okay to let interfaces touch
+    # the routing table.
+    for ifc in [self.bridge] + self.wifi:
+      ifc.initialize()
+      logging.debug('%s initialized', ifc.name)
+
     self._interface_update_counter = 0
     self._try_wlan_after = {'5': 0, '2.4': 0}
 
diff --git a/conman/interface.py b/conman/interface.py
index d814950..0a26959 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -38,6 +38,9 @@
     self._gateway_ip = None
     self.metric = metric
 
+    # Until this is set True, the routing table will not be touched.
+    self._initialized = False
+
   def _connection_check(self, check_acs):
     """Check this interface's connection status.
 
@@ -47,11 +50,17 @@
     Returns:
       Whether the connection is working.
     """
+    # Until initialized, we want to act as if the interface is down.
+    if not self._initialized:
+      logging.debug('%s not initialized; not running connection_check%s',
+                    self.name, ' (ACS)' if check_acs else '')
+      return None
+
     if not self.links:
       logging.debug('Connection check for %s failed due to no links', self.name)
       return False
 
-    logging.debug('Gateway ip for %s is %s', self.name, self._gateway_ip)
+    logging.debug('Gateway IP for %s is %s', self.name, self._gateway_ip)
     if self._gateway_ip is None:
       logging.debug('Connection check for %s failed due to no gateway IP',
                     self.name)
@@ -160,6 +169,14 @@
     return result
 
   def _ip_route(self, *args):
+    if not self._initialized:
+      logging.debug('Not initialized, not running %s %s',
+                    ' '.join(self.IP_ROUTE), ' '.join(args))
+      return ''
+
+    return self._really_ip_route(*args)
+
+  def _really_ip_route(self, *args):
     try:
       logging.debug('%s calling ip route %s', self.name, ' '.join(args))
       return subprocess.check_output(self.IP_ROUTE + list(args))
@@ -236,6 +253,15 @@
     elif maybe_had_access and not has_access:
       self.delete_route()
 
+  def initialize(self):
+    """Tell the interface it has its initial state.
+
+    Until this is called, the interface won't run connection checks or touch the
+    routing table.
+    """
+    self._initialized = True
+    self.update_routes()
+
 
 class Bridge(Interface):
   """Represents the wired bridge."""
diff --git a/conman/interface_test.py b/conman/interface_test.py
index 9905fea..6368a83 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -30,7 +30,7 @@
     else:
       raise ValueError('Invalid fake connection_check script.')
 
-  def _ip_route(self, *args):
+  def _really_ip_route(self, *args):
     if not args:
       return '\n'.join(self.routing_table.values() +
                        ['1.2.3.4/24 dev %s proto kernel scope link' % self.name,
@@ -145,6 +145,11 @@
 
   b.add_moca_station(0)
   b.set_gateway_ip('192.168.1.1')
+  # Everything should fail because the interface is not initialized.
+  wvtest.WVFAIL(b.acs())
+  wvtest.WVFAIL(b.internet())
+  wvtest.WVFAIL(b.current_route())
+  b.initialize()
   wvtest.WVPASS(b.acs())
   wvtest.WVPASS(b.internet())
   wvtest.WVPASS(b.current_route())
@@ -183,6 +188,7 @@
   """Test Wifi."""
   w = Wifi('wcli0', '21')
   w.set_connection_check_result('succeed')
+  w.initialize()
 
   try:
     wpa_path = tempfile.mkdtemp()
diff --git a/sysmgr/utest/test_fan.cc b/sysmgr/utest/test_fan.cc
index b2044b3..fe228d3 100644
--- a/sysmgr/utest/test_fan.cc
+++ b/sysmgr/utest/test_fan.cc
@@ -44,7 +44,10 @@
     bruno_base::LogMessage::LogToDebug(bruno_base::LS_INFO);
   }
 
-  bruno_platform_peripheral::FanControl fan_control(NULL);
+  bruno_platform_peripheral::Platform platform(
+      "Unknown", bruno_platform_peripheral::BRUNO_UNKNOWN, false, false);
+  platform.Init();
+  bruno_platform_peripheral::FanControl fan_control(&platform);
 
   fan_control.Init(NULL);
   uint16_t fan_speed;
diff --git a/taxonomy/ethernet.py b/taxonomy/ethernet.py
index 2cec4ac..f277184 100644
--- a/taxonomy/ethernet.py
+++ b/taxonomy/ethernet.py
@@ -55,9 +55,11 @@
     '1c:b0:94': ['htc'],
     '38:e7:d8': ['htc'],
     '50:2e:5c': ['htc'],
+    '64:a7:69': ['htc'],
     '7c:61:93': ['htc'],
     '90:e7:c4': ['htc'],
     'b4:ce:f6': ['htc'],
+    'd8:b3:77': ['htc'],
     'e8:99:c4': ['htc'],
 
     '0c:48:85': ['lg'],
diff --git a/taxonomy/pcaptest.py b/taxonomy/pcaptest.py
index 62ae7bc..363f327 100644
--- a/taxonomy/pcaptest.py
+++ b/taxonomy/pcaptest.py
@@ -17,10 +17,14 @@
   ('Unknown', './testdata/pcaps/iPhone 3GS 2.4GHz.pcap'),
   ('Unknown', './testdata/pcaps/iPhone 3GS 2.4GHz M137LL.pcap'),
   ('Unknown', './testdata/pcaps/HTC Evo 2.4GHz.pcap'),
+  ('Unknown', './testdata/pcaps/HTC Incredible 2.4GHz.pcap'),
+  ('Unknown', './testdata/pcaps/HTC Inspire 2.4GHz.pcap'),
+  ('Unknown', './testdata/pcaps/HTC Sensation 2.4GHz.pcap'),
   ('Unknown', './testdata/pcaps/HTC Thunderbolt 2.4GHz.pcap'),
   ('Unknown', './testdata/pcaps/Lenovo_T440_80211ac_2x2_Windows8_2_4_GHz.pcap'),
   ('Unknown', './testdata/pcaps/LG E900 2.4GHz.pcap'),
   ('Unknown', './testdata/pcaps/LG G2X 2.4GHz.pcap'),
+  ('Unknown', './testdata/pcaps/LG Revolution 2.4GHz.pcap'),
   ('Unknown', './testdata/pcaps/MediaTek MT7610U 2.4GHz.pcap'),
   ('Unknown', './testdata/pcaps/Motorola Droid 2 2.4GHz.pcap'),
   ('Unknown', './testdata/pcaps/Motorola Droid 3 2.4GHz.pcap'),
diff --git a/taxonomy/testdata/dhcp.leases b/taxonomy/testdata/dhcp.leases
index cc99348..6256fd5 100644
--- a/taxonomy/testdata/dhcp.leases
+++ b/taxonomy/testdata/dhcp.leases
@@ -41,3 +41,5 @@
 1432237016 00:26:4a:e6:b7:4e 192.168.42.32 iPhoone-3GS
 1432237016 d0:23:db:a2:e5:02 192.168.42.33 iPhoone-4s
 1432237016 00:26:4a:c2:89:58 192.168.42.33 iPhoone-3GS
+1432237016 74:c2:46:fc:bb:d6 192.168.42.34 AmazonDashButton
+1432237016 04:0c:ce:cf:40:2c 192.168.42.34 MacbookAir2010
diff --git a/taxonomy/testdata/dhcp.signatures b/taxonomy/testdata/dhcp.signatures
index 21fae31..bb5fae6 100644
--- a/taxonomy/testdata/dhcp.signatures
+++ b/taxonomy/testdata/dhcp.signatures
@@ -33,3 +33,5 @@
 00:26:4a:e6:b7:4e 1,3,6,15,119,252
 d0:23:db:a2:e5:02 1,3,6,15,119,252
 00:26:4a:c2:89:58 1,3,6,15,119,252
+74:c2:46:fc:bb:d6 1,3,6
+04:0c:ce:cf:40:2c 1,3,6,15,119,95,252,44,46
diff --git a/taxonomy/testdata/pcaps/Amazon Dash Button 2.4GHz.pcap b/taxonomy/testdata/pcaps/Amazon Dash Button 2.4GHz.pcap
new file mode 100644
index 0000000..7e32ff7
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Amazon Dash Button 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC Incredible 2.4GHz.pcap b/taxonomy/testdata/pcaps/HTC Incredible 2.4GHz.pcap
new file mode 100644
index 0000000..b0c27db
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC Incredible 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC Inspire 2.4GHz.pcap b/taxonomy/testdata/pcaps/HTC Inspire 2.4GHz.pcap
new file mode 100644
index 0000000..88201ec
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC Inspire 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC Sensation 2.4GHz.pcap b/taxonomy/testdata/pcaps/HTC Sensation 2.4GHz.pcap
new file mode 100644
index 0000000..990d80c
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC Sensation 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG Revolution 2.4GHz.pcap b/taxonomy/testdata/pcaps/LG Revolution 2.4GHz.pcap
new file mode 100644
index 0000000..062e15f
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG Revolution 2.4GHz.pcap
Binary files differ
diff --git "a/taxonomy/testdata/pcaps/MacBook Air late 2010 \050A1369\051 2.4GHz.pcap" "b/taxonomy/testdata/pcaps/MacBook Air late 2010 \050A1369\051 2.4GHz.pcap"
new file mode 100644
index 0000000..9462dd7
--- /dev/null
+++ "b/taxonomy/testdata/pcaps/MacBook Air late 2010 \050A1369\051 2.4GHz.pcap"
Binary files differ
diff --git "a/taxonomy/testdata/pcaps/MacBook Air late 2010 \050A1369\051 5GHz.pcap" "b/taxonomy/testdata/pcaps/MacBook Air late 2010 \050A1369\051 5GHz.pcap"
new file mode 100644
index 0000000..01ade63
--- /dev/null
+++ "b/taxonomy/testdata/pcaps/MacBook Air late 2010 \050A1369\051 5GHz.pcap"
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Galaxy Note 2 2.4GHz.pcap b/taxonomy/testdata/pcaps/Samsung Galaxy Note 2 2.4GHz.pcap
new file mode 100644
index 0000000..12390b0
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Galaxy Note 2 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Galaxy Note 2 5GHz.pcap b/taxonomy/testdata/pcaps/Samsung Galaxy Note 2 5GHz.pcap
new file mode 100644
index 0000000..90051fe
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Galaxy Note 2 5GHz.pcap
Binary files differ
diff --git a/taxonomy/wifi.py b/taxonomy/wifi.py
index ebed08f..dad59aa 100644
--- a/taxonomy/wifi.py
+++ b/taxonomy/wifi.py
@@ -23,7 +23,7 @@
 
 
 database = {
-    'wifi|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:110c,htagg:19,htmcs:000000ff|assoc:0,1,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:110c,htagg:19,htmcs:000000ff|os:dashbutton':
+    'wifi3|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:110c,htagg:19,htmcs:000000ff|assoc:0,1,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:110c,htagg:19,htmcs:000000ff|os:dashbutton':
         ('BCM43362', 'Amazon Dash Button', '2.4GHz'),
 
     'wifi|probe:0,1,50|assoc:0,1,50,48,221(0050f2,2)|os:kindle':
@@ -366,6 +366,11 @@
     'wifi|probe:0,1,50,3,45,221(00904c,51),htcap:182c|assoc:0,1,33,36,48,50,45,221(00904c,51),221(0050f2,2),htcap:182c|os:macos':
         ('BCM4322', 'MacBook late 2008 (A1278)', '2.4GHz'),
 
+    'wifi3|probe:0,1,45,221(001018,2),221(00904c,51),htcap:087e,htagg:1b,htmcs:0000ffff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:087e,htagg:1b,htmcs:0000ffff,txpow:0f07|os:macos':
+        ('BCM43224', 'MacBook Air late 2010 (A1369)', '5GHz'),
+    'wifi3|probe:0,1,50,45,221(001018,2),221(00904c,51),htcap:187c,htagg:1b,htmcs:0000ffff|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:187c,htagg:1b,htmcs:0000ffff,txpow:1207|os:macos':
+        ('BCM43224', 'MacBook Air late 2010 (A1369)', '2.4GHz'),
+
     'wifi|probe:0,1,45,221(00904c,51),htcap:086e,htagg:1b,htmcs:0000ffff|assoc:0,1,33,36,48,45,221(00904c,51),221(0050f2,2),htcap:086e,htagg:1b,htmcs:0000ffff|os:macos':
         ('BCM4322', 'MacBook Air late 2011', '5GHz'),
 
@@ -581,6 +586,14 @@
         ('BCM4330', 'Samsung Galaxy Note 2', '5GHz'),
     'wifi3|probe:0,1,45,221(001018,2),221(00904c,51),htcap:0062,htagg:1a,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:0062,htagg:1a,htmcs:000000ff,txpow:0e09|oui:murata':
         ('BCM4330', 'Samsung Galaxy Note 2', '5GHz'),
+    'wifi3|probe:0,1,45,3,221(001018,2),221(00904c,51),htcap:0062,htagg:1a,htmcs:000000ff|assoc:0,1,33,36,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:0062,htagg:1a,htmcs:000000ff,txpow:0e09|oui:samsung':
+        ('BCM4330', 'Samsung Galaxy Note 2', '5GHz'),
+    'wifi3|probe:0,1,45,3,221(001018,2),221(00904c,51),htcap:0062,htagg:1a,htmcs:000000ff|assoc:0,1,33,36,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:0062,htagg:1a,htmcs:000000ff,txpow:0e09|oui:murata':
+        ('BCM4330', 'Samsung Galaxy Note 2', '5GHz'),
+    'wifi3|probe:0,1,45,3,221(001018,2),221(00904c,51),htcap:0062,htagg:1a,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:0062,htagg:1a,htmcs:000000ff,txpow:0e09|oui:samsung':
+        ('BCM4330', 'Samsung Galaxy Note 2', '5GHz'),
+    'wifi3|probe:0,1,45,3,221(001018,2),221(00904c,51),htcap:0062,htagg:1a,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:0062,htagg:1a,htmcs:000000ff,txpow:0e09|oui:murata':
+        ('BCM4330', 'Samsung Galaxy Note 2', '5GHz'),
     'wifi3|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:1020,htagg:1a,htmcs:000000ff|assoc:0,1,33,36,50,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:1020,htagg:1a,htmcs:000000ff,txpow:1209|oui:samsung':
         ('BCM4330', 'Samsung Galaxy Note 2', '2.4GHz'),
     'wifi3|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:1020,htagg:1a,htmcs:000000ff|assoc:0,1,33,36,50,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:1020,htagg:1a,htmcs:000000ff,txpow:1209|oui:murata':
diff --git a/wifi/configs.py b/wifi/configs.py
index f0c8fa9..7518872 100644
--- a/wifi/configs.py
+++ b/wifi/configs.py
@@ -16,6 +16,8 @@
     'WifiReverseBandsteering',
     'WifiHostapdLogging',
     'WifiHostapdDebug',
+    'WifiShortAggTimeout',
+    'WifiNoAggTimeout',
 ]
 for _i in EXPERIMENTS:
   experiment.register(_i)
diff --git a/wifi/wifi.py b/wifi/wifi.py
index 8f6fb7d..de8ced4 100755
--- a/wifi/wifi.py
+++ b/wifi/wifi.py
@@ -4,6 +4,7 @@
 
 from __future__ import print_function
 
+import glob
 import os
 import re
 import subprocess
@@ -506,6 +507,24 @@
   Returns:
     Whether hostapd was started successfully.
   """
+  aggfiles = glob.glob('/sys/kernel/debug/ieee80211/phy*/' +
+                       'netdev:%s/default_agg_timeout' % interface)
+  if not aggfiles:
+    # This can happen on non-mac80211 interfaces.
+    utils.log('agg_timeout: no default_agg_timeout files for %r', interface)
+  else:
+    if experiment.enabled('WifiShortAggTimeout'):
+      utils.log('Using short agg_timeout.')
+      agg = 500
+    elif experiment.enabled('WifiNoAggTimeout'):
+      utils.log('Disabling agg_timeout.')
+      agg = 0
+    else:
+      utils.log('Using default long agg_timeout.')
+      agg = 5000
+    for aggfile in aggfiles:
+      open(aggfile, 'w').write(str(agg))
+
   pid_filename = utils.get_filename(
       'hostapd', utils.FILENAME_KIND.pid, interface, tmp=True)
   alivemonitor_filename = utils.get_filename(