blob: 7b9c7ec2aa19247745fca30a55f25d5e47acc542 [file] [log] [blame]
// 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/error.h"
#include "shill/logging.h"
#include "shill/pending_activation_store.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 {
const char kPhoneNumber[] = "#777";
const char kPropertyConnectNumber[] = "number";
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),
weak_cdma_ptr_factory_(this),
activation_state_(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED),
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): Update PRL for activation over cellular.
// See crbug.com/197330.
}
void CellularCapabilityUniversalCDMA::InitProxies() {
SLOG(Cellular, 2) << __func__;
modem_cdma_proxy_.reset(
proxy_factory()->CreateMM1ModemModemCdmaProxy(cellular()->dbus_path(),
cellular()->dbus_owner()));
modem_cdma_proxy_->set_activation_state_callback(
Bind(&CellularCapabilityUniversalCDMA::OnActivationStateChangedSignal,
weak_cdma_ptr_factory_.GetWeakPtr()));
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) {
// Currently activation over the cellular network is not supported using
// ModemManager-next. Service activation is currently carried through over
// non-cellular networks and only the final step of the OTA activation
// procedure ("automatic activation") is performed by this class.
OnUnsupportedOperation(__func__, error);
}
void CellularCapabilityUniversalCDMA::CompleteActivation(Error *error) {
SLOG(Cellular, 2) << __func__;
if (cellular()->state() < Cellular::kStateEnabled) {
Error::PopulateAndLog(error, Error::kInvalidArguments,
"Unable to activate in state " +
Cellular::GetStateString(cellular()->state()));
return;
}
ActivateAutomatic();
}
void CellularCapabilityUniversalCDMA::ActivateAutomatic() {
if (activation_code_.empty()) {
SLOG(Cellular, 2) << "OTA activation cannot be run in the presence of no "
<< "activation code.";
return;
}
PendingActivationStore::State state =
modem_info()->pending_activation_store()->GetActivationState(
PendingActivationStore::kIdentifierMEID, meid());
if (state == PendingActivationStore::kStatePending) {
SLOG(Cellular, 2) << "There's already a pending activation. Ignoring.";
return;
}
if (state == PendingActivationStore::kStateActivated) {
SLOG(Cellular, 2) << "A call to OTA activation has already completed "
<< "successfully. Ignoring.";
return;
}
// Mark as pending activation, so that shill can recover if anything fails
// during OTA activation.
modem_info()->pending_activation_store()->SetActivationState(
PendingActivationStore::kIdentifierMEID,
meid(),
PendingActivationStore::kStatePending);
// Initiate OTA activation.
ResultCallback activation_callback =
Bind(&CellularCapabilityUniversalCDMA::OnActivateReply,
weak_cdma_ptr_factory_.GetWeakPtr(),
ResultCallback());
Error error;
modem_cdma_proxy_->Activate(
activation_code_, &error, activation_callback, kTimeoutActivate);
}
void CellularCapabilityUniversalCDMA::UpdatePendingActivationState() {
SLOG(Cellular, 2) << __func__;
if (IsActivated()) {
SLOG(Cellular, 3) << "CDMA service activated. Clear store.";
modem_info()->pending_activation_store()->RemoveEntry(
PendingActivationStore::kIdentifierMEID, meid());
return;
}
PendingActivationStore::State state =
modem_info()->pending_activation_store()->GetActivationState(
PendingActivationStore::kIdentifierMEID, meid());
if (IsActivating() && state != PendingActivationStore::kStateFailureRetry) {
SLOG(Cellular, 3) << "OTA activation in progress. Nothing to do.";
return;
}
switch (state) {
case PendingActivationStore::kStateFailureRetry:
SLOG(Cellular, 3) << "OTA activation failed. Scheduling a retry.";
cellular()->dispatcher()->PostTask(
Bind(&CellularCapabilityUniversalCDMA::ActivateAutomatic,
weak_cdma_ptr_factory_.GetWeakPtr()));
break;
case PendingActivationStore::kStateActivated:
SLOG(Cellular, 3) << "OTA Activation has completed successfully. "
<< "Waiting for activation state update to finalize.";
break;
default:
break;
}
}
bool CellularCapabilityUniversalCDMA::IsServiceActivationRequired() const {
// If there is no online payment portal information, it's safer to assume
// the service does not require activation.
if (!modem_info()->cellular_operator_info())
return false;
const CellularService::OLP *olp =
modem_info()->cellular_operator_info()->GetOLPBySID(UintToString(sid_));
if (!olp)
return false;
// We could also use the MDN to determine whether or not the service is
// activated, however, the CDMA ActivatonState property is a more absolute
// and fine-grained indicator of activation status.
return (activation_state_ == MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED);
}
bool CellularCapabilityUniversalCDMA::IsActivated() const {
return (activation_state_ == MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED);
}
void CellularCapabilityUniversalCDMA::OnServiceCreated() {
SLOG(Cellular, 2) << __func__;
// TODO (armansito): Set storage identifier here based on the superclass
// implementation.
UpdateServiceActivationStateProperty();
UpdateServingOperator();
HandleNewActivationStatus(MM_CDMA_ACTIVATION_ERROR_NONE);
UpdatePendingActivationState();
UpdateOLP();
}
void CellularCapabilityUniversalCDMA::UpdateServiceActivationStateProperty() {
bool activation_required = IsServiceActivationRequired();
cellular()->service()->SetActivateOverNonCellularNetwork(activation_required);
string activation_state;
if (IsActivating())
activation_state = flimflam::kActivationStateActivating;
else if (activation_required)
activation_state = flimflam::kActivationStateNotActivated;
else
activation_state = flimflam::kActivationStateActivated;
cellular()->service()->SetActivationState(activation_state);
}
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);
if (cellular()->service().get())
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());
activation_code_ = provider->activation_code();
// 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::OnActivationStateChangedSignal(
uint32 activation_state,
uint32 activation_error,
const DBusPropertiesMap &status_changes) {
SLOG(Cellular, 2) << __func__;
activation_state_ =
static_cast<MMModemCdmaActivationState>(activation_state);
string value;
if (DBusProperties::GetString(status_changes, "mdn", &value))
set_mdn(value);
if (DBusProperties::GetString(status_changes, "min", &value))
set_min(value);
SLOG(Cellular, 2) << "Activation state: "
<< GetActivationStateString(activation_state_);
HandleNewActivationStatus(activation_error);
UpdatePendingActivationState();
}
void CellularCapabilityUniversalCDMA::OnActivateReply(
const ResultCallback &callback,
const Error &error) {
SLOG(Cellular, 2) << __func__;
if (error.IsSuccess()) {
LOG(INFO) << "Activation completed successfully.";
modem_info()->pending_activation_store()->SetActivationState(
PendingActivationStore::kIdentifierMEID,
meid(),
PendingActivationStore::kStateActivated);
} else {
LOG(ERROR) << "Activation failed with error: " << error;
modem_info()->pending_activation_store()->SetActivationState(
PendingActivationStore::kIdentifierMEID,
meid(),
PendingActivationStore::kStateFailureRetry);
}
UpdatePendingActivationState();
// CellularCapabilityUniversalCDMA::ActivateAutomatic passes a dummy
// ResultCallback when it calls Activate on the proxy object, in which case
// |callback.is_null()| will return true.
if (!callback.is_null())
callback.Run(error);
}
void CellularCapabilityUniversalCDMA::HandleNewActivationStatus(uint32 error) {
SLOG(Cellular, 2) << __func__ << "(" << error << ")";
if (!cellular()->service().get()) {
LOG(ERROR) << "In " << __func__ << "(): service is null.";
return;
}
SLOG(Cellular, 2) << "Activation State: " << activation_state_;
cellular()->service()->SetActivationState(
GetActivationStateString(activation_state_));
cellular()->service()->set_error(GetActivationErrorString(error));
UpdateOLP();
}
// static
string CellularCapabilityUniversalCDMA::GetActivationStateString(
uint32 state) {
switch (state) {
case MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED:
return flimflam::kActivationStateActivated;
case MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING:
return flimflam::kActivationStateActivating;
case MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED:
return flimflam::kActivationStateNotActivated;
case MM_MODEM_CDMA_ACTIVATION_STATE_PARTIALLY_ACTIVATED:
return flimflam::kActivationStatePartiallyActivated;
default:
return flimflam::kActivationStateUnknown;
}
}
// static
string CellularCapabilityUniversalCDMA::GetActivationErrorString(
uint32 error) {
switch (error) {
case MM_CDMA_ACTIVATION_ERROR_WRONG_RADIO_INTERFACE:
return flimflam::kErrorNeedEvdo;
case MM_CDMA_ACTIVATION_ERROR_ROAMING:
return flimflam::kErrorNeedHomeNetwork;
case MM_CDMA_ACTIVATION_ERROR_COULD_NOT_CONNECT:
case MM_CDMA_ACTIVATION_ERROR_SECURITY_AUTHENTICATION_FAILED:
case MM_CDMA_ACTIVATION_ERROR_PROVISIONING_FAILED:
return flimflam::kErrorOtaspFailed;
case MM_CDMA_ACTIVATION_ERROR_NONE:
return "";
case MM_CDMA_ACTIVATION_ERROR_NO_SIGNAL:
default:
return flimflam::kErrorActivationFailed;
}
}
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::IsActivating() const {
PendingActivationStore::State state =
modem_info()->pending_activation_store()->GetActivationState(
PendingActivationStore::kIdentifierMEID, meid());
return (state == PendingActivationStore::kStatePending) ||
(state == PendingActivationStore::kStateFailureRetry) ||
(activation_state_ == MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING);
}
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::SetupConnectProperties(
DBusPropertiesMap *properties) {
(*properties)[kPropertyConnectNumber].writer().append_string(
kPhoneNumber);
}
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 (DBusProperties::GetUint32(
properties,
MM_MODEM_MODEMCDMA_PROPERTY_ACTIVATIONSTATE,
&uint_value)) {
activation_state_ = static_cast<MMModemCdmaActivationState>(uint_value);
HandleNewActivationStatus(MM_CDMA_ACTIVATION_ERROR_NONE);
}
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