shill: Register the modem, create service and obtain signal strength.

Also, listen for registration info and signal quality change notifications.

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

Change-Id: I19e5e97257bc53f2004d6b7f0606da2553be4b5d
Reviewed-on: http://gerrit.chromium.org/gerrit/5328
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/cellular.cc b/cellular.cc
index 2ca6951..5a26d75 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -108,10 +108,6 @@
       state_(kStateDisabled),
       dbus_owner_(owner),
       dbus_path_(path),
-      service_(new CellularService(control_interface,
-                                   dispatcher,
-                                   manager,
-                                   this)),
       service_registered_(false),
       allow_roaming_(false),
       prl_version_(0),
@@ -183,7 +179,6 @@
   }
   GetModemInfo();
   GetModemRegistrationState();
-  ReportEnabled();
   // TODO(petkov): Device::Start();
 }
 
@@ -193,6 +188,7 @@
   cdma_proxy_.reset();
   manager_->DeregisterService(service_);
   service_ = NULL;  // Breaks a reference cycle.
+  state_ = kStateDisabled;
   // TODO(petkov): Device::Stop();
 }
 
@@ -209,7 +205,7 @@
     case kTypeCDMA:
       cdma_proxy_.reset(
           ProxyFactory::factory()->CreateModemCDMAProxy(
-              dbus_path_, dbus_owner_));
+              this, dbus_path_, dbus_owner_));
       break;
     default: NOTREACHED();
   }
@@ -285,6 +281,7 @@
       break;
     default: NOTREACHED();
   }
+  HandleNewRegistrationState();
 }
 
 void Cellular::GetCDMARegistrationState() {
@@ -301,15 +298,94 @@
   NOTIMPLEMENTED();
 }
 
