shill: cellular: Implement 'Reset' for modems managed by ModemManager.

BUG=chromium-os:37678
TEST=Tested the following:
1. Build and run unit tests.
2. Run the following command and verify that the modem device (eth0)
   managed by ModemManager is reset:

     dbus-send --system --print-reply --dest=org.chromium.flimflam \
         /device/eth0 org.chromium.flimflam.Device.Reset

Change-Id: Iedb015a09aec97ad96bded8a9dab8d8d88505ce0
Reviewed-on: https://gerrit.chromium.org/gerrit/40591
Commit-Queue: Ben Chan <benchan@chromium.org>
Reviewed-by: Ben Chan <benchan@chromium.org>
Tested-by: Ben Chan <benchan@chromium.org>
diff --git a/cellular.cc b/cellular.cc
index 55dd803..4c81826 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -330,6 +330,11 @@
   capability_->ChangePIN(old_pin, new_pin, error, callback);
 }
 
+void Cellular::Reset(Error *error, const ResultCallback &callback) {
+  SLOG(Cellular, 2) << __func__;
+  capability_->Reset(error, callback);
+}
+
 void Cellular::SetCarrier(const string &carrier,
                           Error *error, const ResultCallback &callback) {
   SLOG(Cellular, 2) << __func__ << "(" << carrier << ")";
diff --git a/cellular.h b/cellular.h
index cfe7b59..e89aa64 100644
--- a/cellular.h
+++ b/cellular.h
@@ -192,6 +192,7 @@
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
                          Error *error, const ResultCallback &callback);
+  virtual void Reset(Error *error, const ResultCallback &callback);
   virtual void SetCarrier(const std::string &carrier,
                           Error *error, const ResultCallback &callback);
 
diff --git a/cellular_capability.cc b/cellular_capability.cc
index 99aab01..aa54768 100644
--- a/cellular_capability.cc
+++ b/cellular_capability.cc
@@ -26,6 +26,7 @@
 const int CellularCapability::kTimeoutDisconnect = 45000;
 const int CellularCapability::kTimeoutEnable = 45000;
 const int CellularCapability::kTimeoutRegister = 90000;
+const int CellularCapability::kTimeoutReset = 90000;
 const int CellularCapability::kTimeoutScan = 120000;
 
 CellularCapability::CellularCapability(Cellular *cellular,
@@ -86,6 +87,11 @@
   OnUnsupportedOperation(__func__, error);
 }
 
+void CellularCapability::Reset(Error *error,
+                               const ResultCallback &/*callback*/) {
+  OnUnsupportedOperation(__func__, error);
+}
+
 void CellularCapability::SetCarrier(const std::string &/*carrier*/,
                                     Error *error,
                                     const ResultCallback &/*callback*/) {
diff --git a/cellular_capability.h b/cellular_capability.h
index 3ebcbff..e1d79de 100644
--- a/cellular_capability.h
+++ b/cellular_capability.h
@@ -65,6 +65,7 @@
   static const int kTimeoutDisconnect;
   static const int kTimeoutEnable;
   static const int kTimeoutRegister;
+  static const int kTimeoutReset;
   static const int kTimeoutScan;
 
   static const char kModemPropertyIMSI[];
@@ -137,6 +138,7 @@
                          Error *error, const ResultCallback &callback);
 
   // The default implementation fails by returning an error.
+  virtual void Reset(Error *error, const ResultCallback &callback);
   virtual void SetCarrier(const std::string &carrier,
                           Error *error, const ResultCallback &callback);
 
diff --git a/cellular_capability_universal.cc b/cellular_capability_universal.cc
index ed759d8..29c7cc2 100644
--- a/cellular_capability_universal.cc
+++ b/cellular_capability_universal.cc
@@ -128,6 +128,7 @@
       preferred_mode_(MM_MODEM_MODE_NONE),
       home_provider_(NULL),
       provider_requires_roaming_(false),
+      resetting_(false),
       scanning_supported_(true),
       scanning_(false),
       scan_interval_(0),
@@ -840,6 +841,30 @@
   sim_proxy_->ChangePin(old_pin, new_pin, error, callback, kTimeoutDefault);
 }
 
