shill: Register GSM modem as part of device start up.

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

Change-Id: Ie8f8a8ce4fd185226a9f55f4fcd62224e7318c3c
Reviewed-on: http://gerrit.chromium.org/gerrit/6683
Tested-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: Paul Stewart <pstew@chromium.org>
diff --git a/Makefile b/Makefile
index 7930355..2cbc883 100644
--- a/Makefile
+++ b/Makefile
@@ -104,6 +104,7 @@
 	manager_dbus_adaptor.o \
 	modem.o \
 	modem_cdma_proxy.o \
+	modem_gsm_network_proxy.o \
 	modem_info.o \
 	modem_manager.o \
 	modem_manager_proxy.o \
diff --git a/cellular.cc b/cellular.cc
index 6883366..7a1825a 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -163,9 +163,9 @@
       allow_roaming_(false),
       scanning_(false),
       scan_interval_(0) {
+  store_.RegisterConstString(flimflam::kCarrierProperty, &carrier_);
   store_.RegisterConstString(flimflam::kDBusConnectionProperty, &dbus_owner_);
   store_.RegisterConstString(flimflam::kDBusObjectProperty, &dbus_path_);
-  store_.RegisterConstString(flimflam::kCarrierProperty, &carrier_);
   store_.RegisterBool(flimflam::kCellularAllowRoamingProperty, &allow_roaming_);
   store_.RegisterConstString(flimflam::kEsnProperty, &esn_);
   store_.RegisterConstString(flimflam::kFirmwareRevisionProperty,
@@ -182,6 +182,8 @@
   store_.RegisterConstString(flimflam::kMinProperty, &min_);
   store_.RegisterConstString(flimflam::kModelIDProperty, &model_id_);
   store_.RegisterConstUint16(flimflam::kPRLVersionProperty, &cdma_.prl_version);
+  store_.RegisterConstString(flimflam::kSelectedNetworkProperty,
+                             &selected_network_);
 
   HelpRegisterDerivedStrIntPair(flimflam::kSIMLockStatusProperty,
                                 &Cellular::SimLockStatusToProperty,
@@ -351,7 +353,9 @@
           dbus_path_, dbus_owner_));
   switch (type_) {
     case kTypeGSM:
-      NOTIMPLEMENTED();
+      gsm_network_proxy_.reset(
+          ProxyFactory::factory()->CreateModemGSMNetworkProxy(
+              this, dbus_path_, dbus_owner_));
       break;
     case kTypeCDMA:
       cdma_proxy_.reset(
@@ -419,8 +423,10 @@
 }
 
 void Cellular::RegisterGSMModem() {
-  // TODO(petkov): Invoke ModemManager.Modem.Gsm.Network.Register.
-  NOTIMPLEMENTED();
+  LOG(INFO) << "Registering on: "
+            << (selected_network_.empty() ? "home network" : selected_network_);
+  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
+  gsm_network_proxy_->Register(selected_network_);
 }
 
 void Cellular::GetModemInfo() {
@@ -712,6 +718,23 @@
   HandleNewSignalQuality(strength);
 }
 
+void Cellular::OnGSMNetworkModeChanged(uint32 mode) {
+  // TODO(petkov): Implement this.
+  NOTIMPLEMENTED();
+}
+
+void Cellular::OnGSMRegistrationInfoChanged(uint32 status,
+                                            const string &operator_code,
+                                            const string &operator_name) {
+  // TODO(petkov): Implement this.
+  NOTIMPLEMENTED();
+}
+
+void Cellular::OnGSMSignalQualityChanged(uint32 quality) {
+  // TODO(petkov): Implement this.
+  NOTIMPLEMENTED();
+}
+
 void Cellular::OnModemStateChanged(uint32 old_state,
                                    uint32 new_state,
                                    uint32 reason) {
diff --git a/cellular.h b/cellular.h
index 4409a6a..da5d5f8 100644
--- a/cellular.h
+++ b/cellular.h
@@ -14,6 +14,7 @@
 #include "shill/dbus_properties.h"
 #include "shill/device.h"
 #include "shill/modem_cdma_proxy_interface.h"
+#include "shill/modem_gsm_network_proxy_interface.h"
 #include "shill/modem_proxy_interface.h"
 #include "shill/refptr_types.h"
 #include "shill/shill_event.h"
@@ -25,6 +26,7 @@
 
 class Cellular : public Device,
                  public ModemCDMAProxyListener,
+                 public ModemGSMNetworkProxyListener,
                  public ModemProxyListener {
  public:
   enum Type {
@@ -185,10 +187,10 @@
   FRIEND_TEST(CellularTest, GetTypeString);
   FRIEND_TEST(CellularTest, InitProxiesCDMA);
   FRIEND_TEST(CellularTest, InitProxiesGSM);
-  FRIEND_TEST(CellularTest, Start);
   FRIEND_TEST(CellularTest, StartConnected);
+  FRIEND_TEST(CellularTest, StartCDMARegister);
+  FRIEND_TEST(CellularTest, StartGSM);
   FRIEND_TEST(CellularTest, StartLinked);
-  FRIEND_TEST(CellularTest, StartRegister);
 
   static const char kPhoneNumberCDMA[];
   static const char kPhoneNumberGSM[];
@@ -264,6 +266,13 @@
                                               uint32 state_evdo);
   virtual void OnCDMASignalQualityChanged(uint32 strength);
 
+  // Signal callbacks inherited from ModemGSMNetworkProxyListener.
+  virtual void OnGSMNetworkModeChanged(uint32 mode);
+  virtual void OnGSMRegistrationInfoChanged(uint32 status,
+                                            const std::string &operator_code,
+                                            const std::string &operator_name);
+  virtual void OnGSMSignalQualityChanged(uint32 quality);
+
   // Signal callbacks inherited from ModemProxyListener.
   virtual void OnModemStateChanged(uint32 old_state,
                                    uint32 new_state,
@@ -278,6 +287,7 @@
   scoped_ptr<ModemProxyInterface> proxy_;
   scoped_ptr<ModemSimpleProxyInterface> simple_proxy_;
   scoped_ptr<ModemCDMAProxyInterface> cdma_proxy_;
+  scoped_ptr<ModemGSMNetworkProxyInterface> gsm_network_proxy_;
 
   CDMA cdma_;
 
@@ -300,6 +310,7 @@
   std::string hardware_revision_;
   bool scanning_;
   uint16 scan_interval_;
+  std::string selected_network_;
   std::vector<Network> found_networks_;
   SimLockStatus sim_lock_status_;
   Operator home_provider_;
diff --git a/cellular_unittest.cc b/cellular_unittest.cc
index b3e59ad..ba84506 100644
--- a/cellular_unittest.cc
+++ b/cellular_unittest.cc
@@ -18,6 +18,7 @@
 #include "shill/mock_dhcp_provider.h"
 #include "shill/mock_manager.h"
 #include "shill/mock_modem_cdma_proxy.h"
+#include "shill/mock_modem_gsm_network_proxy.h"
 #include "shill/mock_modem_proxy.h"
 #include "shill/mock_modem_simple_proxy.h"
 #include "shill/mock_sockets.h"
@@ -117,6 +118,7 @@
         proxy_(new MockModemProxy()),
         simple_proxy_(new MockModemSimpleProxy()),
         cdma_proxy_(new MockModemCDMAProxy()),
+        gsm_network_proxy_(new MockModemGSMNetworkProxy()),
         proxy_factory_(this),
         dhcp_config_(new MockDHCPConfig(&control_interface_,
                                         &dispatcher_,
@@ -172,6 +174,13 @@
       return test_->cdma_proxy_.release();
     }
 
+    virtual ModemGSMNetworkProxyInterface *CreateModemGSMNetworkProxy(
+        ModemGSMNetworkProxyListener *listener,
+        const string &path,
+        const string &service) {
+      return test_->gsm_network_proxy_.release();
+    }
+
    private:
     CellularTest *test_;
   };
@@ -195,6 +204,7 @@
   scoped_ptr<MockModemProxy> proxy_;
   scoped_ptr<MockModemSimpleProxy> simple_proxy_;
   scoped_ptr<MockModemCDMAProxy> cdma_proxy_;
+  scoped_ptr<MockModemGSMNetworkProxy> gsm_network_proxy_;
   TestProxyFactory proxy_factory_;
 
   MockDHCPProvider dhcp_provider_;
@@ -325,16 +335,7 @@
             device_->GetCDMAActivationErrorString(1234));
 }
 
-TEST_F(CellularTest, Start) {
-  EXPECT_CALL(*proxy_, Enable(true)).Times(1);
-  EXPECT_CALL(*simple_proxy_, GetStatus())
-      .WillOnce(Return(DBusPropertiesMap()));
-  EXPECT_CALL(*proxy_, GetInfo()).WillOnce(Return(ModemProxyInterface::Info()));
-  device_->Start();
-  EXPECT_EQ(Cellular::kStateEnabled, device_->state_);
-}
-
-TEST_F(CellularTest, StartRegister) {
+TEST_F(CellularTest, StartCDMARegister) {
   device_->type_ = Cellular::kTypeCDMA;
   EXPECT_CALL(*proxy_, Enable(true)).Times(1);
   EXPECT_CALL(*simple_proxy_, GetStatus())
@@ -354,6 +355,19 @@
   EXPECT_EQ(flimflam::kRoamingStateHome, device_->service_->roaming_state());
 }
 
+TEST_F(CellularTest, StartGSM) {
+  device_->type_ = Cellular::kTypeGSM;
+  static const char kNetwork[] = "My Favorite GSM Network";
+  device_->selected_network_ = kNetwork;
+  EXPECT_CALL(*proxy_, Enable(true)).Times(1);
+  EXPECT_CALL(*gsm_network_proxy_, Register(kNetwork)).Times(1);
+  EXPECT_CALL(*simple_proxy_, GetStatus())
+      .WillOnce(Return(DBusPropertiesMap()));
+  EXPECT_CALL(*proxy_, GetInfo()).WillOnce(Return(ModemProxyInterface::Info()));
+  device_->Start();
+  EXPECT_EQ(Cellular::kStateEnabled, device_->state_);
+}
+
 TEST_F(CellularTest, StartConnected) {
   EXPECT_CALL(device_info_, GetFlags(device_->interface_index(), _))
       .WillOnce(Return(true));
diff --git a/mock_modem_gsm_network_proxy.h b/mock_modem_gsm_network_proxy.h
new file mode 100644
index 0000000..478deac
--- /dev/null
+++ b/mock_modem_gsm_network_proxy.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_MOCK_MODEM_GSM_NETWORK_PROXY_H_
+#define SHILL_MOCK_MODEM_GSM_NETWORK_PROXY_H_
+
+#include <gmock/gmock.h>
+
+#include "shill/modem_gsm_network_proxy_interface.h"
+
+namespace shill {
+
+class MockModemGSMNetworkProxy : public ModemGSMNetworkProxyInterface {
+ public:
+  MOCK_METHOD1(Register, void(const std::string &network_id));
+};
+
+}  // namespace shill
+
+#endif  // SHILL_MOCK_MODEM_GSM_NETWORK_PROXY_H_
diff --git a/modem_gsm_network_proxy.cc b/modem_gsm_network_proxy.cc
new file mode 100644
index 0000000..587cc0b
--- /dev/null
+++ b/modem_gsm_network_proxy.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/modem_gsm_network_proxy.h"
+
+#include <base/logging.h>
+
+using std::string;
+
+namespace shill {
+
+ModemGSMNetworkProxy::ModemGSMNetworkProxy(
+    ModemGSMNetworkProxyListener *listener,
+    DBus::Connection *connection,
+    const string &path,
+    const string &service)
+    : proxy_(listener, connection, path, service) {}
+
+ModemGSMNetworkProxy::~ModemGSMNetworkProxy() {}
+
+void ModemGSMNetworkProxy::Register(const string &network_id) {
+  proxy_.Register(network_id);
+}
+
+ModemGSMNetworkProxy::Proxy::Proxy(ModemGSMNetworkProxyListener *listener,
+                                   DBus::Connection *connection,
+                                   const string &path,
+                                   const string &service)
+    : DBus::ObjectProxy(*connection, path, service.c_str()),
+      listener_(listener) {}
+
+ModemGSMNetworkProxy::Proxy::~Proxy() {}
+
+void ModemGSMNetworkProxy::Proxy::SignalQuality(const uint32 &quality) {
+  VLOG(2) << __func__ << "(" << quality << ")";
+  listener_->OnGSMSignalQualityChanged(quality);
+}
+
+void ModemGSMNetworkProxy::Proxy::RegistrationInfo(
+    const uint32_t &status,
+    const string &operator_code,
+    const string &operator_name) {
+  VLOG(2) << __func__ << "(" << status << ", " << operator_code << ", "
+          << operator_name << ")";
+  listener_->OnGSMRegistrationInfoChanged(status, operator_code, operator_name);
+}
+
+void ModemGSMNetworkProxy::Proxy::NetworkMode(const uint32_t &mode) {
+  VLOG(2) << __func__ << "(" << mode << ")";
+  listener_->OnGSMNetworkModeChanged(mode);
+}
+
+}  // namespace shill
diff --git a/modem_gsm_network_proxy.h b/modem_gsm_network_proxy.h
new file mode 100644
index 0000000..816ec7c
--- /dev/null
+++ b/modem_gsm_network_proxy.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_MODEM_GSM_NETWORK_PROXY_
+#define SHILL_MODEM_GSM_NETWORK_PROXY_
+
+#include "shill/dbus_bindings/modem-gsm-network.h"
+#include "shill/modem_gsm_network_proxy_interface.h"
+
+namespace shill {
+
+class ModemGSMNetworkProxy : public ModemGSMNetworkProxyInterface {
+ public:
+  // Constructs a ModemManager.Modem.Gsm.Network DBus object proxy at |path|
+  // owned by |service|. Caught signals will be dispatched to |listener|.
+  ModemGSMNetworkProxy(ModemGSMNetworkProxyListener *listener,
+                       DBus::Connection *connection,
+                       const std::string &path,
+                       const std::string &service);
+  virtual ~ModemGSMNetworkProxy();
+
+  // Inherited from ModemGSMNetworkProxyInterface.
+  virtual void Register(const std::string &network_id);
+
+ private:
+  class Proxy
+      : public org::freedesktop::ModemManager::Modem::Gsm::Network_proxy,
+        public DBus::ObjectProxy {
+   public:
+    Proxy(ModemGSMNetworkProxyListener *listener,
+          DBus::Connection *connection,
+          const std::string &path,
+          const std::string &service);
+    virtual ~Proxy();
+
+   private:
+    // Signal callbacks inherited from ModemManager::Modem::Gsm::Network_proxy.
+    virtual void SignalQuality(const uint32 &quality);
+    virtual void RegistrationInfo(const uint32_t &status,
+                                  const std::string &operator_code,
+                                  const std::string &operator_name);
+    virtual void NetworkMode(const uint32_t &mode);
+
+    ModemGSMNetworkProxyListener *listener_;
+
+    DISALLOW_COPY_AND_ASSIGN(Proxy);
+  };
+
+  Proxy proxy_;
+
+  DISALLOW_COPY_AND_ASSIGN(ModemGSMNetworkProxy);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_MODEM_GSM_NETWORK_PROXY_
diff --git a/modem_gsm_network_proxy_interface.h b/modem_gsm_network_proxy_interface.h
new file mode 100644
index 0000000..414042f
--- /dev/null
+++ b/modem_gsm_network_proxy_interface.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_MODEM_GSM_NETWORK_PROXY_INTERFACE_
+#define SHILL_MODEM_GSM_NETWORK_PROXY_INTERFACE_
+
+#include <string>
+
+#include <base/basictypes.h>
+
+namespace shill {
+
+// These are the methods that a ModemManager.Modem.Gsm.Network proxy must
+// support. The interface is provided so that it can be mocked in tests.
+class ModemGSMNetworkProxyInterface {
+ public:
+  virtual ~ModemGSMNetworkProxyInterface() {}
+
+  virtual void Register(const std::string &network_id) = 0;
+};
+
+// ModemManager.Modem.Gsm.Network signal listener to be associated with the
+// proxy.
+class ModemGSMNetworkProxyListener {
+ public:
+  virtual ~ModemGSMNetworkProxyListener() {}
+
+  virtual void OnGSMNetworkModeChanged(uint32 mode) = 0;
+  virtual void OnGSMRegistrationInfoChanged(
+      uint32 status,
+      const std::string &operator_code,
+      const std::string &operator_name) = 0;
+  virtual void OnGSMSignalQualityChanged(uint32 quality) = 0;
+};
+
+}  // namespace shill
+
+#endif  // SHILL_MODEM_GSM_NETWORK_PROXY_INTERFACE_
diff --git a/proxy_factory.cc b/proxy_factory.cc
index c5191c6..5945bbe 100644
--- a/proxy_factory.cc
+++ b/proxy_factory.cc
@@ -9,6 +9,7 @@
 #include "shill/dbus_properties_proxy.h"
 #include "shill/dhcpcd_proxy.h"
 #include "shill/modem_cdma_proxy.h"
+#include "shill/modem_gsm_network_proxy.h"
 #include "shill/modem_manager_proxy.h"
 #include "shill/modem_proxy.h"
 #include "shill/modem_simple_proxy.h"
@@ -65,6 +66,13 @@
   return new ModemCDMAProxy(listener, connection(), path, service);
 }
 
+ModemGSMNetworkProxyInterface *ProxyFactory::CreateModemGSMNetworkProxy(
+    ModemGSMNetworkProxyListener *listener,
+    const string &path,
+    const string &service) {
+  return new ModemGSMNetworkProxy(listener, connection(), path, service);
+}
+
 SupplicantProcessProxyInterface *ProxyFactory::CreateSupplicantProcessProxy(
     const char *dbus_path,
     const char *dbus_addr) {
diff --git a/proxy_factory.h b/proxy_factory.h
index 777051a..5ba9f73 100644
--- a/proxy_factory.h
+++ b/proxy_factory.h
@@ -20,6 +20,8 @@
 class DHCPProxyInterface;
 class ModemCDMAProxyInterface;
 class ModemCDMAProxyListener;
+class ModemGSMNetworkProxyInterface;
+class ModemGSMNetworkProxyListener;
 class ModemManager;
 class ModemManagerProxyInterface;
 class ModemProxyInterface;
@@ -59,6 +61,11 @@
       const std::string &path,
       const std::string &service);
 
+  virtual ModemGSMNetworkProxyInterface *CreateModemGSMNetworkProxy(
+      ModemGSMNetworkProxyListener *listener,
+      const std::string &path,
+      const std::string &service);
+
   virtual SupplicantProcessProxyInterface *CreateSupplicantProcessProxy(
       const char *dbus_path,
       const char *dbus_addr);