-void Cellular::ReportEnabled() {
+bool Cellular::IsModemRegistered() {
+  return cdma_.registration_state_1x !=
+      MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN ||
+      cdma_.registration_state_evdo !=
+      MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+  // TODO(petkov): Handle GSM states.
+}
+
+void Cellular::HandleNewRegistrationState() {
+  VLOG(2) << __func__;
+  if (!IsModemRegistered()) {
+    service_ = NULL;
+    if (state_ == kStateConnected || state_ == kStateRegistered) {
+      state_ = kStateEnabled;
+    }
+    return;
+  }
+
+  if (state_ == kStateEnabled) {
+    state_ = kStateRegistered;
+  }
+  if (!service_.get()) {
+    // For now, no endpoint is created. Revisit if necessary.
+    CreateService();
+  }
+  GetModemSignalQuality();
+  // TODO(petkov): Update the service.
+}
+
+void Cellular::GetModemSignalQuality() {
+  uint32 strength = 0;
+  switch (type_) {
+    case kTypeGSM:
+      strength = GetGSMSignalQuality();
+      break;
+    case kTypeCDMA:
+      strength = GetCDMASignalQuality();
+      break;
+    default: NOTREACHED();
+  }
+  HandleNewSignalQuality(strength);
+}
+
+uint32 Cellular::GetCDMASignalQuality() {
+  CHECK_EQ(kTypeCDMA, type_);
+  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
+  return cdma_proxy_->GetSignalQuality();
+}
+
+uint32 Cellular::GetGSMSignalQuality() {
   // TODO(petkov): Implement this.
   NOTIMPLEMENTED();
+  return 0;
+}
+
+void Cellular::HandleNewSignalQuality(uint32 strength) {
+  VLOG(2) << "Signal strength: " << strength;
+  if (service_.get()) {
+    service_->set_strength(strength);
+  }
+}
+
+void Cellular::CreateService() {
+  CHECK(!service_.get());
+  service_ =
+      new CellularService(control_interface_, dispatcher_, manager_, this);
+  // TODO(petkov): Set activation_state.
+  // TODO(petkov): Set operator.
+  // TODO(petkov): Set old_url/usage_url.
 }
 
 bool Cellular::TechnologyIs(const Device::Technology type) {
   return type == Device::kCellular;
 }
 
+void Cellular::OnCDMARegistrationStateChanged(uint32 state_1x,
+                                              uint32 state_evdo) {
+  CHECK_EQ(kTypeCDMA, type_);
+  cdma_.registration_state_1x = state_1x;
+  cdma_.registration_state_evdo = state_evdo;
+  HandleNewRegistrationState();
+}
+
+void Cellular::OnCDMASignalQualityChanged(uint32 strength) {
+  CHECK_EQ(kTypeCDMA, type_);
+  HandleNewSignalQuality(strength);
+}
+
 Stringmaps Cellular::EnumerateNetworks() {
   Stringmaps to_return;
   for (vector<Network>::const_iterator it = found_networks_.begin();
diff --git a/cellular.h b/cellular.h
index a88cb09..4f8efbf 100644
--- a/cellular.h
+++ b/cellular.h
@@ -11,16 +11,17 @@
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "shill/device.h"
+#include "shill/modem_cdma_proxy_interface.h"
 #include "shill/refptr_types.h"
 #include "shill/shill_event.h"
 
 namespace shill {
 
-class ModemCDMAProxyInterface;
 class ModemProxyInterface;
 class ModemSimpleProxyInterface;
 
-class Cellular : public Device {
+class Cellular : public Device,
+                 public ModemCDMAProxyListener {
  public:
   enum Type {
     kTypeGSM,
@@ -98,6 +99,7 @@
 
  private:
   FRIEND_TEST(CellularTest, GetCDMARegistrationState);
+  FRIEND_TEST(CellularTest, GetCDMASignalQuality);
   FRIEND_TEST(CellularTest, GetModemInfo);
   FRIEND_TEST(CellularTest, GetModemStatus);
   FRIEND_TEST(CellularTest, GetStateString);
@@ -124,19 +126,40 @@
   void GetModemStatus();
   void GetGSMProperties();
   void RegisterGSMModem();
-  void ReportEnabled();
 
   // Obtains the modem identifiers: MEID for CDMA; IMEI, IMSI, SPN and MSISDN
   // for GSM.
   void GetModemIdentifiers();
 
-  // Obtain modem's manufacturer, model ID, and hardware revision.
+  // Obtains modem's manufacturer, model ID, and hardware revision.
   void GetModemInfo();
 
   void GetModemRegistrationState();
   void GetCDMARegistrationState();
   void GetGSMRegistrationState();
 
+  // Processes a change in the modem registration state, possibly creating,
+  // destroying or updating the CellularService.
+  void HandleNewRegistrationState();
+
+  void CreateService();
+
+  void GetModemSignalQuality();
+  uint32 GetCDMASignalQuality();
+  uint32 GetGSMSignalQuality();
+
+  void HandleNewSignalQuality(uint32 strength);
+
+  // Returns true if the modem is registered. Note that this method looks at the
+  // latest CDMA/GSM registration info obtained from the modem rather than the
+  // device |state_|.
+  bool IsModemRegistered();
+
+  // Signal callbacks from ModemCDMAProxyListener.
+  virtual void OnCDMARegistrationStateChanged(uint32 state_1x,
+                                              uint32 state_evdo);
+  virtual void OnCDMASignalQualityChanged(uint32 strength);
+
   Type type_;
   State state_;
 
@@ -148,7 +171,7 @@
 
   CDMA cdma_;
 
-  ServiceRefPtr service_;
+  CellularServiceRefPtr service_;
   bool service_registered_;
 
   // Properties
diff --git a/cellular_service.h b/cellular_service.h
index e074202..ad39dab 100644
--- a/cellular_service.h
+++ b/cellular_service.h
@@ -26,10 +26,14 @@
                   EventDispatcher *dispatcher,
                   Manager *manager,
                   const CellularRefPtr &device);
-  ~CellularService();
+  virtual ~CellularService();
+
   void Connect();
   void Disconnect();
 
+  uint8 strength() const { return strength_; }
+  void set_strength(uint8 strength) { strength_ = strength; }
+
  protected:
   virtual std::string CalculateState() { return "idle"; }
 
@@ -50,6 +54,7 @@
 
   CellularRefPtr cellular_;
   const std::string type_;
+
   DISALLOW_COPY_AND_ASSIGN(CellularService);
 };
 
diff --git a/cellular_unittest.cc b/cellular_unittest.cc
index 6b3e575..26f9eec 100644
--- a/cellular_unittest.cc
+++ b/cellular_unittest.cc
@@ -7,6 +7,7 @@
 #include <chromeos/dbus/service_constants.h>
 #include <mm/mm-modem.h>
 
+#include "shill/cellular_service.h"
 #include "shill/mock_modem_cdma_proxy.h"
 #include "shill/mock_modem_proxy.h"
 #include "shill/mock_modem_simple_proxy.h"
@@ -63,6 +64,7 @@
     }
 
     virtual ModemCDMAProxyInterface *CreateModemCDMAProxy(
+        ModemCDMAProxyListener *listener,
         const string &path,
         const string &service) {
       return test_->cdma_proxy_.release();
@@ -207,6 +209,7 @@
 }
 
 TEST_F(CellularTest, GetCDMARegistrationState) {
+  EXPECT_FALSE(device_->service_.get());
   EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
             device_->cdma_.registration_state_1x);
   EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
@@ -216,12 +219,32 @@
       .WillOnce(DoAll(
           SetArgumentPointee<0>(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED),
           SetArgumentPointee<1>(MM_MODEM_CDMA_REGISTRATION_STATE_HOME)));
+  EXPECT_CALL(*cdma_proxy_, GetSignalQuality()).WillOnce(Return(90));
   device_->cdma_proxy_.reset(cdma_proxy_.release());
   device_->GetModemRegistrationState();
   EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
             device_->cdma_.registration_state_1x);
   EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_HOME,
             device_->cdma_.registration_state_evdo);
+  EXPECT_TRUE(device_->service_.get());
+}
+
+TEST_F(CellularTest, GetCDMASignalQuality) {
+  const int kStrength = 90;
+  device_->type_ = Cellular::kTypeCDMA;
+  EXPECT_CALL(*cdma_proxy_, GetSignalQuality())
+      .Times(2)
+      .WillRepeatedly(Return(kStrength));
+  device_->cdma_proxy_.reset(cdma_proxy_.release());
+
+  EXPECT_FALSE(device_->service_.get());
+  device_->GetModemSignalQuality();
+
+  device_->service_ = new CellularService(
+      &control_interface_, &dispatcher_, &manager_, device_);
+  EXPECT_EQ(0, device_->service_->strength());
+  device_->GetModemSignalQuality();
+  EXPECT_EQ(kStrength, device_->service_->strength());
 }
 
 }  // namespace shill
