shill: cellular: Implement CDMA activation over non-cellular.
Added OTASP activation logic over a non-cellular network to
CellularCapabilityUniversalCDMA.
BUG=chromium:221698,chromium:217324
TEST=1. Build and run unit tests.
2. - Acquire an unactivated Gobi3k (this can be achieved on an
activated modem by cancelling the account and factory
resetting the modem).
- Reimage the Chromebook (with the above Gobi3k) with an image
which was compiled with 'USE="-gobi qmi"'.
- If the carrier is not already set to Verizon Wireless, switch
firmware to the Verizon firmware.
- Verify that the modem supported by ModemManager and not cromo. The
modem properties should be exposed and the CDMA
ActivationState property should be 'not-activated'. All of this can
be verified using mmcli.
- The Chrome OS UI should display the cellular network as 'not
activated'. Use the UI to activate the service.
- Once payment is done, service should be marked as
"Activating". /var/cache/shill/activating_iccid_store.profile
should have a new entry under "[meid_list]" set to "1".
- Monitor /var/log/net.log (with a debug scope of at least
"cellular" and level "-3"). Cellular activation related
messages should be logged. ModemManager should be in the
process of "automatic activation" and logging appropriate
updates.
- The modem should eventually reset. Afterwards the UI should
pop-up a "3G network activated" dialog.
Change-Id: Iff9d1b88012dad1d3552af12d9ae7f510a988a54
Reviewed-on: https://gerrit.chromium.org/gerrit/49696
Reviewed-by: Arman Uguray <armansito@chromium.org>
Commit-Queue: Arman Uguray <armansito@chromium.org>
Tested-by: Arman Uguray <armansito@chromium.org>
diff --git a/cellular_capability_universal_cdma.cc b/cellular_capability_universal_cdma.cc
index 69e9bf1..8fb4d2f 100644
--- a/cellular_capability_universal_cdma.cc
+++ b/cellular_capability_universal_cdma.cc
@@ -11,7 +11,9 @@
#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
@@ -44,14 +46,14 @@
: 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): 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).
+ // TODO(armansito): Update PRL for activation over cellular.
// See crbug.com/197330.
}
@@ -60,6 +62,9 @@
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();
}
@@ -73,34 +78,137 @@
const string &carrier,
Error *error,
const ResultCallback &callback) {
- // TODO(armansito): Implement activation.
+ // 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) {
- // TODO(armansito): Implement activation.
- OnUnsupportedOperation(__func__, 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::DisconnectCleanup() {
+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());
+ // TODO(armansito): Read the activation code from CellularOperatorInfo
+ Error error;
+ modem_cdma_proxy_->Activate(
+ activation_code_, &error, activation_callback, kTimeoutActivate);
+}
+
+void CellularCapabilityUniversalCDMA::UpdatePendingActivationState() {
SLOG(Cellular, 2) << __func__;
- // TODO(armansito): Handle activation logic here.
+ 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.
- bool activation_required = IsServiceActivationRequired();
- cellular()->service()->SetActivationState(
- activation_required ?
- flimflam::kActivationStateNotActivated :
- flimflam::kActivationStateActivated);
- cellular()->service()->SetActivateOverNonCellularNetwork(activation_required);
+ 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())
@@ -119,7 +227,8 @@
ReplaceSubstringsAfterOffset(&post_data, 0, "${mdn}", mdn());
ReplaceSubstringsAfterOffset(&post_data, 0, "${meid}", meid());
olp.SetPostData(post_data);
- cellular()->service()->SetOLP(olp);
+ if (cellular()->service().get())
+ cellular()->service()->SetOLP(olp);
}
void CellularCapabilityUniversalCDMA::GetProperties() {
@@ -181,6 +290,8 @@
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
@@ -198,6 +309,99 @@
}
}
+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();
+ 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.
}
@@ -209,6 +413,15 @@
// 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 ||
@@ -342,6 +555,13 @@
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);
}