shill: cellular: Determine if service activation is required.

BUG=chrome-os-partner:10631
TEST=Build and run unit tests.

Change-Id: I2d23994b07aca2fc685318273f6dcc8a76a344f9
Reviewed-on: https://gerrit.chromium.org/gerrit/37358
Commit-Ready: Ben Chan <benchan@chromium.org>
Reviewed-by: Ben Chan <benchan@chromium.org>
Tested-by: Ben Chan <benchan@chromium.org>
diff --git a/Makefile b/Makefile
index 52f8a35..ff228ba 100644
--- a/Makefile
+++ b/Makefile
@@ -322,6 +322,7 @@
 	mock_arp_client.o \
 	mock_async_connection.o \
 	mock_cellular.o \
+	mock_cellular_operator_info.o \
 	mock_cellular_service.o \
 	mock_connection.o \
 	mock_control.o \
diff --git a/cellular.h b/cellular.h
index 11a3684..67b9ab5 100644
--- a/cellular.h
+++ b/cellular.h
@@ -233,6 +233,7 @@
   FRIEND_TEST(CellularCapabilityUniversalTest, AllowRoaming);
   FRIEND_TEST(CellularCapabilityUniversalTest, CreateFriendlyServiceName);
   FRIEND_TEST(CellularCapabilityUniversalTest, Connect);
+  FRIEND_TEST(CellularCapabilityUniversalTest, IsServiceActivationRequired);
   FRIEND_TEST(CellularCapabilityUniversalTest, StopModemConnected);
   FRIEND_TEST(CellularServiceTest, FriendlyName);
   FRIEND_TEST(CellularTest, CreateService);
diff --git a/cellular_capability.cc b/cellular_capability.cc
index cb99a40..81382ab 100644
--- a/cellular_capability.cc
+++ b/cellular_capability.cc
@@ -43,6 +43,10 @@
   Error::PopulateAndLog(error, Error::kNotSupported, message);
 }
 
+bool CellularCapability::IsServiceActivationRequired() const {
+  return false;
+}
+
 void CellularCapability::RegisterOnNetwork(
     const string &/*network_id*/,
     Error *error, const ResultCallback &/*callback*/) {
diff --git a/cellular_capability.h b/cellular_capability.h
index 0603ea7..647a9b3 100644
--- a/cellular_capability.h
+++ b/cellular_capability.h
@@ -105,6 +105,10 @@
   virtual void Activate(const std::string &carrier,
                         Error *error, const ResultCallback &callback) = 0;
 
+  // Returns true if service activation is required. Returns false by default
+  // in this base class.
+  virtual bool IsServiceActivationRequired() const;
+
   // Network registration.
   virtual void RegisterOnNetwork(const std::string &network_id,
                                  Error *error,
diff --git a/cellular_capability_universal.cc b/cellular_capability_universal.cc
index f90819c..7ad4c37 100644
--- a/cellular_capability_universal.cc
+++ b/cellular_capability_universal.cc
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include "shill/adaptor_interfaces.h"
+#include "shill/cellular_operator_info.h"
 #include "shill/cellular_service.h"
 #include "shill/dbus_properties_proxy_interface.h"
 #include "shill/error.h"
@@ -368,6 +369,8 @@
         cellular()->address() + "_" + imsi_);
   }
   cellular()->service()->SetActivationState(
+      IsServiceActivationRequired() ?
+      flimflam::kActivationStateNotActivated :
       flimflam::kActivationStateActivated);
   UpdateServingOperator();
 }
@@ -630,6 +633,30 @@
   }
 }
 
+bool CellularCapabilityUniversal::IsServiceActivationRequired() const {
+  // If there is no online payment portal information, it's safer to assume
+  // the service does not require activation.
+  if (!cellular()->cellular_operator_info())
+    return false;
+
+  CellularService::OLP olp;
+  if (!cellular()->cellular_operator_info()->GetOLP(operator_id_, &olp))
+    return false;
+
+  // To avoid false positives, it's safer to assume the service does not
+  // require activation if MDN is not set.
+  if (mdn_.empty())
+    return false;
+
+  // If MDN contains only zeros ('+' and '-' characters are ignored),
+  // the service requires activation.
+  for (size_t i = 0; i < mdn_.size(); ++i) {
+    if (mdn_[i] != '0' && mdn_[i] != '-' && mdn_[i] != '+')
+      return false;
+  }
+  return true;
+}
+
 // always called from an async context
 void CellularCapabilityUniversal::Register(const ResultCallback &callback) {
   SLOG(Cellular, 2) << __func__ << " \"" << selected_network_ << "\"";
diff --git a/cellular_capability_universal.h b/cellular_capability_universal.h
index c92c99e..ec2ee2a 100644
--- a/cellular_capability_universal.h
+++ b/cellular_capability_universal.h
@@ -73,6 +73,7 @@
   virtual void OnServiceCreated();
   virtual void SetupConnectProperties(DBusPropertiesMap *properties);
   virtual void GetProperties();
+  virtual bool IsServiceActivationRequired() const;
   virtual void Register(const ResultCallback &callback);
 
   virtual void RegisterOnNetwork(const std::string &network_id,
@@ -129,6 +130,7 @@
   FRIEND_TEST(CellularCapabilityUniversalTest, CreateFriendlyServiceName);
   FRIEND_TEST(CellularCapabilityUniversalTest, DisconnectNoProxy);
   FRIEND_TEST(CellularCapabilityUniversalTest, GetTypeString);
+  FRIEND_TEST(CellularCapabilityUniversalTest, IsServiceActivationRequired);
   FRIEND_TEST(CellularCapabilityUniversalTest, PropertiesChanged);
   FRIEND_TEST(CellularCapabilityUniversalTest, Scan);
   FRIEND_TEST(CellularCapabilityUniversalTest, ScanFailure);
diff --git a/cellular_capability_universal_unittest.cc b/cellular_capability_universal_unittest.cc
index cf0b0c3..3bcdd0c 100644
--- a/cellular_capability_universal_unittest.cc
+++ b/cellular_capability_universal_unittest.cc
@@ -20,6 +20,7 @@
 #include "shill/error.h"
 #include "shill/event_dispatcher.h"
 #include "shill/mock_adaptors.h"
+#include "shill/mock_cellular_operator_info.h"
 #include "shill/mock_cellular_service.h"
 #include "shill/mock_dbus_properties_proxy.h"
 #include "shill/mock_glib.h"
@@ -231,6 +232,7 @@
   TestProxyFactory proxy_factory_;
   CellularCapabilityUniversal *capability_;  // Owned by |cellular_|.
   NiceMock<DeviceMockAdaptor> *device_adaptor_;  // Owned by |cellular_|.
+  MockCellularOperatorInfo cellular_operator_info_;
   mobile_provider_db *provider_db_;
   CellularRefPtr cellular_;
   MockCellularService *service_;  // owned by cellular_
@@ -934,4 +936,29 @@
             capability_->CreateFriendlyServiceName());
 }
 
