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.h b/cellular.h
index c36a4b4..bf55017 100644
--- a/cellular.h
+++ b/cellular.h
@@ -246,7 +246,8 @@
   FRIEND_TEST(CellularCapabilityUniversalMainTest, SetHomeProvider);
   FRIEND_TEST(CellularCapabilityUniversalMainTest, StartModemAlreadyEnabled);
   FRIEND_TEST(CellularCapabilityUniversalMainTest, StopModemConnected);
-  FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateIccidActivationState);
+  FRIEND_TEST(CellularCapabilityUniversalMainTest,
+              UpdatePendingActivationState);
   FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateOLP);
   FRIEND_TEST(CellularCapabilityUniversalMainTest,
               UpdateOperatorInfoViaOperatorId);
diff --git a/cellular_capability.cc b/cellular_capability.cc
index 641ddd5..2e5631b 100644
--- a/cellular_capability.cc
+++ b/cellular_capability.cc
@@ -20,7 +20,7 @@
 const char CellularCapability::kModemPropertyIMSI[] = "imsi";
 const char CellularCapability::kModemPropertyState[] = "State";
 // All timeout values are in milliseconds
-const int CellularCapability::kTimeoutActivate = 120000;
+const int CellularCapability::kTimeoutActivate = 300000;
 const int CellularCapability::kTimeoutConnect = 45000;
 const int CellularCapability::kTimeoutDefault = 5000;
 const int CellularCapability::kTimeoutDisconnect = 45000;
diff --git a/cellular_capability_universal.cc b/cellular_capability_universal.cc
index 8581c12..0625195 100644
--- a/cellular_capability_universal.cc
+++ b/cellular_capability_universal.cc
@@ -518,10 +518,10 @@
   }
   reset_done_ = true;
   activation_wait_for_registration_callback_.Cancel();
-  UpdateIccidActivationState();
+  UpdatePendingActivationState();
 }
 
-void CellularCapabilityUniversal::UpdateIccidActivationState() {
+void CellularCapabilityUniversal::UpdatePendingActivationState() {
   SLOG(Cellular, 2) << __func__;
 
   bool registered =
@@ -775,7 +775,7 @@
   if (!callback.is_null())
     callback.Run(error);
 
-  UpdateIccidActivationState();
+  UpdatePendingActivationState();
 }
 
 bool CellularCapabilityUniversal::AllowRoaming() {
@@ -1614,7 +1614,7 @@
 void CellularCapabilityUniversal::OnMdnChanged(
     const string &mdn) {
   mdn_ = NormalizeMdn(mdn);
-  UpdateIccidActivationState();
+  UpdatePendingActivationState();
 }
 
 void CellularCapabilityUniversal::OnModemManufacturerChanged(
@@ -1791,7 +1791,7 @@
 
   // If the modem registered with the network and the current ICCID is pending
   // activation, then reset the modem.
-  UpdateIccidActivationState();
+  UpdatePendingActivationState();
 }
 
 void CellularCapabilityUniversal::OnModemStateChangedSignal(
@@ -1853,7 +1853,7 @@
 
 void CellularCapabilityUniversal::OnSimIdentifierChanged(const string &id) {
   sim_identifier_ = id;
-  UpdateIccidActivationState();
+  UpdatePendingActivationState();
 }
 
 void CellularCapabilityUniversal::OnOperatorIdChanged(
diff --git a/cellular_capability_universal.h b/cellular_capability_universal.h
index 9c85fc4..19dd09d 100644
--- a/cellular_capability_universal.h
+++ b/cellular_capability_universal.h
@@ -125,7 +125,14 @@
   // provider.
   virtual void UpdateOLP();
 
-  const std::string &mdn() const { return mdn_; };
+  // Post-payment activation handlers.
+  virtual void UpdatePendingActivationState();
+
+  const std::string &mdn() const { return mdn_; }
+  void set_mdn(const std::string &mdn) { mdn_ = mdn; }
+
+  const std::string &min() const { return min_; }
+  void set_min(const std::string &min) { min_ = min; }
 
   const std::string &meid() const { return meid_; }
   void set_meid(const std::string &meid) { meid_ = meid; }
@@ -163,8 +170,8 @@
   friend class CellularCapabilityTest;
   friend class CellularCapabilityUniversalTest;
   friend class CellularCapabilityUniversalCDMATest;
-  FRIEND_TEST(CellularCapabilityUniversalCDMATest, PropertiesChanged);
-  FRIEND_TEST(CellularCapabilityUniversalCDMATest, UpdateOLP);
+  FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, PropertiesChanged);
+  FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, UpdateOLP);
   FRIEND_TEST(CellularCapabilityUniversalMainTest, AllowRoaming);
   FRIEND_TEST(CellularCapabilityUniversalMainTest,
               ActivationWaitForRegisterTimeout);
@@ -195,7 +202,8 @@
   FRIEND_TEST(CellularCapabilityUniversalMainTest, StartModem);
   FRIEND_TEST(CellularCapabilityUniversalMainTest, StopModem);
   FRIEND_TEST(CellularCapabilityUniversalMainTest, StopModemConnected);
-  FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateIccidActivationState);
+  FRIEND_TEST(CellularCapabilityUniversalMainTest,
+              UpdatePendingActivationState);
   FRIEND_TEST(CellularCapabilityUniversalMainTest,
               UpdateServiceActivationState);
   FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateServiceName);