+void CellularCapabilityUniversal::Reset(Error *error,
+                                        const ResultCallback &callback) {
+  SLOG(Cellular, 2) << __func__;
+  CHECK(error);
+  if (resetting_) {
+    Error::PopulateAndLog(error, Error::kInProgress, "Already resetting");
+    return;
+  }
+  ResultCallback cb = Bind(&CellularCapabilityUniversal::OnResetReply,
+                           weak_ptr_factory_.GetWeakPtr(), callback);
+  modem_proxy_->Reset(error, cb, kTimeoutReset);
+  if (!error->IsFailure()) {
+    resetting_ = true;
+  }
+}
+
+void CellularCapabilityUniversal::OnResetReply(const ResultCallback &callback,
+                                               const Error &error) {
+  SLOG(Cellular, 2) << __func__;
+  resetting_ = false;
+  if (!callback.is_null())
+    callback.Run(error);
+}
+
 void CellularCapabilityUniversal::Scan(Error *error,
                                        const ResultCallback &callback) {
   SLOG(Cellular, 2) << __func__;
diff --git a/cellular_capability_universal.h b/cellular_capability_universal.h
index f1a7b4a..e0ea986 100644
--- a/cellular_capability_universal.h
+++ b/cellular_capability_universal.h
@@ -94,6 +94,7 @@
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
                          Error *error, const ResultCallback &callback);
+  virtual void Reset(Error *error, const ResultCallback &callback);
 
   virtual void Scan(Error *error, const ResultCallback &callback);
   virtual std::string GetNetworkTechnologyString() const;
@@ -135,6 +136,7 @@
   FRIEND_TEST(CellularCapabilityUniversalTest, IsServiceActivationRequired);
   FRIEND_TEST(CellularCapabilityUniversalTest, OnListBearersReply);
   FRIEND_TEST(CellularCapabilityUniversalTest, PropertiesChanged);
+  FRIEND_TEST(CellularCapabilityUniversalTest, Reset);
   FRIEND_TEST(CellularCapabilityUniversalTest, Scan);
   FRIEND_TEST(CellularCapabilityUniversalTest, ScanFailure);
   FRIEND_TEST(CellularCapabilityUniversalTest, SetHomeProvider);
@@ -255,6 +257,7 @@
   // Method callbacks
   void OnRegisterReply(const ResultCallback &callback,
                        const Error &error);
+  void OnResetReply(const ResultCallback &callback, const Error &error);
   void OnScanReply(const ResultCallback &callback,
                    const ScanResults &results,
                    const Error &error);
@@ -306,6 +309,7 @@
   std::string selected_network_;
   Stringmaps found_networks_;
   std::deque<Stringmap> apn_try_list_;
+  bool resetting_;
   bool scanning_supported_;
   bool scanning_;
   uint16 scan_interval_;
diff --git a/cellular_capability_universal_unittest.cc b/cellular_capability_universal_unittest.cc
index 32a801c..8a7ac73 100644
--- a/cellular_capability_universal_unittest.cc
+++ b/cellular_capability_universal_unittest.cc
@@ -708,6 +708,25 @@
   return static_cast<size_t>(value) == arg.size();
 }
 
+TEST_F(CellularCapabilityUniversalTest, Reset) {
+  // Save pointers to proxies before they are lost by the call to InitProxies
+  mm1::MockModemProxy *modem_proxy = modem_proxy_.get();
+  SetUp();
+  EXPECT_CALL(*modem_proxy, set_state_changed_callback(_));
+  capability_->InitProxies();
+
+  Error error;
+  ResultCallback reset_callback;
+
+  EXPECT_CALL(*modem_proxy, Reset(_, _, CellularCapability::kTimeoutReset))
+      .WillOnce(SaveArg<1>(&reset_callback));
+
+  capability_->Reset(&error, ResultCallback());
+  EXPECT_TRUE(capability_->resetting_);
+  reset_callback.Run(error);
+  EXPECT_FALSE(capability_->resetting_);
+}
+
 // Validates that OnScanReply does not crash with a null callback.
 TEST_F(CellularCapabilityUniversalTest, ScanWithNullCallback) {
   Error error;