diff --git a/device.cc b/device.cc
index 51615b9..e212cbf 100644
--- a/device.cc
+++ b/device.cc
@@ -51,6 +51,8 @@
       running_(false),
       link_name_(link_name),
       unique_id_(link_name),
+      control_interface_(control_interface),
+      dispatcher_(dispatcher),
       manager_(manager),
       adaptor_(control_interface->CreateDeviceAdaptor(this)) {
 
diff --git a/device.h b/device.h
index 08548e6..be2ba2a 100644
--- a/device.h
+++ b/device.h
@@ -110,6 +110,8 @@
   bool running_;
   const std::string link_name_;
   const std::string unique_id_;
+  ControlInterface *control_interface_;
+  EventDispatcher *dispatcher_;
   Manager *manager_;
   IPConfigRefPtr ipconfig_;
 
diff --git a/mock_modem_cdma_proxy.h b/mock_modem_cdma_proxy.h
index afdf183..fdd9d93 100644
--- a/mock_modem_cdma_proxy.h
+++ b/mock_modem_cdma_proxy.h
@@ -15,6 +15,7 @@
  public:
   MOCK_METHOD2(GetRegistrationState, void(uint32 *cdma_1x_state,
                                           uint32 *evdo_state));
+  MOCK_METHOD0(GetSignalQuality, uint32());
 };
 
 }  // namespace shill