@@ -358,7 +366,6 @@
   static std::string GenerateNewGenericServiceName();
 
   // Post-payment activation handlers.
-  void UpdateIccidActivationState();
   void ResetAfterActivation();
   void UpdateServiceActivationState();
   void OnResetAfterActivationReply(const Error &error);
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);
 }
diff --git a/cellular_capability_universal_cdma.h b/cellular_capability_universal_cdma.h
index c794b69..a3d4e95 100644
--- a/cellular_capability_universal_cdma.h
+++ b/cellular_capability_universal_cdma.h
@@ -9,6 +9,8 @@
 #include "shill/cellular.h"
 #include "shill/mm1_modem_modemcdma_proxy_interface.h"
 
+#include <base/memory/weak_ptr.h>
+
 #include <string>
 #include <vector>
 
@@ -23,15 +25,19 @@
       ProxyFactory *proxy_factory,
       ModemInfo *modem_info);
 
+  // Returns true if the service is activated.
+  bool IsActivated() const;
+
   // Inherited from CellularCapability.
   virtual void Activate(const std::string &carrier,
                         Error *error, const ResultCallback &callback);
   virtual void CompleteActivation(Error *error);
   virtual std::string CreateFriendlyServiceName();
-  virtual void DisconnectCleanup();
   virtual void GetProperties();
   virtual std::string GetRoamingStateString() const;
+  virtual bool IsActivating() const;
   virtual bool IsRegistered();
+  virtual bool IsServiceActivationRequired() const;
   virtual void OnDBusPropertiesChanged(
       const std::string &interface,
       const DBusPropertiesMap &changed_properties,
@@ -63,27 +69,62 @@
 
   virtual void UpdateOLP();
 
+  // Post-payment activation handlers.
+  virtual void UpdatePendingActivationState();
+
  private:
   friend class CellularCapabilityUniversalCDMATest;
-  FRIEND_TEST(CellularCapabilityUniversalCDMATest, CreateFriendlyServiceName);
-  FRIEND_TEST(CellularCapabilityUniversalCDMATest, OnCDMARegistrationChanged);
-  FRIEND_TEST(CellularCapabilityUniversalCDMATest, PropertiesChanged);
-  FRIEND_TEST(CellularCapabilityUniversalCDMATest, UpdateOLP);
-  FRIEND_TEST(CellularCapabilityUniversalCDMATest, UpdateOperatorInfo);
+  FRIEND_TEST(CellularCapabilityUniversalCDMADispatcherTest,
+              UpdatePendingActivationState);
+  FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, ActivateAutomatic);
+  FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest,
+              CreateFriendlyServiceName);
+  FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, IsActivating);
+  FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest,
+              IsServiceActivationRequired);
+  FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest,
+              OnCDMARegistrationChanged);
+  FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, PropertiesChanged);
+  FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, UpdateOLP);
+  FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, UpdateOperatorInfo);
+  FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest,
+              UpdateServiceActivationStateProperty);
 
   // CDMA property change handlers
   virtual void OnModemCDMAPropertiesChanged(
       const DBusPropertiesMap &properties,
       const std::vector<std::string> &invalidated_properties);
