shill: Transition Cellular's state to Registered when the modem is registered.

Also, process GSM network technology types and roaming states and update the
Cellular service appropriately.

BUG=chromium-os:19676
TEST=unit tests, tested on device

Change-Id: I6606a01a07cf635cb344aef95c3145eaf2290fba
Reviewed-on: http://gerrit.chromium.org/gerrit/6768
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/cellular.cc b/cellular.cc
index 272f863..22dc67c 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -139,6 +139,10 @@
       activation_state(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED),
       prl_version(0) {}
 
+Cellular::GSM::GSM()
+    : registration_state(MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN),
+      access_technology(MM_MODEM_GSM_ACCESS_TECH_UNKNOWN) {}
+
 Cellular::Cellular(ControlInterface *control_interface,
                    EventDispatcher *dispatcher,
                    Manager *manager,
@@ -227,8 +231,29 @@
 string Cellular::GetNetworkTechnologyString() const {
   switch (type_) {
     case kTypeGSM:
-      // TODO(petkov): Implement this.
-      NOTIMPLEMENTED();
+      if (gsm_.registration_state == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
+          gsm_.registration_state == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
+        switch (gsm_.access_technology) {
+          case MM_MODEM_GSM_ACCESS_TECH_GSM:
+          case MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT:
+            // TODO(petkov): Add "GSM" to system_api:service_constants.h.
+            return "GSM";
+          case MM_MODEM_GSM_ACCESS_TECH_GPRS:
+            return flimflam::kNetworkTechnologyGprs;
+          case MM_MODEM_GSM_ACCESS_TECH_EDGE:
+            return flimflam::kNetworkTechnologyEdge;
+          case MM_MODEM_GSM_ACCESS_TECH_UMTS:
+            return flimflam::kNetworkTechnologyUmts;
+          case MM_MODEM_GSM_ACCESS_TECH_HSDPA:
+          case MM_MODEM_GSM_ACCESS_TECH_HSUPA:
+          case MM_MODEM_GSM_ACCESS_TECH_HSPA:
+            return flimflam::kNetworkTechnologyHspa;
+          case MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS:
+            return flimflam::kNetworkTechnologyHspaPlus;
+          default:
+            NOTREACHED();
+        }
+      }
       break;
     case kTypeCDMA:
       if (cdma_.registration_state_evdo !=
@@ -249,8 +274,14 @@
 string Cellular::GetRoamingStateString() const {
   switch (type_) {
     case kTypeGSM:
-      // TODO(petkov): Implement this.
-      NOTIMPLEMENTED();
+      switch (gsm_.registration_state) {
+        case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
+          return flimflam::kRoamingStateHome;
+        case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
+          return flimflam::kRoamingStateRoaming;
+        default:
+          break;
+      }
       break;
     case kTypeCDMA: {
       uint32 state = cdma_.registration_state_evdo;
@@ -419,8 +450,9 @@
 }
 
 void Cellular::GetGSMProperties() {
-  // TODO(petkov): Implement this.
-  NOTIMPLEMENTED();
+  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
+  gsm_.access_technology = gsm_network_proxy_->AccessTechnology();
+  VLOG(2) << "GSM AccessTechnology: " << gsm_.access_technology;
 }
 
 void Cellular::RegisterGSMModem() {
@@ -463,8 +495,15 @@
 }
 
 void Cellular::GetGSMRegistrationState() {
-  // TODO(petkov): Implement this.
-  NOTIMPLEMENTED();
+  CHECK_EQ(kTypeGSM, type_);
+  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
+  ModemGSMNetworkProxyInterface::RegistrationInfo info =
+      gsm_network_proxy_->GetRegistrationInfo();
+  gsm_.registration_state = info._1;
+  gsm_.network_id = info._2;
+  gsm_.operator_name = info._3;
+  VLOG(2) << "GSM Registration: " << gsm_.registration_state << ", "
+          << gsm_.network_id << ", "  << gsm_.operator_name;
 }
 
 void Cellular::HandleNewRegistrationState() {
@@ -502,6 +541,8 @@
   }
   service_->set_network_tech(network_tech);
   service_->set_roaming_state(GetRoamingStateString());
+  // TODO(petkov): For GSM, update the serving operator based on the network id
+  // and the mobile provider database.
 }
 
 void Cellular::GetModemSignalQuality() {
@@ -727,8 +768,11 @@
 void Cellular::OnGSMRegistrationInfoChanged(uint32 status,
                                             const string &operator_code,
                                             const string &operator_name) {
-  // TODO(petkov): Implement this.
-  NOTIMPLEMENTED();
+  CHECK_EQ(kTypeGSM, type_);
+  gsm_.registration_state = status;
+  gsm_.network_id = operator_code;
+  gsm_.operator_name = operator_name;
+  HandleNewRegistrationState();
 }
 
 void Cellular::OnGSMSignalQualityChanged(uint32 quality) {
diff --git a/cellular.h b/cellular.h
index da5d5f8..6d1fa1d 100644
--- a/cellular.h
+++ b/cellular.h
@@ -117,18 +117,6 @@
     DISALLOW_COPY_AND_ASSIGN(Network);
   };
 
-  struct CDMA {
-    CDMA();
-
-    uint32 registration_state_evdo;
-    uint32 registration_state_1x;
-    uint32 activation_state;
-
-    uint16 prl_version;
-    std::string payment_url;
-    std::string usage_url;
-  };
-
   struct SimLockStatus {
    public:
     SimLockStatus() : retries_left(0) {}
@@ -177,21 +165,44 @@
   FRIEND_TEST(CellularTest, Connect);
   FRIEND_TEST(CellularTest, GetCDMAActivationStateString);
   FRIEND_TEST(CellularTest, GetCDMAActivationErrorString);
+  FRIEND_TEST(CellularTest, GetCDMANetworkTechnologyString);
   FRIEND_TEST(CellularTest, GetCDMARegistrationState);
+  FRIEND_TEST(CellularTest, GetCDMARoamingStateString);
   FRIEND_TEST(CellularTest, GetCDMASignalQuality);
+  FRIEND_TEST(CellularTest, GetGSMNetworkTechnologyString);
+  FRIEND_TEST(CellularTest, GetGSMRoamingStateString);
   FRIEND_TEST(CellularTest, GetModemInfo);
   FRIEND_TEST(CellularTest, GetModemStatus);
-  FRIEND_TEST(CellularTest, GetNetworkTechnologyString);
-  FRIEND_TEST(CellularTest, GetRoamingStateString);
   FRIEND_TEST(CellularTest, GetStateString);
   FRIEND_TEST(CellularTest, GetTypeString);
   FRIEND_TEST(CellularTest, InitProxiesCDMA);
   FRIEND_TEST(CellularTest, InitProxiesGSM);
   FRIEND_TEST(CellularTest, StartConnected);
   FRIEND_TEST(CellularTest, StartCDMARegister);
-  FRIEND_TEST(CellularTest, StartGSM);
+  FRIEND_TEST(CellularTest, StartGSMRegister);
   FRIEND_TEST(CellularTest, StartLinked);
 
+  struct CDMA {
+    CDMA();
+
+    uint32 registration_state_evdo;
+    uint32 registration_state_1x;
+    uint32 activation_state;
+
+    uint16 prl_version;
+    std::string payment_url;
+    std::string usage_url;
+  };
+
+  struct GSM {
+    GSM();
+
+    uint32 registration_state;
+    uint32 access_technology;
+    std::string network_id;
+    std::string operator_name;
+  };
+
   static const char kPhoneNumberCDMA[];
   static const char kPhoneNumberGSM[];
 
@@ -290,6 +301,7 @@
   scoped_ptr<ModemGSMNetworkProxyInterface> gsm_network_proxy_;
 
   CDMA cdma_;
+  GSM gsm_;
 
   CellularServiceRefPtr service_;
 
diff --git a/cellular_unittest.cc b/cellular_unittest.cc
index ba84506..043acbf 100644
--- a/cellular_unittest.cc
+++ b/cellular_unittest.cc
@@ -239,7 +239,7 @@
   EXPECT_EQ("CellularTypeCDMA", device_->GetTypeString());
 }
 
-TEST_F(CellularTest, GetNetworkTechnologyString) {
+TEST_F(CellularTest, GetCDMANetworkTechnologyString) {
   device_->type_ = Cellular::kTypeCDMA;
   EXPECT_EQ("", device_->GetNetworkTechnologyString());
   device_->cdma_.registration_state_evdo =
@@ -254,7 +254,41 @@
             device_->GetNetworkTechnologyString());
 }
 
-TEST_F(CellularTest, GetRoamingStateString) {
+TEST_F(CellularTest, GetGSMNetworkTechnologyString) {
+  device_->type_ = Cellular::kTypeGSM;
+  EXPECT_EQ("", device_->GetNetworkTechnologyString());
+  device_->gsm_.access_technology = MM_MODEM_GSM_ACCESS_TECH_GSM;
+  EXPECT_EQ("", device_->GetNetworkTechnologyString());
+  device_->gsm_.registration_state = MM_MODEM_GSM_NETWORK_REG_STATUS_HOME;
+  // TODO(petkov): Define "GSM" in system_api:service_constants.h.
+  EXPECT_EQ("GSM", device_->GetNetworkTechnologyString());
+  device_->gsm_.access_technology = MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT;
+  EXPECT_EQ("GSM", device_->GetNetworkTechnologyString());
+  device_->gsm_.access_technology = MM_MODEM_GSM_ACCESS_TECH_GPRS;
+  EXPECT_EQ(flimflam::kNetworkTechnologyGprs,
+            device_->GetNetworkTechnologyString());
+  device_->gsm_.access_technology = MM_MODEM_GSM_ACCESS_TECH_EDGE;
+  EXPECT_EQ(flimflam::kNetworkTechnologyEdge,
+            device_->GetNetworkTechnologyString());
+  device_->gsm_.access_technology = MM_MODEM_GSM_ACCESS_TECH_UMTS;
+  EXPECT_EQ(flimflam::kNetworkTechnologyUmts,
+            device_->GetNetworkTechnologyString());
+  device_->gsm_.registration_state = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING;
+  device_->gsm_.access_technology = MM_MODEM_GSM_ACCESS_TECH_HSDPA;
+  EXPECT_EQ(flimflam::kNetworkTechnologyHspa,
+            device_->GetNetworkTechnologyString());
+  device_->gsm_.access_technology = MM_MODEM_GSM_ACCESS_TECH_HSUPA;
+  EXPECT_EQ(flimflam::kNetworkTechnologyHspa,
+            device_->GetNetworkTechnologyString());
+  device_->gsm_.access_technology = MM_MODEM_GSM_ACCESS_TECH_HSPA;
+  EXPECT_EQ(flimflam::kNetworkTechnologyHspa,
+            device_->GetNetworkTechnologyString());
+  device_->gsm_.access_technology = MM_MODEM_GSM_ACCESS_TECH_HSPA_PLUS;
+  EXPECT_EQ(flimflam::kNetworkTechnologyHspaPlus,
+            device_->GetNetworkTechnologyString());
+}
+
+TEST_F(CellularTest, GetCDMARoamingStateString) {
   device_->type_ = Cellular::kTypeCDMA;
   EXPECT_EQ(flimflam::kRoamingStateUnknown, device_->GetRoamingStateString());
   device_->cdma_.registration_state_evdo =
@@ -279,6 +313,21 @@
   EXPECT_EQ(flimflam::kRoamingStateRoaming, device_->GetRoamingStateString());
 }
 
+TEST_F(CellularTest, GetGSMRoamingStateString) {
+  device_->type_ = Cellular::kTypeGSM;
+  EXPECT_EQ(flimflam::kRoamingStateUnknown, device_->GetRoamingStateString());
+  device_->gsm_.registration_state = MM_MODEM_GSM_NETWORK_REG_STATUS_HOME;
+  EXPECT_EQ(flimflam::kRoamingStateHome, device_->GetRoamingStateString());
+  device_->gsm_.registration_state = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING;
+  EXPECT_EQ(flimflam::kRoamingStateRoaming, device_->GetRoamingStateString());
+  device_->gsm_.registration_state = MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING;
+  EXPECT_EQ(flimflam::kRoamingStateUnknown, device_->GetRoamingStateString());
+  device_->gsm_.registration_state = MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED;
+  EXPECT_EQ(flimflam::kRoamingStateUnknown, device_->GetRoamingStateString());
+  device_->gsm_.registration_state = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE;
+  EXPECT_EQ(flimflam::kRoamingStateUnknown, device_->GetRoamingStateString());
+}
+
 TEST_F(CellularTest, GetStateString) {
   EXPECT_EQ("CellularStateDisabled",
             device_->GetStateString(Cellular::kStateDisabled));
@@ -355,7 +404,7 @@
   EXPECT_EQ(flimflam::kRoamingStateHome, device_->service_->roaming_state());
 }
 
-TEST_F(CellularTest, StartGSM) {
+TEST_F(CellularTest, StartGSMRegister) {
   device_->type_ = Cellular::kTypeGSM;
   static const char kNetwork[] = "My Favorite GSM Network";
   device_->selected_network_ = kNetwork;
@@ -363,9 +412,20 @@
   EXPECT_CALL(*gsm_network_proxy_, Register(kNetwork)).Times(1);
   EXPECT_CALL(*simple_proxy_, GetStatus())
       .WillOnce(Return(DBusPropertiesMap()));
+  EXPECT_CALL(*gsm_network_proxy_, AccessTechnology())
+      .WillOnce(Return(MM_MODEM_GSM_ACCESS_TECH_EDGE));
   EXPECT_CALL(*proxy_, GetInfo()).WillOnce(Return(ModemProxyInterface::Info()));
+  ModemGSMNetworkProxyInterface::RegistrationInfo reg_info;
+  reg_info._1 = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING;
+  EXPECT_CALL(*gsm_network_proxy_, GetRegistrationInfo())
+      .WillOnce(Return(reg_info));
   device_->Start();
-  EXPECT_EQ(Cellular::kStateEnabled, device_->state_);
+  dispatcher_.DispatchPendingEvents();
+  EXPECT_EQ(Cellular::kStateRegistered, device_->state_);
+  ASSERT_TRUE(device_->service_.get());
+  EXPECT_EQ(flimflam::kNetworkTechnologyEdge,
+            device_->service_->network_tech());
+  EXPECT_EQ(flimflam::kRoamingStateRoaming, device_->service_->roaming_state());
 }
 
 TEST_F(CellularTest, StartConnected) {
@@ -522,8 +582,8 @@
 TEST_F(CellularTest, Connect) {
   Error error;
   EXPECT_CALL(device_info_, GetFlags(device_->interface_index(), _))
-      .WillOnce(Return(true))
-      .WillOnce(Return(true));
+      .Times(2)
+      .WillRepeatedly(Return(true));
   device_->state_ = Cellular::kStateConnected;
   device_->Connect(&error);
   EXPECT_EQ(Error::kAlreadyConnected, error.type());
diff --git a/mock_modem_gsm_network_proxy.h b/mock_modem_gsm_network_proxy.h
index 633f25f..ebf6bd6 100644
--- a/mock_modem_gsm_network_proxy.h
+++ b/mock_modem_gsm_network_proxy.h
@@ -18,6 +18,8 @@
   virtual ~MockModemGSMNetworkProxy();
 
   MOCK_METHOD1(Register, void(const std::string &network_id));
+  MOCK_METHOD0(GetRegistrationInfo, RegistrationInfo());
+  MOCK_METHOD0(AccessTechnology, uint32());
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockModemGSMNetworkProxy);
diff --git a/modem_gsm_network_proxy.cc b/modem_gsm_network_proxy.cc
index 587cc0b..abbe710 100644
--- a/modem_gsm_network_proxy.cc
+++ b/modem_gsm_network_proxy.cc
@@ -23,6 +23,15 @@
   proxy_.Register(network_id);
 }
 
+ModemGSMNetworkProxyInterface::RegistrationInfo
+ModemGSMNetworkProxy::GetRegistrationInfo() {
+  return proxy_.GetRegistrationInfo();
+}
+
+uint32 ModemGSMNetworkProxy::AccessTechnology() {
+  return proxy_.AccessTechnology();
+}
+
 ModemGSMNetworkProxy::Proxy::Proxy(ModemGSMNetworkProxyListener *listener,
                                    DBus::Connection *connection,
                                    const string &path,
diff --git a/modem_gsm_network_proxy.h b/modem_gsm_network_proxy.h
index 816ec7c..2a8df41 100644
--- a/modem_gsm_network_proxy.h
+++ b/modem_gsm_network_proxy.h
@@ -22,6 +22,8 @@
 
   // Inherited from ModemGSMNetworkProxyInterface.
   virtual void Register(const std::string &network_id);
+  virtual RegistrationInfo GetRegistrationInfo();
+  virtual uint32 AccessTechnology();
 
  private:
   class Proxy
diff --git a/modem_gsm_network_proxy_interface.h b/modem_gsm_network_proxy_interface.h
index 414042f..a461d46 100644
--- a/modem_gsm_network_proxy_interface.h
+++ b/modem_gsm_network_proxy_interface.h
@@ -8,6 +8,7 @@
 #include <string>
 
 #include <base/basictypes.h>
+#include <dbus-c++/types.h>
 
 namespace shill {
 
@@ -15,9 +16,15 @@
 // support. The interface is provided so that it can be mocked in tests.
 class ModemGSMNetworkProxyInterface {
  public:
+  typedef DBus::Struct<uint32, std::string, std::string> RegistrationInfo;
+
   virtual ~ModemGSMNetworkProxyInterface() {}
 
   virtual void Register(const std::string &network_id) = 0;
+  virtual RegistrationInfo GetRegistrationInfo() = 0;
+
+  // Properties.
+  virtual uint32 AccessTechnology() = 0;
 };
 
 // ModemManager.Modem.Gsm.Network signal listener to be associated with the