network_3GSuspendResume: test that autoconnect works after suspend

network_3GSuspendResume: test with autoconnect works after suspend, by
checking for several suspend resume cycles that we autoconnect when
autoconneect is True, and that we do not autoconnect if it is False.
Change the suspend behavior of the test so that it uses the path that
is 'normally' used when suspend.  This path will causes cromo to
disconnect and existing data sessions.

BUG=chromium-os:17556
TEST=./run_remote_tests.sh --build network_3GSuspendResume --remote=

Change-Id: Iecd8558b2bd447f87b02683a6a70ac1d70ba6f3e
Reviewed-on: http://gerrit.chromium.org/gerrit/4045
Tested-by: Jason Glasgow <jglasgow@chromium.org>
Reviewed-by: Jason Glasgow <jglasgow@chromium.org>
diff --git a/client/cros/sys_power.py b/client/cros/sys_power.py
index d8c8454..62bc5e2 100644
--- a/client/cros/sys_power.py
+++ b/client/cros/sys_power.py
@@ -13,6 +13,8 @@
 import os
 
 SUSPEND_CMD='/usr/bin/powerd_suspend'
+REQUEST_SUSPEND_CMD = ('/usr/bin/dbus-send --system /'
+                       'org.chromium.PowerManager.RequestSuspend')
 
 def set_state(state):
     """
@@ -43,3 +45,10 @@
     """
     set_power_state('standby')
 
+def request_suspend():
+    """
+    Requests that powerd suspend the machine using the same path as if
+    the users had requested a suspend.  This will disconnect the
+    modem, lock the screen, etc.
+    """
+    os.system(REQUEST_SUSPEND_CMD)
diff --git a/client/site_tests/network_3GSuspendResume/control b/client/site_tests/network_3GSuspendResume/control
index 3d5480f..c435a1a 100644
--- a/client/site_tests/network_3GSuspendResume/control
+++ b/client/site_tests/network_3GSuspendResume/control
@@ -8,6 +8,8 @@
 CRITERIA = """
     Check the 3g state of the device after suspend and resume.  Verify
     that 3g can be enabled under all situations after resuming of device.
+    Verify that if autoconnect is turned on that the modem autoconnects
+    after resuming.
 """
 
 TIME = "SHORT"
@@ -20,3 +22,5 @@
 """
 
 job.run_test('network_3GSuspendResume')
+
+job.run_test('network_3GSuspendResume', scenario_group='autoconnect')
diff --git a/client/site_tests/network_3GSuspendResume/network_3GSuspendResume.py b/client/site_tests/network_3GSuspendResume/network_3GSuspendResume.py
index e1c95b3..634ce68 100644
--- a/client/site_tests/network_3GSuspendResume/network_3GSuspendResume.py
+++ b/client/site_tests/network_3GSuspendResume/network_3GSuspendResume.py
@@ -34,7 +34,11 @@
         ],
         'stress': [
             'scenario_suspend_3g_random',
+        ],
+        'autoconnect': [
+            'scenario_autoconnect',
         ]
+
     }
 
     modem_status_outputs = [
@@ -47,8 +51,7 @@
     # This function returns True when cellular service is available.  Otherwise,
     # if the timeout period has been hit, it returns false.
     def cellular_service_available(self, timeout=60):
-        flim = flimflam.FlimFlam(dbus.SystemBus())
-        service = flim.FindCellularService(timeout)
+        service = self.flim.FindCellularService(timeout)
         if service:
             logging.info('Cellular service is available.')
             return service
@@ -63,7 +66,7 @@
         return False
 
     def get_powered(self, device):
-        properties = device.GetProperties()
+        properties = device.GetProperties(utf8_strings=True)
         logging.debug(properties)
         logging.info('Power state of cellular device is %s.',
                      ['off', 'on'][properties['Powered']])
@@ -85,7 +88,13 @@
         alarm_time = rtc.get_seconds() + duration
         logging.info('Suspending machine for: %d.\n' % duration)
         rtc.set_wake_alarm(alarm_time)
-        sys_power.suspend_to_ram()
+        sys_power.request_suspend()
+        # it is expected that the following sleep starts before the
+        # suspend, because the request_suspend interface is NOT
+        # synchronous.  This means the sleep should wake immediately
+        # after resume.
+        time.sleep(duration)
+        logging.info('Machine resumed')
 
         # Race condition hack alert: Before we added this sleep, this
         # test was very sensitive to the relative timing of the test
@@ -104,14 +113,13 @@
     # returns with UnknownMethod called until some time later.
     def __get_cellular_device(self, timeout=30):
         start_time = time.time()
-        flim = flimflam.FlimFlam(dbus.SystemBus())
-        device = flim.FindCellularDevice(timeout)
+        device = self.flim.FindCellularDevice(timeout)
 
         properties = None
         timeout = start_time + timeout
         while properties is None and time.time() < timeout:
             try:
-                properties = device.GetProperties()
+                properties = device.GetProperties(utf8_strings=True)
             except:
                 properties = None
 
@@ -177,6 +185,37 @@
         device = self.__get_cellular_device()
         self.set_powered(device, 1)
 
+    # This verifies that autoconnect works.
+    def scenario_autoconnect(self):
+        device = self.__get_cellular_device()
+        self.set_powered(device, 1)
+        service = self.flim.FindCellularService(30)
+        if not service:
+            raise error.TestError('Unable to find cellular service')
+
+        props = service.GetProperties(utf8_strings=True)
+        if props['AutoConnect']:
+            expected_states = ['ready', 'online', 'portal']
+        else:
+            expected_states = ['idle']
+
+        for _ in xrange(5):
+            self.suspend_resume(10)
+
+            # wait for the device to come back
+            device = self.__get_cellular_device()
+
+            # verify the service state is correct
+            service = self.flim.FindCellularService(30)
+            if not service:
+                raise error.TestFail('Cannot find cellular service')
+
+            state, _ = self.flim.WaitForServiceState(service,
+                                                     expected_states, 30)
+            if not state in expected_states:
+                raise error.TestFail('Cellular state %s not in %s as expected'
+                                     % (state, ', '.join(expected_states)))
+
     # Returns 1 if modem_status returned output within duration.
     # otherwise, returns 0
     def get_modem_status(self, duration=60):
@@ -226,24 +265,24 @@
         #     raise error.TestFail('Cellular service was not connectable at '
         #                          'the end of %s' % function_name)
 
-    def run_once(self, scenarios='all'):
+    def run_once(self, scenario_group='all'):
         # Replace the test type with the list of tests
-        if scenarios not in network_3GSuspendResume.scenarios.keys():
-            scenarios = 'all'
-        logging.info('Running scenario: %s' % scenarios)
-        scenarios = network_3GSuspendResume.scenarios[scenarios]
+        if scenario_group not in network_3GSuspendResume.scenarios.keys():
+            scenario_group = 'all'
+        logging.info('Running scenario group: %s' % scenario_group)
+        scenarios = network_3GSuspendResume.scenarios[scenario_group]
 
         # Run all scenarios twice, first with autoconnect off, then with
         # autoconnect on
         for autoconnect in [False, True]:
 
+            self.flim = flimflam.FlimFlam(dbus.SystemBus())
             device = self.__get_cellular_device()
             if not device:
                 raise error.TestFail('Cannot find cellular device.')
             self.set_powered(device, 1)
 
-            flim = flimflam.FlimFlam(dbus.SystemBus())
-            service = flim.FindCellularService(30)
+            service = self.flim.FindCellularService(30)
             if not service:
                 raise error.TestFail('Cannot find cellular service.')