shill: Handle setting of cellular's AllowRoaming property.

Disconnect the cellular device when roaming and roaming is disallowed. Broadcast
changes in the property.

BUG=chromium-os:25849
TEST=unit tests

Change-Id: I099f85152871cf8e64e96ded72b40712bd66dbd1
Reviewed-on: https://gerrit.chromium.org/gerrit/15406
Tested-by: Darin Petkov <petkov@chromium.org>
Reviewed-by: Eric Shienbrood <ers@chromium.org>
Commit-Ready: Darin Petkov <petkov@chromium.org>
diff --git a/cellular.cc b/cellular.cc
index 7cd274d..349e4c6 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -316,7 +316,12 @@
 void Cellular::OnConnected() {
   VLOG(2) << __func__;
   SetState(kStateConnected);
-  EstablishLink();
+  if (!capability_->allow_roaming() &&
+      service_->roaming_state() == flimflam::kRoamingStateRoaming) {
+    Disconnect(NULL);
+  } else {
+    EstablishLink();
+  }
 }
 
 void Cellular::OnConnectFailed() {
diff --git a/cellular.h b/cellular.h
index 578f32c..705b794 100644
--- a/cellular.h
+++ b/cellular.h
@@ -184,6 +184,7 @@
   FRIEND_TEST(CellularTest, StartCDMARegister);
   FRIEND_TEST(CellularTest, StartGSMRegister);
   FRIEND_TEST(CellularTest, StartLinked);
+  FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
   FRIEND_TEST(CellularCapabilityTest, EnableModemFail);
   FRIEND_TEST(CellularCapabilityTest, EnableModemSucceed);
   FRIEND_TEST(CellularCapabilityTest, GetModemInfo);
diff --git a/cellular_capability.cc b/cellular_capability.cc
index 5b166a8..96b6a92 100644
--- a/cellular_capability.cc
+++ b/cellular_capability.cc
@@ -9,6 +9,7 @@
 #include "shill/adaptor_interfaces.h"
 #include "shill/cellular.h"
 #include "shill/error.h"
+#include "shill/property_accessor.h"
 #include "shill/proxy_factory.h"
 
 using base::Callback;
@@ -64,7 +65,9 @@
       proxy_factory_(proxy_factory) {
   PropertyStore *store = cellular->mutable_store();
   store->RegisterConstString(flimflam::kCarrierProperty, &carrier_);
-  store->RegisterBool(flimflam::kCellularAllowRoamingProperty, &allow_roaming_);
+  HelpRegisterDerivedBool(flimflam::kCellularAllowRoamingProperty,
+                          &CellularCapability::GetAllowRoaming,
+                          &CellularCapability::SetAllowRoaming);
   store->RegisterConstBool(flimflam::kSupportNetworkScanProperty,
                            &scanning_supported_);
   store->RegisterConstString(flimflam::kEsnProperty, &esn_);
@@ -83,6 +86,30 @@
 
 CellularCapability::~CellularCapability() {}
 
+void CellularCapability::HelpRegisterDerivedBool(
+    const string &name,
+    bool(CellularCapability::*get)(Error *error),
+    void(CellularCapability::*set)(const bool &value, Error *error)) {
+  cellular()->mutable_store()->RegisterDerivedBool(
+      name,
+      BoolAccessor(
+          new CustomAccessor<CellularCapability, bool>(this, get, set)));
+}
+
+void CellularCapability::SetAllowRoaming(const bool &value, Error */*error*/) {
+  VLOG(2) << __func__ << "(" << allow_roaming_ << "->" << value << ")";
+  if (allow_roaming_ == value) {
+    return;
+  }
+  allow_roaming_ = value;
+  if (!value && GetRoamingStateString() == flimflam::kRoamingStateRoaming) {
+    Error error;
+    cellular()->Disconnect(&error);
+  }
+  cellular()->adaptor()->EmitBoolChanged(
+      flimflam::kCellularAllowRoamingProperty, value);
+}
+
 void CellularCapability::StartModem() {
   VLOG(2) << __func__;
   proxy_.reset(proxy_factory()->CreateModemProxy(
diff --git a/cellular_capability.h b/cellular_capability.h
index 78d9524..0b9a3af 100644
--- a/cellular_capability.h
+++ b/cellular_capability.h
@@ -183,6 +183,7 @@
   friend class CellularCapabilityTest;
   FRIEND_TEST(CellularCapabilityGSMTest, SetStorageIdentifier);
   FRIEND_TEST(CellularCapabilityGSMTest, UpdateStatus);
+  FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
   FRIEND_TEST(CellularCapabilityTest, GetModemInfo);
   FRIEND_TEST(CellularCapabilityTest, GetModemStatus);
   FRIEND_TEST(CellularServiceTest, FriendlyName);
@@ -193,6 +194,14 @@
   FRIEND_TEST(CellularTest, Connect);
   FRIEND_TEST(CellularTest, DisconnectModem);
 
+  void HelpRegisterDerivedBool(
+      const std::string &name,
+      bool(CellularCapability::*get)(Error *error),
+      void(CellularCapability::*set)(const bool &value, Error *error));
+
+  bool GetAllowRoaming(Error */*error*/) { return allow_roaming_; }
+  void SetAllowRoaming(const bool &value, Error *error);
+
   Cellular *cellular_;
 
   // Store cached copies of singletons for speed/ease of testing.
diff --git a/cellular_capability_gsm.h b/cellular_capability_gsm.h
index d1eac54..4933039 100644
--- a/cellular_capability_gsm.h
+++ b/cellular_capability_gsm.h
@@ -62,6 +62,7 @@
   friend class CellularTest;
   friend class CellularCapabilityGSMTest;
   FRIEND_TEST(CellularTest, StartGSMRegister);
+  FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
   FRIEND_TEST(CellularCapabilityGSMTest, CreateDeviceFromProperties);
   FRIEND_TEST(CellularCapabilityGSMTest, CreateFriendlyServiceName);
   FRIEND_TEST(CellularCapabilityGSMTest, GetIMEI);
diff --git a/cellular_capability_unittest.cc b/cellular_capability_unittest.cc
index 09ca81f..3e18772 100644
--- a/cellular_capability_unittest.cc
+++ b/cellular_capability_unittest.cc
@@ -23,6 +23,7 @@
 #include "shill/proxy_factory.h"
 
 using std::string;
+using testing::InSequence;
 using testing::NiceMock;
 
 namespace shill {
@@ -189,4 +190,32 @@
   EXPECT_EQ(Cellular::kStateDisabled, cellular_->state_);
 }
 
+TEST_F(CellularCapabilityTest, AllowRoaming) {
+  EXPECT_FALSE(capability_->GetAllowRoaming(NULL));
+  capability_->SetAllowRoaming(false, NULL);
+  EXPECT_FALSE(capability_->GetAllowRoaming(NULL));
+
+  {
+    InSequence seq;
+    EXPECT_CALL(*device_adaptor_, EmitBoolChanged(
+        flimflam::kCellularAllowRoamingProperty, true));
+    EXPECT_CALL(*device_adaptor_, EmitBoolChanged(
+        flimflam::kCellularAllowRoamingProperty, false));
+  }
+
+  cellular_->state_ = Cellular::kStateConnected;
+  dynamic_cast<CellularCapabilityGSM *>(capability_)->registration_state_ =
+      MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING;
+  capability_->SetAllowRoaming(true, NULL);
+  EXPECT_TRUE(capability_->GetAllowRoaming(NULL));
+  EXPECT_EQ(Cellular::kStateConnected, cellular_->state_);
+
+  EXPECT_CALL(*proxy_, Disconnect());
+  SetProxy();
+  cellular_->state_ = Cellular::kStateConnected;
+  capability_->SetAllowRoaming(false, NULL);
+  EXPECT_FALSE(capability_->GetAllowRoaming(NULL));
+  EXPECT_EQ(Cellular::kStateRegistered, cellular_->state_);
+}
+
 }  // namespace shill