-  void OnCDMARegistrationChanged(
-      MMModemCdmaRegistrationState state_1x,
-      MMModemCdmaRegistrationState state_evdo,
-      uint32_t sid, uint32_t nid);
+  void OnCDMARegistrationChanged(MMModemCdmaRegistrationState state_1x,
+                                 MMModemCdmaRegistrationState state_evdo,
+                                 uint32_t sid, uint32_t nid);
+
+  // CDMA activation handlers
+  void ActivateAutomatic();
+  void OnActivationStateChangedSignal(uint32 activation_state,
+                                      uint32 activation_error,
+                                      const DBusPropertiesMap &status_changes);
+  void OnActivateReply(const ResultCallback &callback,
+                       const Error &error);
+  void HandleNewActivationStatus(uint32 error);
+
+  void UpdateServiceActivationStateProperty();
+
+  static std::string GetActivationStateString(uint32 state);
+  static std::string GetActivationErrorString(uint32 error);
 
   void UpdateOperatorInfo();
   void UpdateServingOperator();
 
   scoped_ptr<mm1::ModemModemCdmaProxyInterface> modem_cdma_proxy_;
+  // TODO(armansito): Should probably call this |weak_ptr_factory_| after
+  // 3gpp refactor
+  base::WeakPtrFactory<CellularCapabilityUniversalCDMA> weak_cdma_ptr_factory_;
+
+  // CDMA ActivationState property.
+  MMModemCdmaActivationState activation_state_;
+
+  // The activation code needed for OTASP activation.
+  std::string activation_code_;
 
   MMModemCdmaRegistrationState cdma_1x_registration_state_;
   MMModemCdmaRegistrationState cdma_evdo_registration_state_;
diff --git a/cellular_capability_universal_cdma_unittest.cc b/cellular_capability_universal_cdma_unittest.cc
index eae9390..d26ad36 100644
--- a/cellular_capability_universal_cdma_unittest.cc
+++ b/cellular_capability_universal_cdma_unittest.cc
@@ -29,6 +29,7 @@
 #include "shill/mock_mm1_modem_simple_proxy.h"
 #include "shill/mock_mm1_sim_proxy.h"
 #include "shill/mock_modem_info.h"
+#include "shill/mock_pending_activation_store.h"
 #include "shill/nice_mock_control.h"
 #include "shill/proxy_factory.h"
 
