shill: cellular: Disable modem before system goes into suspend.

This CL changes the termination action in cellular code to disable the
modem, instead of simply disconnecting from network, before the system
goes into suspend mode. The modem is re-enabled, and conditionally
auto-connected back to network, after the system resumes from suspend
mode.

BUG=chromium:298403
TEST=Tested the following:
1. Build and run unit tests.
2. Run the following autotest tests with E362, E396, ALT3100, MU736:
   - network_3GSmokeTest
   - network_3GModemControl
   - network_MobileSuspendResume
3. Manually verify the following:
   - When auto-connect is enabled, suspend the system for 20s and then
     resume it. Verify from /var/log/net.log that the modem is disabled
     before the system goes into suspend mode, and re-enabled and
     reconnected to network after the system is resumed.
   - When auto-connect is disabled, suspend the system for 20s and then
     resume it. Verify from /var/log/net.log that the modem is disabled
     before the system goes into suspend mode, and re-enabled but not
     reconnected to network after the system is resumed.

Change-Id: Id60eb7bc25b6484c13f28331062d7b67c726a0ad
Reviewed-on: https://chromium-review.googlesource.com/171374
Reviewed-by: Thieu Le <thieule@chromium.org>
Tested-by: Ben Chan <benchan@chromium.org>
Commit-Queue: Ben Chan <benchan@chromium.org>
diff --git a/cellular.cc b/cellular.cc
index 7342650..6cd213e 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -282,6 +282,10 @@
     SetState(kStateDisabled);
   set_traffic_monitor_enabled(false);
   callback.Run(error);
+  // In case no termination action was executed (and TerminationActionComplete
+  // was not invoked) in response to a suspend request, any registered
+  // termination action needs to be removed explicitly.
+  manager()->RemoveTerminationAction(FriendlyName());
 }
 
 void Cellular::InitCapability(Type type) {
@@ -577,6 +581,17 @@
   }
 }
 
+void Cellular::OnDisabled() {
+  SetEnabled(false);
+}
+
+void Cellular::OnEnabled() {
+  manager()->AddTerminationAction(FriendlyName(),
+                                  Bind(&Cellular::StartTermination,
+                                       weak_ptr_factory_.GetWeakPtr()));
+  SetEnabled(true);
+}
+
 void Cellular::OnConnecting() {
   if (service_)
     service_->SetState(Service::kStateAssociating);
@@ -588,9 +603,6 @@
     SLOG(Cellular, 2) << "Already connected";
     return;
   }
-  Closure start_cb = Bind(&Cellular::StartTermination,
-                          weak_ptr_factory_.GetWeakPtr());
-  manager()->AddTerminationAction(FriendlyName(), start_cb);
   SetState(kStateConnected);
   if (!service_) {
     LOG(INFO) << "Disconnecting due to no cellular service.";
@@ -631,8 +643,6 @@
     metrics()->NotifyCellularDeviceFailure(error);
     OnDisconnectFailed();
   }
-  manager()->TerminationActionComplete(FriendlyName());
-  manager()->RemoveTerminationAction(FriendlyName());
 }
 
 void Cellular::OnDisconnected() {
@@ -742,11 +752,11 @@
     HandleNewRegistrationState();
   }
   if (new_state == kModemStateDisabled) {
-    SetEnabled(false);
+    OnDisabled();
   } else if (new_state >= kModemStateEnabled) {
     if (old_state < kModemStateEnabled) {
       // Just became enabled, update enabled state.
-      SetEnabled(true);
+      OnEnabled();
     }
     if ((new_state == kModemStateEnabled ||
          new_state == kModemStateSearching ||
@@ -791,7 +801,24 @@
 void Cellular::StartTermination() {
   LOG(INFO) << __func__;
   Error error;
-  Disconnect(&error);
+  SetEnabledNonPersistent(
+      false,
+      &error,
+      Bind(&Cellular::OnTerminationCompleted, weak_ptr_factory_.GetWeakPtr()));
+  if (error.IsFailure() && error.type() != Error::kInProgress) {
+    // If we fail to disable the modem right away, proceed to suspend instead of
+    // wasting the time to wait for the suspend delay to expire.
+    LOG(WARNING)
+        << "Proceed to suspend even through the modem is not yet disabled: "
+        << error;
+    OnTerminationCompleted(error);
+  }
+}
+
+void Cellular::OnTerminationCompleted(const Error &error) {
+  LOG(INFO) << __func__ << ": " << error;
+  manager()->TerminationActionComplete(FriendlyName());
+  manager()->RemoveTerminationAction(FriendlyName());
 }
 
 bool Cellular::DisconnectCleanup() {