shill: Handle modem state changes reported by modem-manager.

Also, adjust correctly to initial state reported by
modem-manager. This involved some adjustments to the
StartModem path, e.g., to make the MM Enable operation
conditional on whether the current modem state is not
already Enabled. Other streamlining done for the StartModem
path includes:

- eliminating the GetModemStatus call in the GSM
  case. All the needed information (such as IMSI)
  is obtained in later calls.
- making the Register call conditional on the modem
  not already being registered.

I added a couple of unit tests to check responses to
state changes, but many more tests could be created.

BUG=chromium-os:19962
TEST=Testing includes running mm-enable and
mm-disable while the modem is in the disabled, enabled,
registered, and connected states, and making sure that
shill updates its own state appropriately, and that the
UI properly reflects the new state.  Also ran mm-connect
and mm-disconnect to see that the UI (and shill) respond
correctly to connect and disconnect events. Similarly,
started up shill while modem started out in these various
states.

Tested only using MM classic API. Code is in place to make
this work for the MM1 API, but I haven't tried testing while
the new modem manager is running.

Change-Id: I4f276bee62b64e8f1260a39f18c3c2a5de8d2798
Reviewed-on: https://gerrit.chromium.org/gerrit/20517
Commit-Ready: Eric Shienbrood <ers@chromium.org>
Reviewed-by: Eric Shienbrood <ers@chromium.org>
Tested-by: Eric Shienbrood <ers@chromium.org>
diff --git a/cellular_capability_gsm.cc b/cellular_capability_gsm.cc
index 5492bed..284c043 100644
--- a/cellular_capability_gsm.cc
+++ b/cellular_capability_gsm.cc
@@ -124,12 +124,15 @@
   ResultCallback cb_ignore_error =
         Bind(&CellularCapabilityGSM::StepCompletedCallback,
                    weak_ptr_factory_.GetWeakPtr(), callback, true, tasks);
-  tasks->push_back(Bind(&CellularCapabilityGSM::EnableModem,
-                        weak_ptr_factory_.GetWeakPtr(), cb));
-  tasks->push_back(Bind(&CellularCapabilityGSM::Register,
-                        weak_ptr_factory_.GetWeakPtr(), cb));
-  tasks->push_back(Bind(&CellularCapabilityGSM::GetModemStatus,
-                        weak_ptr_factory_.GetWeakPtr(), cb));
+  if (!cellular()->IsUnderlyingDeviceEnabled())
+    tasks->push_back(Bind(&CellularCapabilityGSM::EnableModem,
+                          weak_ptr_factory_.GetWeakPtr(), cb));
+  // If we're within range of the home network, the modem will try to
+  // register once it's enabled, or may be already registered if we
+  // started out enabled.
+  if (!IsUnderlyingDeviceRegistered() && !selected_network_.empty())
+    tasks->push_back(Bind(&CellularCapabilityGSM::Register,
+                          weak_ptr_factory_.GetWeakPtr(), cb));
   tasks->push_back(Bind(&CellularCapabilityGSM::GetIMEI,
                         weak_ptr_factory_.GetWeakPtr(), cb));
   tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
@@ -141,13 +144,33 @@
   tasks->push_back(Bind(&CellularCapabilityGSM::GetProperties,
                         weak_ptr_factory_.GetWeakPtr(), cb));
   tasks->push_back(Bind(&CellularCapabilityGSM::GetModemInfo,
-                        weak_ptr_factory_.GetWeakPtr(), cb));
+                        weak_ptr_factory_.GetWeakPtr(), cb_ignore_error));
   tasks->push_back(Bind(&CellularCapabilityGSM::FinishEnable,
                         weak_ptr_factory_.GetWeakPtr(), cb));
 
   RunNextStep(tasks);
 }
 