@@ -46,10 +47,10 @@
 
 class CellularCapabilityUniversalCDMATest : public testing::Test {
  public:
-  CellularCapabilityUniversalCDMATest()
+  CellularCapabilityUniversalCDMATest(EventDispatcher *dispatcher)
       : capability_(NULL),
         device_adaptor_(NULL),
-        modem_info_(NULL, &dispatcher_, NULL, NULL, NULL),
+        modem_info_(NULL, dispatcher, NULL, NULL, NULL),
         bearer_proxy_(new mm1::MockBearerProxy()),
         modem_3gpp_proxy_(new mm1::MockModemModem3gppProxy()),
         modem_cdma_proxy_(new mm1::MockModemModemCdmaProxy()),
@@ -165,7 +166,6 @@
 
   CellularCapabilityUniversalCDMA *capability_;
   NiceMock<DeviceMockAdaptor> *device_adaptor_;
-  EventDispatcher dispatcher_;
   MockModemInfo modem_info_;
   MockGLib glib_;
   scoped_ptr<mm1::MockBearerProxy> bearer_proxy_;
@@ -189,7 +189,24 @@
 // static
 const char CellularCapabilityUniversalCDMATest::kMeid[] = "11111111111111";
 
-TEST_F(CellularCapabilityUniversalCDMATest, PropertiesChanged) {
+class CellularCapabilityUniversalCDMAMainTest
+    : public CellularCapabilityUniversalCDMATest {
+ public:
+  CellularCapabilityUniversalCDMAMainTest()
+      : CellularCapabilityUniversalCDMATest(&dispatcher_) {}
+
+ private:
+  EventDispatcher dispatcher_;
+};
+
+class CellularCapabilityUniversalCDMADispatcherTest
+    : public CellularCapabilityUniversalCDMATest {
+ public:
+  CellularCapabilityUniversalCDMADispatcherTest()
+      : CellularCapabilityUniversalCDMATest(NULL) {}
+};
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest, PropertiesChanged) {
   // Set up mock modem CDMA properties.
   DBusPropertiesMap modem_cdma_properties;
   modem_cdma_properties[MM_MODEM_MODEMCDMA_PROPERTY_MEID].
@@ -218,7 +235,7 @@
   EXPECT_EQ(kEsn, capability_->esn());
 }
 
-TEST_F(CellularCapabilityUniversalCDMATest, OnCDMARegistrationChanged) {
+TEST_F(CellularCapabilityUniversalCDMAMainTest, OnCDMARegistrationChanged) {
   EXPECT_EQ(0, capability_->sid_);
   EXPECT_EQ(0, capability_->nid_);
   EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
@@ -262,21 +279,24 @@
   EXPECT_EQ("us", capability_->provider_.GetCountry());
 }
 
-TEST_F(CellularCapabilityUniversalCDMATest, UpdateOperatorInfo) {
+TEST_F(CellularCapabilityUniversalCDMAMainTest, UpdateOperatorInfo) {
   EXPECT_EQ("", capability_->provider_.GetCode());
   EXPECT_EQ("", capability_->provider_.GetName());
   EXPECT_EQ("", capability_->provider_.GetCountry());
+  EXPECT_TRUE(capability_->activation_code_.empty());
 
   capability_->UpdateOperatorInfo();
   EXPECT_EQ("", capability_->provider_.GetCode());
   EXPECT_EQ("", capability_->provider_.GetName());
   EXPECT_EQ("", capability_->provider_.GetCountry());
+  EXPECT_TRUE(capability_->activation_code_.empty());
 
 
   capability_->UpdateOperatorInfo();
   EXPECT_EQ("", capability_->provider_.GetCode());
   EXPECT_EQ("", capability_->provider_.GetName());
   EXPECT_EQ("", capability_->provider_.GetCountry());
+  EXPECT_TRUE(capability_->activation_code_.empty());
 
   capability_->sid_ = 1;
   EXPECT_CALL(*modem_info_.mock_cellular_operator_info(),
@@ -287,12 +307,14 @@
   EXPECT_EQ("", capability_->provider_.GetCode());
   EXPECT_EQ("", capability_->provider_.GetName());
   EXPECT_EQ("", capability_->provider_.GetCountry());
+  EXPECT_TRUE(capability_->activation_code_.empty());
 
   CellularOperatorInfo::CellularOperator *provider =
       new CellularOperatorInfo::CellularOperator();
 
   provider->country_ = "us";
   provider->is_primary_ = true;
+  provider->activation_code_ = "1234";
   provider->name_list_.push_back(
       CellularOperatorInfo::LocalizedName("Test", ""));
 
@@ -307,9 +329,10 @@
   EXPECT_EQ("1", capability_->provider_.GetCode());
   EXPECT_EQ("Test", capability_->provider_.GetName());
   EXPECT_EQ("us", capability_->provider_.GetCountry());
+  EXPECT_EQ("1234", capability_->activation_code_);
 }
 
-TEST_F(CellularCapabilityUniversalCDMATest, CreateFriendlyServiceName) {
+TEST_F(CellularCapabilityUniversalCDMAMainTest, CreateFriendlyServiceName) {
   CellularCapabilityUniversalCDMA::friendly_service_name_id_cdma_ = 0;
   EXPECT_EQ(0, capability_->sid_);
   EXPECT_EQ("CDMANetwork0", capability_->CreateFriendlyServiceName());
@@ -334,7 +357,7 @@
   EXPECT_EQ("Test", capability_->CreateFriendlyServiceName());
 }
 
-TEST_F(CellularCapabilityUniversalCDMATest, UpdateOLP) {
+TEST_F(CellularCapabilityUniversalCDMAMainTest, UpdateOLP) {
   CellularService::OLP test_olp;
   test_olp.SetURL("http://testurl");
   test_olp.SetMethod("POST");
@@ -357,4 +380,194 @@
   EXPECT_EQ("esn=0&mdn=3&meid=4", olp.GetPostData());
 }
 
+TEST_F(CellularCapabilityUniversalCDMAMainTest, ActivateAutomatic) {
+  mm1::MockModemModemCdmaProxy *cdma_proxy = modem_cdma_proxy_.get();
+  SetUp();
+  capability_->InitProxies();
+
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              GetActivationState(_,_))
+      .Times(0);
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              SetActivationState(_,_,_))
+      .Times(0);
+  EXPECT_CALL(*cdma_proxy, Activate(_,_,_,_)).Times(0);
+  capability_->ActivateAutomatic();
+  Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+  Mock::VerifyAndClearExpectations(modem_cdma_proxy_.get());
+
+  capability_->activation_code_ = "1234";
+
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              GetActivationState(PendingActivationStore::kIdentifierMEID, _))
+      .WillOnce(Return(PendingActivationStore::kStatePending))
+      .WillOnce(Return(PendingActivationStore::kStateActivated));
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              SetActivationState(_,_,_))
+      .Times(0);
+  EXPECT_CALL(*cdma_proxy, Activate(_,_,_,_)).Times(0);
+  capability_->ActivateAutomatic();
+  capability_->ActivateAutomatic();
+  Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+  Mock::VerifyAndClearExpectations(modem_cdma_proxy_.get());
+
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              GetActivationState(PendingActivationStore::kIdentifierMEID, _))
+      .WillOnce(Return(PendingActivationStore::kStateUnknown))
+      .WillOnce(Return(PendingActivationStore::kStateFailureRetry));
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              SetActivationState(_,_, PendingActivationStore::kStatePending))
+      .Times(2);
+  EXPECT_CALL(*cdma_proxy, Activate(_,_,_,_)).Times(2);
+  capability_->ActivateAutomatic();
+  capability_->ActivateAutomatic();
+  Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+  Mock::VerifyAndClearExpectations(modem_cdma_proxy_.get());
+}
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest, IsServiceActivationRequired) {
+  CellularService::OLP olp;
+  EXPECT_CALL(*modem_info_.mock_cellular_operator_info(), GetOLPBySID(_))
+      .WillOnce(Return((const CellularService::OLP *)NULL))
+      .WillRepeatedly(Return(&olp));
+  capability_->activation_state_ =
+      MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+  EXPECT_FALSE(capability_->IsServiceActivationRequired());
+  EXPECT_TRUE(capability_->IsServiceActivationRequired());
+  capability_->activation_state_ =
+      MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+  EXPECT_FALSE(capability_->IsServiceActivationRequired());
+  capability_->activation_state_ =
+      MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED;
+  EXPECT_FALSE(capability_->IsServiceActivationRequired());
+}
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest,
+       UpdateServiceActivationStateProperty) {
+  CellularService::OLP olp;
+  EXPECT_CALL(*modem_info_.mock_cellular_operator_info(), GetOLPBySID(_))
+      .WillRepeatedly(Return(&olp));
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              GetActivationState(_,_))
+      .WillOnce(Return(PendingActivationStore::kStatePending))
+      .WillRepeatedly(Return(PendingActivationStore::kStateUnknown));
+
+  capability_->activation_state_ =
+      MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+  EXPECT_CALL(*service_,
+              SetActivationState(flimflam::kActivationStateActivating))
+      .Times(1);
+  capability_->UpdateServiceActivationStateProperty();
+  Mock::VerifyAndClearExpectations(service_);
+
+  EXPECT_CALL(*service_,
+              SetActivationState(flimflam::kActivationStateNotActivated))
+      .Times(1);
+  capability_->UpdateServiceActivationStateProperty();
+  Mock::VerifyAndClearExpectations(service_);
+
+  capability_->activation_state_ =
+      MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+  EXPECT_CALL(*service_,
+              SetActivationState(flimflam::kActivationStateActivating))
+      .Times(1);
+  capability_->UpdateServiceActivationStateProperty();
+  Mock::VerifyAndClearExpectations(service_);
+
+  capability_->activation_state_ =
+      MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED;
+  EXPECT_CALL(*service_,
+              SetActivationState(flimflam::kActivationStateActivated))
+      .Times(1);
+  capability_->UpdateServiceActivationStateProperty();
+  Mock::VerifyAndClearExpectations(service_);
+  Mock::VerifyAndClearExpectations(modem_info_.mock_cellular_operator_info());
+  Mock::VerifyAndClearExpectations(
+      modem_info_.mock_pending_activation_store());
+}
+
+TEST_F(CellularCapabilityUniversalCDMAMainTest, IsActivating) {
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              GetActivationState(_,_))
+      .WillOnce(Return(PendingActivationStore::kStatePending))
+      .WillOnce(Return(PendingActivationStore::kStatePending))
+      .WillOnce(Return(PendingActivationStore::kStateFailureRetry))
+      .WillRepeatedly(Return(PendingActivationStore::kStateUnknown));
+
+  capability_->activation_state_ =
+      MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+  EXPECT_TRUE(capability_->IsActivating());
+  EXPECT_TRUE(capability_->IsActivating());
+  capability_->activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+  EXPECT_TRUE(capability_->IsActivating());
+  EXPECT_TRUE(capability_->IsActivating());
+  capability_->activation_state_ =
+      MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+  EXPECT_FALSE(capability_->IsActivating());
+}
+
+TEST_F(CellularCapabilityUniversalCDMADispatcherTest,
+       UpdatePendingActivationState) {
+  capability_->activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED;
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(), RemoveEntry(_,_))
+      .Times(1);
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              GetActivationState(_,_))
+      .Times(0);
+  EXPECT_CALL(*modem_info_.mock_dispatcher(), PostTask(_)).Times(0);
+  capability_->UpdatePendingActivationState();
+  Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+  Mock::VerifyAndClearExpectations(modem_info_.mock_dispatcher());
+
+  capability_->activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(), RemoveEntry(_,_))
+      .Times(0);
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              GetActivationState(_,_))
+      .Times(2)
+      .WillRepeatedly(Return(PendingActivationStore::kStateUnknown));
+  EXPECT_CALL(*modem_info_.mock_dispatcher(), PostTask(_)).Times(0);
+  capability_->UpdatePendingActivationState();
+  Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+  Mock::VerifyAndClearExpectations(modem_info_.mock_dispatcher());
+
+  capability_->activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED;
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(), RemoveEntry(_,_))
+      .Times(0);
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              GetActivationState(_,_))
+      .Times(2)
+      .WillRepeatedly(Return(PendingActivationStore::kStatePending));
+  EXPECT_CALL(*modem_info_.mock_dispatcher(), PostTask(_)).Times(0);
+  capability_->UpdatePendingActivationState();
+  Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+  Mock::VerifyAndClearExpectations(modem_info_.mock_dispatcher());
+
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(), RemoveEntry(_,_))
+      .Times(0);
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              GetActivationState(_,_))
+      .Times(2)
+      .WillRepeatedly(Return(PendingActivationStore::kStateFailureRetry));
+  EXPECT_CALL(*modem_info_.mock_dispatcher(), PostTask(_)).Times(1);
+  capability_->UpdatePendingActivationState();
+  Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+  Mock::VerifyAndClearExpectations(modem_info_.mock_dispatcher());
+
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(), RemoveEntry(_,_))
+      .Times(0);
+  EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
+              GetActivationState(_,_))
+      .Times(4)
+      .WillOnce(Return(PendingActivationStore::kStateActivated))
+      .WillOnce(Return(PendingActivationStore::kStateActivated))
+      .WillOnce(Return(PendingActivationStore::kStateUnknown))
+      .WillOnce(Return(PendingActivationStore::kStateUnknown));
+  EXPECT_CALL(*modem_info_.mock_dispatcher(), PostTask(_)).Times(0);
+  capability_->UpdatePendingActivationState();
+  capability_->UpdatePendingActivationState();
+  Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
+  Mock::VerifyAndClearExpectations(modem_info_.mock_dispatcher());
+}
+
 }  // namespace shill