diff --git a/modem_cdma_proxy.cc b/modem_cdma_proxy.cc
index aab7c4a..4380c2d 100644
--- a/modem_cdma_proxy.cc
+++ b/modem_cdma_proxy.cc
@@ -10,10 +10,11 @@
 
 namespace shill {
 
-ModemCDMAProxy::ModemCDMAProxy(DBus::Connection *connection,
+ModemCDMAProxy::ModemCDMAProxy(ModemCDMAProxyListener *listener,
+                               DBus::Connection *connection,
                                const string &path,
                                const string &service)
-    : proxy_(connection, path, service) {}
+    : proxy_(listener, connection, path, service) {}
 
 ModemCDMAProxy::~ModemCDMAProxy() {}
 
@@ -22,10 +23,16 @@
   proxy_.GetRegistrationState(*cdma_1x_state, *evdo_state);
 }
 
-ModemCDMAProxy::Proxy::Proxy(DBus::Connection *connection,
+uint32 ModemCDMAProxy::GetSignalQuality() {
+  return proxy_.GetSignalQuality();
+}
+
+ModemCDMAProxy::Proxy::Proxy(ModemCDMAProxyListener *listener,
+                             DBus::Connection *connection,
                              const string &path,
                              const string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+    : DBus::ObjectProxy(*connection, path, service.c_str()),
+      listener_(listener) {}
 
 ModemCDMAProxy::Proxy::~Proxy() {}
 
@@ -39,17 +46,15 @@
 }
 
 void ModemCDMAProxy::Proxy::SignalQuality(const uint32 &quality) {
-  VLOG(2) << __func__;
-  // TODO(petkov): Implement this.
-  NOTIMPLEMENTED();
+  VLOG(2) << __func__ << "(" << quality << ")";
+  listener_->OnCDMASignalQualityChanged(quality);
 }
 
 void ModemCDMAProxy::Proxy::RegistrationStateChanged(
-    const uint32_t &cdma_1x_state,
-    const uint32_t &evdo_state) {
-  VLOG(2) << __func__;
-  // TODO(petkov): Implement this.
-  NOTIMPLEMENTED();
+    const uint32 &cdma_1x_state,
+    const uint32 &evdo_state) {
+  VLOG(2) << __func__ << "(" << cdma_1x_state << ", " << evdo_state << ")";
+  listener_->OnCDMARegistrationStateChanged(cdma_1x_state, evdo_state);
 }
 
 }  // namespace shill
diff --git a/modem_cdma_proxy.h b/modem_cdma_proxy.h
index b1ef84f..33d021f 100644
--- a/modem_cdma_proxy.h
+++ b/modem_cdma_proxy.h
@@ -13,19 +13,24 @@
 
 class ModemCDMAProxy : public ModemCDMAProxyInterface {
  public:
-  ModemCDMAProxy(DBus::Connection *connection,
+  // Constructs a ModemManager.Modem.CDMA DBus object proxy at |path| owned by
+  // |service|. Caught signals will be dispatched to |listener|.
+  ModemCDMAProxy(ModemCDMAProxyListener *listener,
+                 DBus::Connection *connection,
                  const std::string &path,
                  const std::string &service);
   virtual ~ModemCDMAProxy();
 
   // Inherited from ModemCDMAProxyInterface.
   virtual void GetRegistrationState(uint32 *cdma_1x_state, uint32 *evdo_state);
+  virtual uint32 GetSignalQuality();
 
  private:
   class Proxy : public org::freedesktop::ModemManager::Modem::Cdma_proxy,
                 public DBus::ObjectProxy {
    public:
-    Proxy(DBus::Connection *connection,
+    Proxy(ModemCDMAProxyListener *listener,
+          DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
@@ -37,8 +42,10 @@
         const uint32 &activation_error,
         const DBusPropertiesMap &status_changes);
     virtual void SignalQuality(const uint32 &quality);
-    virtual void RegistrationStateChanged(const uint32_t &cdma_1x_state,
-                                          const uint32_t &evdo_state);
+    virtual void RegistrationStateChanged(const uint32 &cdma_1x_state,
+                                          const uint32 &evdo_state);
+
+    ModemCDMAProxyListener *listener_;
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
   };
diff --git a/modem_cdma_proxy_interface.h b/modem_cdma_proxy_interface.h
index c4f4c9c..04f058e 100644
--- a/modem_cdma_proxy_interface.h
+++ b/modem_cdma_proxy_interface.h
@@ -17,6 +17,18 @@
 
   virtual void GetRegistrationState(uint32 *cdma_1x_state,
                                     uint32 *evdo_state) = 0;
+
+  virtual uint32 GetSignalQuality() = 0;
+};
+
+// ModemManager.Modem.CDMA signal listener to be associated with the proxy.
+class ModemCDMAProxyListener {
+ public:
+  virtual ~ModemCDMAProxyListener() {}
+
+  virtual void OnCDMARegistrationStateChanged(uint32 state_1x,
+                                              uint32 state_evdo) = 0;
+  virtual void OnCDMASignalQualityChanged(uint32 strength) = 0;
 };
 
 }  // namespace shill
diff --git a/proxy_factory.cc b/proxy_factory.cc
index 3d250ac..2e90348 100644
--- a/proxy_factory.cc
+++ b/proxy_factory.cc
@@ -57,9 +57,10 @@
 }
 
 ModemCDMAProxyInterface *ProxyFactory::CreateModemCDMAProxy(
+    ModemCDMAProxyListener *listener,
     const string &path,
     const string &service) {
-  return new ModemCDMAProxy(connection(), path, service);
+  return new ModemCDMAProxy(listener, connection(), path, service);
 }
 
 SupplicantProcessProxyInterface *ProxyFactory::CreateSupplicantProcessProxy(
diff --git a/proxy_factory.h b/proxy_factory.h
index 6c1b0f4..0fc1cbd 100644
--- a/proxy_factory.h
+++ b/proxy_factory.h
@@ -19,6 +19,7 @@
 class DHCPProxyInterface;
 class Modem;
 class ModemCDMAProxyInterface;
+class ModemCDMAProxyListener;
 class ModemManager;
 class ModemManagerProxyInterface;
 class ModemProxyInterface;
@@ -52,6 +53,7 @@
       const std::string &service);
 
   virtual ModemCDMAProxyInterface *CreateModemCDMAProxy(
+      ModemCDMAProxyListener *listener,
       const std::string &path,
       const std::string &service);
 
diff --git a/wifi.cc b/wifi.cc
index e245d76..2cbaed1 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -51,8 +51,6 @@
              link,
              interface_index),
       task_factory_(this),
-      control_interface_(control_interface),
-      dispatcher_(dispatcher),
       bgscan_short_interval_(0),
       bgscan_signal_threshold_(0),
       scan_pending_(false),
diff --git a/wifi.h b/wifi.h
index c5bad6b..2808b79 100644
--- a/wifi.h
+++ b/wifi.h
@@ -59,8 +59,6 @@
   void ScanDoneTask();
 
   ScopedRunnableMethodFactory<WiFi> task_factory_;
-  ControlInterface *control_interface_;
-  EventDispatcher *dispatcher_;
   scoped_ptr<SupplicantProcessProxyInterface> supplicant_process_proxy_;
   scoped_ptr<SupplicantInterfaceProxyInterface> supplicant_interface_proxy_;
   EndpointMap endpoint_by_bssid_;