shill: Disconnect modem before attempting to activate

When we're asked to activate a modem, assume that Chrome knows what it
is doing and disconnect the modem first.  This lets Chrome not have
complicate the activation process by waiting for an asyncronous
disconnect first.

BUG=chromium-os:36301
TEST=Modems still activate as before.  This shouldn't be surprising
because Chrome is still doing lots of work to disconnect a service
before trying to activate.  The real magic will happen when we take
those parts out of Chrome.

Change-Id: Ic41fa7f4ec759d6ec5c650708792ab6aa083dde5
Reviewed-on: https://gerrit.chromium.org/gerrit/38060
Commit-Ready: Christopher Wiley <wiley@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Tested-by: Christopher Wiley <wiley@chromium.org>
diff --git a/cellular_capability_cdma.cc b/cellular_capability_cdma.cc
index 31dc5d8..44fc8cc 100644
--- a/cellular_capability_cdma.cc
+++ b/cellular_capability_cdma.cc
@@ -135,20 +135,31 @@
                                       Error *error,
                                       const ResultCallback &callback) {
   SLOG(Cellular, 2) << __func__ << "(" << carrier << ")";
-  if (cellular()->state() != Cellular::kStateEnabled &&
-      cellular()->state() != Cellular::kStateRegistered) {
+  if (cellular()->state() == Cellular::kStateEnabled ||
+      cellular()->state() == Cellular::kStateRegistered) {
+    ActivationResultCallback cb =
+        Bind(&CellularCapabilityCDMA::OnActivateReply,
+             weak_ptr_factory_.GetWeakPtr(),
+             callback);
+    proxy_->Activate(carrier, error, cb, kTimeoutActivate);
+  } else if (cellular()->state() == Cellular::kStateConnected ||
+             cellular()->state() == Cellular::kStateLinked) {
+    ResultCallback continue_activation_callback =
+        Bind(&CellularCapabilityCDMA::OnDisconnectBeforeActivate,
+             weak_ptr_factory_.GetWeakPtr(),
+             carrier,
+             callback);
+    cellular()->DisconnectWithCallback(continue_activation_callback, error);
+  } else {
     Error::PopulateAndLog(error, Error::kInvalidArguments,
                           "Unable to activate in " +
                           Cellular::GetStateString(cellular()->state()));
-    return;
   }
-  ActivationResultCallback cb = Bind(&CellularCapabilityCDMA::OnActivateReply,
-                                     weak_ptr_factory_.GetWeakPtr(), callback);
-  proxy_->Activate(carrier, error, cb, kTimeoutActivate);
 }
 
 void CellularCapabilityCDMA::HandleNewActivationState(uint32 error) {
   if (!cellular()->service().get()) {
+    LOG(ERROR) << "In " << __func__ << "(): service is null.";
     return;
   }
   cellular()->service()->SetActivationState(
@@ -282,12 +293,30 @@
   if (error.IsSuccess()) {
     if (status == MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR) {
       activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+    } else {
+      LOG(WARNING) << "Modem activation failed with status: "
+                   << GetActivationErrorString(status) << " (" << status << ")";
     }
     HandleNewActivationState(status);
+  } else {
+    LOG(ERROR) << "Activate() failed with error: " << error;
   }
   callback.Run(error);
 }
 
+void CellularCapabilityCDMA::OnDisconnectBeforeActivate(
+    const string &carrier, const ResultCallback &callback, const Error &error) {
+  SLOG(Cellular, 2) << __func__;
+  if (error.IsSuccess()) {
+    Error ignored_error;
+    Activate(carrier, &ignored_error, callback);
+  } else {
+    LOG(WARNING) << "Tried to disconnect before activating cellular service"
+                 << " and failed.";
+    HandleNewActivationState(MM_MODEM_CDMA_ACTIVATION_ERROR_UNKNOWN);
+  }
+}
+
 void CellularCapabilityCDMA::OnGetRegistrationStateReply(
     uint32 state_1x, uint32 state_evdo, const Error &error) {
   SLOG(Cellular, 2) << __func__;