diff --git a/cellular_capability_universal_unittest.cc b/cellular_capability_universal_unittest.cc
index 60e2fe6..02f9e4f 100644
--- a/cellular_capability_universal_unittest.cc
+++ b/cellular_capability_universal_unittest.cc
@@ -1610,7 +1610,7 @@
   capability_->OnActivationWaitForRegisterTimeout();
 }
 
-TEST_F(CellularCapabilityUniversalMainTest, UpdateIccidActivationState) {
+TEST_F(CellularCapabilityUniversalMainTest, UpdatePendingActivationState) {
   const char kIccid[] = "1234567";
 
   mm1::MockModemProxy *modem_proxy = modem_proxy_.get();
@@ -1624,7 +1624,7 @@
   EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
               GetActivationState(PendingActivationStore::kIdentifierICCID, _))
       .Times(0);
-  capability_->UpdateIccidActivationState();
+  capability_->UpdatePendingActivationState();
   Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
 
   // ICCID known.
@@ -1641,7 +1641,7 @@
                                  kIccid,
                                  PendingActivationStore::kStateActivated))
       .Times(1);
-  capability_->UpdateIccidActivationState();
+  capability_->UpdatePendingActivationState();
   Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
 
   // Before reset, not registered.
@@ -1654,14 +1654,14 @@
               SetActivationState(flimflam::kActivationStateActivating))
      .Times(2);
   EXPECT_CALL(*modem_proxy, Reset(_, _, _)).Times(0);
