Merge "conman:  WifiSimulateWireless experiment."
diff --git a/conman/Makefile b/conman/Makefile
index 8ff1719..0faf301 100644
--- a/conman/Makefile
+++ b/conman/Makefile
@@ -9,6 +9,7 @@
       echo 'echo "(gpylint-missing)" >&2'; \
     fi \
 )
+NOINSTALL=%_test.py options.py experiment.py experiment_testutils.py
 
 all:
 
@@ -35,7 +36,7 @@
 
 install:
 	mkdir -p $(LIBDIR) $(BINDIR)
-	$(INSTALL) -m 0644 $(filter-out %_test.py, $(wildcard *.py)) $(LIBDIR)/
+	$(INSTALL) -m 0644 $(filter-out $(NOINSTALL), $(wildcard *.py)) $(LIBDIR)/
 	$(INSTALL) -m 0755 main.py $(LIBDIR)/
 	rm -f $(BINDIR)/conman
 	ln -s /usr/conman/main.py $(BINDIR)/conman
diff --git a/conman/experiment.py b/conman/experiment.py
new file mode 120000
index 0000000..b9d7638
--- /dev/null
+++ b/conman/experiment.py
@@ -0,0 +1 @@
+../experiment.py
\ No newline at end of file
diff --git a/conman/experiment_testutils.py b/conman/experiment_testutils.py
new file mode 120000
index 0000000..cca000f
--- /dev/null
+++ b/conman/experiment_testutils.py
@@ -0,0 +1 @@
+../experiment_testutils.py
\ No newline at end of file
diff --git a/conman/interface.py b/conman/interface.py
index f575bf3..82d1506 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -8,6 +8,7 @@
 import re
 import subprocess
 
+import experiment
 import wpactrl
 
 METRIC_5GHZ = 20
@@ -15,6 +16,10 @@
 METRIC_24GHZ = 22
 METRIC_TEMPORARY_CONNECTION_CHECK = 99
 
+experiment.register('WifiSimulateWireless')
+CWMP_PATH = '/tmp/cwmp'
+MAX_ACS_FAILURE_S = 60
+
 
 class Interface(object):
   """Represents an interface.
@@ -310,6 +315,33 @@
       os.unlink(self._acs_autoprovisioning_filepath)
     super(Bridge, self).delete_route()
 
+  def _connection_check(self, check_acs):
+    """Support for WifiSimulateWireless."""
+    failure_s = self._acs_session_failure_s()
+    if (experiment.enabled('WifiSimulateWireless')
+        and failure_s < MAX_ACS_FAILURE_S):
+      logging.debug('WifiSimulateWireless: failing bridge connection check (no '
+                    'ACS contact for %d seconds, max %d seconds)',
+                    failure_s, MAX_ACS_FAILURE_S)
+      return False
+
+    return super(Bridge, self)._connection_check(check_acs)
+
+  def _acs_session_failure_s(self):
+    """How long have we been failing to connect to the ACS?
+
+    Returns:
+      The number of seconds between the last attempted ACS session and the last
+      successful ACS session.
+    """
+    contact = os.path.join(CWMP_PATH, 'acscontact')
+    connected = os.path.join(CWMP_PATH, 'acsconnected')
+
+    if not os.path.exists(contact) or not os.path.exists(connected):
+      return 0
+
+    return os.stat(contact).st_mtime - os.stat(connected).st_mtime
+
 
 class Wifi(Interface):
   """Represents a wireless interface."""
diff --git a/conman/interface_test.py b/conman/interface_test.py
index 034c5b0..4283a2a 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -7,17 +7,21 @@
 import os
 import shutil
 import tempfile
+import time
+
+# This has to be called before another module calls it with a higher log level.
+# pylint: disable=g-import-not-at-top
+logging.basicConfig(level=logging.DEBUG)
 
 # This is in site-packages on the device, but not when running tests, and so
 # raises lint errors.
 # pylint: disable=g-bad-import-order
 import wpactrl
 
+import experiment_testutils
 import interface
 from wvtest import wvtest
 
-logging.basicConfig(level=logging.DEBUG)
-
 
 class FakeInterfaceMixin(object):
   """Replace Interface methods which interact with the system."""
@@ -420,5 +424,69 @@
     shutil.rmtree(wifiinfo_path)
 
 
+@wvtest.wvtest
+def simulate_wireless_test():
+  """Test the WifiSimulateWireless experiment."""
+  unused_raii = experiment_testutils.MakeExperimentDirs()
+
+  tmp_dir = tempfile.mkdtemp()
+  interface.CWMP_PATH = tempfile.mkdtemp()
+  interface.MAX_ACS_FAILURE_S = 1
+
+  contact = os.path.join(interface.CWMP_PATH, 'acscontact')
+  connected = os.path.join(interface.CWMP_PATH, 'acsconnected')
+
+  try:
+    autoprov_filepath = os.path.join(tmp_dir, 'autoprov')
+    b = Bridge('br0', '10', acs_autoprovisioning_filepath=autoprov_filepath)
+    b.add_moca_station(0)
+    b.set_gateway_ip('192.168.1.1')
+    b.set_connection_check_result('succeed')
+    b.initialize()
+
+    # Initially, the connection check passes.
+    wvtest.WVPASS(b.internet())
+
+    # Enable the experiment.
+    experiment_testutils.enable('WifiSimulateWireless')
+    # Calling update_routes overwrites the connection status cache, which we
+    # need in order to see the effects we are looking for immediately
+    # (ConnectionManager calls this every few seconds).
+    b.update_routes()
+    wvtest.WVFAIL(b.internet())
+
+    # Create an ACS connection attempt.
+    open(contact, 'w')
+    b.update_routes()
+    wvtest.WVFAIL(b.internet())
+
+    # Record success.
+    open(connected, 'w')
+    b.update_routes()
+    wvtest.WVFAIL(b.internet())
+
+    # Disable the experiment and the connection check should pass again.
+    experiment_testutils.disable('WifiSimulateWireless')
+    b.update_routes()
+    wvtest.WVPASS(b.internet())
+
+    # Reenable the experiment and the connection check should fail again.
+    experiment_testutils.enable('WifiSimulateWireless')
+    b.update_routes()
+    wvtest.WVFAIL(b.internet())
+
+    # Wait until we've failed for long enough for the experiment to "expire",
+    # then log another attempt without success.  Make sure the connection check
+    # passes.
+    time.sleep(interface.MAX_ACS_FAILURE_S)
+    open(contact, 'w')
+    b.update_routes()
+    wvtest.WVPASS(b.internet())
+
+  finally:
+    shutil.rmtree(tmp_dir)
+    shutil.rmtree(interface.CWMP_PATH)
+
+
 if __name__ == '__main__':
   wvtest.wvtest_main()