+TEST_F(CellularCapabilityUniversalTest, IsServiceActivationRequired) {
+  capability_->mdn_ = "0000000000";
+  cellular_->cellular_operator_info_ = NULL;
+  EXPECT_FALSE(capability_->IsServiceActivationRequired());
+
+  cellular_->cellular_operator_info_ = &cellular_operator_info_;
+  EXPECT_CALL(cellular_operator_info_, GetOLP(_, _))
+      .WillOnce(Return(false))
+      .WillRepeatedly(Return(true));
+  EXPECT_FALSE(capability_->IsServiceActivationRequired());
+
+  capability_->mdn_ = "";
+  EXPECT_FALSE(capability_->IsServiceActivationRequired());
+  capability_->mdn_ = "1234567890";
+  EXPECT_FALSE(capability_->IsServiceActivationRequired());
+  capability_->mdn_ = "+1-234-567-890";
+  EXPECT_FALSE(capability_->IsServiceActivationRequired());
+  capability_->mdn_ = "0000000000";
+  EXPECT_TRUE(capability_->IsServiceActivationRequired());
+  capability_->mdn_ = "0-000-000-000";
+  EXPECT_TRUE(capability_->IsServiceActivationRequired());
+  capability_->mdn_ = "+0-000-000-000";
+  EXPECT_TRUE(capability_->IsServiceActivationRequired());
+}
+
 }  // namespace shill
diff --git a/cellular_operator_info.h b/cellular_operator_info.h
index da6b2ad..f09f319 100644
--- a/cellular_operator_info.h
+++ b/cellular_operator_info.h
@@ -20,14 +20,15 @@
 class CellularOperatorInfo {
  public:
   explicit CellularOperatorInfo(GLib *glib);
-  ~CellularOperatorInfo();
+  virtual ~CellularOperatorInfo();
 
   // Loads the operator info from |info_file_path|. Returns true on success.
-  bool Load(const FilePath &info_file_path);
+  virtual bool Load(const FilePath &info_file_path);
 
   // Gets the online payment portal info of the operator with ID |operator_id|.
   // Returns true if the info is found.
-  bool GetOLP(const std::string &operator_id, CellularService::OLP *olp);
+  virtual bool GetOLP(const std::string &operator_id,
+                      CellularService::OLP *olp);
 
  private:
   KeyFileStore info_file_;
diff --git a/mock_cellular_operator_info.cc b/mock_cellular_operator_info.cc
new file mode 100644
index 0000000..f5cc5ca
--- /dev/null
+++ b/mock_cellular_operator_info.cc
@@ -0,0 +1,14 @@
+// Copyright (c) 2012 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/mock_cellular_operator_info.h"
+
+namespace shill {
+
+MockCellularOperatorInfo::MockCellularOperatorInfo()
+    : CellularOperatorInfo(NULL) {}
+
+MockCellularOperatorInfo::~MockCellularOperatorInfo() {}
+
+}  // namespace shill
diff --git a/mock_cellular_operator_info.h b/mock_cellular_operator_info.h
new file mode 100644
index 0000000..2acb0db
--- /dev/null
+++ b/mock_cellular_operator_info.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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_CELLULAR_OPERATOR_INFO_H_
+#define SHILL_MOCK_CELLULAR_OPERATOR_INFO_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "shill/cellular_operator_info.h"
+
+namespace shill {
+
+class MockCellularOperatorInfo : public CellularOperatorInfo {
+ public:
+  MockCellularOperatorInfo();
+  virtual ~MockCellularOperatorInfo();
+
+  MOCK_METHOD1(Load, bool(const FilePath &info_file_path));
+  MOCK_METHOD2(GetOLP, bool(const std::string &operator_id,
+                            CellularService::OLP *olp));
+};
+
+}  // namespace shill
+
+#endif  // SHILL_MOCK_CELLULAR_OPERATOR_INFO_H_