-  capability_->UpdateIccidActivationState();
+  capability_->UpdatePendingActivationState();
   Mock::VerifyAndClearExpectations(modem_proxy);
 
   // Before reset, registered.
   capability_->registration_state_ =
       MM_MODEM_3GPP_REGISTRATION_STATE_HOME;
   EXPECT_CALL(*modem_proxy, Reset(_, _, _)).Times(1);
-  capability_->UpdateIccidActivationState();
+  capability_->UpdatePendingActivationState();
   Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
 
   // Not registered.
@@ -1672,14 +1672,14 @@
                                  kIccid))
       .Times(2).WillRepeatedly(Return(PendingActivationStore::kStateActivated));
   EXPECT_CALL(*service_, AutoConnect()).Times(0);
-  capability_->UpdateIccidActivationState();
+  capability_->UpdatePendingActivationState();
   Mock::VerifyAndClearExpectations(service_);
 
   // Service, registered.
   capability_->registration_state_ =
       MM_MODEM_3GPP_REGISTRATION_STATE_HOME;
   EXPECT_CALL(*service_, AutoConnect()).Times(1);
-  capability_->UpdateIccidActivationState();
+  capability_->UpdatePendingActivationState();
 
   cellular_->service_->activation_state_ =
       flimflam::kActivationStateNotActivated;