+bool CellularCapabilityGSM::IsUnderlyingDeviceRegistered() const {
+  switch (cellular()->modem_state()) {
+    case Cellular::kModemStateUnknown:
+    case Cellular::kModemStateDisabled:
+    case Cellular::kModemStateInitializing:
+    case Cellular::kModemStateLocked:
+    case Cellular::kModemStateDisabling:
+    case Cellular::kModemStateEnabling:
+    case Cellular::kModemStateEnabled:
+      return false;
+    case Cellular::kModemStateSearching:
+    case Cellular::kModemStateRegistered:
+    case Cellular::kModemStateDisconnecting:
+    case Cellular::kModemStateConnecting:
+    case Cellular::kModemStateConnected:
+      return true;
+  }
+  return false;
+}
+
 void CellularCapabilityGSM::ReleaseProxies() {
   SLOG(Cellular, 2) << __func__;
   CellularCapabilityClassic::ReleaseProxies();
@@ -168,7 +191,7 @@
 }
 
 void CellularCapabilityGSM::UpdateStatus(const DBusPropertiesMap &properties) {
-  if (ContainsKey(properties, kPropertyIMSI)) {
+  if (ContainsKey(properties, kModemPropertyIMSI)) {
     SetHomeProvider();
   }
 }
@@ -550,6 +573,16 @@
           registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING);
 }
 
+void CellularCapabilityGSM::SetUnregistered(bool searching) {
+  // If we're already in some non-registered state, don't override that
+  if (registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
+      registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
+    registration_state_ =
+        (searching ? MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING :
+                     MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE);
+  }
+}
+
 void CellularCapabilityGSM::RequirePIN(
     const std::string &pin, bool require,
     Error *error, const ResultCallback &callback) {
@@ -725,33 +758,37 @@
 void CellularCapabilityGSM::OnDBusPropertiesChanged(
     const string &interface,
     const DBusPropertiesMap &properties,
-    const vector<string> &/* invalidated_properties */) {
-  if (interface != MM_MODEM_INTERFACE)
-    return;
-  uint32 access_technology = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
-  if (DBusProperties::GetUint32(properties,
-                                kPropertyAccessTechnology,
-                                &access_technology)) {
-    SetAccessTechnology(access_technology);
-  }
-  bool emit = false;
-  uint32 locks = 0;
-  if (DBusProperties::GetUint32(
-          properties, kPropertyEnabledFacilityLocks, &locks)) {
-    sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
-    emit = true;
-  }
-  if (DBusProperties::GetString(
-          properties, kPropertyUnlockRequired, &sim_lock_status_.lock_type)) {
-    emit = true;
-  }
-  if (DBusProperties::GetUint32(
-          properties, kPropertyUnlockRetries, &sim_lock_status_.retries_left)) {
-    emit = true;
-  }
-  if (emit) {
-    cellular()->adaptor()->EmitKeyValueStoreChanged(
-        flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
+    const vector<string> &invalidated_properties) {
+  CellularCapabilityClassic::OnDBusPropertiesChanged(interface,
+                                                     properties,
+                                                     invalidated_properties);
+  if (interface == MM_MODEM_GSM_NETWORK_INTERFACE) {
+    uint32 access_technology = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+    if (DBusProperties::GetUint32(properties,
+                                  kPropertyAccessTechnology,
+                                  &access_technology)) {
+      SetAccessTechnology(access_technology);
+    }
+  } else if (interface == MM_MODEM_GSM_CARD_INTERFACE) {
+    bool emit = false;
+    uint32 locks = 0;
+    if (DBusProperties::GetUint32(
+        properties, kPropertyEnabledFacilityLocks, &locks)) {
+      sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
+      emit = true;
+    }
+    if (DBusProperties::GetString(
+        properties, kPropertyUnlockRequired, &sim_lock_status_.lock_type)) {
+      emit = true;
+    }
+    if (DBusProperties::GetUint32(
+        properties, kPropertyUnlockRetries, &sim_lock_status_.retries_left)) {
+      emit = true;
+    }
+    if (emit) {
+      cellular()->adaptor()->EmitKeyValueStoreChanged(
+          flimflam::kSIMLockStatusProperty, SimLockStatusToProperty(NULL));
+    }
   }
 }