shill: Perform modem disconnect based on modem state.

The cellular state may not accurately reflect whether the underlying
modem is connecting/connected.  If CellularCapabilityUniversal::Connect()
is called and while that call is still in progress, a call is made to
CellularCapabilityUniversal::StopModem(), the cellular state does not
indicate that the modem is connecting so StopModem() does not
attempt to disconnect.  It's better to use the modem state to determine
if we need to disconnect the modem.

Also in this CL is a check to ignore stale modem Connected state
transitions.  The stale state transition can occur when the modem is
asked to connect but is then quickly disabled.  In this case, the
Connected state can show up after the modem has been disabled.

BUG=chromium-os:32241
TEST=Unit tests, cellular_Smoke, cellular_ModemControl

Change-Id: I7cc3316a287729529b097ca41c579b7ea6ecefdb
Reviewed-on: https://gerrit.chromium.org/gerrit/28441
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Thieu Le <thieule@chromium.org>
Commit-Ready: Thieu Le <thieule@chromium.org>
diff --git a/cellular.cc b/cellular.cc
index 889caf8..32c2ccd 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -217,6 +217,12 @@
   return IsEnabledModemState(modem_state_);
 }
 
+bool Cellular::IsModemRegistered() const {
+  return (modem_state_ == Cellular::kModemStateRegistered ||
+          modem_state_ == Cellular::kModemStateConnecting ||
+          modem_state_ == Cellular::kModemStateConnected);
+}
+
 // static
 bool Cellular::IsEnabledModemState(ModemState state) {
   switch (state) {
@@ -549,9 +555,13 @@
         capability_->SetUnregistered(new_state == kModemStateSearching);
         HandleNewRegistrationState();
       } else {
-        SLOG(Cellular, 2) << __func__ << "Ignoring state change to Enabled";
+        SLOG(Cellular, 2) << __func__ << ": Ignoring state change to Enabled";
       }
+      // Intentionally falls through.
     case kModemStateRegistered:
+      // If the modem state changes from Connecting/Connected/Disconnecting
+      // to Registered/Enabled/Searching, then it's an indication that the
+      // modem has been disconnected or got disconnected by the network.
       if (old_state == kModemStateConnected ||
           old_state == kModemStateConnecting ||
           old_state == kModemStateDisconnecting)
@@ -561,7 +571,10 @@
       OnConnecting();
       break;
     case kModemStateConnected:
-      OnConnected();
+      if (old_state == kModemStateConnecting)
+        OnConnected();
+      else
+        SLOG(Cellular, 2) << __func__ << ": Ignoring state change to Connected";
       break;
     default:
       break;