@@ -1691,11 +1691,11 @@
   EXPECT_CALL(*service_,
               SetActivationState(flimflam::kActivationStateActivated))
       .Times(3);
-  capability_->UpdateIccidActivationState();
+  capability_->UpdatePendingActivationState();
 
   // Device is linked.
   cellular_->state_ = Cellular::kStateLinked;
-  capability_->UpdateIccidActivationState();
+  capability_->UpdatePendingActivationState();
 
   // Got valid MDN.
   cellular_->state_ = Cellular::kStateRegistered;
@@ -1703,7 +1703,7 @@
   EXPECT_CALL(*modem_info_.mock_pending_activation_store(),
               RemoveEntry(PendingActivationStore::kIdentifierICCID, kIccid))
       .Times(1);
-  capability_->UpdateIccidActivationState();
+  capability_->UpdatePendingActivationState();
 
   Mock::VerifyAndClearExpectations(service_);
   Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
@@ -1721,7 +1721,7 @@
               SetActivationState(_, _, _))
     .Times(0);
   EXPECT_CALL(*service_, SetActivationState(_)).Times(0);
-  capability_->UpdateIccidActivationState();
+  capability_->UpdatePendingActivationState();
   Mock::VerifyAndClearExpectations(service_);
   Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
 
@@ -1741,7 +1741,7 @@
   EXPECT_CALL(*service_,
               SetActivationState(flimflam::kActivationStateActivated))
     .Times(1);
