shill: Implement CellularCapabilityUniversalCDMA.

This CL implements the logic necessary to connect to a CDMA network
while running modemmanager-next. This code currently works only on
Gobi3k modems with Chromebooks running modemmanager-next with the QMI
plugin. This CL does not implement activation logic.

BUG=chromium:219821
TEST=1. Build and run unit tests.
     2. On a Chromebook with a Gobi3k modem which has been activated on
        a CDMA network (currently only Verizon Wireless):
        1. Build and install ChromeOS with USE="qmi -gobi",
        2. Make sure that the modem is running a Verizon firmware.
        3. On the Settings page, clicking on Mobile Data should reveal
           Verizon Wireless as the current cellular network.
        4. Make sure that we can connect/disconnect to Verizon Wireless.
           All actions that do not require network activation should be
           working.

Change-Id: I55f3bf9640e2d88615280591c027043a05c10bf5
Reviewed-on: https://gerrit.chromium.org/gerrit/41797
Reviewed-by: Arman Uguray <armansito@chromium.org>
Tested-by: Arman Uguray <armansito@chromium.org>
Commit-Queue: Arman Uguray <armansito@chromium.org>
diff --git a/cellular_capability_universal_cdma.cc b/cellular_capability_universal_cdma.cc
new file mode 100644
index 0000000..69e9bf1
--- /dev/null
+++ b/cellular_capability_universal_cdma.cc
@@ -0,0 +1,364 @@
+// Copyright (c) 2013 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/cellular_capability_universal_cdma.h"
+
+#include <chromeos/dbus/service_constants.h>
+#include <base/stringprintf.h>
+#include <base/string_number_conversions.h>
+#include <base/string_util.h>
+
+#include "shill/cellular_operator_info.h"
+#include "shill/dbus_properties_proxy_interface.h"
+#include "shill/logging.h"
+#include "shill/proxy_factory.h"
+
+#ifdef MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN
+#error "Do not include mm-modem.h"
+#endif
+
+using base::UintToString;
+
+using std::string;
+using std::vector;
+
+namespace shill {
+
+namespace {
+
+string FormattedSID(const string &sid) {
+  return "[SID=" + sid + "]";
+}
+
+}  // namespace
+
+// static
+unsigned int
+CellularCapabilityUniversalCDMA::friendly_service_name_id_cdma_ = 0;
+
+CellularCapabilityUniversalCDMA::CellularCapabilityUniversalCDMA(
+    Cellular *cellular,
+    ProxyFactory *proxy_factory,
+    ModemInfo *modem_info)
+    : CellularCapabilityUniversal(cellular,
+                                  proxy_factory,
+                                  modem_info),
+      cdma_1x_registration_state_(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN),
+      cdma_evdo_registration_state_(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN),
+      nid_(0),
+      sid_(0) {
+  SLOG(Cellular, 2) << "Cellular capability constructed: Universal CDMA";
+  // TODO(armansito): Need to expose PRL version here to carry through
+  // activation. PRL version is not obtainable from ModemManager and will
+  // need to be stored in a special place (such as CellularOperatorInfo).
+  // See crbug.com/197330.
+}
+
+void CellularCapabilityUniversalCDMA::InitProxies() {
+  SLOG(Cellular, 2) << __func__;
+  modem_cdma_proxy_.reset(
+      proxy_factory()->CreateMM1ModemModemCdmaProxy(cellular()->dbus_path(),
+                                                    cellular()->dbus_owner()));
+  CellularCapabilityUniversal::InitProxies();
+}
+
+void CellularCapabilityUniversalCDMA::ReleaseProxies() {
+  SLOG(Cellular, 2) << __func__;
+  modem_cdma_proxy_.reset();
+  CellularCapabilityUniversal::ReleaseProxies();
+}
+
+void CellularCapabilityUniversalCDMA::Activate(
+    const string &carrier,
+    Error *error,
+    const ResultCallback &callback) {
+  // TODO(armansito): Implement activation.
+  OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapabilityUniversalCDMA::CompleteActivation(Error *error) {
+  // TODO(armansito): Implement activation.
+  OnUnsupportedOperation(__func__, error);
+}
+
+void CellularCapabilityUniversalCDMA::DisconnectCleanup() {
+  SLOG(Cellular, 2) << __func__;
+  // TODO(armansito): Handle activation logic here.
+}
+
+void CellularCapabilityUniversalCDMA::OnServiceCreated() {
+  SLOG(Cellular, 2) << __func__;
+  // TODO (armansito): Set storage identifier here  based on the superclass
+  // implementation.
+  bool activation_required = IsServiceActivationRequired();
+  cellular()->service()->SetActivationState(
+      activation_required ?
+      flimflam::kActivationStateNotActivated :
+      flimflam::kActivationStateActivated);
+  cellular()->service()->SetActivateOverNonCellularNetwork(activation_required);
+  UpdateServingOperator();
+  UpdateOLP();
+}
+
+void CellularCapabilityUniversalCDMA::UpdateOLP() {
+  SLOG(Cellular,2) << __func__;
+  if (!modem_info()->cellular_operator_info())
+    return;
+
+  const CellularService::OLP *result =
+      modem_info()->cellular_operator_info()->GetOLPBySID(
+          UintToString(sid_));
+  if (!result)
+    return;
+
+  CellularService::OLP olp;
+  olp.CopyFrom(*result);
+  string post_data = olp.GetPostData();
+  ReplaceSubstringsAfterOffset(&post_data, 0, "${esn}", esn());
+  ReplaceSubstringsAfterOffset(&post_data, 0, "${mdn}", mdn());
+  ReplaceSubstringsAfterOffset(&post_data, 0, "${meid}", meid());
+  olp.SetPostData(post_data);
+  cellular()->service()->SetOLP(olp);
+}
+
+void CellularCapabilityUniversalCDMA::GetProperties() {
+  SLOG(Cellular, 2) << __func__;
+  CellularCapabilityUniversal::GetProperties();
+
+  scoped_ptr<DBusPropertiesProxyInterface> properties_proxy(
+      proxy_factory()->CreateDBusPropertiesProxy(cellular()->dbus_path(),
+                                                 cellular()->dbus_owner()));
+  DBusPropertiesMap properties(
+      properties_proxy->GetAll(MM_DBUS_INTERFACE_MODEM_MODEMCDMA));
+  OnModemCDMAPropertiesChanged(properties, vector<string>());
+}
+
+string CellularCapabilityUniversalCDMA::CreateFriendlyServiceName() {
+  SLOG(Cellular, 2) << __func__ << ": " << GetRoamingStateString();
+
+  if (provider_.GetCode().empty()) {
+    UpdateOperatorInfo();
+  }
+
+  string name = provider_.GetName();
+  if (!name.empty()) {
+    // TODO(armansito): We may need to show the provider name in a
+    // specific way if roaming.
+    return name;
+  }
+
+  string code = provider_.GetCode();
+  if (!code.empty()) {
+    return "cellular_sid_" + code;
+  }
+
+  return base::StringPrintf("CDMANetwork%u", friendly_service_name_id_cdma_++);
+}
+
+void CellularCapabilityUniversalCDMA::UpdateOperatorInfo() {
+  SLOG(Cellular, 2) << __func__;
+
+  if (sid_ == 0 || !modem_info()->cellular_operator_info()) {
+    SLOG(Cellular, 2) << "No provider is currently available.";
+    provider_.SetCode("");
+    return;
+  }
+
+  string sid = UintToString(sid_);
+  const CellularOperatorInfo::CellularOperator *provider =
+      modem_info()->cellular_operator_info()->GetCellularOperatorBySID(sid);
+  if (!provider) {
+    SLOG(Cellular, 2) << "CDMA provider with "
+                      << FormattedSID(sid)
+                      << " not found.";
+    return;
+  }
+
+  if (!provider->name_list().empty()) {
+    provider_.SetName(provider->name_list()[0].name);
+  }
+  provider_.SetCode(sid);
+  provider_.SetCountry(provider->country());
+
+  // TODO(armansito): The CDMA interface only returns information about the
+  // current serving carrier, so for now both the home provider and the
+  // serving operator will be the same in case of roaming. We should figure
+  // out if there is a way to (and whether or not it is necessary to)
+  // determine if we're roaming.
+  cellular()->set_home_provider(provider_);
+
+  UpdateServingOperator();
+}
+
+void CellularCapabilityUniversalCDMA::UpdateServingOperator() {
+  SLOG(Cellular, 2) << __func__;
+  if (cellular()->service().get()) {
+    cellular()->service()->SetServingOperator(cellular()->home_provider());
+  }
+}
+
+void CellularCapabilityUniversalCDMA::Register(const ResultCallback &callback) {
+  // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+void CellularCapabilityUniversalCDMA::RegisterOnNetwork(
+    const string &network_id,
+    Error *error,
+    const ResultCallback &callback) {
+  // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+bool CellularCapabilityUniversalCDMA::IsRegistered() {
+  return (cdma_1x_registration_state_ ==
+              MM_MODEM_CDMA_REGISTRATION_STATE_HOME ||
+          cdma_1x_registration_state_ ==
+              MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING ||
+          cdma_evdo_registration_state_ ==
+              MM_MODEM_CDMA_REGISTRATION_STATE_HOME ||
+          cdma_evdo_registration_state_ ==
+              MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING);
+}
+
+void CellularCapabilityUniversalCDMA::SetUnregistered(bool /*searching*/) {
+  cdma_1x_registration_state_ = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+  cdma_evdo_registration_state_ = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+}
+
+void CellularCapabilityUniversalCDMA::RequirePIN(
+    const string &pin, bool require,
+    Error *error, const ResultCallback &callback) {
+  // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+void CellularCapabilityUniversalCDMA::EnterPIN(
+    const string &pin,
+    Error *error,
+    const ResultCallback &callback) {
+  // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+void CellularCapabilityUniversalCDMA::UnblockPIN(
+    const string &unblock_code,
+    const string &pin,
+    Error *error,
+    const ResultCallback &callback) {
+  // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+void CellularCapabilityUniversalCDMA::ChangePIN(
+    const string &old_pin, const string &new_pin,
+    Error *error, const ResultCallback &callback) {
+  // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+void CellularCapabilityUniversalCDMA::Scan(Error *error,
+                                           const ResultCallback &callback) {
+  // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+void CellularCapabilityUniversalCDMA::OnSimPathChanged(
+    const string &sim_path) {
+  // TODO(armansito): Remove once 3GPP is implemented in its own class.
+}
+
+string CellularCapabilityUniversalCDMA::GetRoamingStateString() const {
+  uint32 state = cdma_evdo_registration_state_;
+  if (state == MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) {
+    state = cdma_1x_registration_state_;
+  }
+  switch (state) {
+    case MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN:
+    case MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED:
+      break;
+    case MM_MODEM_CDMA_REGISTRATION_STATE_HOME:
+      return flimflam::kRoamingStateHome;
+    case MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING:
+      return flimflam::kRoamingStateRoaming;
+    default:
+      NOTREACHED();
+  }
+  return flimflam::kRoamingStateUnknown;
+}
+
+void CellularCapabilityUniversalCDMA::OnDBusPropertiesChanged(
+    const string &interface,
+    const DBusPropertiesMap &changed_properties,
+    const vector<string> &invalidated_properties) {
+  SLOG(Cellular, 2) << __func__ << "(" << interface << ")";
+  if (interface == MM_DBUS_INTERFACE_MODEM_MODEMCDMA) {
+    OnModemCDMAPropertiesChanged(changed_properties, invalidated_properties);
+  } else {
+    CellularCapabilityUniversal::OnDBusPropertiesChanged(
+        interface, changed_properties, invalidated_properties);
+  }
+}
+
+void CellularCapabilityUniversalCDMA::OnModemCDMAPropertiesChanged(
+    const DBusPropertiesMap &properties,
+    const std::vector<std::string> &/*invalidated_properties*/) {
+  SLOG(Cellular, 2) << __func__;
+  string str_value;
+  if (DBusProperties::GetString(properties,
+                                MM_MODEM_MODEMCDMA_PROPERTY_MEID,
+                                &str_value))
+    set_meid(str_value);
+  if (DBusProperties::GetString(properties,
+                                MM_MODEM_MODEMCDMA_PROPERTY_ESN,
+                                &str_value))
+    set_esn(str_value);
+
+  uint32_t sid = sid_;
+  uint32_t nid = nid_;
+  MMModemCdmaRegistrationState state_1x = cdma_1x_registration_state_;
+  MMModemCdmaRegistrationState state_evdo = cdma_evdo_registration_state_;
+  bool registration_changed = false;
+  uint32_t uint_value;
+  if (DBusProperties::GetUint32(
+      properties,
+      MM_MODEM_MODEMCDMA_PROPERTY_CDMA1XREGISTRATIONSTATE,
+      &uint_value)) {
+    state_1x = static_cast<MMModemCdmaRegistrationState>(uint_value);
+    registration_changed = true;
+  }
+  if (DBusProperties::GetUint32(
+      properties,
+      MM_MODEM_MODEMCDMA_PROPERTY_EVDOREGISTRATIONSTATE,
+      &uint_value)) {
+    state_evdo = static_cast<MMModemCdmaRegistrationState>(uint_value);
+    registration_changed = true;
+  }
+  if (DBusProperties::GetUint32(
+      properties,
+      MM_MODEM_MODEMCDMA_PROPERTY_SID,
+      &uint_value)) {
+    sid = uint_value;
+    registration_changed = true;
+  }
+  if (DBusProperties::GetUint32(
+      properties,
+      MM_MODEM_MODEMCDMA_PROPERTY_NID,
+      &uint_value)) {
+    nid = uint_value;
+    registration_changed = true;
+  }
+  if (registration_changed)
+    OnCDMARegistrationChanged(state_1x, state_evdo, sid, nid);
+}
+
+void CellularCapabilityUniversalCDMA::OnCDMARegistrationChanged(
+      MMModemCdmaRegistrationState state_1x,
+      MMModemCdmaRegistrationState state_evdo,
+      uint32_t sid, uint32_t nid) {
+  SLOG(Cellular, 2) << __func__
+                    << ": state_1x=" << state_1x
+                    << ", state_evdo=" << state_evdo;
+  cdma_1x_registration_state_ = state_1x;
+  cdma_evdo_registration_state_ = state_evdo;
+  sid_ = sid;
+  nid_ = nid;
+  UpdateOperatorInfo();
+  cellular()->HandleNewRegistrationState();
+}
+
+}  // namespace shill