-  capability_->UpdateIccidActivationState();
+  capability_->UpdatePendingActivationState();
   Mock::VerifyAndClearExpectations(service_);
   Mock::VerifyAndClearExpectations(modem_info_.mock_pending_activation_store());
 }
diff --git a/cellular_operator_info.h b/cellular_operator_info.h
index 54d422b..e737a2c 100644
--- a/cellular_operator_info.h
+++ b/cellular_operator_info.h
@@ -132,9 +132,11 @@
     friend class CellularOperatorInfoImpl;
     friend class CellularCapabilityUniversalCDMATest;
     FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateStorageIdentifier);
-    FRIEND_TEST(CellularCapabilityUniversalCDMATest, CreateFriendlyServiceName);
-    FRIEND_TEST(CellularCapabilityUniversalCDMATest, OnCDMARegistrationChanged);
-    FRIEND_TEST(CellularCapabilityUniversalCDMATest, UpdateOperatorInfo);
+    FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest,
+                CreateFriendlyServiceName);
+    FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest,
+                OnCDMARegistrationChanged);
+    FRIEND_TEST(CellularCapabilityUniversalCDMAMainTest, UpdateOperatorInfo);
 
     std::string country_;
     std::string identifier_;
diff --git a/cellular_service.h b/cellular_service.h
index 19d1ff6..12a7335 100644
--- a/cellular_service.h
+++ b/cellular_service.h
@@ -127,7 +127,8 @@
   friend class CellularServiceTest;
   FRIEND_TEST(CellularCapabilityGSMTest, SetupApnTryList);
   FRIEND_TEST(CellularCapabilityTest, TryApns);
-  FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateIccidActivationState);
+  FRIEND_TEST(CellularCapabilityUniversalMainTest,
+              UpdatePendingActivationState);
   FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateServiceName);
   FRIEND_TEST(CellularCapabilityUniversalMainTest, UpdateStorageIdentifier);
   FRIEND_TEST(CellularTest, Connect);
diff --git a/pending_activation_store.h b/pending_activation_store.h
index 633283b..f27af77 100644
--- a/pending_activation_store.h
+++ b/pending_activation_store.h
@@ -41,6 +41,9 @@
     // This state indicates that a timeout has expired in which the modem
     // failed to register to a network.
     kStatePendingTimeout,
+    // This state is used in CDMA activation to indicate that OTA activation
+    // failed and was scheduled for a retry.
+    kStateFailureRetry,
     kStateMax,
   };
 
diff --git a/pending_activation_store_unittest.cc b/pending_activation_store_unittest.cc
index 380724a..0565ed7 100644
--- a/pending_activation_store_unittest.cc
+++ b/pending_activation_store_unittest.cc
@@ -196,7 +196,10 @@
   // File contains invalid entry
   EXPECT_CALL(*mock_store,
               GetInt(PendingActivationStore::kMeidGroupId, kEntry, _))
-      .WillOnce(DoAll(SetArgumentPointee<2>(4), Return(true)));
+      .WillOnce(DoAll(
+          SetArgumentPointee<2>(
+              static_cast<int>(PendingActivationStore::kStateMax)),
+          Return(true)));
   EXPECT_EQ(PendingActivationStore::kStateUnknown,
             store_.GetActivationState(PendingActivationStore::kIdentifierMEID,
                                       kEntry));
@@ -255,7 +258,7 @@
       kEntry, static_cast<PendingActivationStore::State>(-1)));
   EXPECT_FALSE(store_.SetActivationState(
       PendingActivationStore::kIdentifierICCID,
-      kEntry, static_cast<PendingActivationStore::State>(4)));
+      kEntry, PendingActivationStore::kStateMax));
   EXPECT_FALSE(store_.SetActivationState(
       PendingActivationStore::kIdentifierICCID,
       kEntry, PendingActivationStore::kStateUnknown));