Add async support for shill client calls.

Many of the modem manager method invocations performed by the
shill cellular support have been converted to be asynchronous,
based on the dbus-c++ support for asynchronous proxies.
I've also hooked this up with the mechanism that was recently
introduced that allows adaptor methods to defer returning
results to callers of shill methods, so that we now have
an end-to-end asynchronous solution.

Adaptor methods that want to be non-blocking create a Returner
object, which will later be used to send back the result
of the operation. The cellular device code that implements
the method (or the cellular service code, depending on the
method), wraps the Returner in an AsyncCallHandler object,
which is passed to the proxy methods as the "data" argument.
When the reply callback for the method occurs, it can pull
the Returner object out of the AsyncCallHandler and use it
to return the result.

In the case of an operation like Enable that involves multiple
client method invocations, the object that wraps the returner
is a MultiStepAsyncCallHandler, a subclass of AsyncCallHandler,
that also wraps a list of Tasks comprising the compound
operation.

In either case, a callback indicates that the handling of
the method is complete by calling Complete() on the AsyncCallHandler.
This is an overloaded method - without any argument, it returns
a success result to the caller of the shill method. With an
Error argument, it returns the DBus::Error equivalent of that
error to the caller. For a MultiStepAsyncCallHandler, calling
Complete() with no argument removes the next Task from the
list and posts it to the main loop, unless there are no
remaining tasks, in which case it returns the result to
the caller of the original shill method.

I've converted the following operations to work asynchronously
end-to-end:
Enable, Register, EnterPIN, RequirePIN, ChangePIN, UnblockPIN,
Activate.
Connect and Scan have been changed to be asynchronous on the
proxy side, but not yet on the adaptor side. For Enable, all of
the individual proxy operations are now done asynchronously,
except for fetching of individual properties.

I've moved all the ModemProxy and ModemSimpleProxy method
invocations from Cellular into CellularCapability. Now all
operations to the modem are done through a Capability interface.

There is a memory leak issue noted in the file async_call_handler.h

BUG=chromium-os:23433,chromium-os:22732
TEST=unit tests, testing on device

Change-Id: I54254dd36d116a1e9089bc9b1a60fa06a3098bd5
Reviewed-on: https://gerrit.chromium.org/gerrit/12564
Commit-Ready: Eric Shienbrood <ers@chromium.org>
Reviewed-by: Eric Shienbrood <ers@chromium.org>
Tested-by: Eric Shienbrood <ers@chromium.org>
diff --git a/Makefile b/Makefile
index 8cd3c3f..ffa297f 100644
--- a/Makefile
+++ b/Makefile
@@ -72,6 +72,7 @@
 DBUS_BINDINGS = $(DBUS_ADAPTOR_BINDINGS) $(DBUS_PROXY_BINDINGS)
 
 SHILL_OBJS = \
+	async_call_handler.o \
 	async_connection.o \
 	byte_string.o \
 	callback_list.o \
@@ -79,6 +80,7 @@
 	cellular_capability.o \
 	cellular_capability_cdma.o \
 	cellular_capability_gsm.o \
+	cellular_error.o \
 	cellular_service.o \
 	connection.o \
 	crypto_des_cbc.o \
@@ -162,6 +164,7 @@
 	callback_list_unittest.o \
 	cellular_capability_cdma_unittest.o \
 	cellular_capability_gsm_unittest.o \
+	cellular_capability_unittest.o \
 	cellular_service_unittest.o \
 	cellular_unittest.o \
 	crypto_des_cbc_unittest.o \
diff --git a/async_call_handler.cc b/async_call_handler.cc
new file mode 100644
index 0000000..83da75e
--- /dev/null
+++ b/async_call_handler.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/async_call_handler.h"
+
+#include "shill/adaptor_interfaces.h"
+#include "shill/error.h"
+
+namespace shill {
+
+AsyncCallHandler::AsyncCallHandler()
+    : returner_(NULL) { }
+
+AsyncCallHandler::AsyncCallHandler(ReturnerInterface *returner)
+    : returner_(returner) { }
+
+bool AsyncCallHandler::CompleteOperation() {
+  DoReturn();
+  return true;
+}
+
+bool AsyncCallHandler::CompleteOperationWithError(const Error &error) {
+  if (returner_) {
+    returner_->ReturnError(error);
+    returner_ = NULL;
+  }
+  return true;
+}
+
+bool AsyncCallHandler::Complete() {
+  return CompleteOperation();
+}
+
+bool AsyncCallHandler::Complete(const Error &error) {
+  if (error.IsSuccess())
+    return CompleteOperation();
+  else
+    return CompleteOperationWithError(error);
+}
+
+void AsyncCallHandler::DoReturn() {
+  if (returner_) {
+    returner_->Return();
+    returner_ = NULL;
+  }
+}
+
+}  // namespace shill
diff --git a/async_call_handler.h b/async_call_handler.h
new file mode 100644
index 0000000..af88be0
--- /dev/null
+++ b/async_call_handler.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_ASYNC_CALL_HANDLER_
+#define SHILL_ASYNC_CALL_HANDLER_
+
+#include <base/basictypes.h>
+
+namespace shill {
+
+class Error;
+class ReturnerInterface;
+
+// TODO(ers): An AsyncCallHandler object for an in-flight operation can leak if
+// an ObjectProxy disappears before the object is deleted. This can probably
+// be handled by changing dbus-c++ to allow registering a user-supplied
+// 'free' function for the 'data' argument passed to proxy methods. In that
+// case, the objects will be freed when pending dbus calls are canceled.
+//
+// The same issue appears to be relevant to Returner objects as well.
+
+
+// This class implements reply handling for code that makes asynchronous
+// client calls. The default behavior, embodied in this class, is to
+// return a result or error to the pending adaptor method invocation,
+// if any. This behavior may be overridden or extended by specializations
+// of this class.
+//
+// <external-client> --- [method call] ---> shill adaptor
+class AsyncCallHandler {
+ public:
+  AsyncCallHandler();
+  explicit AsyncCallHandler(ReturnerInterface *returner);
+  virtual ~AsyncCallHandler() { }
+
+  // Signal successful completion of the handling of a reply to a
+  // single asynchronous client operation. Returns true if a terminal
+  // state has been reached, i.e., the AsyncCallHandler is no longer
+  // needed. The base class always returns true.
+  bool Complete();
+  // Signal completion of the handling of a reply to a single
+  // asynchronous client operation which resulted in an error.
+  // Returns true if a terminal state has been reached, i.e.,
+  // the AsyncCallHandler is no longer needed. The base class always
+  // returns true.
+  bool Complete(const Error &error);
+
+ protected:
+  ReturnerInterface *returner() const { return returner_; }
+
+  virtual bool CompleteOperation();
+  virtual bool CompleteOperationWithError(const Error &error);
+
+  void DoReturn();
+
+ private:
+  // scoped_ptr not used here because Returners delete themselves
+  ReturnerInterface *returner_;
+
+  DISALLOW_COPY_AND_ASSIGN(AsyncCallHandler);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_ASYNC_CALL_HANDLER_
diff --git a/cellular.cc b/cellular.cc
index e26d1e9..9577a95 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -17,6 +17,7 @@
 #include <mm/mm-modem.h>
 #include <mobile_provider.h>
 
+#include "shill/adaptor_interfaces.h"
 #include "shill/cellular_capability_cdma.h"
 #include "shill/cellular_capability_gsm.h"
 #include "shill/cellular_service.h"
@@ -26,7 +27,6 @@
 #include "shill/error.h"
 #include "shill/event_dispatcher.h"
 #include "shill/manager.h"
-#include "shill/modem_simple_proxy_interface.h"
 #include "shill/profile.h"
 #include "shill/property_accessor.h"
 #include "shill/proxy_factory.h"
@@ -39,9 +39,6 @@
 
 namespace shill {
 
-const char Cellular::kConnectPropertyPhoneNumber[] = "number";
-const char Cellular::kPropertyIMSI[] = "imsi";
-
 Cellular::Operator::Operator() {
   SetName("");
   SetCode("");
@@ -101,40 +98,25 @@
              address,
              interface_index,
              Technology::kCellular),
-      proxy_factory_(ProxyFactory::GetInstance()),
       state_(kStateDisabled),
       modem_state_(kModemStateUnknown),
       dbus_owner_(owner),
       dbus_path_(path),
       provider_db_(provider_db),
-      task_factory_(this),
-      allow_roaming_(false) {
+      task_factory_(this) {
   PropertyStore *store = this->mutable_store();
-  store->RegisterConstString(flimflam::kCarrierProperty, &carrier_);
   store->RegisterConstString(flimflam::kDBusConnectionProperty, &dbus_owner_);
   store->RegisterConstString(flimflam::kDBusObjectProperty, &dbus_path_);
-  store->RegisterBool(flimflam::kCellularAllowRoamingProperty, &allow_roaming_);
-  store->RegisterConstString(flimflam::kEsnProperty, &esn_);
-  store->RegisterConstString(flimflam::kFirmwareRevisionProperty,
-                             &firmware_revision_);
-  store->RegisterConstString(flimflam::kHardwareRevisionProperty,
-                             &hardware_revision_);
   store->RegisterConstStringmap(flimflam::kHomeProviderProperty,
                                 &home_provider_.ToDict());
-  store->RegisterConstString(flimflam::kImeiProperty, &imei_);
-  store->RegisterConstString(flimflam::kImsiProperty, &imsi_);
-  store->RegisterConstString(flimflam::kManufacturerProperty, &manufacturer_);
-  store->RegisterConstString(flimflam::kMdnProperty, &mdn_);
-  store->RegisterConstString(flimflam::kMeidProperty, &meid_);
-  store->RegisterConstString(flimflam::kMinProperty, &min_);
-  store->RegisterConstString(flimflam::kModelIDProperty, &model_id_);
-
-  InitCapability(type);  // For now, only a single capability is supported.
+  // For now, only a single capability is supported.
+  InitCapability(type, ProxyFactory::GetInstance());
 
   VLOG(2) << "Cellular device " << this->link_name() << " initialized.";
 }
 
-Cellular::~Cellular() {}
+Cellular::~Cellular() {
+}
 
 // static
 string Cellular::GetStateString(State state) {
@@ -156,145 +138,96 @@
 
 void Cellular::Start() {
   LOG(INFO) << __func__ << ": " << GetStateString(state_);
+  if (state_ != kStateDisabled) {
+    return;
+  }
   Device::Start();
-  capability_->OnDeviceStarted();
-  InitProxies();
-  EnableModem();
-  capability_->Register();
-  GetModemStatus();
-  capability_->GetIdentifiers();
-  capability_->GetProperties();
-  GetModemInfo();
-  capability_->GetRegistrationState();
+  if (modem_state_ == kModemStateEnabled) {
+    // Modem already enabled.
+    OnModemEnabled();
+    return;
+  }
+  // TODO(ers): this should not be done automatically. It should
+  // require an explicit Enable request to start the modem.
+  capability_->StartModem();
 }
 
 void Cellular::Stop() {
-  capability_->OnDeviceStopped();
   DestroyService();
   DisconnectModem();
-  DisableModem();
-  proxy_.reset();
-  simple_proxy_.reset();
+  if (state_ != kStateDisabled)
+    capability_->DisableModem(NULL);
+  capability_->StopModem();
   Device::Stop();
 }
 
-void Cellular::InitCapability(Type type) {
+void Cellular::InitCapability(Type type, ProxyFactory *proxy_factory) {
   // TODO(petkov): Consider moving capability construction into a factory that's
   // external to the Cellular class.
   VLOG(2) << __func__ << "(" << type << ")";
   switch (type) {
     case kTypeGSM:
-      capability_.reset(new CellularCapabilityGSM(this));
+      capability_.reset(new CellularCapabilityGSM(this, proxy_factory));
       break;
     case kTypeCDMA:
-      capability_.reset(new CellularCapabilityCDMA(this));
+      capability_.reset(new CellularCapabilityCDMA(this, proxy_factory));
       break;
     default: NOTREACHED();
   }
 }
 
-void Cellular::InitProxies() {
-  VLOG(2) << __func__;
-  proxy_.reset(proxy_factory_->CreateModemProxy(this, dbus_path_, dbus_owner_));
-  simple_proxy_.reset(
-      proxy_factory_->CreateModemSimpleProxy(dbus_path_, dbus_owner_));
-}
-
-void Cellular::EnableModem() {
-  CHECK_EQ(kStateDisabled, state_);
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  try {
-    proxy_->Enable(true);
-  } catch (const DBus::Error e) {
-    LOG(WARNING) << "Enable failed: " << e.what();
-  }
-  SetState(kStateEnabled);
-}
-
-void Cellular::DisableModem() {
-  VLOG(2) << __func__;
+void Cellular::OnModemEnabled() {
+  VLOG(2) << __func__ << ": " << GetStateString(state_);
   if (state_ == kStateDisabled) {
-    return;
+    SetState(kStateEnabled);
   }
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583). Note,
-  // however, that this is invoked by the Stop method, so some extra refactoring
-  // may be needed to prevent the device instance from being destroyed before
-  // the modem manager calls are complete. Maybe Disconnect and Disable need to
-  // be handled by the parent Modem class.
-  try {
-    proxy_->Enable(false);
-  } catch (const DBus::Error e) {
-    LOG(WARNING) << "Disable failed: " << e.what();
-  }
-  SetState(kStateDisabled);
 }
 
-void Cellular::GetModemStatus() {
-  CHECK_EQ(kStateEnabled, state_);
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  DBusPropertiesMap properties = simple_proxy_->GetStatus();
-  DBusProperties::GetString(properties, "carrier", &carrier_);
-  DBusProperties::GetString(properties, "meid", &meid_);
-  DBusProperties::GetString(properties, "imei", &imei_);
-  DBusProperties::GetString(properties, kPropertyIMSI, &imsi_);
-  DBusProperties::GetString(properties, "esn", &esn_);
-  DBusProperties::GetString(properties, "mdn", &mdn_);
-  DBusProperties::GetString(properties, "min", &min_);
-  DBusProperties::GetString(
-      properties, "firmware_revision", &firmware_revision_);
-
-  uint32 state = 0;
-  if (DBusProperties::GetUint32(properties, "state", &state)) {
-    modem_state_ = static_cast<ModemState>(state);
+void Cellular::OnModemDisabled() {
+  VLOG(2) << __func__ << ": " << GetStateString(state_);
+  if (state_ != kStateDisabled) {
+    SetState(kStateDisabled);
   }
-
-  capability_->UpdateStatus(properties);
 }
 
-void Cellular::Activate(const std::string &carrier, Error *error) {
-  capability_->Activate(carrier, error);
+void Cellular::Activate(const std::string &carrier,
+                        ReturnerInterface *returner) {
+  capability_->Activate(carrier, new AsyncCallHandler(returner));
 }
 
-void Cellular::RegisterOnNetwork(const string &network_id, Error *error) {
-  capability_->RegisterOnNetwork(network_id, error);
+void Cellular::RegisterOnNetwork(const string &network_id,
+                                 ReturnerInterface *returner) {
+  capability_->RegisterOnNetwork(network_id, new AsyncCallHandler(returner));
 }
 
 void Cellular::RequirePIN(
     const string &pin, bool require, ReturnerInterface *returner) {
   VLOG(2) << __func__ << "(" << returner << ")";
-  capability_->RequirePIN(pin, require, returner);
+  capability_->RequirePIN(pin, require, new AsyncCallHandler(returner));
 }
 
 void Cellular::EnterPIN(const string &pin, ReturnerInterface *returner) {
   VLOG(2) << __func__ << "(" << returner << ")";
-  capability_->EnterPIN(pin, returner);
+  capability_->EnterPIN(pin, new AsyncCallHandler(returner));
 }
 
 void Cellular::UnblockPIN(const string &unblock_code,
                           const string &pin,
                           ReturnerInterface *returner) {
   VLOG(2) << __func__ << "(" << returner << ")";
-  capability_->UnblockPIN(unblock_code, pin, returner);
+  capability_->UnblockPIN(unblock_code, pin, new AsyncCallHandler(returner));
 }
 
 void Cellular::ChangePIN(
-    const string &old_pin, const string &new_pin, ReturnerInterface *returner) {
+    const string &old_pin, const string &new_pin,
+    ReturnerInterface *returner) {
   VLOG(2) << __func__ << "(" << returner << ")";
-  capability_->ChangePIN(old_pin, new_pin, returner);
+  capability_->ChangePIN(old_pin, new_pin, new AsyncCallHandler(returner));
 }
 
-void Cellular::Scan(Error *error) {
-  capability_->Scan(error);
-}
-
-void Cellular::GetModemInfo() {
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  ModemProxyInterface::Info info = proxy_->GetInfo();
-  manufacturer_ = info._1;
-  model_id_ = info._2;
-  hardware_revision_ = info._3;
-  VLOG(2) << "ModemInfo: " << manufacturer_ << ", " << model_id_ << ", "
-          << hardware_revision_;
+void Cellular::Scan(Error * /*error*/) {
+  // TODO(ers): for now report immediate success.
+  capability_->Scan(NULL);
 }
 
 void Cellular::HandleNewRegistrationState() {
@@ -318,14 +251,11 @@
     SetState(kStateRegistered);
   }
   if (!service_.get()) {
-    // For now, no endpoint is created. Revisit if necessary.
     CreateService();
   }
   capability_->GetSignalQuality();
-  if (state_ == kStateRegistered && modem_state_ == kModemStateConnected) {
-    SetState(kStateConnected);
-    EstablishLink();
-  }
+  if (state_ == kStateRegistered && modem_state_ == kModemStateConnected)
+    OnConnected();
   service_->SetNetworkTechnology(capability_->GetNetworkTechnologyString());
   service_->SetRoamingState(capability_->GetRoamingStateString());
 }
@@ -371,7 +301,7 @@
   }
   CHECK_EQ(kStateRegistered, state_);
 
-  if (!allow_roaming_ &&
+  if (!capability_->allow_roaming() &&
       service_->roaming_state() == flimflam::kRoamingStateRoaming) {
     Error::PopulateAndLog(error, Error::kNotOnHomeNetwork,
                           "Roaming disallowed; connection request ignored.");
@@ -380,20 +310,19 @@
 
   DBusPropertiesMap properties;
   capability_->SetupConnectProperties(&properties);
-
-  // Defer connect because we may be in a dbus-c++ callback.
-  dispatcher()->PostTask(
-      task_factory_.NewRunnableMethod(&Cellular::ConnectTask, properties));
+  capability_->Connect(properties);
 }
 
-void Cellular::ConnectTask(const DBusPropertiesMap &properties) {
+void Cellular::OnConnected() {
   VLOG(2) << __func__;
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  simple_proxy_->Connect(properties);
   SetState(kStateConnected);
   EstablishLink();
 }
 
+void Cellular::OnConnectFailed() {
+  // TODO(ers): Signal failure.
+}
+
 void Cellular::Disconnect(Error *error) {
   VLOG(2) << __func__;
   if (state_ != kStateConnected &&
@@ -402,14 +331,12 @@
         error, Error::kInProgress, "Not connected; request ignored.");
     return;
   }
-  // Defer because we may be in a dbus-c++ callback.
-  dispatcher()->PostTask(
-      task_factory_.NewRunnableMethod(&Cellular::DisconnectTask));
+  capability_->Disconnect();
 }
 
-void Cellular::DisconnectTask() {
+void Cellular::OnDisconnected() {
   VLOG(2) << __func__;
-  DisconnectModem();
+  SetState(kStateRegistered);
 }
 
 void Cellular::DisconnectModem() {
@@ -423,12 +350,11 @@
   // may be needed to prevent the device instance from being destroyed before
   // the modem manager calls are complete. Maybe Disconnect and Disable need to
   // be handled by the parent Modem class.
-  try {
-    proxy_->Disconnect();
-  } catch (const DBus::Error e) {
-    LOG(WARNING) << "Disconnect failed: " << e.what();
-  }
-  SetState(kStateRegistered);
+  capability_->Disconnect();
+}
+
+void Cellular::OnDisconnectFailed() {
+  // TODO(ers): Signal failure.
 }
 
 void Cellular::EstablishLink() {
@@ -462,13 +388,6 @@
   }
 }
 
-void Cellular::OnModemStateChanged(uint32 /*old_state*/,
-                                   uint32 /*new_state*/,
-                                   uint32 /*reason*/) {
-  // TODO(petkov): Implement this.
-  NOTIMPLEMENTED();
-}
-
 void Cellular::OnModemManagerPropertiesChanged(
     const DBusPropertiesMap &properties) {
   capability_->OnModemManagerPropertiesChanged(properties);
diff --git a/cellular.h b/cellular.h
index 792231c..742f25b 100644
--- a/cellular.h
+++ b/cellular.h
@@ -23,11 +23,9 @@
 
 class CellularCapability;
 class Error;
-class ModemSimpleProxyInterface;
 class ProxyFactory;
 
-class Cellular : public Device,
-                 public ModemProxyDelegate {
+class Cellular : public Device {
  public:
   enum Type {
     kTypeGSM,
@@ -89,9 +87,6 @@
     DISALLOW_COPY_AND_ASSIGN(Operator);
   };
 
-  static const char kConnectPropertyPhoneNumber[];
-  static const char kPropertyIMSI[];
-
   // |owner| is the ModemManager DBus service owner (e.g., ":1.17"). |path| is
   // the ModemManager.Modem DBus object path (e.g.,
   // "/org/chromium/ModemManager/Gobi/0").
@@ -116,9 +111,8 @@
   // failure, leaves it unchanged otherwise.
   void Disconnect(Error *error);
 
-  // Asynchronously activates the modem. Populates |error| on failure, leaves it
-  // unchanged otherwise.
-  void Activate(const std::string &carrier, Error *error);
+  // Asynchronously activates the modem. Returns an error on failure.
+  void Activate(const std::string &carrier, ReturnerInterface *returner);
 
   const CellularServiceRefPtr &service() const { return service_; }
 
@@ -136,28 +130,9 @@
   const std::string &dbus_owner() const { return dbus_owner_; }
   const std::string &dbus_path() const { return dbus_path_; }
 
-  const std::string &carrier() const { return carrier_; }
-
   const Operator &home_provider() const { return home_provider_; }
   void set_home_provider(const Operator &oper);
 
-  const std::string &meid() const { return meid_; }
-  void set_meid(const std::string &meid) { meid_ = meid; }
-
-  const std::string &imei() const { return imei_; }
-  void set_imei(const std::string &imei) { imei_ = imei; }
-
-  const std::string &imsi() const { return imsi_; }
-  void set_imsi(const std::string &imsi) { imsi_ = imsi; }
-
-  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; }
-
-  ProxyFactory *proxy_factory() const { return proxy_factory_; }
-
   void HandleNewSignalQuality(uint32 strength);
 
   // Processes a change in the modem registration state, possibly creating,
@@ -172,7 +147,8 @@
   virtual bool TechnologyIs(Technology::Identifier type) const;
   virtual void LinkEvent(unsigned int flags, unsigned int change);
   virtual void Scan(Error *error);
-  virtual void RegisterOnNetwork(const std::string &network_id, Error *error);
+  virtual void RegisterOnNetwork(const std::string &network_id,
+                                 ReturnerInterface *returner);
   virtual void RequirePIN(
       const std::string &pin, bool require, ReturnerInterface *returner);
   virtual void EnterPIN(const std::string &pin, ReturnerInterface *returner);
@@ -183,8 +159,18 @@
                          const std::string &new_pin,
                          ReturnerInterface *returner);
 
+  void OnModemEnabled();
+  void OnModemDisabled();
+  void OnConnected();
+  void OnConnectFailed();
+  void OnDisconnected();
+  void OnDisconnectFailed();
+
+  static Error ConvertDBusError(const DBus::Error &dbus_error);
+
  private:
   friend class CellularTest;
+  friend class CellularCapabilityTest;
   friend class CellularCapabilityCDMATest;
   friend class CellularCapabilityGSMTest;
   friend class ModemTest;
@@ -195,13 +181,14 @@
   FRIEND_TEST(CellularTest, Connect);
   FRIEND_TEST(CellularTest, DisableModem);
   FRIEND_TEST(CellularTest, DisconnectModem);
-  FRIEND_TEST(CellularTest, GetModemInfo);
-  FRIEND_TEST(CellularTest, GetModemStatus);
-  FRIEND_TEST(CellularTest, InitProxies);
   FRIEND_TEST(CellularTest, StartConnected);
   FRIEND_TEST(CellularTest, StartCDMARegister);
   FRIEND_TEST(CellularTest, StartGSMRegister);
   FRIEND_TEST(CellularTest, StartLinked);
+  FRIEND_TEST(CellularCapabilityTest, EnableModemFail);
+  FRIEND_TEST(CellularCapabilityTest, EnableModemSucceed);
+  FRIEND_TEST(CellularCapabilityTest, GetModemInfo);
+  FRIEND_TEST(CellularCapabilityTest, GetModemStatus);
 
   void SetState(State state);
 
@@ -213,29 +200,13 @@
   // to the network-connected state and bring the network interface up.
   void EstablishLink();
 
-  void InitCapability(Type type);
-  void InitProxies();
-
-  void EnableModem();
-  void DisableModem();
-  void GetModemStatus();
-
-  // Obtains modem's manufacturer, model ID, and hardware revision.
-  void GetModemInfo();
+  void InitCapability(Type type, ProxyFactory *proxy_factory);
 
   void HandleNewRegistrationStateTask();
 
   void CreateService();
   void DestroyService();
 
-  // Signal callbacks inherited from ModemProxyDelegate.
-  virtual void OnModemStateChanged(uint32 old_state,
-                                   uint32 new_state,
-                                   uint32 reason);
-
-  // Store cached copies of singletons for speed/ease of testing.
-  ProxyFactory *proxy_factory_;
-
   State state_;
   ModemState modem_state_;
 
@@ -243,8 +214,6 @@
 
   const std::string dbus_owner_;  // ModemManager.Modem
   const std::string dbus_path_;  // ModemManager.Modem
-  scoped_ptr<ModemProxyInterface> proxy_;
-  scoped_ptr<ModemSimpleProxyInterface> simple_proxy_;
 
   mobile_provider_db *provider_db_;
 
@@ -253,18 +222,6 @@
   ScopedRunnableMethodFactory<Cellular> task_factory_;
 
   // Properties
-  bool allow_roaming_;
-  std::string carrier_;
-  std::string meid_;
-  std::string imei_;
-  std::string imsi_;
-  std::string esn_;
-  std::string mdn_;
-  std::string min_;
-  std::string model_id_;
-  std::string manufacturer_;
-  std::string firmware_revision_;
-  std::string hardware_revision_;
   Operator home_provider_;
 
   DISALLOW_COPY_AND_ASSIGN(Cellular);
diff --git a/cellular_capability.cc b/cellular_capability.cc
index 9a3a2e2..6750b76 100644
--- a/cellular_capability.cc
+++ b/cellular_capability.cc
@@ -1,77 +1,289 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "shill/cellular_capability.h"
 
+#include <chromeos/dbus/service_constants.h>
+
 #include "shill/adaptor_interfaces.h"
 #include "shill/cellular.h"
 #include "shill/error.h"
+#include "shill/proxy_factory.h"
 
+using base::Callback;
 using std::string;
 
 namespace shill {
 
-CellularCapability::CellularCapability(Cellular *cellular)
-    : cellular_(cellular),
-      proxy_factory_(cellular->proxy_factory()) {}
+const char CellularCapability::kConnectPropertyPhoneNumber[] = "number";
+const char CellularCapability::kPropertyIMSI[] = "imsi";
+const int CellularCapability::kTimeoutActivate = 120000;  // ms
+const int CellularCapability::kTimeoutConnect = 45000;  // ms
+const int CellularCapability::kTimeoutDefault = 5000;  // ms
+const int CellularCapability::kTimeoutRegister = 90000;  // ms
+const int CellularCapability::kTimeoutScan = 120000;  // ms
+
+CellularCapability::MultiStepAsyncCallHandler::MultiStepAsyncCallHandler(
+    EventDispatcher *dispatcher)
+    : AsyncCallHandler(),
+      dispatcher_(dispatcher) {
+}
+
+CellularCapability::MultiStepAsyncCallHandler::~MultiStepAsyncCallHandler() {
+}
+
+void CellularCapability::MultiStepAsyncCallHandler::AddTask(Task *task) {
+  tasks_.push_back(task);
+}
+
+bool CellularCapability::MultiStepAsyncCallHandler::CompleteOperation() {
+  if (tasks_.empty()) {
+    DoReturn();
+    return true;
+  } else {
+    PostNextTask();
+  }
+  return false;
+}
+
+void CellularCapability::MultiStepAsyncCallHandler::PostNextTask() {
+  VLOG(2) << __func__ << ": " << tasks_.size() << " remaining actions";
+  if (tasks_.empty())
+    return;
+  Task *task = tasks_[0];
+  tasks_.weak_erase(tasks_.begin());
+  dispatcher_->PostTask(task);
+}
+
+CellularCapability::CellularCapability(Cellular *cellular,
+                                       ProxyFactory *proxy_factory)
+    : allow_roaming_(false),
+      cellular_(cellular),
+      proxy_factory_(proxy_factory) {
+  PropertyStore *store = cellular->mutable_store();
+  store->RegisterConstString(flimflam::kCarrierProperty, &carrier_);
+  store->RegisterBool(flimflam::kCellularAllowRoamingProperty, &allow_roaming_);
+  store->RegisterConstString(flimflam::kEsnProperty, &esn_);
+  store->RegisterConstString(flimflam::kFirmwareRevisionProperty,
+                             &firmware_revision_);
+  store->RegisterConstString(flimflam::kHardwareRevisionProperty,
+                             &hardware_revision_);
+  store->RegisterConstString(flimflam::kImeiProperty, &imei_);
+  store->RegisterConstString(flimflam::kImsiProperty, &imsi_);
+  store->RegisterConstString(flimflam::kManufacturerProperty, &manufacturer_);
+  store->RegisterConstString(flimflam::kMdnProperty, &mdn_);
+  store->RegisterConstString(flimflam::kMeidProperty, &meid_);
+  store->RegisterConstString(flimflam::kMinProperty, &min_);
+  store->RegisterConstString(flimflam::kModelIDProperty, &model_id_);
+}
 
 CellularCapability::~CellularCapability() {}
 
-EventDispatcher *CellularCapability::dispatcher() const {
-  return cellular()->dispatcher();
+void CellularCapability::StartModem() {
+  VLOG(2) << __func__;
+  proxy_.reset(proxy_factory()->CreateModemProxy(
+      this, cellular()->dbus_path(), cellular()->dbus_owner()));
+  simple_proxy_.reset(proxy_factory()->CreateModemSimpleProxy(
+      this, cellular()->dbus_path(), cellular()->dbus_owner()));
 }
 
-void CellularCapability::Activate(const string &/*carrier*/, Error *error) {
-  Error::PopulateAndLog(error, Error::kInvalidArguments,
-                        "Activation not supported.");
+void CellularCapability::StopModem() {
+  VLOG(2) << __func__;
+  proxy_.reset();
+  simple_proxy_.reset();
 }
 
-void CellularCapability::Register() {}
+void CellularCapability::CompleteOperation(AsyncCallHandler *reply_handler) {
+  if (reply_handler && reply_handler->Complete())
+      delete reply_handler;
+}
+
+void CellularCapability::CompleteOperation(AsyncCallHandler *reply_handler,
+                                           const Error &error) {
+  if (reply_handler && reply_handler->Complete(error))
+    delete reply_handler;
+}
+
+void CellularCapability::OnUnsupportedOperation(
+    const char *operation,
+    AsyncCallHandler *call_handler) {
+  Error error;
+  string message("The ");
+  message.append(operation).append(" operation is not supported.");
+  Error::PopulateAndLog(&error, Error::kNotSupported, message);
+  CompleteOperation(call_handler, error);
+}
+
+void CellularCapability::EnableModem(AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__;
+  proxy_->Enable(true, call_handler, kTimeoutDefault);
+}
+
+// TODO(ers): convert to async once we have a way for the callback
+// to distinguish an enable from a disable, and once the Stop() operation
+// has been converted to multi-step async.
+void CellularCapability::DisableModem(AsyncCallHandler * /*call_handler*/) {
+  try {
+    proxy_->Enable(false);
+    cellular()->OnModemDisabled();
+  } catch (const DBus::Error e) {
+    LOG(WARNING) << "Disable failed: " << e.what();
+  }
+}
+
+void CellularCapability::GetModemStatus(AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__;
+  simple_proxy_->GetModemStatus(call_handler, kTimeoutDefault);
+}
+
+void CellularCapability::GetModemInfo(AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__;
+  proxy_->GetModemInfo(call_handler, kTimeoutDefault);
+}
+
+// TODO(ers): make this async (supply an AsyncCallHandler arg)
+void CellularCapability::Connect(const DBusPropertiesMap &properties) {
+  VLOG(2) << __func__;
+  simple_proxy_->Connect(properties, NULL, kTimeoutConnect);
+}
+
+// TODO(ers): convert to async once the Stop() operation
+// has been converted to multi-step async.
+void CellularCapability::Disconnect() {
+  VLOG(2) << __func__;
+  try {
+    proxy_->Disconnect();
+    cellular()->OnDisconnected();
+  } catch (const DBus::Error e) {
+    LOG(WARNING) << "Disconnect failed: " << e.what();
+  }
+}
+
+void CellularCapability::Activate(const string &/*carrier*/,
+                                  AsyncCallHandler *call_handler) {
+  OnUnsupportedOperation(__func__, call_handler);
+}
 
 void CellularCapability::RegisterOnNetwork(
-    const string &/*network_id*/, Error *error) {
-  Error::PopulateAndLog(error, Error::kNotSupported,
-                        "Network registration not supported.");
+    const string &/*network_id*/, AsyncCallHandler *call_handler) {
+  OnUnsupportedOperation(__func__, call_handler);
 }
 
 void CellularCapability::RequirePIN(
-    const string &/*pin*/, bool /*require*/, ReturnerInterface *returner) {
-  Error error;
-  Error::PopulateAndLog(
-      &error, Error::kNotSupported, "RequirePIN not supported.");
-  returner->ReturnError(error);
+    const string &/*pin*/, bool /*require*/, AsyncCallHandler *call_handler) {
+  OnUnsupportedOperation(__func__, call_handler);
 }
 
 void CellularCapability::EnterPIN(const string &/*pin*/,
-                                  ReturnerInterface *returner) {
-  Error error;
-  Error::PopulateAndLog(
-      &error, Error::kNotSupported, "EnterPIN not supported.");
-  returner->ReturnError(error);
+                                  AsyncCallHandler *call_handler) {
+  OnUnsupportedOperation(__func__, call_handler);
 }
 
 void CellularCapability::UnblockPIN(const string &/*unblock_code*/,
                                     const string &/*pin*/,
-                                    ReturnerInterface *returner) {
-  Error error;
-  Error::PopulateAndLog(
-      &error, Error::kNotSupported, "UnblockPIN not supported.");
-  returner->ReturnError(error);
+                                    AsyncCallHandler *call_handler) {
+  OnUnsupportedOperation(__func__, call_handler);
 }
 
 void CellularCapability::ChangePIN(const string &/*old_pin*/,
                                    const string &/*new_pin*/,
-                                   ReturnerInterface *returner) {
-  Error error;
-  Error::PopulateAndLog(
-      &error, Error::kNotSupported, "ChangePIN not supported.");
-  returner->ReturnError(error);
+                                   AsyncCallHandler *call_handler) {
+  OnUnsupportedOperation(__func__, call_handler);
 }
 
-void CellularCapability::Scan(Error *error) {
-  Error::PopulateAndLog(
-      error, Error::kNotSupported, "Network scanning not supported.");
+void CellularCapability::Scan(AsyncCallHandler *call_handler) {
+  OnUnsupportedOperation(__func__, call_handler);
+}
+
+void CellularCapability::OnModemEnableCallback(const Error &error,
+                                               AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << "(" << error << ")";
+  if (error.IsSuccess())
+    cellular()->OnModemEnabled();
+  CompleteOperation(call_handler, error);
+}
+
+void CellularCapability::OnGetModemStatusCallback(
+    const DBusPropertiesMap &props, const Error &error,
+    AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << " " << props.size() << " props. error " << error;
+  if (error.IsSuccess()) {
+    DBusProperties::GetString(props, "carrier", &carrier_);
+    DBusProperties::GetString(props, "meid", &meid_);
+    DBusProperties::GetString(props, "imei", &imei_);
+    DBusProperties::GetString(props, kPropertyIMSI, &imsi_);
+    DBusProperties::GetString(props, "esn", &esn_);
+    DBusProperties::GetString(props, "mdn", &mdn_);
+    DBusProperties::GetString(props, "min", &min_);
+    DBusProperties::GetString(props, "firmware_revision", &firmware_revision_);
+
+    uint32 state;
+    if (DBusProperties::GetUint32(props, "state", &state))
+      cellular()->set_modem_state(static_cast<Cellular::ModemState>(state));
+
+    UpdateStatus(props);
+  }
+  CompleteOperation(call_handler, error);
+}
+
+void CellularCapability::OnGetModemInfoCallback(
+    const ModemHardwareInfo &info,
+    const Error &error,
+    AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << "(" << error << ")";
+  if (error.IsSuccess()) {
+    manufacturer_ = info._1;
+    model_id_ = info._2;
+    hardware_revision_ = info._3;
+    VLOG(2) << __func__ << ": " << info._1 << ", " << info._2 << ", "
+            << info._3;
+  }
+  CompleteOperation(call_handler, error);
+}
+
+void CellularCapability::OnConnectCallback(const Error &error,
+                                           AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << "(" << error << ")";
+  if (error.IsSuccess())
+    cellular()->OnConnected();
+  else
+    cellular()->OnConnectFailed();
+  CompleteOperation(call_handler, error);
+}
+
+void CellularCapability::OnDisconnectCallback(const Error &error,
+                                              AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << "(" << error << ")";
+  if (error.IsSuccess())
+    cellular()->OnDisconnected();
+  else
+    cellular()->OnDisconnectFailed();
+  CompleteOperation(call_handler, error);
+}
+
+void CellularCapability::OnModemStateChanged(
+    uint32 old_state, uint32 new_state, uint32 reason) {
+  VLOG(2) << __func__ << "(" << old_state << ", " << new_state << ", "
+          << reason << ")";
+  // TODO(petkov): Complete this (crosbug.com/19662)
+#if 0
+  modem_state_ = static_cast<ModemState>(new_state);
+  if (old_state == new_state) {
+    return;
+  }
+  switch (new_state) {
+    case kModemStateEnabled:
+      if (old_state == kModemStateDisabled ||
+          old_state == kModemStateEnabling) {
+        Start();
+      }
+      // TODO(petkov): Handle the case when the state is downgraded to Enabled.
+      break;
+    default:
+      break;
+  }
+#endif
 }
 
 }  // namespace shill
diff --git a/cellular_capability.h b/cellular_capability.h
index cf0f848..da5c5ee 100644
--- a/cellular_capability.h
+++ b/cellular_capability.h
@@ -8,8 +8,16 @@
 #include <string>
 
 #include <base/basictypes.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/memory/scoped_vector.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
+#include "shill/async_call_handler.h"
 #include "shill/dbus_properties.h"
+#include "shill/modem_proxy_interface.h"
+#include "shill/modem_simple_proxy_interface.h"
+
+class Task;
 
 namespace shill {
 
@@ -17,23 +25,28 @@
 class Error;
 class EventDispatcher;
 class ProxyFactory;
-class ReturnerInterface;
 
-// Cellular devices instantiate subclasses of CellularCapability that handle the
-// specific modem technologies and capabilities.
-class CellularCapability {
+// Cellular devices instantiate subclasses of CellularCapability that
+// handle the specific modem technologies and capabilities.
+class CellularCapability : public ModemProxyDelegate,
+                           public ModemSimpleProxyDelegate {
  public:
+  static const int kTimeoutActivate;
+  static const int kTimeoutConnect;
+  static const int kTimeoutDefault;
+  static const int kTimeoutRegister;
+  static const int kTimeoutScan;
+
+  static const char kConnectPropertyPhoneNumber[];
+  static const char kPropertyIMSI[];
+
+
   // |cellular| is the parent Cellular device.
-  CellularCapability(Cellular *cellular);
+  CellularCapability(Cellular *cellular, ProxyFactory *proxy_factory);
   virtual ~CellularCapability();
 
   Cellular *cellular() const { return cellular_; }
   ProxyFactory *proxy_factory() const { return proxy_factory_; }
-  EventDispatcher *dispatcher() const;
-
-  // Invoked on starting and stopping the cellular device.
-  virtual void OnDeviceStarted() = 0;
-  virtual void OnDeviceStopped() = 0;
 
   // Invoked by the parent Cellular device when a new service is created.
   virtual void OnServiceCreated() = 0;
@@ -42,40 +55,60 @@
 
   virtual void SetupConnectProperties(DBusPropertiesMap *properties) = 0;
 
-  // Activates the modem. Populates |error| on failure, leaves it unchanged
-  // otherwise. The default implementation fails by populating |error|.
-  virtual void Activate(const std::string &carrier, Error *error);
+  bool allow_roaming() const { return allow_roaming_; }
+
+  // StartModem attempts to put the modem in a state in which it is
+  // usable for creating services and establishing connections (if
+  // network conditions permit). It potentially consists of multiple
+  // non-blocking calls to the modem-manager server. After each call,
+  // control is passed back up to the main loop. Each time a reply to
+  // a non-blocking call is received, the operation advances to the next
+  // step, until either an error occurs in one of them, or all the steps
+  // have been completed, at which point StartModem() is finished.
+  virtual void StartModem();
+  virtual void StopModem();
+  // EnableModem ensures that the modem device is powered.
+  virtual void EnableModem(AsyncCallHandler *call_handler);
+  virtual void DisableModem(AsyncCallHandler *call_handler);
+  virtual void GetModemStatus(AsyncCallHandler *call_handler);
+  virtual void GetModemInfo(AsyncCallHandler *call_handler);
+  virtual void Connect(const DBusPropertiesMap &properties);
+  virtual void Disconnect();
+
+  // Activates the modem. Returns an Error on failure.
+  // The default implementation fails by returning a kNotSupported error
+  // to the caller.
+  virtual void Activate(const std::string &carrier,
+                        AsyncCallHandler *call_handler);
 
   // Network registration.
-  virtual void Register();
-  virtual void RegisterOnNetwork(const std::string &network_id, Error *error);
+  virtual void RegisterOnNetwork(const std::string &network_id,
+                                 AsyncCallHandler *call_handler);
   virtual bool IsRegistered() = 0;
 
-  // Retrieves identifiers associated with the modem and the capability.
-  virtual void GetIdentifiers() = 0;
-
-  virtual void GetProperties() = 0;
+  virtual void GetProperties(AsyncCallHandler *call_handler) = 0;
 
   // Retrieves the current cellular signal strength.
   virtual void GetSignalQuality() = 0;
 
-  virtual void GetRegistrationState() = 0;
+  virtual void GetRegistrationState(AsyncCallHandler *call_handler) = 0;
 
   virtual std::string CreateFriendlyServiceName() = 0;
 
   // PIN management. The default implementation fails by returning an error.
   virtual void RequirePIN(
-      const std::string &pin, bool require, ReturnerInterface *returner);
-  virtual void EnterPIN(const std::string &pin, ReturnerInterface *returner);
+      const std::string &pin, bool require, AsyncCallHandler *call_handler);
+  virtual void EnterPIN(const std::string &pin, AsyncCallHandler *call_handler);
   virtual void UnblockPIN(const std::string &unblock_code,
                           const std::string &pin,
-                          ReturnerInterface *returner);
+                          AsyncCallHandler *call_handler);
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
-                         ReturnerInterface *returner);
+                         AsyncCallHandler *call_handler);
 
-  // Network scanning. The default implementation fails by populating |error|.
-  virtual void Scan(Error *error);
+  // Network scanning. The default implementation fails by invoking
+  // the reply handler with an error.
+  virtual void Scan(AsyncCallHandler *call_handler);
 
   // Returns an empty string if the network technology is unknown.
   virtual std::string GetNetworkTechnologyString() const = 0;
@@ -85,12 +118,87 @@
   virtual void OnModemManagerPropertiesChanged(
       const DBusPropertiesMap &properties) = 0;
 
+  // Method and signal callbacks inherited from ModemProxyDelegate.
+  virtual void OnModemStateChanged(
+      uint32 old_state, uint32 new_state, uint32 reason);
+  virtual void OnModemEnableCallback(const Error &error,
+                                     AsyncCallHandler *call_handler);
+  virtual void OnGetModemInfoCallback(const ModemHardwareInfo &info,
+                                      const Error &error,
+                                      AsyncCallHandler *call_handler);
+
+  // Method and signal callbacks inherited from ModemSimpleProxyDelegate.
+  virtual void OnGetModemStatusCallback(const DBusPropertiesMap &props,
+                                        const Error &error,
+                                        AsyncCallHandler *call_handler);
+  virtual void OnConnectCallback(const Error &error,
+                                 AsyncCallHandler *call_handler);
+  virtual void OnDisconnectCallback(const Error &error,
+                                    AsyncCallHandler *call_handler);
+
+ protected:
+  class MultiStepAsyncCallHandler : public AsyncCallHandler {
+   public:
+    explicit MultiStepAsyncCallHandler(EventDispatcher *dispatcher);
+    virtual ~MultiStepAsyncCallHandler();
+
+    // Override the non-error case
+    virtual bool CompleteOperation();
+
+    void AddTask(Task *task);
+    void PostNextTask();
+
+   private:
+    EventDispatcher *dispatcher_;
+    ScopedVector<Task> tasks_;
+
+    DISALLOW_COPY_AND_ASSIGN(MultiStepAsyncCallHandler);
+  };
+
+  static void CompleteOperation(AsyncCallHandler *reply_handler);
+  static void CompleteOperation(AsyncCallHandler *reply_handler,
+                                const Error &error);
+  // Generic synthetic callback by which a Capability class can indicate
+  // that a requested operation is not supported.
+  void static OnUnsupportedOperation(const char *operation,
+                                     AsyncCallHandler *call_handler);
+
+  // Properties
+  bool allow_roaming_;
+  std::string carrier_;
+  std::string meid_;
+  std::string imei_;
+  std::string imsi_;
+  std::string esn_;
+  std::string mdn_;
+  std::string min_;
+  std::string model_id_;
+  std::string manufacturer_;
+  std::string firmware_revision_;
+  std::string hardware_revision_;
+
  private:
   friend class CellularTest;
+  friend class CellularCapabilityTest;
+  FRIEND_TEST(CellularCapabilityGSMTest, UpdateStatus);
+  FRIEND_TEST(CellularCapabilityTest, GetModemInfo);
+  FRIEND_TEST(CellularCapabilityTest, GetModemStatus);
+  FRIEND_TEST(CellularServiceTest, FriendlyName);
+  FRIEND_TEST(CellularTest, StartCDMARegister);
+  FRIEND_TEST(CellularTest, StartConnected);
+  FRIEND_TEST(CellularTest, StartGSMRegister);
+  FRIEND_TEST(CellularTest, StartLinked);
+  FRIEND_TEST(CellularTest, Connect);
+  FRIEND_TEST(CellularTest, DisconnectModem);
 
   Cellular *cellular_;
+
+  // Store cached copies of singletons for speed/ease of testing.
   ProxyFactory *proxy_factory_;
 
+  scoped_ptr<ModemProxyInterface> proxy_;
+  scoped_ptr<ModemSimpleProxyInterface> simple_proxy_;
+
   DISALLOW_COPY_AND_ASSIGN(CellularCapability);
 };
 
diff --git a/cellular_capability_cdma.cc b/cellular_capability_cdma.cc
index bf03db6..85b9625 100644
--- a/cellular_capability_cdma.cc
+++ b/cellular_capability_cdma.cc
@@ -22,8 +22,9 @@
 
 const char CellularCapabilityCDMA::kPhoneNumber[] = "#777";
 
-CellularCapabilityCDMA::CellularCapabilityCDMA(Cellular *cellular)
-    : CellularCapability(cellular),
+CellularCapabilityCDMA::CellularCapabilityCDMA(Cellular *cellular,
+                                               ProxyFactory *proxy_factory)
+    : CellularCapability(cellular, proxy_factory),
       task_factory_(this),
       activation_state_(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED),
       registration_state_evdo_(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN),
@@ -34,14 +35,31 @@
   store->RegisterConstUint16(flimflam::kPRLVersionProperty, &prl_version_);
 }
 
-void CellularCapabilityCDMA::OnDeviceStarted() {
-  VLOG(2) << __func__;
+
+void CellularCapabilityCDMA::StartModem()
+{
+  CellularCapability::StartModem();
   proxy_.reset(proxy_factory()->CreateModemCDMAProxy(
       this, cellular()->dbus_path(), cellular()->dbus_owner()));
+
+  MultiStepAsyncCallHandler *call_handler =
+      new MultiStepAsyncCallHandler(cellular()->dispatcher());
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+      &CellularCapabilityCDMA::EnableModem, call_handler));
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+      &CellularCapabilityCDMA::GetModemStatus, call_handler));
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+      &CellularCapabilityCDMA::GetMEID, call_handler));
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+      &CellularCapabilityCDMA::GetModemInfo, call_handler));
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+      &CellularCapabilityCDMA::GetRegistrationState, call_handler));
+  call_handler->PostNextTask();
 }
 
-void CellularCapabilityCDMA::OnDeviceStopped() {
+void CellularCapabilityCDMA::StopModem() {
   VLOG(2) << __func__;
+  CellularCapability::StopModem();
   proxy_.reset();
 }
 
@@ -73,39 +91,23 @@
 
 void CellularCapabilityCDMA::SetupConnectProperties(
     DBusPropertiesMap *properties) {
-  (*properties)[Cellular::kConnectPropertyPhoneNumber].writer().append_string(
+  (*properties)[kConnectPropertyPhoneNumber].writer().append_string(
       kPhoneNumber);
 }
 
-void CellularCapabilityCDMA::Activate(const string &carrier, Error *error) {
+void CellularCapabilityCDMA::Activate(const string &carrier,
+                                      AsyncCallHandler *call_handler) {
   VLOG(2) << __func__ << "(" << carrier << ")";
   if (cellular()->state() != Cellular::kStateEnabled &&
       cellular()->state() != Cellular::kStateRegistered) {
-    Error::PopulateAndLog(error, Error::kInvalidArguments,
+    Error error;
+    Error::PopulateAndLog(&error, Error::kInvalidArguments,
                           "Unable to activate in " +
                           Cellular::GetStateString(cellular()->state()));
+    CompleteOperation(call_handler, error);
     return;
   }
-  // Defer because we may be in a dbus-c++ callback.
-  dispatcher()->PostTask(
-      task_factory_.NewRunnableMethod(
-          &CellularCapabilityCDMA::ActivateTask, carrier));
-}
-
-void CellularCapabilityCDMA::ActivateTask(const string &carrier) {
-  VLOG(2) << __func__ << "(" << carrier << ")";
-  if (cellular()->state() != Cellular::kStateEnabled &&
-      cellular()->state() != Cellular::kStateRegistered) {
-    LOG(ERROR) << "Unable to activate in "
-               << Cellular::GetStateString(cellular()->state());
-    return;
-  }
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  uint32 status = proxy_->Activate(carrier);
-  if (status == MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR) {
-    activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
-  }
-  HandleNewActivationState(status);
+  proxy_->Activate(carrier, call_handler, kTimeoutActivate);
 }
 
 void CellularCapabilityCDMA::HandleNewActivationState(uint32 error) {
@@ -152,16 +154,17 @@
   }
 }
 
-void CellularCapabilityCDMA::GetIdentifiers() {
+void CellularCapabilityCDMA::GetMEID(AsyncCallHandler *call_handler) {
   VLOG(2) << __func__;
-  if (cellular()->meid().empty()) {
+  if (meid_.empty()) {
     // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-    cellular()->set_meid(proxy_->MEID());
-    VLOG(2) << "MEID: " << cellular()->meid();
+    meid_ = proxy_->MEID();
+    VLOG(2) << "MEID: " << meid_;
   }
+  CompleteOperation(call_handler);
 }
 
-void CellularCapabilityCDMA::GetProperties() {
+void CellularCapabilityCDMA::GetProperties(AsyncCallHandler */*call_handler*/) {
   VLOG(2) << __func__;
   // No properties.
 }
@@ -207,20 +210,22 @@
   cellular()->HandleNewSignalQuality(strength);
 }
 
-void CellularCapabilityCDMA::GetRegistrationState() {
+void CellularCapabilityCDMA::GetRegistrationState(
+    AsyncCallHandler *call_handler) {
   VLOG(2) << __func__;
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
+  // TODO(petkov) Switch to asynchronous calls (crosbug.com/17583).
   proxy_->GetRegistrationState(
       &registration_state_1x_, &registration_state_evdo_);
   VLOG(2) << "CDMA Registration: 1x(" << registration_state_1x_
           << ") EVDO(" << registration_state_evdo_ << ")";
   cellular()->HandleNewRegistrationState();
+  CompleteOperation(call_handler);
 }
 
 string CellularCapabilityCDMA::CreateFriendlyServiceName() {
   VLOG(2) << __func__;
-  if (!cellular()->carrier().empty()) {
-    return cellular()->carrier();
+  if (!carrier_.empty()) {
+    return carrier_;
   }
   return base::StringPrintf("CDMANetwork%u", friendly_service_name_id_++);
 }
@@ -232,19 +237,30 @@
   }
 }
 
+void CellularCapabilityCDMA::OnActivateCallback(
+    uint32 status,
+    const Error &error,
+    AsyncCallHandler *call_handler) {
+  if (error.IsFailure()) {
+    CompleteOperation(call_handler, error);
+    return;
+  }
+
+  if (status == MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR) {
+    activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+  }
+  HandleNewActivationState(status);
+  CompleteOperation(call_handler);
+}
+
 void CellularCapabilityCDMA::OnCDMAActivationStateChanged(
     uint32 activation_state,
     uint32 activation_error,
     const DBusPropertiesMap &status_changes) {
   VLOG(2) << __func__;
   string mdn;
-  if (DBusProperties::GetString(status_changes, "mdn", &mdn)) {
-    cellular()->set_mdn(mdn);
-  }
-  string min;
-  if (DBusProperties::GetString(status_changes, "min", &min)) {
-    cellular()->set_min(min);
-  }
+  DBusProperties::GetString(status_changes, "mdn", &mdn_);
+  DBusProperties::GetString(status_changes, "min", &min_);
   if (DBusProperties::GetString(status_changes, "payment_url", &payment_url_) &&
       cellular()->service().get()) {
     cellular()->service()->set_payment_url(payment_url_);
diff --git a/cellular_capability_cdma.h b/cellular_capability_cdma.h
index c3c2dfe..7726e22 100644
--- a/cellular_capability_cdma.h
+++ b/cellular_capability_cdma.h
@@ -18,27 +18,27 @@
                                public ModemCDMAProxyDelegate {
 
  public:
-  CellularCapabilityCDMA(Cellular *cellular);
+  CellularCapabilityCDMA(Cellular *cellular, ProxyFactory *proxy_factory);
 
   // Inherited from CellularCapability.
-  virtual void OnDeviceStarted();
-  virtual void OnDeviceStopped();
+  virtual void StartModem();
+  virtual void StopModem();
   virtual void OnServiceCreated();
   virtual void UpdateStatus(const DBusPropertiesMap &properties);
   virtual void SetupConnectProperties(DBusPropertiesMap *properties);
-  virtual void Activate(const std::string &carrier, Error *error);
+  virtual void Activate(const std::string &carrier,
+                        AsyncCallHandler *call_handler);
   virtual bool IsRegistered();
   virtual std::string CreateFriendlyServiceName();
   virtual std::string GetNetworkTechnologyString() const;
   virtual std::string GetRoamingStateString() const;
   virtual void GetSignalQuality();
-  virtual void GetRegistrationState();
-  virtual void GetProperties();
+  virtual void GetRegistrationState(AsyncCallHandler *call_handler);
+  virtual void GetProperties(AsyncCallHandler *call_handler);
   virtual void OnModemManagerPropertiesChanged(
       const DBusPropertiesMap &properties);
 
-  // Obtains the MEID.
-  virtual void GetIdentifiers();
+  virtual void GetMEID(AsyncCallHandler *call_handler);
 
   uint32 activation_state() const { return activation_state_; }
   uint32 registration_state_evdo() const { return registration_state_evdo_; }
@@ -46,6 +46,8 @@
 
  private:
   friend class CellularCapabilityCDMATest;
+  FRIEND_TEST(CellularCapabilityCDMATest, Activate);
+  FRIEND_TEST(CellularCapabilityCDMATest, ActivateError);
   FRIEND_TEST(CellularCapabilityCDMATest, CreateFriendlyServiceName);
   FRIEND_TEST(CellularCapabilityCDMATest, GetActivationStateString);
   FRIEND_TEST(CellularCapabilityCDMATest, GetActivationErrorString);
@@ -53,8 +55,6 @@
 
   static const char kPhoneNumber[];
 
-  void ActivateTask(const std::string &carrier);
-
   void HandleNewActivationState(uint32 error);
 
   // Updates the serving operator on the active service.
@@ -71,6 +71,8 @@
   virtual void OnCDMARegistrationStateChanged(uint32 state_1x,
                                               uint32 state_evdo);
   virtual void OnCDMASignalQualityChanged(uint32 strength);
+  virtual void OnActivateCallback(uint32 status, const Error &error,
+                                  AsyncCallHandler *call_handler);
 
   scoped_ptr<ModemCDMAProxyInterface> proxy_;
 
diff --git a/cellular_capability_cdma_unittest.cc b/cellular_capability_cdma_unittest.cc
index 1b74025..64f3714 100644
--- a/cellular_capability_cdma_unittest.cc
+++ b/cellular_capability_cdma_unittest.cc
@@ -12,6 +12,7 @@
 #include "shill/cellular_service.h"
 #include "shill/error.h"
 #include "shill/event_dispatcher.h"
+#include "shill/mock_adaptors.h"
 #include "shill/mock_glib.h"
 #include "shill/mock_manager.h"
 #include "shill/mock_metrics.h"
@@ -24,6 +25,28 @@
 
 namespace shill {
 
+class TestAsyncCallHandler : public AsyncCallHandler {
+public:
+  explicit TestAsyncCallHandler(ReturnerInterface *returner)
+      : AsyncCallHandler(returner) { }
+  virtual ~TestAsyncCallHandler() { }
+
+  bool CompleteOperationWithError(const Error &error) {
+    error_.Populate(error.type(), error.message());
+    return AsyncCallHandler::CompleteOperationWithError(error);
+  }
+
+  static const Error &error() { return error_; }
+
+private:
+  // static because AsyncCallHandlers are deleted before callbacks return
+  static Error error_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestAsyncCallHandler);
+};
+
+Error TestAsyncCallHandler::error_;
+
 class CellularCapabilityCDMATest : public testing::Test {
  public:
   CellularCapabilityCDMATest()
@@ -95,15 +118,17 @@
 }
 
 TEST_F(CellularCapabilityCDMATest, Activate) {
-  Error error;
   SetDeviceState(Cellular::kStateEnabled);
-  EXPECT_CALL(*proxy_, Activate(kTestCarrier))
-      .WillOnce(Return(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR));
-  capability_->Activate(kTestCarrier, &error);
-  EXPECT_TRUE(error.IsSuccess());
+  EXPECT_CALL(*proxy_,
+              Activate(kTestCarrier, _, CellularCapability::kTimeoutActivate));
+  MockReturner returner;
+  scoped_ptr<TestAsyncCallHandler> handler(new TestAsyncCallHandler(&returner));
   SetProxy();
+  capability_->Activate(kTestCarrier, handler.get());
   SetService();
-  dispatcher_.DispatchPendingEvents();
+  capability_->OnActivateCallback(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR,
+                                  Error(), NULL);
+  EXPECT_TRUE(TestAsyncCallHandler::error().IsSuccess());
   EXPECT_EQ(MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING,
             capability_->activation_state());
   EXPECT_EQ(flimflam::kActivationStateActivating,
@@ -112,19 +137,21 @@
 }
 
 TEST_F(CellularCapabilityCDMATest, ActivateError) {
-  Error error;
-  capability_->Activate(kTestCarrier, &error);
-  EXPECT_EQ(Error::kInvalidArguments, error.type());
+  MockReturner returner;
+  TestAsyncCallHandler *handler = new TestAsyncCallHandler(&returner);
+  capability_->Activate(kTestCarrier, handler);
+  EXPECT_EQ(Error::kInvalidArguments, TestAsyncCallHandler::error().type());
 
-  error.Reset();
+  handler = new TestAsyncCallHandler(&returner);
   SetDeviceState(Cellular::kStateRegistered);
-  EXPECT_CALL(*proxy_, Activate(kTestCarrier))
-      .WillOnce(Return(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_SIGNAL));
-  capability_->Activate(kTestCarrier, &error);
-  EXPECT_TRUE(error.IsSuccess());
+  EXPECT_CALL(*proxy_, Activate(kTestCarrier, handler,
+                                CellularCapability::kTimeoutActivate));
   SetProxy();
   SetService();
-  dispatcher_.DispatchPendingEvents();
+  capability_->Activate(kTestCarrier, handler);
+  capability_->OnActivateCallback(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_SIGNAL,
+                                  Error(), handler);
+  EXPECT_TRUE(TestAsyncCallHandler::error().IsFailure());
   EXPECT_EQ(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED,
             capability_->activation_state());
   EXPECT_EQ(flimflam::kActivationStateNotActivated,
@@ -176,15 +203,6 @@
             CellularCapabilityCDMA::GetActivationErrorString(1234));
 }
 
-TEST_F(CellularCapabilityCDMATest, GetIdentifiers) {
-  EXPECT_CALL(*proxy_, MEID()).WillOnce(Return(kMEID));
-  SetProxy();
-  capability_->GetIdentifiers();
-  EXPECT_EQ(kMEID, cellular_->meid());
-  capability_->GetIdentifiers();
-  EXPECT_EQ(kMEID, cellular_->meid());
-}
-
 TEST_F(CellularCapabilityCDMATest, IsRegisteredEVDO) {
   EXPECT_FALSE(capability_->IsRegistered());
   SetRegistrationStateEVDO(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN);
@@ -265,7 +283,7 @@
   EXPECT_CALL(*proxy_, GetSignalQuality()).WillOnce(Return(90));
   SetProxy();
   EXPECT_CALL(manager_, RegisterService(_));
-  capability_->GetRegistrationState();
+  capability_->GetRegistrationState(NULL);
   dispatcher_.DispatchPendingEvents();
   EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
             capability_->registration_state_1x());
@@ -279,7 +297,7 @@
   EXPECT_EQ("CDMANetwork0", capability_->CreateFriendlyServiceName());
   EXPECT_EQ("CDMANetwork1", capability_->CreateFriendlyServiceName());
   static const char kTestCarrier[] = "A Carrier";
-  cellular_->carrier_ = kTestCarrier;
+  capability_->carrier_ = kTestCarrier;
   EXPECT_EQ(kTestCarrier, capability_->CreateFriendlyServiceName());
 }
 
diff --git a/cellular_capability_gsm.cc b/cellular_capability_gsm.cc
index d5e25f5..1590ac9 100644
--- a/cellular_capability_gsm.cc
+++ b/cellular_capability_gsm.cc
@@ -14,6 +14,7 @@
 
 #include "shill/adaptor_interfaces.h"
 #include "shill/cellular_service.h"
+#include "shill/error.h"
 #include "shill/property_accessor.h"
 #include "shill/proxy_factory.h"
 
@@ -38,8 +39,9 @@
 const char CellularCapabilityGSM::kPropertyUnlockRequired[] = "UnlockRequired";
 const char CellularCapabilityGSM::kPropertyUnlockRetries[] = "UnlockRetries";
 
-CellularCapabilityGSM::CellularCapabilityGSM(Cellular *cellular)
-    : CellularCapability(cellular),
+CellularCapabilityGSM::CellularCapabilityGSM(Cellular *cellular,
+                                             ProxyFactory *proxy_factory)
+    : CellularCapability(cellular, proxy_factory),
       task_factory_(this),
       registration_state_(MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN),
       access_technology_(MM_MODEM_GSM_ACCESS_TECH_UNKNOWN),
@@ -79,8 +81,9 @@
               this, get, set)));
 }
 
-void CellularCapabilityGSM::OnDeviceStarted() {
-  VLOG(2) << __func__;
+void CellularCapabilityGSM::StartModem()
+{
+  CellularCapability::StartModem();
   card_proxy_.reset(
       proxy_factory()->CreateModemGSMCardProxy(this,
                                                cellular()->dbus_path(),
@@ -89,10 +92,44 @@
       proxy_factory()->CreateModemGSMNetworkProxy(this,
                                                   cellular()->dbus_path(),
                                                   cellular()->dbus_owner()));
+  MultiStepAsyncCallHandler *call_handler =
+      new MultiStepAsyncCallHandler(cellular()->dispatcher());
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+                      &CellularCapabilityGSM::EnableModem, call_handler));
+
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+                      &CellularCapabilityGSM::Register, call_handler));
+
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+                      &CellularCapabilityGSM::GetModemStatus, call_handler));
+
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+                      &CellularCapabilityGSM::GetIMEI, call_handler));
+
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+                      &CellularCapabilityGSM::GetIMSI, call_handler));
+
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+                      &CellularCapabilityGSM::GetSPN, call_handler));
+
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+                      &CellularCapabilityGSM::GetMSISDN, call_handler));
+
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+                      &CellularCapabilityGSM::GetProperties, call_handler));
+
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+                      &CellularCapabilityGSM::GetModemInfo, call_handler));
+
+  call_handler->AddTask(task_factory_.NewRunnableMethod(
+                      &CellularCapabilityGSM::GetRegistrationState,
+                      call_handler));
+  call_handler->PostNextTask();
 }
 
-void CellularCapabilityGSM::OnDeviceStopped() {
+void CellularCapabilityGSM::StopModem() {
   VLOG(2) << __func__;
+  CellularCapability::StopModem();
   card_proxy_.reset();
   network_proxy_.reset();
 }
@@ -104,53 +141,56 @@
 }
 
 void CellularCapabilityGSM::UpdateStatus(const DBusPropertiesMap &properties) {
-  if (ContainsKey(properties, Cellular::kPropertyIMSI)) {
+  if (ContainsKey(properties, kPropertyIMSI)) {
     SetHomeProvider();
   }
 }
 
 void CellularCapabilityGSM::SetupConnectProperties(
     DBusPropertiesMap *properties) {
-  (*properties)[Cellular::kConnectPropertyPhoneNumber].writer().append_string(
+  (*properties)[kConnectPropertyPhoneNumber].writer().append_string(
       kPhoneNumber);
   // TODO(petkov): Setup apn and "home_only".
 }
 
-void CellularCapabilityGSM::GetIdentifiers() {
+void CellularCapabilityGSM::GetIMEI(AsyncCallHandler *call_handler){
   VLOG(2) << __func__;
-  if (cellular()->imei().empty()) {
-    // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-    cellular()->set_imei(card_proxy_->GetIMEI());
+  if (imei_.empty()) {
+    card_proxy_->GetIMEI(call_handler, kTimeoutDefault);
+  } else {
+    VLOG(2) << "Already have IMEI " << imei_;
+    CompleteOperation(call_handler);
   }
-  if (cellular()->imsi().empty()) {
-    // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-    try {
-      cellular()->set_imsi(card_proxy_->GetIMSI());
-      VLOG(2) << "IMSI: " << cellular()->imsi();
-    } catch (const DBus::Error e) {
-      LOG(WARNING) << "Unable to obtain IMSI: " << e.what();
-    }
+}
+
+void CellularCapabilityGSM::GetIMSI(AsyncCallHandler *call_handler){
+  VLOG(2) << __func__;
+  if (imsi_.empty()) {
+    card_proxy_->GetIMSI(call_handler, kTimeoutDefault);
+  } else {
+    VLOG(2) << "Already have IMSI " << imsi_;
+    CompleteOperation(call_handler);
   }
+}
+
+void CellularCapabilityGSM::GetSPN(AsyncCallHandler *call_handler){
+  VLOG(2) << __func__;
   if (spn_.empty()) {
-    // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-    try {
-      spn_ =  card_proxy_->GetSPN();
-      VLOG(2) << "SPN: " << spn_;
-    } catch (const DBus::Error e) {
-      // Some modems don't support this call so catch the exception explicitly.
-      LOG(WARNING) << "Unable to obtain SPN: " << e.what();
-    }
+    card_proxy_->GetSPN(call_handler, kTimeoutDefault);
+  } else {
+    VLOG(2) << "Already have SPN " << spn_;
+    CompleteOperation(call_handler);
   }
-  if (cellular()->mdn().empty()) {
-    try {
-      // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-      cellular()->set_mdn(card_proxy_->GetMSISDN());
-      VLOG(2) << "MSISDN/MDN: " << cellular()->mdn();
-    } catch (const DBus::Error e) {
-      LOG(WARNING) << "Unable to obtain MSISDN/MDN: " << e.what();
-    }
+}
+
+void CellularCapabilityGSM::GetMSISDN(AsyncCallHandler *call_handler){
+  VLOG(2) << __func__;
+  if (mdn_.empty()) {
+    card_proxy_->GetMSISDN(call_handler, kTimeoutDefault);
+  } else {
+    VLOG(2) << "Already have MSISDN " << mdn_;
+    CompleteOperation(call_handler);
   }
-  SetHomeProvider();
 }
 
 void CellularCapabilityGSM::GetSignalQuality() {
@@ -160,27 +200,19 @@
   cellular()->HandleNewSignalQuality(strength);
 }
 
-void CellularCapabilityGSM::GetRegistrationState() {
+void CellularCapabilityGSM::GetRegistrationState(
+    AsyncCallHandler *call_handler) {
   VLOG(2) << __func__;
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  ModemGSMNetworkProxyInterface::RegistrationInfo info =
-      network_proxy_->GetRegistrationInfo();
-  registration_state_ = info._1;
-  serving_operator_.SetCode(info._2);
-  serving_operator_.SetName(info._3);
-  VLOG(2) << "GSM Registration: " << registration_state_ << ", "
-          << serving_operator_.GetCode() << ", "
-          << serving_operator_.GetName();
-  UpdateOperatorInfo();
-  cellular()->HandleNewRegistrationState();
+  network_proxy_->GetRegistrationInfo(call_handler, kTimeoutDefault);
 }
 
-void CellularCapabilityGSM::GetProperties() {
+void CellularCapabilityGSM::GetProperties(AsyncCallHandler *call_handler) {
   VLOG(2) << __func__;
   // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
   uint32 tech = network_proxy_->AccessTechnology();
   SetAccessTechnology(tech);
   VLOG(2) << "GSM AccessTechnology: " << tech;
+  CompleteOperation(call_handler);
 }
 
 string CellularCapabilityGSM::CreateFriendlyServiceName() {
@@ -192,8 +224,8 @@
   if (!serving_operator_.GetName().empty()) {
     return serving_operator_.GetName();
   }
-  if (!cellular()->carrier().empty()) {
-    return cellular()->carrier();
+  if (!carrier_.empty()) {
+    return carrier_;
   }
   if (!serving_operator_.GetCode().empty()) {
     return "cellular_" + serving_operator_.GetCode();
@@ -202,16 +234,16 @@
 }
 
 void CellularCapabilityGSM::SetHomeProvider() {
-  VLOG(2) << __func__ << "(IMSI: " << cellular()->imsi()
+  VLOG(2) << __func__ << "(IMSI: " << imsi_
           << " SPN: " << spn_ << ")";
   // TODO(petkov): The test for NULL provider_db should be done by
   // mobile_provider_lookup_best_match.
-  if (cellular()->imsi().empty() || !cellular()->provider_db()) {
+  if (imsi_.empty() || !cellular()->provider_db()) {
     return;
   }
   mobile_provider *provider =
       mobile_provider_lookup_best_match(
-          cellular()->provider_db(), spn_.c_str(), cellular()->imsi().c_str());
+          cellular()->provider_db(), spn_.c_str(), imsi_.c_str());
   if (!provider) {
     VLOG(2) << "GSM provider not found.";
     return;
@@ -311,22 +343,39 @@
       flimflam::kCellularApnListProperty, apn_list_);
 }
 
-void CellularCapabilityGSM::Register() {
-  LOG(INFO) << __func__ << " \"" << selected_network_ << "\"";
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  network_proxy_->Register(selected_network_);
-  // TODO(petkov): Handle registration failure including trying the home network
-  // when selected_network_ is not empty.
+void CellularCapabilityGSM::Register(AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << " \"" << selected_network_ << "\"";
+  network_proxy_->Register(selected_network_, call_handler,
+                           kTimeoutRegister);
 }
 
 void CellularCapabilityGSM::RegisterOnNetwork(
-    const string &network_id, Error */*error*/) {
-  LOG(INFO) << __func__ << "(" << network_id << ")";
-  // Defer because we may be in a dbus-c++ callback.
-  dispatcher()->PostTask(
-      task_factory_.NewRunnableMethod(
-          &CellularCapabilityGSM::RegisterOnNetworkTask,
-          network_id));
+    const string &network_id, AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << "(" << network_id << ")";
+  desired_network_ = network_id;
+  network_proxy_->Register(network_id, call_handler,
+                           kTimeoutRegister);
+}
+
+void CellularCapabilityGSM::OnRegisterCallback(const Error &error,
+                                               AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << "(" << error << ")";
+
+  if (error.IsSuccess()) {
+    selected_network_ = desired_network_;
+    desired_network_.clear();
+    CompleteOperation(call_handler);
+    return;
+  }
+  // If registration on the desired network failed,
+  // try to register on the home network.
+  if (!desired_network_.empty()) {
+    desired_network_.clear();
+    selected_network_.clear();
+    Register(call_handler);
+    return;
+  }
+  CompleteOperation(call_handler, error);
 }
 
 bool CellularCapabilityGSM::IsRegistered() {
@@ -334,134 +383,60 @@
           registration_state_ == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING);
 }
 
-void CellularCapabilityGSM::RegisterOnNetworkTask(const string &network_id) {
-  LOG(INFO) << __func__ << "(" << network_id << ")";
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  network_proxy_->Register(network_id);
-  // TODO(petkov): Handle registration failure.
-  selected_network_ = network_id;
-}
-
 void CellularCapabilityGSM::RequirePIN(
-    const string &pin, bool require, ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  // Defer because we may be in a dbus-c++ callback.
-  dispatcher()->PostTask(
-      task_factory_.NewRunnableMethod(
-          &CellularCapabilityGSM::RequirePINTask, pin, require, returner));
-}
-
-void CellularCapabilityGSM::RequirePINTask(
-    const string &pin, bool require, ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  try {
-    card_proxy_->EnablePIN(pin, require);
-  } catch (DBus::Error e) {
-    LOG(ERROR) << "EnablePIN failed: " << e.name() << "/" << e.message();
-    returner->ReturnError(Error(Error::kInternalError));
-    return;
-  }
-  returner->Return();
+    const string &pin, bool require, AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << "(" << call_handler << ")";
+  card_proxy_->EnablePIN(pin, require, call_handler, kTimeoutDefault);
 }
 
 void CellularCapabilityGSM::EnterPIN(const string &pin,
-                                     ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  // Defer because we may be in a dbus-c++ callback.
-  dispatcher()->PostTask(
-      task_factory_.NewRunnableMethod(
-          &CellularCapabilityGSM::EnterPINTask, pin, returner));
-}
-
-void CellularCapabilityGSM::EnterPINTask(const string &pin,
-                                         ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  try {
-    card_proxy_->SendPIN(pin);
-  } catch (DBus::Error e) {
-    LOG(ERROR) << "EnterPIN failed: " << e.name() << "/" << e.message();
-    returner->ReturnError(Error(Error::kInternalError));
-    return;
-  }
-  returner->Return();
+                                     AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << "(" << call_handler << ")";
+  card_proxy_->SendPIN(pin, call_handler, kTimeoutDefault);
 }
 
 void CellularCapabilityGSM::UnblockPIN(const string &unblock_code,
                                        const string &pin,
-                                       ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  // Defer because we may be in a dbus-c++ callback.
-  dispatcher()->PostTask(
-      task_factory_.NewRunnableMethod(
-          &CellularCapabilityGSM::UnblockPINTask, unblock_code, pin, returner));
-}
-
-void CellularCapabilityGSM::UnblockPINTask(const string &unblock_code,
-                                           const string &pin,
-                                           ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  try {
-    card_proxy_->SendPUK(unblock_code, pin);
-  } catch (DBus::Error e) {
-    LOG(ERROR) << "SendPUK failed: " << e.name() << "/" << e.message();
-    returner->ReturnError(Error(Error::kInternalError));
-    return;
-  }
-  returner->Return();
+                                       AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << "(" << call_handler << ")";
+  card_proxy_->SendPUK(unblock_code, pin, call_handler,
+                       kTimeoutDefault);
 }
 
 void CellularCapabilityGSM::ChangePIN(
-    const string &old_pin, const string &new_pin, ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  // Defer because we may be in a dbus-c++ callback.
-  dispatcher()->PostTask(
-      task_factory_.NewRunnableMethod(
-          &CellularCapabilityGSM::ChangePINTask, old_pin, new_pin, returner));
+    const string &old_pin, const string &new_pin,
+    AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__ << "(" << call_handler << ")";
+  card_proxy_->ChangePIN(old_pin, new_pin, call_handler,
+                         kTimeoutDefault);
 }
 
-void CellularCapabilityGSM::ChangePINTask(
-    const string &old_pin, const string &new_pin, ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  try {
-    card_proxy_->ChangePIN(old_pin, new_pin);
-  } catch (DBus::Error e) {
-    LOG(ERROR) << "ChangePIN failed: " << e.name() << "/" << e.message();
-    returner->ReturnError(Error(Error::kInternalError));
-    return;
-  }
-  returner->Return();
-}
-
-void CellularCapabilityGSM::Scan(Error */*error*/) {
-  VLOG(2) << __func__;
-  // Defer because we may be in a dbus-c++ callback.
-  dispatcher()->PostTask(
-      task_factory_.NewRunnableMethod(&CellularCapabilityGSM::ScanTask));
-}
-
-void CellularCapabilityGSM::ScanTask() {
+void CellularCapabilityGSM::Scan(AsyncCallHandler *call_handler) {
   VLOG(2) << __func__;
   // TODO(petkov): Defer scan requests if a scan is in progress already.
-  //
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583). This is a
-  // must for this call which is basically a stub at this point.
-  ModemGSMNetworkProxyInterface::ScanResults results = network_proxy_->Scan();
-  found_networks_.clear();
-  for (ModemGSMNetworkProxyInterface::ScanResults::const_iterator it =
-           results.begin(); it != results.end(); ++it) {
-    found_networks_.push_back(ParseScanResult(*it));
-  }
+  network_proxy_->Scan(call_handler, kTimeoutScan);
 }
 
-Stringmap CellularCapabilityGSM::ParseScanResult(
-    const ModemGSMNetworkProxyInterface::ScanResult &result) {
+void CellularCapabilityGSM::OnScanCallback(const GSMScanResults &results,
+                                           const Error &error,
+                                           AsyncCallHandler *call_handler) {
+  VLOG(2) << __func__;
+  if (error.IsFailure()) {
+    CompleteOperation(call_handler, error);
+    return;
+  }
+  found_networks_.clear();
+  for (GSMScanResults::const_iterator it = results.begin();
+       it != results.end(); ++it) {
+    found_networks_.push_back(ParseScanResult(*it));
+  }
+  CompleteOperation(call_handler);
+}
+
+Stringmap CellularCapabilityGSM::ParseScanResult(const GSMScanResult &result) {
   Stringmap parsed;
-  for (ModemGSMNetworkProxyInterface::ScanResult::const_iterator it =
-           result.begin(); it != result.end(); ++it) {
+  for (GSMScanResult::const_iterator it = result.begin();
+       it != result.end(); ++it) {
     // TODO(petkov): Define these in system_api/service_constants.h. The
     // numerical values are taken from 3GPP TS 27.007 Section 7.3.
     static const char * const kStatusString[] = {
@@ -589,16 +564,83 @@
 }
 
 void CellularCapabilityGSM::OnGSMRegistrationInfoChanged(
-    uint32 status, const string &operator_code, const string &operator_name) {
-  registration_state_ = status;
-  serving_operator_.SetCode(operator_code);
-  serving_operator_.SetName(operator_name);
-  UpdateOperatorInfo();
-  cellular()->HandleNewRegistrationState();
+    uint32 status, const string &operator_code, const string &operator_name,
+    const Error &error, AsyncCallHandler *call_handler) {
+  if (error.IsSuccess()) {
+    VLOG(2) << __func__ << ": regstate=" << status
+            << ", opercode=" << operator_code
+            << ", opername=" << operator_name;
+    registration_state_ = status;
+    serving_operator_.SetCode(operator_code);
+    serving_operator_.SetName(operator_name);
+    UpdateOperatorInfo();
+    cellular()->HandleNewRegistrationState();
+  }
+  CompleteOperation(call_handler, error);
 }
 
 void CellularCapabilityGSM::OnGSMSignalQualityChanged(uint32 quality) {
   cellular()->HandleNewSignalQuality(quality);
 }
 
+void CellularCapabilityGSM::OnGetIMEICallback(const string &imei,
+                                              const Error &error,
+                                              AsyncCallHandler *call_handler) {
+  if (error.IsSuccess()) {
+    VLOG(2) << "IMEI: " << imei;
+    imei_ = imei;
+  } else {
+    VLOG(2) << "GetIMEI failed - " << error;
+  }
+  CompleteOperation(call_handler, error);
+}
+
+void CellularCapabilityGSM::OnGetIMSICallback(const string &imsi,
+                                              const Error &error,
+                                              AsyncCallHandler *call_handler) {
+  if (error.IsSuccess()) {
+    VLOG(2) << "IMSI: " << imsi;
+    imsi_ = imsi;
+    SetHomeProvider();
+  } else {
+    VLOG(2) << "GetIMSI failed - " << error;
+  }
+  CompleteOperation(call_handler, error);
+}
+
+void CellularCapabilityGSM::OnGetSPNCallback(const string &spn,
+                                             const Error &error,
+                                             AsyncCallHandler *call_handler) {
+  if (error.IsSuccess()) {
+    VLOG(2) << "SPN: " << spn;
+    spn_ = spn;
+    SetHomeProvider();
+  } else {
+    VLOG(2) << "GetSPN failed - " << error;
+  }
+  // Ignore the error - it's not fatal.
+  CompleteOperation(call_handler);
+}
+
+void CellularCapabilityGSM::OnGetMSISDNCallback(
+    const string &msisdn, const Error &error, AsyncCallHandler *call_handler) {
+  if (error.IsSuccess()) {
+    VLOG(2) << "MSISDN: " << msisdn;
+    mdn_ = msisdn;
+  } else {
+    VLOG(2) << "GetMSISDN failed - " << error;
+  }
+  // Ignore the error - it's not fatal.
+  CompleteOperation(call_handler);
+}
+
+void CellularCapabilityGSM::OnPINOperationCallback(
+    const Error &error, AsyncCallHandler *call_handler) {
+  if (error.IsFailure())
+    VLOG(2) << "PIN operation failed - " << error;
+  else
+    VLOG(2) << "PIN operation complete";
+  CompleteOperation(call_handler, error);
+}
+
 }  // namespace shill
diff --git a/cellular_capability_gsm.h b/cellular_capability_gsm.h
index fe1001e..d19738d 100644
--- a/cellular_capability_gsm.h
+++ b/cellular_capability_gsm.h
@@ -26,39 +26,41 @@
   static const char kPropertyUnlockRequired[];
   static const char kPropertyUnlockRetries[];
 
-  CellularCapabilityGSM(Cellular *cellular);
+  CellularCapabilityGSM(Cellular *cellular, ProxyFactory *proxy_factory);
 
   // Inherited from CellularCapability.
-  virtual void OnDeviceStarted();
-  virtual void OnDeviceStopped();
+  virtual void StartModem();
+  virtual void StopModem();
   virtual void OnServiceCreated();
   virtual void UpdateStatus(const DBusPropertiesMap &properties);
   virtual void SetupConnectProperties(DBusPropertiesMap *properties);
   virtual void GetSignalQuality();
-  virtual void GetRegistrationState();
-  virtual void GetProperties();
-  virtual void Register();
-  virtual void RegisterOnNetwork(const std::string &network_id, Error *error);
+  virtual void GetRegistrationState(AsyncCallHandler *call_handler);
+  virtual void GetProperties(AsyncCallHandler *call_handler);
+  virtual void GetIMEI(AsyncCallHandler *call_handler);
+  virtual void GetIMSI(AsyncCallHandler *call_handler);
+  virtual void GetSPN(AsyncCallHandler *call_handler);
+  virtual void GetMSISDN(AsyncCallHandler *call_handler);
+  virtual void Register(AsyncCallHandler *call_handler);
+  virtual void RegisterOnNetwork(const std::string &network_id,
+                                 AsyncCallHandler *call_handler);
   virtual bool IsRegistered();
   virtual std::string CreateFriendlyServiceName();
   virtual void RequirePIN(
-      const std::string &pin, bool require, ReturnerInterface *returner);
-  virtual void EnterPIN(const std::string &pin, ReturnerInterface *returner);
+      const std::string &pin, bool require, AsyncCallHandler *call_handler);
+  virtual void EnterPIN(const std::string &pin, AsyncCallHandler *call_handler);
   virtual void UnblockPIN(const std::string &unblock_code,
                           const std::string &pin,
-                          ReturnerInterface *returner);
+                          AsyncCallHandler *call_handler);
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
-                         ReturnerInterface *returner);
-  virtual void Scan(Error *error);
+                         AsyncCallHandler *call_handler);
+  virtual void Scan(AsyncCallHandler *call_handler);
   virtual std::string GetNetworkTechnologyString() const;
   virtual std::string GetRoamingStateString() const;
   virtual void OnModemManagerPropertiesChanged(
       const DBusPropertiesMap &properties);
 
-  // Obtains the IMEI, IMSI, SPN and MSISDN.
-  virtual void GetIdentifiers();
-
   const std::string &spn() const { return spn_; }
   const std::string &sim_lock_type() const {
     return sim_lock_status_.lock_type;
@@ -66,8 +68,17 @@
   uint32 sim_lock_retries_left() const { return sim_lock_status_.retries_left; }
 
  private:
+  friend class CellularTest;
   friend class CellularCapabilityGSMTest;
   FRIEND_TEST(CellularCapabilityGSMTest, CreateFriendlyServiceName);
+  FRIEND_TEST(CellularCapabilityGSMTest, GetIMEI);
+  FRIEND_TEST(CellularCapabilityGSMTest, GetIMSI);
+  FRIEND_TEST(CellularCapabilityGSMTest, GetMSISDN);
+  FRIEND_TEST(CellularCapabilityGSMTest, GetSPN);
+  FRIEND_TEST(CellularCapabilityGSMTest, RequirePIN);
+  FRIEND_TEST(CellularCapabilityGSMTest, EnterPIN);
+  FRIEND_TEST(CellularCapabilityGSMTest, UnblockPIN);
+  FRIEND_TEST(CellularCapabilityGSMTest, ChangePIN);
   FRIEND_TEST(CellularCapabilityGSMTest, InitAPNList);
   FRIEND_TEST(CellularCapabilityGSMTest, ParseScanResult);
   FRIEND_TEST(CellularCapabilityGSMTest, ParseScanResultProviderLookup);
@@ -76,6 +87,7 @@
   FRIEND_TEST(CellularCapabilityGSMTest, SetAccessTechnology);
   FRIEND_TEST(CellularCapabilityGSMTest, SetHomeProvider);
   FRIEND_TEST(CellularCapabilityGSMTest, UpdateOperatorInfo);
+  FRIEND_TEST(CellularCapabilityGSMTest, GetRegistrationState);
 
   struct SimLockStatus {
    public:
@@ -96,18 +108,6 @@
   static const char kPhoneNumber[];
   static const char kPropertyAccessTechnology[];
 
-  void RegisterOnNetworkTask(const std::string &network_id);
-  void RequirePINTask(
-      const std::string &pin, bool require, ReturnerInterface *returner);
-  void EnterPINTask(const std::string &pin, ReturnerInterface *returner);
-  void UnblockPINTask(const std::string &unblock_code,
-                      const std::string &pin,
-                      ReturnerInterface *returner);
-  void ChangePINTask(const std::string &old_pin,
-                     const std::string &new_pin,
-                     ReturnerInterface *returner);
-  void ScanTask();
-
   void SetAccessTechnology(uint32 access_technology);
 
   // Sets the upper level information about the home cellular provider from the
@@ -124,8 +124,7 @@
   // Initializes the |apn_list_| property based on the current |home_provider_|.
   void InitAPNList();
 
-  Stringmap ParseScanResult(
-      const ModemGSMNetworkProxyInterface::ScanResult &result);
+  Stringmap ParseScanResult(const GSMScanResult &result);
 
   StrIntPair SimLockStatusToProperty(Error *error);
 
@@ -138,8 +137,31 @@
   virtual void OnGSMNetworkModeChanged(uint32 mode);
   virtual void OnGSMRegistrationInfoChanged(uint32 status,
                                             const std::string &operator_code,
-                                            const std::string &operator_name);
+                                            const std::string &operator_name,
+                                            const Error &error,
+                                            AsyncCallHandler *call_handler);
   virtual void OnGSMSignalQualityChanged(uint32 quality);
+  //
+  // Method callbacks inherited from ModemGSMNetworkProxyDelegate.
+  virtual void OnRegisterCallback(const Error &error,
+                                  AsyncCallHandler *call_handler);
+  virtual void OnGetIMEICallback(const std::string &imei,
+                                 const Error &error,
+                                 AsyncCallHandler *call_handler);
+  virtual void OnGetIMSICallback(const std::string &imsi,
+                                 const Error &error,
+                                 AsyncCallHandler *call_handler);
+  virtual void OnGetSPNCallback(const std::string &spn,
+                                const Error &error,
+                                AsyncCallHandler *call_handler);
+  virtual void OnGetMSISDNCallback(const std::string &msisdn,
+                                   const Error &error,
+                                   AsyncCallHandler *call_handler);
+  virtual void OnPINOperationCallback(const Error &error,
+                                      AsyncCallHandler *call_handler);
+  virtual void OnScanCallback(const GSMScanResults &results,
+                              const Error &error,
+                              AsyncCallHandler *call_handler);
 
   scoped_ptr<ModemGSMCardProxyInterface> card_proxy_;
   scoped_ptr<ModemGSMNetworkProxyInterface> network_proxy_;
@@ -151,6 +173,7 @@
   Cellular::Operator serving_operator_;
   std::string spn_;
   mobile_provider *home_provider_;
+  std::string desired_network_;
 
   // Properties.
   std::string selected_network_;
diff --git a/cellular_capability_gsm_unittest.cc b/cellular_capability_gsm_unittest.cc
index 831a1ec..64aac79 100644
--- a/cellular_capability_gsm_unittest.cc
+++ b/cellular_capability_gsm_unittest.cc
@@ -25,6 +25,28 @@
 
 namespace shill {
 
+class GSMTestAsyncCallHandler : public AsyncCallHandler {
+public:
+  explicit GSMTestAsyncCallHandler(ReturnerInterface *returner)
+      : AsyncCallHandler(returner) { }
+  virtual ~GSMTestAsyncCallHandler() { }
+
+  bool CompleteOperationWithError(const Error &error) {
+    error_.Populate(error.type(), error.message());
+    return AsyncCallHandler::CompleteOperationWithError(error);
+  }
+
+  static const Error &error() { return error_; }
+
+private:
+  // static because AsyncCallHandlers are deleted before callbacks return
+  static Error error_;
+
+  DISALLOW_COPY_AND_ASSIGN(GSMTestAsyncCallHandler);
+};
+
+Error GSMTestAsyncCallHandler::error_;
+
 class CellularCapabilityGSMTest : public testing::Test {
  public:
   CellularCapabilityGSMTest()
@@ -62,6 +84,7 @@
 
  protected:
   static const char kTestMobileProviderDBPath[];
+  static const char kTestNetwork[];
   static const char kTestCarrier[];
   static const char kPIN[];
   static const char kPUK[];
@@ -110,6 +133,7 @@
 const char CellularCapabilityGSMTest::kTestMobileProviderDBPath[] =
     "provider_db_unittest.bfd";
 const char CellularCapabilityGSMTest::kTestCarrier[] = "The Cellular Carrier";
+const char CellularCapabilityGSMTest::kTestNetwork[] = "310555";
 const char CellularCapabilityGSMTest::kPIN[] = "9876";
 const char CellularCapabilityGSMTest::kPUK[] = "8765";
 const char CellularCapabilityGSMTest::kIMEI[] = "987654321098765";
@@ -120,26 +144,39 @@
   EXPECT_TRUE(cellular_->store().Contains(flimflam::kSIMLockStatusProperty));
 }
 
-TEST_F(CellularCapabilityGSMTest, GetIdentifiers) {
-  EXPECT_CALL(*card_proxy_, GetIMEI()).WillOnce(Return(kIMEI));
-  EXPECT_CALL(*card_proxy_, GetIMSI()).WillOnce(Return(kIMSI));
-  EXPECT_CALL(*card_proxy_, GetSPN()).WillOnce(Return(kTestCarrier));
-  EXPECT_CALL(*card_proxy_, GetMSISDN()).WillOnce(Return(kMSISDN));
+TEST_F(CellularCapabilityGSMTest, GetIMEI) {
+  EXPECT_CALL(*card_proxy_, GetIMEI(NULL, _));
   SetCardProxy();
+  capability_->GetIMEI(NULL);
+  capability_->OnGetIMEICallback(kIMEI, Error(), NULL);
+  EXPECT_EQ(kIMEI, capability_->imei_);
+}
+
+TEST_F(CellularCapabilityGSMTest, GetIMSI) {
+  EXPECT_CALL(*card_proxy_, GetIMSI(NULL, _));
+  SetCardProxy();
+  capability_->GetIMSI(NULL);
+  capability_->OnGetIMSICallback(kIMSI, Error(), NULL);
+  EXPECT_EQ(kIMSI, capability_->imsi_);
   InitProviderDB();
-  capability_->GetIdentifiers();
-  EXPECT_EQ(kIMEI, cellular_->imei());
-  EXPECT_EQ(kIMSI, cellular_->imsi());
-  EXPECT_EQ(kTestCarrier, capability_->spn());
-  EXPECT_EQ(kMSISDN, cellular_->mdn());
-  EXPECT_EQ(kTestCarrier, cellular_->home_provider().GetName());
-  EXPECT_EQ("us", cellular_->home_provider().GetCountry());
-  EXPECT_EQ("310038", cellular_->home_provider().GetCode());
-  capability_->GetIdentifiers();
-  EXPECT_EQ(kIMEI, cellular_->imei());
-  EXPECT_EQ(kIMSI, cellular_->imsi());
-  EXPECT_EQ(kTestCarrier, capability_->spn());
-  EXPECT_EQ(kMSISDN, cellular_->mdn());
+  capability_->OnGetIMSICallback("310240123456789", Error(), NULL);
+  EXPECT_EQ("T-Mobile", cellular_->home_provider().GetName());
+}
+
+TEST_F(CellularCapabilityGSMTest, GetMSISDN) {
+  EXPECT_CALL(*card_proxy_, GetMSISDN(NULL, _));
+  SetCardProxy();
+  capability_->GetMSISDN(NULL);
+  capability_->OnGetMSISDNCallback(kMSISDN, Error(), NULL);
+  EXPECT_EQ(kMSISDN, capability_->mdn_);
+}
+
+TEST_F(CellularCapabilityGSMTest, GetSPN) {
+  EXPECT_CALL(*card_proxy_, GetSPN(NULL, _));
+  SetCardProxy();
+  capability_->GetSPN(NULL);
+  capability_->OnGetSPNCallback(kTestCarrier, Error(), NULL);
+  EXPECT_EQ(kTestCarrier, capability_->spn_);
 }
 
 TEST_F(CellularCapabilityGSMTest, GetSignalQuality) {
@@ -153,13 +190,13 @@
 }
 
 TEST_F(CellularCapabilityGSMTest, RegisterOnNetwork) {
-  Error error;
-  EXPECT_CALL(*network_proxy_, Register(kTestCarrier));
-  capability_->RegisterOnNetwork(kTestCarrier, &error);
-  EXPECT_TRUE(error.IsSuccess());
+  EXPECT_CALL(*network_proxy_, Register(kTestNetwork, NULL,
+                                        CellularCapability::kTimeoutRegister));
   SetNetworkProxy();
+  capability_->RegisterOnNetwork(kTestNetwork, NULL);
   dispatcher_.DispatchPendingEvents();
-  EXPECT_EQ(kTestCarrier, capability_->selected_network_);
+  capability_->OnRegisterCallback(Error(), NULL);
+  EXPECT_EQ(kTestNetwork, capability_->selected_network_);
 }
 
 TEST_F(CellularCapabilityGSMTest, IsRegistered) {
@@ -178,62 +215,87 @@
   EXPECT_TRUE(capability_->IsRegistered());
 }
 
+TEST_F(CellularCapabilityGSMTest, GetRegistrationState) {
+  ASSERT_FALSE(capability_->IsRegistered());
+  EXPECT_CALL(*network_proxy_,
+              GetRegistrationInfo(NULL, CellularCapability::kTimeoutDefault));
+  SetNetworkProxy();
+  capability_->GetRegistrationState(NULL);
+  capability_->OnGSMRegistrationInfoChanged(
+      MM_MODEM_GSM_NETWORK_REG_STATUS_HOME, kTestNetwork, kTestCarrier,
+      Error(), NULL);
+  EXPECT_TRUE(capability_->IsRegistered());
+  EXPECT_EQ(MM_MODEM_GSM_NETWORK_REG_STATUS_HOME,
+            capability_->registration_state_);
+}
+
 TEST_F(CellularCapabilityGSMTest, RequirePIN) {
-  EXPECT_CALL(*card_proxy_, EnablePIN(kPIN, true));
+  EXPECT_CALL(*card_proxy_, EnablePIN(kPIN, true,
+                                      _, CellularCapability::kTimeoutDefault));
   MockReturner returner;
   EXPECT_CALL(returner, Return());
   EXPECT_CALL(returner, ReturnError(_)).Times(0);
-  capability_->RequirePIN(kPIN, true, &returner);
+  GSMTestAsyncCallHandler *handler = new GSMTestAsyncCallHandler(&returner);
   SetCardProxy();
-  dispatcher_.DispatchPendingEvents();
+  capability_->RequirePIN(kPIN, true, handler);
+  capability_->OnPINOperationCallback(Error(), handler);
+  EXPECT_TRUE(GSMTestAsyncCallHandler::error().IsSuccess());
 }
 
 TEST_F(CellularCapabilityGSMTest, EnterPIN) {
-  EXPECT_CALL(*card_proxy_, SendPIN(kPIN));
+  EXPECT_CALL(*card_proxy_,
+              SendPIN(kPIN, _, CellularCapability::kTimeoutDefault));
   MockReturner returner;
   EXPECT_CALL(returner, Return());
   EXPECT_CALL(returner, ReturnError(_)).Times(0);
-  capability_->EnterPIN(kPIN, &returner);
+  GSMTestAsyncCallHandler *handler = new GSMTestAsyncCallHandler(&returner);
   SetCardProxy();
-  dispatcher_.DispatchPendingEvents();
+  capability_->EnterPIN(kPIN, handler);
+  capability_->OnPINOperationCallback(Error(), handler);
+  EXPECT_TRUE(GSMTestAsyncCallHandler::error().IsSuccess());
 }
 
 TEST_F(CellularCapabilityGSMTest, UnblockPIN) {
-  EXPECT_CALL(*card_proxy_, SendPUK(kPUK, kPIN));
+  EXPECT_CALL(*card_proxy_,
+              SendPUK(kPUK, kPIN, _, CellularCapability::kTimeoutDefault));
   MockReturner returner;
   EXPECT_CALL(returner, Return());
   EXPECT_CALL(returner, ReturnError(_)).Times(0);
-  capability_->UnblockPIN(kPUK, kPIN, &returner);
+  GSMTestAsyncCallHandler *handler = new GSMTestAsyncCallHandler(&returner);
   SetCardProxy();
-  dispatcher_.DispatchPendingEvents();
+  capability_->UnblockPIN(kPUK, kPIN, handler);
+  capability_->OnPINOperationCallback(Error(), handler);
+  EXPECT_TRUE(GSMTestAsyncCallHandler::error().IsSuccess());
 }
 
 TEST_F(CellularCapabilityGSMTest, ChangePIN) {
   static const char kOldPIN[] = "1111";
-  EXPECT_CALL(*card_proxy_, ChangePIN(kOldPIN, kPIN));
+  EXPECT_CALL(*card_proxy_, ChangePIN(kOldPIN, kPIN,
+                                      _, CellularCapability::kTimeoutDefault));
   MockReturner returner;
   EXPECT_CALL(returner, Return());
   EXPECT_CALL(returner, ReturnError(_)).Times(0);
-  capability_->ChangePIN(kOldPIN, kPIN, &returner);
+  GSMTestAsyncCallHandler *handler = new GSMTestAsyncCallHandler(&returner);
   SetCardProxy();
-  dispatcher_.DispatchPendingEvents();
+  capability_->ChangePIN(kOldPIN, kPIN, handler);
+  capability_->OnPINOperationCallback(Error(), handler);
+  EXPECT_TRUE(GSMTestAsyncCallHandler::error().IsSuccess());
 }
 
 TEST_F(CellularCapabilityGSMTest, Scan) {
   static const char kID0[] = "123";
   static const char kID1[] = "456";
   Error error;
-  capability_->Scan(&error);
-  EXPECT_TRUE(error.IsSuccess());
-  ModemGSMNetworkProxyInterface::ScanResults results;
-  results.push_back(ModemGSMNetworkProxyInterface::ScanResult());
-  results[0][CellularCapabilityGSM::kNetworkPropertyID] = kID0;
-  results.push_back(ModemGSMNetworkProxyInterface::ScanResult());
-  results[1][CellularCapabilityGSM::kNetworkPropertyID] = kID1;
-  EXPECT_CALL(*network_proxy_, Scan()).WillOnce(Return(results));
+  EXPECT_CALL(*network_proxy_, Scan(_, CellularCapability::kTimeoutScan));
   SetNetworkProxy();
+  capability_->Scan(NULL);
+  GSMScanResults results;
+  results.push_back(GSMScanResult());
+  results[0][CellularCapabilityGSM::kNetworkPropertyID] = kID0;
+  results.push_back(GSMScanResult());
+  results[1][CellularCapabilityGSM::kNetworkPropertyID] = kID1;
   capability_->found_networks_.resize(3, Stringmap());
-  dispatcher_.DispatchPendingEvents();
+  capability_->OnScanCallback(results, Error(), NULL);
   EXPECT_EQ(2, capability_->found_networks_.size());
   EXPECT_EQ(kID0,
             capability_->found_networks_[0][flimflam::kNetworkIdProperty]);
@@ -245,7 +307,7 @@
   static const char kID[] = "123";
   static const char kLongName[] = "long name";
   static const char kShortName[] = "short name";
-  ModemGSMNetworkProxyInterface::ScanResult result;
+  GSMScanResult result;
   result[CellularCapabilityGSM::kNetworkPropertyStatus] = "1";
   result[CellularCapabilityGSM::kNetworkPropertyID] = kID;
   result[CellularCapabilityGSM::kNetworkPropertyLongName] = kLongName;
@@ -265,7 +327,7 @@
 TEST_F(CellularCapabilityGSMTest, ParseScanResultProviderLookup) {
   InitProviderDB();
   static const char kID[] = "310210";
-  ModemGSMNetworkProxyInterface::ScanResult result;
+  GSMScanResult result;
   result[CellularCapabilityGSM::kNetworkPropertyID] = kID;
   Stringmap parsed = capability_->ParseScanResult(result);
   EXPECT_EQ(2, parsed.size());
@@ -298,8 +360,8 @@
 TEST_F(CellularCapabilityGSMTest, UpdateStatus) {
   InitProviderDB();
   DBusPropertiesMap props;
-  cellular_->set_imsi("310240123456789");
-  props[Cellular::kPropertyIMSI].writer().append_string("");
+  capability_->imsi_ = "310240123456789";
+  props[CellularCapability::kPropertyIMSI].writer().append_string("");
   capability_->UpdateStatus(props);
   EXPECT_EQ("T-Mobile", cellular_->home_provider().GetName());
 }
@@ -307,7 +369,7 @@
 TEST_F(CellularCapabilityGSMTest, SetHomeProvider) {
   static const char kCountry[] = "us";
   static const char kCode[] = "310160";
-  cellular_->set_imsi("310240123456789");
+  capability_->imsi_ = "310240123456789";
 
   capability_->SetHomeProvider();  // No mobile provider DB available.
   EXPECT_TRUE(cellular_->home_provider().GetName().empty());
@@ -413,7 +475,7 @@
   capability_->serving_operator_.SetCode("1234");
   EXPECT_EQ("cellular_1234", capability_->CreateFriendlyServiceName());
   static const char kTestCarrier[] = "A GSM Carrier";
-  cellular_->carrier_ = kTestCarrier;
+  capability_->carrier_ = kTestCarrier;
   EXPECT_EQ(kTestCarrier, capability_->CreateFriendlyServiceName());
   static const char kTestOperator[] = "A GSM Operator";
   capability_->serving_operator_.SetName(kTestOperator);
diff --git a/cellular_capability_unittest.cc b/cellular_capability_unittest.cc
new file mode 100644
index 0000000..09ca81f
--- /dev/null
+++ b/cellular_capability_unittest.cc
@@ -0,0 +1,192 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/cellular_capability_gsm.h"
+
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest.h>
+#include <mm/mm-modem.h>
+#include <mobile_provider.h>
+
+#include "shill/cellular.h"
+#include "shill/cellular_service.h"
+#include "shill/error.h"
+#include "shill/event_dispatcher.h"
+#include "shill/mock_adaptors.h"
+#include "shill/mock_modem_cdma_proxy.h"
+#include "shill/mock_modem_gsm_card_proxy.h"
+#include "shill/mock_modem_gsm_network_proxy.h"
+#include "shill/mock_modem_proxy.h"
+#include "shill/mock_modem_simple_proxy.h"
+#include "shill/nice_mock_control.h"
+#include "shill/proxy_factory.h"
+
+using std::string;
+using testing::NiceMock;
+
+namespace shill {
+
+class CellularCapabilityTest : public testing::Test {
+ public:
+  CellularCapabilityTest()
+      : cellular_(new Cellular(&control_,
+                               &dispatcher_,
+                               NULL,
+                               NULL,
+                               "",
+                               "",
+                               0,
+                               Cellular::kTypeGSM,
+                               "",
+                               "",
+                               NULL)),
+        proxy_(new MockModemProxy()),
+        simple_proxy_(new MockModemSimpleProxy()),
+        cdma_proxy_(new MockModemCDMAProxy()),
+        gsm_card_proxy_(new MockModemGSMCardProxy()),
+        gsm_network_proxy_(new MockModemGSMNetworkProxy()),
+        proxy_factory_(this),
+        capability_(NULL),
+        device_adaptor_(NULL) {}
+
+  virtual ~CellularCapabilityTest() {
+    cellular_->service_ = NULL;
+    capability_ = NULL;
+    device_adaptor_ = NULL;
+  }
+
+  virtual void SetUp() {
+    capability_ = cellular_->capability_.get();
+    device_adaptor_ =
+        dynamic_cast<NiceMock<DeviceMockAdaptor> *>(cellular_->adaptor());
+  }
+
+  virtual void TearDown() {
+    capability_->proxy_factory_ = NULL;
+  }
+
+ protected:
+  static const char kTestCarrier[];
+
+  class TestProxyFactory : public ProxyFactory {
+   public:
+    explicit TestProxyFactory(CellularCapabilityTest *test) : test_(test) {}
+
+    virtual ModemProxyInterface *CreateModemProxy(
+        ModemProxyDelegate */*delegate*/,
+        const string &/*path*/,
+        const string &/*service*/) {
+      return test_->proxy_.release();
+    }
+
+    virtual ModemSimpleProxyInterface *CreateModemSimpleProxy(
+        ModemSimpleProxyDelegate */*delegate*/,
+        const string &/*path*/,
+        const string &/*service*/) {
+      return test_->simple_proxy_.release();
+    }
+
+    virtual ModemCDMAProxyInterface *CreateModemCDMAProxy(
+        ModemCDMAProxyDelegate */*delegate*/,
+        const string &/*path*/,
+        const string &/*service*/) {
+      return test_->cdma_proxy_.release();
+    }
+
+    virtual ModemGSMCardProxyInterface *CreateModemGSMCardProxy(
+        ModemGSMCardProxyDelegate */*delegate*/,
+        const string &/*path*/,
+        const string &/*service*/) {
+      return test_->gsm_card_proxy_.release();
+    }
+
+    virtual ModemGSMNetworkProxyInterface *CreateModemGSMNetworkProxy(
+        ModemGSMNetworkProxyDelegate */*delegate*/,
+        const string &/*path*/,
+        const string &/*service*/) {
+      return test_->gsm_network_proxy_.release();
+    }
+
+   private:
+    CellularCapabilityTest *test_;
+  };
+
+  void SetProxy() {
+    capability_->proxy_.reset(proxy_.release());
+  }
+
+  void SetSimpleProxy() {
+    capability_->simple_proxy_.reset(simple_proxy_.release());
+  }
+
+  void SetCellularType(Cellular::Type type) {
+    cellular_->InitCapability(type, &proxy_factory_);
+    capability_ = cellular_->capability_.get();
+  }
+
+  NiceMockControl control_;
+  EventDispatcher dispatcher_;
+  CellularRefPtr cellular_;
+  scoped_ptr<MockModemProxy> proxy_;
+  scoped_ptr<MockModemSimpleProxy> simple_proxy_;
+  scoped_ptr<MockModemCDMAProxy> cdma_proxy_;
+  scoped_ptr<MockModemGSMCardProxy> gsm_card_proxy_;
+  scoped_ptr<MockModemGSMNetworkProxy> gsm_network_proxy_;
+  TestProxyFactory proxy_factory_;
+  CellularCapability *capability_;  // Owned by |cellular_|.
+  NiceMock<DeviceMockAdaptor> *device_adaptor_;  // Owned by |cellular_|.
+};
+
+const char CellularCapabilityTest::kTestCarrier[] = "The Cellular Carrier";
+
+TEST_F(CellularCapabilityTest, GetModemStatus) {
+  SetCellularType(Cellular::kTypeCDMA);
+  DBusPropertiesMap props;
+  props["carrier"].writer().append_string(kTestCarrier);
+  props["unknown-property"].writer().append_string("irrelevant-value");
+  EXPECT_CALL(*simple_proxy_,
+              GetModemStatus(NULL, CellularCapability::kTimeoutDefault));
+  SetSimpleProxy();
+  capability_->GetModemStatus(NULL);
+  capability_->OnGetModemStatusCallback(props, Error(), NULL);
+  EXPECT_EQ(kTestCarrier, capability_->carrier_);
+  EXPECT_EQ(kTestCarrier, cellular_->home_provider_.GetName());
+}
+
+TEST_F(CellularCapabilityTest, GetModemInfo) {
+  static const char kManufacturer[] = "Company";
+  static const char kModelID[] = "Gobi 2000";
+  static const char kHWRev[] = "A00B1234";
+  ModemHardwareInfo info;
+  info._1 = kManufacturer;
+  info._2 = kModelID;
+  info._3 = kHWRev;
+  EXPECT_CALL(*proxy_, GetModemInfo(NULL, CellularCapability::kTimeoutDefault));
+  SetProxy();
+  capability_->GetModemInfo(NULL);
+  capability_->OnGetModemInfoCallback(info, Error(), NULL);
+  EXPECT_EQ(kManufacturer, capability_->manufacturer_);
+  EXPECT_EQ(kModelID, capability_->model_id_);
+  EXPECT_EQ(kHWRev, capability_->hardware_revision_);
+}
+
+TEST_F(CellularCapabilityTest, EnableModemSucceed) {
+  EXPECT_CALL(*proxy_, Enable(true, NULL, CellularCapability::kTimeoutDefault));
+  ASSERT_EQ(Cellular::kStateDisabled, cellular_->state_);
+  SetProxy();
+  capability_->EnableModem(NULL);
+  capability_->OnModemEnableCallback(Error(), NULL);
+  EXPECT_EQ(Cellular::kStateEnabled, cellular_->state_);
+}
+
+TEST_F(CellularCapabilityTest, EnableModemFail) {
+  EXPECT_CALL(*proxy_, Enable(true, NULL, CellularCapability::kTimeoutDefault));
+  ASSERT_EQ(Cellular::kStateDisabled, cellular_->state_);
+  SetProxy();
+  capability_->EnableModem(NULL);
+  capability_->OnModemEnableCallback(Error(Error::kOperationFailed), NULL);
+  EXPECT_EQ(Cellular::kStateDisabled, cellular_->state_);
+}
+
+}  // namespace shill
diff --git a/cellular_error.cc b/cellular_error.cc
new file mode 100644
index 0000000..2e3818a
--- /dev/null
+++ b/cellular_error.cc
@@ -0,0 +1,54 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/cellular_error.h"
+
+#include <base/logging.h>
+#include <mm/mm-modem.h>
+
+using std::string;
+
+#define MM_MODEM_ERROR(error)  MM_MODEM_INTERFACE "." error
+#define MM_MOBILE_ERROR(error) MM_MODEM_GSM_INTERFACE "." error
+
+namespace shill {
+
+static const char *kErrorIncorrectPassword =
+    MM_MOBILE_ERROR(MM_ERROR_MODEM_GSM_INCORRECTPASSWORD);
+static const char *kErrorSimPinRequired =
+    MM_MOBILE_ERROR(MM_ERROR_MODEM_GSM_SIMPINREQUIRED);
+static const char *kErrorSimPukRequired =
+    MM_MOBILE_ERROR(MM_ERROR_MODEM_GSM_SIMPUKREQUIRED);
+
+// static
+void CellularError::FromDBusError(const DBus::Error &dbus_error,
+                                   Error *error) {
+  if (!error)
+    return;
+
+  if (!dbus_error.is_set()) {
+    error->Reset();
+    return;
+  }
+
+  string name(dbus_error.name());
+  const char *msg = dbus_error.message();
+  Error::Type type;
+
+  if (name == kErrorIncorrectPassword)
+    type = Error::kIncorrectPin;
+  else if (name == kErrorSimPinRequired)
+    type = Error::kPinRequired;
+  else if (name == kErrorSimPukRequired)
+    type = Error::kPinBlocked;
+  else
+    type = Error::kOperationFailed;
+
+  if (msg)
+    return error->Populate(type, msg);
+  else
+    return error->Populate(type);
+}
+
+}  // namespace shill
diff --git a/cellular_error.h b/cellular_error.h
new file mode 100644
index 0000000..5db343c
--- /dev/null
+++ b/cellular_error.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_CELLULAR_ERROR_
+#define SHILL_CELLULAR_ERROR_
+
+#include <dbus-c++/error.h>
+
+#include "shill/error.h"
+
+namespace shill {
+
+class CellularError {
+ public:
+  static void FromDBusError(const DBus::Error &dbus_error, Error *error);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(CellularError);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_CELLULAR_ERROR_
diff --git a/cellular_service.cc b/cellular_service.cc
index 11c3db8..68b2544 100644
--- a/cellular_service.cc
+++ b/cellular_service.cc
@@ -58,8 +58,8 @@
 }
 
 void CellularService::ActivateCellularModem(const string &carrier,
-                                            Error *error) {
-  cellular_->Activate(carrier, error);
+                                            ReturnerInterface *returner) {
+  cellular_->Activate(carrier, returner);
 }
 
 bool CellularService::TechnologyIs(const Technology::Identifier type) const {
diff --git a/cellular_service.h b/cellular_service.h
index 048db57..eb36427 100644
--- a/cellular_service.h
+++ b/cellular_service.h
@@ -34,7 +34,8 @@
   // Inherited from Service.
   virtual void Connect(Error *error);
   virtual void Disconnect(Error *error);
-  virtual void ActivateCellularModem(const std::string &carrier, Error *error);
+  virtual void ActivateCellularModem(const std::string &carrier,
+                                     ReturnerInterface *returner);
   virtual bool TechnologyIs(const Technology::Identifier type) const;
 
   // cellular_<MAC>_<Service_Operator_Name>
diff --git a/cellular_service_unittest.cc b/cellular_service_unittest.cc
index 197e081..2b92343 100644
--- a/cellular_service_unittest.cc
+++ b/cellular_service_unittest.cc
@@ -7,6 +7,7 @@
 #include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 
+#include "shill/cellular_capability.h"
 #include "shill/nice_mock_control.h"
 #include "shill/mock_adaptors.h"
 #include "shill/mock_metrics.h"
@@ -70,7 +71,7 @@
 
 TEST_F(CellularServiceTest, FriendlyName) {
   static const char kCarrier[] = "Cellular Carrier";
-  device_->carrier_ = kCarrier;
+  device_->capability_->carrier_ = kCarrier;
   service_ = new CellularService(&control_, NULL, &metrics_, NULL, device_);
   EXPECT_EQ(kCarrier, service_->friendly_name());
 }
diff --git a/cellular_unittest.cc b/cellular_unittest.cc
index 58db9de..192d7cb 100644
--- a/cellular_unittest.cc
+++ b/cellular_unittest.cc
@@ -36,9 +36,11 @@
 using std::string;
 using testing::_;
 using testing::AnyNumber;
+using testing::Invoke;
 using testing::NiceMock;
 using testing::Return;
 using testing::SetArgumentPointee;
+using testing::Unused;
 
 namespace shill {
 
@@ -100,12 +102,6 @@
   CellularTest()
       : manager_(&control_interface_, &dispatcher_, &metrics_, &glib_),
         device_info_(&control_interface_, &dispatcher_, &metrics_, &manager_),
-        proxy_(new MockModemProxy()),
-        simple_proxy_(new MockModemSimpleProxy()),
-        cdma_proxy_(new MockModemCDMAProxy()),
-        gsm_card_proxy_(new MockModemGSMCardProxy()),
-        gsm_network_proxy_(new MockModemGSMNetworkProxy()),
-        proxy_factory_(this),
         dhcp_config_(new MockDHCPConfig(&control_interface_,
                                         &dispatcher_,
                                         &dhcp_provider_,
@@ -123,7 +119,15 @@
                              kDBusOwner,
                              kDBusPath,
                              NULL)),
-        provider_db_(NULL) {}
+        proxy_(new MockModemProxy()),
+        simple_proxy_(new MockModemSimpleProxy()),
+        cdma_proxy_(new MockModemCDMAProxy()),
+        gsm_card_proxy_(new MockModemGSMCardProxy()),
+        gsm_network_proxy_(new MockModemGSMNetworkProxy()),
+        proxy_factory_(this),
+        provider_db_(NULL) {
+    device_->capability_->proxy_factory_ = &proxy_factory_;
+  }
 
   virtual ~CellularTest() {
     mobile_provider_close_db(provider_db_);
@@ -131,8 +135,6 @@
   }
 
   virtual void SetUp() {
-    device_->proxy_factory_ = &proxy_factory_;
-    device_->capability_->proxy_factory_ = &proxy_factory_;
     static_cast<Device *>(device_)->rtnl_handler_ = &rtnl_handler_;
     device_->set_dhcp_provider(&dhcp_provider_);
     EXPECT_CALL(manager_, device_info()).WillRepeatedly(Return(&device_info_));
@@ -141,13 +143,96 @@
 
   virtual void TearDown() {
     device_->state_ = Cellular::kStateDisabled;
-    device_->capability_->proxy_factory_ = NULL;
-    device_->proxy_factory_ = NULL;
+    device_->capability_->proxy_.reset(new MockModemProxy());
     device_->Stop();
     device_->set_dhcp_provider(NULL);
   }
 
+  void InvokeEnable(bool /*enable*/, void *data, int /*timeout*/) {
+    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+    device_->capability_->OnModemEnableCallback(Error(), handler);
+    dispatcher_.DispatchPendingEvents();
+  }
+
+  void InvokeGetModemStatus(void *data, int /*timeout*/) {
+    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+    DBusPropertiesMap props;
+    props["carrier"].writer().append_string(kTestCarrier);
+    props["unknown-property"].writer().append_string("irrelevant-value");
+    device_->capability_->OnGetModemStatusCallback(props, Error(), handler);
+    dispatcher_.DispatchPendingEvents();
+  }
+
+  void InvokeGetModemInfo(void *data, int /*timeout*/) {
+    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+    static const char kManufacturer[] = "Company";
+    static const char kModelID[] = "Gobi 2000";
+    static const char kHWRev[] = "A00B1234";
+    ModemHardwareInfo info;
+    info._1 = kManufacturer;
+    info._2 = kModelID;
+    info._3 = kHWRev;
+    device_->capability_->OnGetModemInfoCallback(info, Error(), handler);
+    dispatcher_.DispatchPendingEvents();
+  }
+
+  void InvokeGetIMEI(void *data, int /*timeout*/) {
+    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+    GetCapabilityGSM()->OnGetIMEICallback(kIMEI, Error(), handler);
+    dispatcher_.DispatchPendingEvents();
+  }
+
+  void InvokeGetIMSI(void *data, int /*timeout*/) {
+    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+    GetCapabilityGSM()->OnGetIMSICallback(kIMSI, Error(), handler);
+    dispatcher_.DispatchPendingEvents();
+  }
+  void InvokeGetSPN(void *data, int /*timeout*/) {
+    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+    GetCapabilityGSM()->OnGetSPNCallback(kTestCarrier, Error(), handler);
+    dispatcher_.DispatchPendingEvents();
+  }
+  void InvokeGetMSISDN(void *data, int /*timeout*/) {
+    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+    GetCapabilityGSM()->OnGetMSISDNCallback(kMSISDN, Error(), handler);
+    dispatcher_.DispatchPendingEvents();
+  }
+  void InvokeRegister(const std::string &/*network_id*/,
+                      void *data, int /*timeout*/) {
+    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+    GetCapabilityGSM()->OnRegisterCallback(Error(), handler);
+    dispatcher_.DispatchPendingEvents();
+  }
+  void InvokeGetRegistrationInfo(void *data, int /*timeout*/) {
+    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+    GSMRegistrationInfo info;
+    static const char kNetworkID[] = "22803";
+    info._1 = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING;
+    info._2 = kNetworkID;
+    info._3 = kTestCarrier;
+    GetCapabilityGSM()->OnGSMRegistrationInfoChanged(info._1, info._2, info._3,
+                                                     Error(), handler);
+    dispatcher_.DispatchPendingEvents();
+  }
+  void InvokeConnect(DBusPropertiesMap /*props*/, void *data, int /*timeout*/) {
+    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+    device_->capability_->OnConnectCallback(Error(), handler);
+    dispatcher_.DispatchPendingEvents();
+  }
+
  protected:
+  static const char kTestDeviceName[];
+  static const char kTestDeviceAddress[];
+  static const int kTestSocket;
+  static const char kDBusOwner[];
+  static const char kDBusPath[];
+  static const char kTestCarrier[];
+  static const char kMEID[];
+  static const char kIMEI[];
+  static const char kIMSI[];
+  static const char kMSISDN[];
+  static const char kTestMobileProviderDBPath[];
+
   class TestProxyFactory : public ProxyFactory {
    public:
     explicit TestProxyFactory(CellularTest *test) : test_(test) {}
@@ -160,6 +245,7 @@
     }
 
     virtual ModemSimpleProxyInterface *CreateModemSimpleProxy(
+        ModemSimpleProxyDelegate */*delegate*/,
         const string &/*path*/,
         const string &/*service*/) {
       return test_->simple_proxy_.release();
@@ -189,24 +275,11 @@
    private:
     CellularTest *test_;
   };
-
-  static const char kTestDeviceName[];
-  static const char kTestDeviceAddress[];
-  static const int kTestSocket;
-  static const char kDBusOwner[];
-  static const char kDBusPath[];
-  static const char kTestCarrier[];
-  static const char kMEID[];
-  static const char kIMEI[];
-  static const char kIMSI[];
-  static const char kMSISDN[];
-  static const char kTestMobileProviderDBPath[];
-
   void StartRTNLHandler();
   void StopRTNLHandler();
 
   void SetCellularType(Cellular::Type type) {
-    device_->InitCapability(type);
+    device_->InitCapability(type, &proxy_factory_);
   }
 
   CellularCapabilityCDMA *GetCapabilityCDMA() {
@@ -225,17 +298,16 @@
   MockDeviceInfo device_info_;
   NiceMock<MockRTNLHandler> rtnl_handler_;
 
+  MockDHCPProvider dhcp_provider_;
+  scoped_refptr<MockDHCPConfig> dhcp_config_;
+
+  CellularRefPtr device_;
   scoped_ptr<MockModemProxy> proxy_;
   scoped_ptr<MockModemSimpleProxy> simple_proxy_;
   scoped_ptr<MockModemCDMAProxy> cdma_proxy_;
   scoped_ptr<MockModemGSMCardProxy> gsm_card_proxy_;
   scoped_ptr<MockModemGSMNetworkProxy> gsm_network_proxy_;
   TestProxyFactory proxy_factory_;
-
-  MockDHCPProvider dhcp_provider_;
-  scoped_refptr<MockDHCPConfig> dhcp_config_;
-
-  CellularRefPtr device_;
   mobile_provider_db *provider_db_;
 };
 
@@ -244,7 +316,7 @@
 const char CellularTest::kDBusOwner[] = ":1.19";
 const char CellularTest::kDBusPath[] = "/org/chromium/ModemManager/Gobi/0";
 const char CellularTest::kTestCarrier[] = "The Cellular Carrier";
-const char CellularTest::kMEID[] = "D1234567EF8901";
+const char CellularTest::kMEID[] = "01234567EF8901";
 const char CellularTest::kIMEI[] = "987654321098765";
 const char CellularTest::kIMSI[] = "123456789012345";
 const char CellularTest::kMSISDN[] = "12345678901";
@@ -267,19 +339,23 @@
 TEST_F(CellularTest, StartCDMARegister) {
   const int kStrength = 90;
   SetCellularType(Cellular::kTypeCDMA);
-  EXPECT_CALL(*proxy_, Enable(true)).Times(1);
-  EXPECT_CALL(*simple_proxy_, GetStatus())
-      .WillOnce(Return(DBusPropertiesMap()));
+  EXPECT_CALL(*proxy_, Enable(true, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+  EXPECT_CALL(*simple_proxy_,
+              GetModemStatus(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetModemStatus));
   EXPECT_CALL(*cdma_proxy_, MEID()).WillOnce(Return(kMEID));
-  EXPECT_CALL(*proxy_, GetInfo()).WillOnce(Return(ModemProxyInterface::Info()));
+  EXPECT_CALL(*proxy_, GetModemInfo(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetModemInfo));
   EXPECT_CALL(*cdma_proxy_, GetRegistrationState(_, _))
       .WillOnce(DoAll(
           SetArgumentPointee<0>(MM_MODEM_CDMA_REGISTRATION_STATE_HOME),
           SetArgumentPointee<1>(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)));
   EXPECT_CALL(*cdma_proxy_, GetSignalQuality()).WillOnce(Return(kStrength));
   device_->Start();
-  EXPECT_EQ(kMEID, device_->meid_);
   dispatcher_.DispatchPendingEvents();
+  EXPECT_EQ(kMEID, device_->capability_->meid_);
+  EXPECT_EQ(kTestCarrier, device_->capability_->carrier_);
   EXPECT_EQ(Cellular::kStateRegistered, device_->state_);
   ASSERT_TRUE(device_->service_.get());
   EXPECT_EQ(flimflam::kNetworkTechnology1Xrtt,
@@ -293,31 +369,39 @@
   ASSERT_TRUE(provider_db_);
   device_->provider_db_ = provider_db_;
   const int kStrength = 70;
-  EXPECT_CALL(*proxy_, Enable(true)).Times(1);
-  EXPECT_CALL(*gsm_network_proxy_, Register("")).Times(1);
-  EXPECT_CALL(*simple_proxy_, GetStatus())
-      .WillOnce(Return(DBusPropertiesMap()));
-  EXPECT_CALL(*gsm_card_proxy_, GetIMEI()).WillOnce(Return(kIMEI));
-  EXPECT_CALL(*gsm_card_proxy_, GetIMSI()).WillOnce(Return(kIMSI));
-  EXPECT_CALL(*gsm_card_proxy_, GetSPN()).WillOnce(Return(kTestCarrier));
-  EXPECT_CALL(*gsm_card_proxy_, GetMSISDN()).WillOnce(Return(kMSISDN));
+  EXPECT_CALL(*proxy_, Enable(true, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+  EXPECT_CALL(*gsm_network_proxy_,
+              Register("", _, CellularCapability::kTimeoutRegister))
+      .WillOnce(Invoke(this, &CellularTest::InvokeRegister));
+  EXPECT_CALL(*simple_proxy_,
+              GetModemStatus(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetModemStatus));
+  EXPECT_CALL(*gsm_card_proxy_, GetIMEI(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetIMEI));
+  EXPECT_CALL(*gsm_card_proxy_, GetIMSI(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetIMSI));
+  EXPECT_CALL(*gsm_card_proxy_, GetSPN(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetSPN));
+  EXPECT_CALL(*gsm_card_proxy_,
+              GetMSISDN(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetMSISDN));
   EXPECT_CALL(*gsm_network_proxy_, AccessTechnology())
       .WillOnce(Return(MM_MODEM_GSM_ACCESS_TECH_EDGE));
-  EXPECT_CALL(*proxy_, GetInfo()).WillOnce(Return(ModemProxyInterface::Info()));
-  ModemGSMNetworkProxyInterface::RegistrationInfo reg_info;
+  EXPECT_CALL(*proxy_, GetModemInfo(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetModemInfo));
   static const char kNetworkID[] = "22803";
-  reg_info._1 = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING;
-  reg_info._2 = kNetworkID;
-  EXPECT_CALL(*gsm_network_proxy_, GetRegistrationInfo())
-      .WillOnce(Return(reg_info));
+  EXPECT_CALL(*gsm_network_proxy_,
+              GetRegistrationInfo(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetRegistrationInfo));
   EXPECT_CALL(*gsm_network_proxy_, GetSignalQuality())
       .WillOnce(Return(kStrength));
   device_->Start();
-  EXPECT_EQ(kIMEI, device_->imei_);
-  EXPECT_EQ(kIMSI, device_->imsi_);
-  EXPECT_EQ(kTestCarrier, GetCapabilityGSM()->spn());
-  EXPECT_EQ(kMSISDN, device_->mdn_);
   dispatcher_.DispatchPendingEvents();
+  EXPECT_EQ(kIMEI, device_->capability_->imei_);
+  EXPECT_EQ(kIMSI, device_->capability_->imsi_);
+  EXPECT_EQ(kTestCarrier, GetCapabilityGSM()->spn());
+  EXPECT_EQ(kMSISDN, device_->capability_->mdn_);
   EXPECT_EQ(Cellular::kStateRegistered, device_->state_);
   ASSERT_TRUE(device_->service_.get());
   EXPECT_EQ(flimflam::kNetworkTechnologyEdge,
@@ -334,11 +418,14 @@
       .WillOnce(Return(true));
   SetCellularType(Cellular::kTypeCDMA);
   device_->set_modem_state(Cellular::kModemStateConnected);
-  device_->meid_ = kMEID;
-  EXPECT_CALL(*proxy_, Enable(true)).Times(1);
-  EXPECT_CALL(*simple_proxy_, GetStatus())
-      .WillOnce(Return(DBusPropertiesMap()));
-  EXPECT_CALL(*proxy_, GetInfo()).WillOnce(Return(ModemProxyInterface::Info()));
+  device_->capability_->meid_ = kMEID;
+  EXPECT_CALL(*proxy_, Enable(true, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+  EXPECT_CALL(*simple_proxy_,
+              GetModemStatus(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetModemStatus));
+  EXPECT_CALL(*proxy_, GetModemInfo(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetModemInfo));
   EXPECT_CALL(*cdma_proxy_, GetRegistrationState(_, _))
       .WillOnce(DoAll(
           SetArgumentPointee<0>(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED),
@@ -354,11 +441,14 @@
       .WillOnce(DoAll(SetArgumentPointee<1>(IFF_UP), Return(true)));
   SetCellularType(Cellular::kTypeCDMA);
   device_->set_modem_state(Cellular::kModemStateConnected);
-  device_->meid_ = kMEID;
-  EXPECT_CALL(*proxy_, Enable(true)).Times(1);
-  EXPECT_CALL(*simple_proxy_, GetStatus())
-      .WillOnce(Return(DBusPropertiesMap()));
-  EXPECT_CALL(*proxy_, GetInfo()).WillOnce(Return(ModemProxyInterface::Info()));
+  device_->capability_->meid_ = kMEID;
+  EXPECT_CALL(*proxy_, Enable(true, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
+  EXPECT_CALL(*simple_proxy_,
+              GetModemStatus(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetModemStatus));
+  EXPECT_CALL(*proxy_, GetModemInfo(_, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeGetModemInfo));
   EXPECT_CALL(*cdma_proxy_, GetRegistrationState(_, _))
       .WillOnce(DoAll(
           SetArgumentPointee<0>(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED),
@@ -375,41 +465,6 @@
   device_->SelectService(NULL);
 }
 
-TEST_F(CellularTest, InitProxies) {
-  device_->InitProxies();
-  EXPECT_TRUE(device_->proxy_.get());
-  EXPECT_TRUE(device_->simple_proxy_.get());
-}
-
-TEST_F(CellularTest, GetModemStatus) {
-  SetCellularType(Cellular::kTypeCDMA);
-  DBusPropertiesMap props;
-  props["carrier"].writer().append_string(kTestCarrier);
-  props["unknown-property"].writer().append_string("irrelevant-value");
-  EXPECT_CALL(*simple_proxy_, GetStatus()).WillOnce(Return(props));
-  device_->simple_proxy_.reset(simple_proxy_.release());
-  device_->state_ = Cellular::kStateEnabled;
-  device_->GetModemStatus();
-  EXPECT_EQ(kTestCarrier, device_->carrier_);
-  EXPECT_EQ(kTestCarrier, device_->home_provider_.GetName());
-}
-
-TEST_F(CellularTest, GetModemInfo) {
-  static const char kManufacturer[] = "Company";
-  static const char kModelID[] = "Gobi 2000";
-  static const char kHWRev[] = "A00B1234";
-  ModemProxyInterface::Info info;
-  info._1 = kManufacturer;
-  info._2 = kModelID;
-  info._3 = kHWRev;
-  EXPECT_CALL(*proxy_, GetInfo()).WillOnce(Return(info));
-  device_->proxy_.reset(proxy_.release());
-  device_->GetModemInfo();
-  EXPECT_EQ(kManufacturer, device_->manufacturer_);
-  EXPECT_EQ(kModelID, device_->model_id_);
-  EXPECT_EQ(kHWRev, device_->hardware_revision_);
-}
-
 TEST_F(CellularTest, CreateService) {
   SetCellularType(Cellular::kTypeCDMA);
   static const char kPaymentURL[] = "https://payment.url";
@@ -428,7 +483,7 @@
 namespace {
 
 MATCHER(ContainsPhoneNumber, "") {
-  return ContainsKey(arg, Cellular::kConnectPropertyPhoneNumber);
+  return ContainsKey(arg, CellularCapability::kConnectPropertyPhoneNumber);
 }
 
 }  // namespace {}
@@ -451,42 +506,40 @@
   device_->service_ = new CellularService(
       &control_interface_, &dispatcher_, &metrics_, &manager_, device_);
 
-  device_->allow_roaming_ = false;
+  device_->capability_->allow_roaming_ = false;
   device_->service_->roaming_state_ = flimflam::kRoamingStateRoaming;
   device_->Connect(&error);
-  ASSERT_TRUE(device_->task_factory_.empty());
   EXPECT_EQ(Error::kNotOnHomeNetwork, error.type());
+
   error.Populate(Error::kSuccess);
-
+  EXPECT_CALL(*simple_proxy_,
+              Connect(ContainsPhoneNumber(), _,
+                      CellularCapability::kTimeoutConnect))
+                .Times(2)
+                .WillRepeatedly(Invoke(this, &CellularTest::InvokeConnect));
+  device_->capability_->simple_proxy_.reset(simple_proxy_.release());
   device_->service_->roaming_state_ = flimflam::kRoamingStateHome;
-  device_->Connect(&error);
-  ASSERT_FALSE(device_->task_factory_.empty());
-  EXPECT_TRUE(error.IsSuccess());
-
-  device_->allow_roaming_ = true;
-  device_->service_->roaming_state_ = flimflam::kRoamingStateRoaming;
+  device_->state_ = Cellular::kStateRegistered;
   device_->Connect(&error);
   EXPECT_TRUE(error.IsSuccess());
-
-  EXPECT_CALL(*simple_proxy_, Connect(ContainsPhoneNumber())).Times(2);
-  device_->simple_proxy_.reset(simple_proxy_.release());
   dispatcher_.DispatchPendingEvents();
+  EXPECT_EQ(Cellular::kStateConnected, device_->state_);
+
+  device_->capability_->allow_roaming_ = true;
+  device_->service_->roaming_state_ = flimflam::kRoamingStateRoaming;
+  device_->state_ = Cellular::kStateRegistered;
+  device_->Connect(&error);
+  EXPECT_TRUE(error.IsSuccess());
+  dispatcher_.DispatchPendingEvents();
+  EXPECT_EQ(Cellular::kStateConnected, device_->state_);
 }
 
 TEST_F(CellularTest, DisconnectModem) {
   device_->DisconnectModem();
   EXPECT_CALL(*proxy_, Disconnect()).Times(1);
-  device_->proxy_.reset(proxy_.release());
+  device_->capability_->proxy_.reset(proxy_.release());
   device_->state_ = Cellular::kStateConnected;
   device_->DisconnectModem();
 }
 
-TEST_F(CellularTest, DisableModem) {
-  device_->DisableModem();
-  EXPECT_CALL(*proxy_, Enable(false)).Times(1);
-  device_->proxy_.reset(proxy_.release());
-  device_->state_ = Cellular::kStateRegistered;
-  device_->DisableModem();
-}
-
 }  // namespace shill
diff --git a/device.cc b/device.cc
index 4ab781d..62e6061 100644
--- a/device.cc
+++ b/device.cc
@@ -177,9 +177,11 @@
 }
 
 void Device::RegisterOnNetwork(const std::string &/*network_id*/,
-                               Error *error) {
-  Error::PopulateAndLog(error, Error::kNotSupported,
+                               ReturnerInterface *returner) {
+  Error error;
+  Error::PopulateAndLog(&error, Error::kNotSupported,
                         "Device doesn't support network registration.");
+  returner->ReturnError(error);
 }
 
 void Device::RequirePIN(
diff --git a/device.h b/device.h
index cb82509..20c4cf3 100644
--- a/device.h
+++ b/device.h
@@ -68,7 +68,8 @@
 
   // The default implementation sets |error| to kNotSupported.
   virtual void Scan(Error *error);
-  virtual void RegisterOnNetwork(const std::string &network_id, Error *error);
+  virtual void RegisterOnNetwork(const std::string &network_id,
+                                 ReturnerInterface *returner);
   virtual void RequirePIN(
       const std::string &pin, bool require, ReturnerInterface *returner);
   virtual void EnterPIN(const std::string &pin, ReturnerInterface *returner);
diff --git a/device_dbus_adaptor.cc b/device_dbus_adaptor.cc
index f18cae8..c59cd25 100644
--- a/device_dbus_adaptor.cc
+++ b/device_dbus_adaptor.cc
@@ -94,9 +94,10 @@
 
 void DeviceDBusAdaptor::Register(const string &network_id,
                                  ::DBus::Error &error) {
-  Error e;
-  device_->RegisterOnNetwork(network_id, &e);
-  e.ToDBusError(&error);
+  VLOG(2) << __func__ << "(" << network_id << ")";
+  Returner *returner = Returner::Create(this);
+  device_->RegisterOnNetwork(network_id, returner);
+  returner->DelayOrReturn(&error);
 }
 
 void DeviceDBusAdaptor::RequirePin(
diff --git a/error.cc b/error.cc
index b1d5dea..28f7d28 100644
--- a/error.cc
+++ b/error.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,6 +17,7 @@
 // static
 const Error::Info Error::kInfos[kNumErrors] = {
   { "Success", "Success (no error)" },
+  { "Failure", "Operation failed (no other information)" },
   { "AlreadyConnected", "Already connected" },
   { "AlreadyExists", "Already exists" },
   { "InProgress", "In progress" },
@@ -35,7 +36,10 @@
   { "OperationAborted", "Operation aborted" },
   { "OperationTimeout", "Operation timeout" },
   { "PassphraseRequired", "Passphrase required" },
-  { "PermissionDenied", "Permission denied" },
+  { "IncorrectPin", "Incorrect PIN" },
+  { "PinRequired", "SIM PIN is required"},
+  { "PinBlocked", "SIM PIN is blocked"},
+  { "PermissionDenied", "Permission denied" }
 };
 
 // static
@@ -103,3 +107,8 @@
 }
 
 }  // namespace shill
+
+std::ostream &operator<<(std::ostream &stream, const shill::Error &error) {
+  stream << error.GetName(error.type()) << ":" << error.message();
+  return stream;
+}
diff --git a/error.h b/error.h
index 3c01047..6dd256a 100644
--- a/error.h
+++ b/error.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,6 +19,7 @@
  public:
   enum Type {
     kSuccess = 0,  // No error.
+    kOperationFailed,  // failure, otherwise unspecified
     kAlreadyConnected,
     kAlreadyExists,
     kInProgress,
@@ -37,6 +38,9 @@
     kOperationAborted,
     kOperationTimeout,
     kPassphraseRequired,
+    kIncorrectPin,
+    kPinRequired,
+    kPinBlocked,
     kPermissionDenied,
     kNumErrors
   };
@@ -87,4 +91,7 @@
 
 }  // namespace shill
 
+// stream operator provided to facilitate logging
+std::ostream &operator<<(std::ostream &stream, const shill::Error &error);
+
 #endif  // SHILL_ERROR_
diff --git a/mock_modem_cdma_proxy.h b/mock_modem_cdma_proxy.h
index a195bd2..e3c0453 100644
--- a/mock_modem_cdma_proxy.h
+++ b/mock_modem_cdma_proxy.h
@@ -19,7 +19,8 @@
   MockModemCDMAProxy();
   virtual ~MockModemCDMAProxy();
 
-  MOCK_METHOD1(Activate, uint32(const std::string &carrier));
+  MOCK_METHOD3(Activate, void(const std::string &carrier,
+                              AsyncCallHandler *call_handler, int timeout));
   MOCK_METHOD2(GetRegistrationState, void(uint32 *cdma_1x_state,
                                           uint32 *evdo_state));
   MOCK_METHOD0(GetSignalQuality, uint32());
diff --git a/mock_modem_gsm_card_proxy.h b/mock_modem_gsm_card_proxy.h
index 42d7c9b..67fda61 100644
--- a/mock_modem_gsm_card_proxy.h
+++ b/mock_modem_gsm_card_proxy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,16 +19,20 @@
   MockModemGSMCardProxy();
   virtual ~MockModemGSMCardProxy();
 
-  MOCK_METHOD0(GetIMEI, std::string());
-  MOCK_METHOD0(GetIMSI, std::string());
-  MOCK_METHOD0(GetSPN, std::string());
-  MOCK_METHOD0(GetMSISDN, std::string());
+  MOCK_METHOD2(GetIMEI, void(AsyncCallHandler *call_handler, int timeout));
+  MOCK_METHOD2(GetIMSI, void(AsyncCallHandler *call_handler, int timeout));
+  MOCK_METHOD2(GetSPN, void(AsyncCallHandler *call_handler, int timeout));
+  MOCK_METHOD2(GetMSISDN, void(AsyncCallHandler *call_handler, int timeout));
 
-  MOCK_METHOD2(EnablePIN, void(const std::string &pin, bool enabled));
-  MOCK_METHOD1(SendPIN, void(const std::string &pin));
-  MOCK_METHOD2(SendPUK, void(const std::string &puk, const std::string &pin));
-  MOCK_METHOD2(ChangePIN,
-               void(const std::string &old_pin, const std::string &new_pin));
+  MOCK_METHOD4(EnablePIN, void(const std::string &pin, bool enabled,
+                               AsyncCallHandler *call_handler, int timeout));
+  MOCK_METHOD3(SendPIN, void(const std::string &pin,
+                             AsyncCallHandler *call_handler, int timeout));
+  MOCK_METHOD4(SendPUK, void(const std::string &puk, const std::string &pin,
+                             AsyncCallHandler *call_handler, int timeout));
+  MOCK_METHOD4(ChangePIN, void(const std::string &old_pin,
+                               const std::string &new_pin,
+                               AsyncCallHandler *call_handler, int timeout));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockModemGSMCardProxy);
diff --git a/mock_modem_gsm_network_proxy.h b/mock_modem_gsm_network_proxy.h
index 8108d02..c10f469 100644
--- a/mock_modem_gsm_network_proxy.h
+++ b/mock_modem_gsm_network_proxy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -19,10 +19,12 @@
   MockModemGSMNetworkProxy();
   virtual ~MockModemGSMNetworkProxy();
 
-  MOCK_METHOD0(GetRegistrationInfo, RegistrationInfo());
+  MOCK_METHOD2(GetRegistrationInfo, void(AsyncCallHandler *call_handler,
+                                         int timeout));
   MOCK_METHOD0(GetSignalQuality, uint32());
-  MOCK_METHOD1(Register, void(const std::string &network_id));
-  MOCK_METHOD0(Scan, ScanResults());
+  MOCK_METHOD3(Register, void(const std::string &network_id,
+                              AsyncCallHandler *call_handler, int timeout));
+  MOCK_METHOD2(Scan, void(AsyncCallHandler *call_handler, int timeout));
   MOCK_METHOD0(AccessTechnology, uint32());
 
  private:
diff --git a/mock_modem_proxy.h b/mock_modem_proxy.h
index 58fabda..e30676b 100644
--- a/mock_modem_proxy.h
+++ b/mock_modem_proxy.h
@@ -12,14 +12,18 @@
 
 namespace shill {
 
+class AsyncCallHandler;
+
 class MockModemProxy : public ModemProxyInterface {
  public:
   MockModemProxy();
   virtual ~MockModemProxy();
 
-  MOCK_METHOD1(Enable, void(const bool enable));
+  MOCK_METHOD3(Enable, void(bool enable, AsyncCallHandler *call_handler,
+                            int timeout));
+  MOCK_METHOD1(Enable, void(bool enable));
   MOCK_METHOD0(Disconnect, void());
-  MOCK_METHOD0(GetInfo, Info());
+  MOCK_METHOD2(GetModemInfo, void(AsyncCallHandler *call_handler, int timeout));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockModemProxy);
diff --git a/mock_modem_simple_proxy.h b/mock_modem_simple_proxy.h
index be6e17e..5f17629 100644
--- a/mock_modem_simple_proxy.h
+++ b/mock_modem_simple_proxy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,8 +17,10 @@
   MockModemSimpleProxy();
   virtual ~MockModemSimpleProxy();
 
-  MOCK_METHOD0(GetStatus, DBusPropertiesMap());
-  MOCK_METHOD1(Connect, void(const DBusPropertiesMap &properties));
+  MOCK_METHOD2(GetModemStatus, void(AsyncCallHandler *call_handler,
+                                    int timeout));
+  MOCK_METHOD3(Connect, void(const DBusPropertiesMap &properties,
+                             AsyncCallHandler *call_handler, int timeout));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockModemSimpleProxy);
diff --git a/modem_cdma_proxy.cc b/modem_cdma_proxy.cc
index 1311cb0..472e6d1 100644
--- a/modem_cdma_proxy.cc
+++ b/modem_cdma_proxy.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,8 @@
 
 #include <base/logging.h>
 
+#include "cellular_error.h"
+
 using std::string;
 
 namespace shill {
@@ -18,8 +20,9 @@
 
 ModemCDMAProxy::~ModemCDMAProxy() {}
 
-uint32 ModemCDMAProxy::Activate(const string &carrier) {
-  return proxy_.Activate(carrier);
+void ModemCDMAProxy::Activate(const string &carrier,
+                                AsyncCallHandler *call_handler, int timeout) {
+  proxy_.Activate(carrier, call_handler, timeout);
 }
 
 void ModemCDMAProxy::GetRegistrationState(uint32 *cdma_1x_state,
@@ -32,6 +35,7 @@
 }
 
 const string ModemCDMAProxy::MEID() {
+  LOG(INFO) << "ModemCDMAProxy::" << __func__;
   return proxy_.Meid();
 }
 
@@ -66,4 +70,14 @@
   delegate_->OnCDMARegistrationStateChanged(cdma_1x_state, evdo_state);
 }
 
+void ModemCDMAProxy::Proxy::ActivateCallback(const uint32 &status,
+                                             const DBus::Error &dberror,
+                                             void *data) {
+  VLOG(2) << __func__ << "(" << status << ")";
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error),
+  delegate_->OnActivateCallback(status, error, call_handler);
+}
+
 }  // namespace shill
diff --git a/modem_cdma_proxy.h b/modem_cdma_proxy.h
index a3172b8..ed1240e 100644
--- a/modem_cdma_proxy.h
+++ b/modem_cdma_proxy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,7 +16,8 @@
 class ModemCDMAProxy : public ModemCDMAProxyInterface {
  public:
   // Constructs a ModemManager.Modem.CDMA DBus object proxy at |path| owned by
-  // |service|. Caught signals will be dispatched to |delegate|.
+  // |service|. Caught signals and asynchronous method replies will be
+  // dispatched to |delegate|.
   ModemCDMAProxy(ModemCDMAProxyDelegate *delegate,
                  DBus::Connection *connection,
                  const std::string &path,
@@ -24,7 +25,8 @@
   virtual ~ModemCDMAProxy();
 
   // Inherited from ModemCDMAProxyInterface.
-  virtual uint32 Activate(const std::string &carrier);
+  virtual void Activate(const std::string &carrier,
+                        AsyncCallHandler *call_handler, int timeout);
   virtual void GetRegistrationState(uint32 *cdma_1x_state, uint32 *evdo_state);
   virtual uint32 GetSignalQuality();
   virtual const std::string MEID();
@@ -49,6 +51,10 @@
     virtual void RegistrationStateChanged(const uint32 &cdma_1x_state,
                                           const uint32 &evdo_state);
 
+    // Method callbacks inherited from ModemManager::Modem::Cdma_proxy.
+    virtual void ActivateCallback(const uint32 &status,
+                                  const DBus::Error &dberror, void *data);
+
     ModemCDMAProxyDelegate *delegate_;
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
diff --git a/modem_cdma_proxy_interface.h b/modem_cdma_proxy_interface.h
index fd95f0e..3329b7b 100644
--- a/modem_cdma_proxy_interface.h
+++ b/modem_cdma_proxy_interface.h
@@ -13,13 +13,19 @@
 
 namespace shill {
 
-// These are the methods that a ModemManager.Modem.CDMA proxy must support. The
-// interface is provided so that it can be mocked in tests.
+class AsyncCallHandler;
+class Error;
+
+// These are the methods that a ModemManager.Modem.CDMA proxy must support.
+// The interface is provided so that it can be mocked in tests.
+// All calls are made asynchronously. Call completion is signalled through
+// the corresponding 'OnXXXCallback' method in the ProxyDelegate interface.
 class ModemCDMAProxyInterface {
  public:
   virtual ~ModemCDMAProxyInterface() {}
 
-  virtual uint32 Activate(const std::string &carrier) = 0;
+  virtual void Activate(const std::string &carrier,
+                        AsyncCallHandler *call_handler, int timeout) = 0;
   virtual void GetRegistrationState(uint32 *cdma_1x_state,
                                     uint32 *evdo_state) = 0;
   virtual uint32 GetSignalQuality() = 0;
@@ -40,6 +46,8 @@
   virtual void OnCDMARegistrationStateChanged(uint32 state_1x,
                                               uint32 state_evdo) = 0;
   virtual void OnCDMASignalQualityChanged(uint32 strength) = 0;
+  virtual void OnActivateCallback(uint32 status, const Error &error,
+                                  AsyncCallHandler *call_handler) = 0;
 };
 
 }  // namespace shill
diff --git a/modem_gsm_card_proxy.cc b/modem_gsm_card_proxy.cc
index a405334..a197664 100644
--- a/modem_gsm_card_proxy.cc
+++ b/modem_gsm_card_proxy.cc
@@ -1,9 +1,12 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "shill/modem_gsm_card_proxy.h"
 
+#include "shill/cellular_error.h"
+#include "shill/error.h"
+
 using std::string;
 
 namespace shill {
@@ -16,37 +19,41 @@
 
 ModemGSMCardProxy::~ModemGSMCardProxy() {}
 
-string ModemGSMCardProxy::GetIMEI() {
-  return proxy_.GetImei();
+void ModemGSMCardProxy::GetIMEI(AsyncCallHandler *call_handler, int timeout) {
+  proxy_.GetImei(call_handler, timeout);
 }
 
-string ModemGSMCardProxy::GetIMSI() {
-  return proxy_.GetImsi();
+void ModemGSMCardProxy::GetIMSI(AsyncCallHandler *call_handler, int timeout) {
+  proxy_.GetImsi(call_handler, timeout);
 }
 
-string ModemGSMCardProxy::GetSPN() {
-  return proxy_.GetSpn();
+void ModemGSMCardProxy::GetSPN(AsyncCallHandler *call_handler, int timeout) {
+  proxy_.GetSpn(call_handler, timeout);
 }
 
-string ModemGSMCardProxy::GetMSISDN() {
-  return proxy_.GetMsIsdn();
+void ModemGSMCardProxy::GetMSISDN(AsyncCallHandler *call_handler, int timeout) {
+  proxy_.GetMsIsdn(call_handler, timeout);
 }
 
-void ModemGSMCardProxy::EnablePIN(const string &pin, bool enabled) {
-  proxy_.EnablePin(pin, enabled);
+void ModemGSMCardProxy::EnablePIN(const string &pin, bool enabled,
+                                  AsyncCallHandler *call_handler, int timeout) {
+  proxy_.EnablePin(pin, enabled, call_handler, timeout);
 }
 
-void ModemGSMCardProxy::SendPIN(const string &pin) {
-  proxy_.SendPin(pin);
+void ModemGSMCardProxy::SendPIN(const string &pin,
+                                AsyncCallHandler *call_handler, int timeout) {
+  proxy_.SendPin(pin, call_handler, timeout);
 }
 
-void ModemGSMCardProxy::SendPUK(const string &puk, const string &pin) {
-  proxy_.SendPuk(puk, pin);
+void ModemGSMCardProxy::SendPUK(const string &puk, const string &pin,
+                                AsyncCallHandler *call_handler, int timeout) {
+  proxy_.SendPuk(puk, pin, call_handler, timeout);
 }
 
 void ModemGSMCardProxy::ChangePIN(const string &old_pin,
-                                  const string &new_pin) {
-  proxy_.ChangePin(old_pin, new_pin);
+                                  const string &new_pin,
+                                  AsyncCallHandler *call_handler, int timeout) {
+  proxy_.ChangePin(old_pin, new_pin, call_handler, timeout);
 }
 
 ModemGSMCardProxy::Proxy::Proxy(ModemGSMCardProxyDelegate *delegate,
@@ -56,6 +63,74 @@
     : DBus::ObjectProxy(*connection, path, service.c_str()),
       delegate_(delegate) {}
 
+void ModemGSMCardProxy::Proxy::GetImeiCallback(const string &imei,
+                                               const DBus::Error &dberror,
+                                               void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnGetIMEICallback(imei, error, call_handler);
+}
+
+void ModemGSMCardProxy::Proxy::GetImsiCallback(const string &imsi,
+                                               const DBus::Error &dberror,
+                                               void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnGetIMSICallback(imsi, error, call_handler);
+}
+
+void ModemGSMCardProxy::Proxy::GetSpnCallback(const string &spn,
+                                              const DBus::Error &dberror,
+                                              void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnGetSPNCallback(spn, error, call_handler);
+}
+
+void ModemGSMCardProxy::Proxy::GetMsIsdnCallback(const string &misisdn,
+                                                 const DBus::Error &dberror,
+                                                 void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnGetMSISDNCallback(misisdn, error, call_handler);
+}
+
+void ModemGSMCardProxy::Proxy::EnablePinCallback(const DBus::Error &dberror,
+                                                 void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnPINOperationCallback(error, call_handler);
+}
+
+void ModemGSMCardProxy::Proxy::SendPinCallback(const DBus::Error &dberror,
+                                               void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnPINOperationCallback(error, call_handler);
+}
+
+void ModemGSMCardProxy::Proxy::SendPukCallback(const DBus::Error &dberror,
+                                               void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnPINOperationCallback(error, call_handler);
+}
+
+void ModemGSMCardProxy::Proxy::ChangePinCallback(const DBus::Error &dberror,
+                                                 void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnPINOperationCallback(error, call_handler);
+}
+
 ModemGSMCardProxy::Proxy::~Proxy() {}
 
 }  // namespace shill
diff --git a/modem_gsm_card_proxy.h b/modem_gsm_card_proxy.h
index 7b494f3..f087467 100644
--- a/modem_gsm_card_proxy.h
+++ b/modem_gsm_card_proxy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -25,15 +25,19 @@
   virtual ~ModemGSMCardProxy();
 
   // Inherited from ModemGSMCardProxyInterface.
-  virtual std::string GetIMEI();
-  virtual std::string GetIMSI();
-  virtual std::string GetSPN();
-  virtual std::string GetMSISDN();
-  virtual void EnablePIN(const std::string &pin, bool enabled);
-  virtual void SendPIN(const std::string &pin);
-  virtual void SendPUK(const std::string &puk, const std::string &pin);
+  virtual void GetIMEI(AsyncCallHandler *call_handler, int timeout);
+  virtual void GetIMSI(AsyncCallHandler *call_handler, int timeout);
+  virtual void GetSPN(AsyncCallHandler *call_handler, int timeout);
+  virtual void GetMSISDN(AsyncCallHandler *call_handler, int timeout);
+  virtual void EnablePIN(const std::string &pin, bool enabled,
+                         AsyncCallHandler *call_handler, int timeout);
+  virtual void SendPIN(const std::string &pin,
+                       AsyncCallHandler *call_handler, int timeout);
+  virtual void SendPUK(const std::string &puk, const std::string &pin,
+                       AsyncCallHandler *call_handler, int timeout);
   virtual void ChangePIN(const std::string &old_pin,
-                         const std::string &new_pin);
+                         const std::string &new_pin,
+                         AsyncCallHandler *call_handler, int timeout);
 
  private:
   class Proxy
@@ -48,7 +52,21 @@
 
    private:
     // Signal callbacks inherited from ModemManager::Modem::Gsm::Card_proxy.
-    // None.
+    // [None]
+
+    // Method callbacks inherited from ModemManager::Modem::Gsm::Card_proxy.
+    virtual void GetImeiCallback(const std::string &imei,
+                                 const DBus::Error &dberror, void *data);
+    virtual void GetImsiCallback(const std::string &imsi,
+                                 const DBus::Error &dberror, void *data);
+    virtual void GetSpnCallback(const std::string &spn,
+                                 const DBus::Error &dberror, void *data);
+    virtual void GetMsIsdnCallback(const std::string &misisdn,
+                                 const DBus::Error &dberror, void *data);
+    virtual void EnablePinCallback(const DBus::Error &dberror, void *data);
+    virtual void SendPinCallback(const DBus::Error &dberror, void *data);
+    virtual void SendPukCallback(const DBus::Error &dberror, void *data);
+    virtual void ChangePinCallback(const DBus::Error &dberror, void *data);
 
     ModemGSMCardProxyDelegate *delegate_;
 
diff --git a/modem_gsm_card_proxy_interface.h b/modem_gsm_card_proxy_interface.h
index ead7816..cff5d5e 100644
--- a/modem_gsm_card_proxy_interface.h
+++ b/modem_gsm_card_proxy_interface.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,22 +9,31 @@
 
 namespace shill {
 
+class AsyncCallHandler;
+class Error;
+
 // These are the methods that a ModemManager.Modem.Gsm.Card proxy must
 // support. The interface is provided so that it can be mocked in tests.
+// All calls are made asynchronously. Call completion is signalled through
+// the corresponding 'OnXXXCallback' method in the ProxyDelegate interface.
 class ModemGSMCardProxyInterface {
  public:
   virtual ~ModemGSMCardProxyInterface() {}
 
-  virtual std::string GetIMEI() = 0;
-  virtual std::string GetIMSI() = 0;
-  virtual std::string GetSPN() = 0;
-  virtual std::string GetMSISDN() = 0;
+  virtual void GetIMEI(AsyncCallHandler *call_handler, int timeout) = 0;
+  virtual void GetIMSI(AsyncCallHandler *call_handler, int timeout) = 0;
+  virtual void GetSPN(AsyncCallHandler *call_handler, int timeout) = 0;
+  virtual void GetMSISDN(AsyncCallHandler *call_handler, int timeout) = 0;
 
-  virtual void EnablePIN(const std::string &pin, bool enabled) = 0;
-  virtual void SendPIN(const std::string &pin) = 0;
-  virtual void SendPUK(const std::string &puk, const std::string &pin) = 0;
+  virtual void EnablePIN(const std::string &pin, bool enabled,
+                         AsyncCallHandler *call_handler, int timeout) = 0;
+  virtual void SendPIN(const std::string &pin,
+                       AsyncCallHandler *call_handler, int timeout) = 0;
+  virtual void SendPUK(const std::string &puk, const std::string &pin,
+                       AsyncCallHandler *call_handler, int timeout) = 0;
   virtual void ChangePIN(const std::string &old_pin,
-                         const std::string &new_pin) = 0;
+                         const std::string &new_pin,
+                         AsyncCallHandler *call_handler, int timeout) = 0;
 };
 
 // ModemManager.Modem.Gsm.Card callback delegate to be associated with the
@@ -32,6 +41,23 @@
 class ModemGSMCardProxyDelegate {
  public:
   virtual ~ModemGSMCardProxyDelegate() {}
+
+  virtual void OnGetIMEICallback(const std::string &imei,
+                                 const Error &e,
+                                 AsyncCallHandler *call_handler) = 0;
+  virtual void OnGetIMSICallback(const std::string &imsi,
+                                 const Error &e,
+                                 AsyncCallHandler *call_handler) = 0;
+  virtual void OnGetSPNCallback(const std::string &spn,
+                                const Error &e,
+                                AsyncCallHandler *call_handler) = 0;
+  virtual void OnGetMSISDNCallback(const std::string &misisdn,
+                                   const Error &e,
+                                   AsyncCallHandler *call_handler) = 0;
+  // Callback used for all four PIN operations: EnablePIN, SendPIN,
+  // SendPUK, and ChangePIN.
+  virtual void OnPINOperationCallback(const Error &e,
+                                      AsyncCallHandler *call_handler) = 0;
 };
 
 }  // namespace shill
diff --git a/modem_gsm_network_proxy.cc b/modem_gsm_network_proxy.cc
index 0303c3b..ca7bbaa 100644
--- a/modem_gsm_network_proxy.cc
+++ b/modem_gsm_network_proxy.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,6 +6,9 @@
 
 #include <base/logging.h>
 
+#include "shill/cellular_error.h"
+#include "shill/error.h"
+
 using std::string;
 
 namespace shill {
@@ -19,21 +22,23 @@
 
 ModemGSMNetworkProxy::~ModemGSMNetworkProxy() {}
 
-ModemGSMNetworkProxyInterface::RegistrationInfo
-ModemGSMNetworkProxy::GetRegistrationInfo() {
-  return proxy_.GetRegistrationInfo();
+void ModemGSMNetworkProxy::GetRegistrationInfo(AsyncCallHandler *call_handler,
+                                               int timeout) {
+  proxy_.GetRegistrationInfo(call_handler, timeout);
 }
 
 uint32 ModemGSMNetworkProxy::GetSignalQuality() {
   return proxy_.GetSignalQuality();
 }
 
-void ModemGSMNetworkProxy::Register(const string &network_id) {
-  proxy_.Register(network_id);
+void ModemGSMNetworkProxy::Register(const string &network_id,
+                                    AsyncCallHandler *call_handler,
+                                    int timeout) {
+  proxy_.Register(network_id, call_handler, timeout);
 }
 
-ModemGSMNetworkProxyInterface::ScanResults ModemGSMNetworkProxy::Scan() {
-  return proxy_.Scan();
+void ModemGSMNetworkProxy::Scan(AsyncCallHandler *call_handler, int timeout) {
+  proxy_.Scan(call_handler, timeout);
 }
 
 uint32 ModemGSMNetworkProxy::AccessTechnology() {
@@ -60,7 +65,8 @@
     const string &operator_name) {
   VLOG(2) << __func__ << "(" << status << ", " << operator_code << ", "
           << operator_name << ")";
-  delegate_->OnGSMRegistrationInfoChanged(status, operator_code, operator_name);
+  delegate_->OnGSMRegistrationInfoChanged(status, operator_code, operator_name,
+                                          Error(), NULL);
 }
 
 void ModemGSMNetworkProxy::Proxy::NetworkMode(const uint32_t &mode) {
@@ -68,4 +74,30 @@
   delegate_->OnGSMNetworkModeChanged(mode);
 }
 
+void ModemGSMNetworkProxy::Proxy::RegisterCallback(const DBus::Error &dberror,
+                                                   void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnRegisterCallback(error, call_handler);
+}
+
+void ModemGSMNetworkProxy::Proxy::GetRegistrationInfoCallback(
+    const GSMRegistrationInfo &info, const DBus::Error &dberror, void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnGSMRegistrationInfoChanged(info._1, info._2, info._3,
+                                          error, call_handler);
+}
+
+void ModemGSMNetworkProxy::Proxy::ScanCallback(const GSMScanResults &results,
+                                               const DBus::Error &dberror,
+                                               void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnScanCallback(results, error, call_handler);
+}
+
 }  // namespace shill
diff --git a/modem_gsm_network_proxy.h b/modem_gsm_network_proxy.h
index 0c897c0..4b0dc50 100644
--- a/modem_gsm_network_proxy.h
+++ b/modem_gsm_network_proxy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,10 +23,11 @@
   virtual ~ModemGSMNetworkProxy();
 
   // Inherited from ModemGSMNetworkProxyInterface.
-  virtual RegistrationInfo GetRegistrationInfo();
+  virtual void GetRegistrationInfo(AsyncCallHandler *call_handler, int timeout);
   virtual uint32 GetSignalQuality();
-  virtual void Register(const std::string &network_id);
-  virtual ScanResults Scan();
+  virtual void Register(const std::string &network_id,
+                        AsyncCallHandler *call_handler, int timeout);
+  virtual void Scan(AsyncCallHandler *call_handler, int timeout);
   virtual uint32 AccessTechnology();
 
  private:
@@ -48,6 +49,14 @@
                                   const std::string &operator_name);
     virtual void NetworkMode(const uint32_t &mode);
 
+    // Method callbacks inherited from ModemManager::Modem::Gsm::Network_proxy.
+    virtual void RegisterCallback(const DBus::Error &dberror, void *data);
+    virtual void GetRegistrationInfoCallback(const GSMRegistrationInfo &info,
+                                             const DBus::Error &dberror,
+                                             void *data);
+    virtual void ScanCallback(const GSMScanResults &results,
+                              const DBus::Error &dberror, void *data);
+
     ModemGSMNetworkProxyDelegate *delegate_;
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
diff --git a/modem_gsm_network_proxy_interface.h b/modem_gsm_network_proxy_interface.h
index 6c1cbaf..e3ab494 100644
--- a/modem_gsm_network_proxy_interface.h
+++ b/modem_gsm_network_proxy_interface.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,37 +14,56 @@
 
 namespace shill {
 
+class AsyncCallHandler;
+class Error;
+
+typedef DBus::Struct<unsigned int, std::string, std::string>
+    GSMRegistrationInfo;
+typedef std::map<std::string, std::string> GSMScanResult;
+typedef std::vector<GSMScanResult> GSMScanResults;
+
 // These are the methods that a ModemManager.Modem.Gsm.Network proxy must
 // support. The interface is provided so that it can be mocked in tests.
+// All calls are made asynchronously. Call completion is signalled through
+// the corresponding 'OnXXXCallback' method in the ProxyDelegate interface.
 class ModemGSMNetworkProxyInterface {
  public:
-  typedef DBus::Struct<uint32, std::string, std::string> RegistrationInfo;
-  typedef std::map<std::string, std::string> ScanResult;
-  typedef std::vector<ScanResult> ScanResults;
-
   virtual ~ModemGSMNetworkProxyInterface() {}
 
-  virtual RegistrationInfo GetRegistrationInfo() = 0;
+  virtual void GetRegistrationInfo(AsyncCallHandler *call_handler,
+                                   int timeout) = 0;
   virtual uint32 GetSignalQuality() = 0;
-  virtual void Register(const std::string &network_id) = 0;
-  virtual ScanResults Scan() = 0;
+  virtual void Register(const std::string &network_id,
+                        AsyncCallHandler *call_handler, int timeout) = 0;
+  virtual void Scan(AsyncCallHandler *call_handler, int timeout) = 0;
 
   // Properties.
   virtual uint32 AccessTechnology() = 0;
 };
 
-// ModemManager.Modem.Gsm.Network signal delegate to be associated with the
-// proxy.
+// ModemManager.Modem.Gsm.Network method reply callback and signal
+// delegate to be associated with the proxy.
 class ModemGSMNetworkProxyDelegate {
  public:
   virtual ~ModemGSMNetworkProxyDelegate() {}
 
   virtual void OnGSMNetworkModeChanged(uint32 mode) = 0;
+  // The following callback handler is used for both the
+  // RegistrationInfo signal and the GetRegistrationInfo
+  // method reply. For the signal case, the |call_handler|
+  // is NULL.
   virtual void OnGSMRegistrationInfoChanged(
       uint32 status,
       const std::string &operator_code,
-      const std::string &operator_name) = 0;
+      const std::string &operator_name,
+      const Error &error,
+      AsyncCallHandler *call_handler) = 0;
   virtual void OnGSMSignalQualityChanged(uint32 quality) = 0;
+  virtual void OnScanCallback(const GSMScanResults &results,
+                              const Error &error,
+                              AsyncCallHandler *call_handler) = 0;
+  virtual void OnRegisterCallback(const Error &error,
+                                  AsyncCallHandler *call_handler) = 0;
 };
 
 }  // namespace shill
diff --git a/modem_manager_proxy.h b/modem_manager_proxy.h
index b01c91c..348dabf 100644
--- a/modem_manager_proxy.h
+++ b/modem_manager_proxy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -45,6 +45,9 @@
     virtual void DeviceAdded(const DBus::Path &device);
     virtual void DeviceRemoved(const DBus::Path &device);
 
+    // Method callbacks inherited from ModemManager_proxy.
+    // [None]
+
     // The owner of this proxy.
     ModemManager *manager_;
 
diff --git a/modem_proxy.cc b/modem_proxy.cc
index 77e0d8c..0a7b85a 100644
--- a/modem_proxy.cc
+++ b/modem_proxy.cc
@@ -6,6 +6,9 @@
 
 #include <base/logging.h>
 
+#include "shill/cellular_error.h"
+#include "shill/error.h"
+
 using std::string;
 
 namespace shill {
@@ -18,7 +21,20 @@
 
 ModemProxy::~ModemProxy() {}
 
-void ModemProxy::Enable(const bool enable) {
+// TODO(ers): Need to handle dbus errors that occur and prevent the
+// RPC from going out. These currently result in exceptions being thrown.
+// We need a way to let the upper layers know that the operation
+// failed in such a case.
+void ModemProxy::Enable(bool enable, AsyncCallHandler *call_handler,
+                        int timeout) {
+  VLOG(2) << __func__ << "(" << enable << ", " << timeout << ")";
+  proxy_.Enable(enable, call_handler, timeout);
+}
+
+// TODO(ers): temporarily support the blocking version
+// of Enable, until Cellular::Stop is converted for async.
+void ModemProxy::Enable(bool enable) {
+  VLOG(2) << __func__ << "(" << enable << ")";
   proxy_.Enable(enable);
 }
 
@@ -26,8 +42,8 @@
   proxy_.Disconnect();
 }
 
-ModemProxyInterface::Info ModemProxy::GetInfo() {
-  return proxy_.GetInfo();
+void ModemProxy::GetModemInfo(AsyncCallHandler *call_handler, int timeout) {
+  proxy_.GetInfo(call_handler, timeout);
 }
 
 ModemProxy::Proxy::Proxy(ModemProxyDelegate *delegate,
@@ -39,11 +55,35 @@
 
 ModemProxy::Proxy::~Proxy() {}
 
-void ModemProxy::Proxy::StateChanged(const uint32 &old,
-                                     const uint32 &_new,
-                                     const uint32 &reason) {
+void ModemProxy::Proxy::StateChanged(
+    const uint32 &old, const uint32 &_new, const uint32 &reason) {
   VLOG(2) << __func__ << "(" << old << ", " << _new << ", " << reason << ")";
   delegate_->OnModemStateChanged(old, _new, reason);
 }
 
+void ModemProxy::Proxy::EnableCallback(const DBus::Error &dberror, void *data) {
+  VLOG(2) << __func__;
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnModemEnableCallback(error, call_handler);
+}
+
+void ModemProxy::Proxy::GetInfoCallback(const ModemHardwareInfo &info,
+                                        const DBus::Error &dberror,
+                                        void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnGetModemInfoCallback(info, error, call_handler);
+}
+
+void ModemProxy::Proxy::DisconnectCallback(const DBus::Error &dberror,
+                                           void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnDisconnectCallback(error, call_handler);
+}
+
 }  // namespace shill
diff --git a/modem_proxy.h b/modem_proxy.h
index 630c210..d5021f6 100644
--- a/modem_proxy.h
+++ b/modem_proxy.h
@@ -26,9 +26,12 @@
   virtual ~ModemProxy();
 
   // Inherited from ModemProxyInterface.
-  virtual void Enable(const bool enable);
+  virtual void Enable(bool enable, AsyncCallHandler *call_handler, int timeout);
+  // TODO(ers): temporarily advertise the blocking version
+  // of Enable, until Cellular::Stop is converted for async.
+  virtual void Enable(bool enable);
   virtual void Disconnect();
-  virtual Info GetInfo();
+  virtual void GetModemInfo(AsyncCallHandler *call_handler, int timeout);
 
  private:
   class Proxy : public org::freedesktop::ModemManager::Modem_proxy,
@@ -42,9 +45,14 @@
 
    private:
     // Signal callbacks inherited from ModemManager::Modem_proxy.
-    virtual void StateChanged(const uint32 &old,
-                              const uint32 &_new,
-                              const uint32 &reason);
+    virtual void StateChanged(
+        const uint32 &old, const uint32 &_new, const uint32 &reason);
+
+    // Method callbacks inherited from ModemManager::Modem_proxy.
+    virtual void EnableCallback(const DBus::Error &dberror, void *data);
+    virtual void GetInfoCallback(const ModemHardwareInfo &info,
+                                 const DBus::Error &dberror, void *data);
+    virtual void DisconnectCallback(const DBus::Error &dberror, void *data);
 
     ModemProxyDelegate *delegate_;
 
diff --git a/modem_proxy_interface.h b/modem_proxy_interface.h
index 0f20d52..0005087 100644
--- a/modem_proxy_interface.h
+++ b/modem_proxy_interface.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,29 +9,49 @@
 
 #include <dbus-c++/types.h>
 
+#include "shill/dbus_properties.h"
+
 namespace shill {
 
+class AsyncCallHandler;
+class Error;
+
+typedef DBus::Struct<std::string, std::string, std::string> ModemHardwareInfo;
+
 // These are the methods that a ModemManager.Modem proxy must support. The
-// interface is provided so that it can be mocked in tests.
+// interface is provided so that it can be mocked in tests. All calls are
+// made asynchronously. Call completion is signalled through the corresponding
+// 'OnXXXCallback' method in the ProxyDelegate interface.
 class ModemProxyInterface {
  public:
-  typedef DBus::Struct<std::string, std::string, std::string> Info;
-
   virtual ~ModemProxyInterface() {}
 
-  virtual void Enable(const bool enable) = 0;
+  virtual void Enable(bool enable, AsyncCallHandler *call_handler,
+                      int timeout) = 0;
+  // TODO(ers): temporarily advertise the blocking version
+  // of Enable, until Cellular::Stop is converted for async.
+  virtual void Enable(bool enable) = 0;
   virtual void Disconnect() = 0;
-  virtual Info GetInfo() = 0;
+  virtual void GetModemInfo(AsyncCallHandler *call_handler, int timeout) = 0;
 };
 
-// ModemManager.Modem signal delegate to be associated with the proxy.
+// ModemManager.Modem signal and method callback delegate
+// to be associated with the proxy.
 class ModemProxyDelegate {
  public:
   virtual ~ModemProxyDelegate() {}
 
-  virtual void OnModemStateChanged(uint32 old_state,
-                                   uint32 new_state,
-                                   uint32 reason) = 0;
+  virtual void OnModemStateChanged(
+      uint32 old_state, uint32 new_state, uint32 reason) = 0;
+
+  // ModemProxyInterface::Enable callback.
+  virtual void OnModemEnableCallback(const Error &error,
+                                     AsyncCallHandler *call_handler) = 0;
+  virtual void OnGetModemInfoCallback(const ModemHardwareInfo &info,
+                                      const Error &error,
+                                      AsyncCallHandler *call_handler) = 0;
+  virtual void OnDisconnectCallback(const Error &error,
+                                    AsyncCallHandler *call_handler) = 0;
 };
 
 }  // namespace shill
diff --git a/modem_simple_proxy.cc b/modem_simple_proxy.cc
index 37c9c83..a902b16 100644
--- a/modem_simple_proxy.cc
+++ b/modem_simple_proxy.cc
@@ -1,33 +1,58 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #include "shill/modem_simple_proxy.h"
 
+#include "shill/cellular_error.h"
+#include "shill/error.h"
+
 using std::string;
 
 namespace shill {
 
-ModemSimpleProxy::ModemSimpleProxy(DBus::Connection *connection,
+ModemSimpleProxy::ModemSimpleProxy(ModemSimpleProxyDelegate *delegate,
+                                   DBus::Connection *connection,
                                    const string &path,
                                    const string &service)
-    : proxy_(connection, path, service) {}
+    : proxy_(delegate, connection, path, service) {}
 
 ModemSimpleProxy::~ModemSimpleProxy() {}
 
-DBusPropertiesMap ModemSimpleProxy::GetStatus() {
-  return proxy_.GetStatus();
+void ModemSimpleProxy::GetModemStatus(AsyncCallHandler *call_handler,
+                                      int timeout) {
+  proxy_.GetStatus(call_handler, timeout);
 }
 
-void ModemSimpleProxy::Connect(const DBusPropertiesMap &properties) {
-  proxy_.Connect(properties);
+void ModemSimpleProxy::Connect(const DBusPropertiesMap &properties,
+                               AsyncCallHandler *call_handler, int timeout) {
+  proxy_.Connect(properties, call_handler, timeout);
 }
 
-ModemSimpleProxy::Proxy::Proxy(DBus::Connection *connection,
+ModemSimpleProxy::Proxy::Proxy(ModemSimpleProxyDelegate *delegate,
+                               DBus::Connection *connection,
                                const string &path,
                                const string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+    : DBus::ObjectProxy(*connection, path, service.c_str()),
+      delegate_(delegate) {}
 
 ModemSimpleProxy::Proxy::~Proxy() {}
 
+void ModemSimpleProxy::Proxy::GetStatusCallback(const DBusPropertiesMap &props,
+                                                const DBus::Error &dberror,
+                                                void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnGetModemStatusCallback(props, error, call_handler);
+}
+
+void ModemSimpleProxy::Proxy::ConnectCallback(const DBus::Error &dberror,
+                                              void *data) {
+  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  delegate_->OnConnectCallback(error, call_handler);
+}
+
 }  // namespace shill
diff --git a/modem_simple_proxy.h b/modem_simple_proxy.h
index 9536cb2..3193366 100644
--- a/modem_simple_proxy.h
+++ b/modem_simple_proxy.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,25 +17,38 @@
 // A proxy to ModemManager.Modem.Simple.
 class ModemSimpleProxy : public ModemSimpleProxyInterface {
  public:
-  ModemSimpleProxy(DBus::Connection *connection,
+  ModemSimpleProxy(ModemSimpleProxyDelegate *delegate,
+                   DBus::Connection *connection,
                    const std::string &path,
                    const std::string &service);
   virtual ~ModemSimpleProxy();
 
   // Inherited from ModemSimpleProxyInterface.
-  virtual DBusPropertiesMap GetStatus();
-  virtual void Connect(const DBusPropertiesMap &properties);
+  virtual void GetModemStatus(AsyncCallHandler *call_handler, int timeout);
+  virtual void Connect(const DBusPropertiesMap &properties,
+                       AsyncCallHandler *call_handler, int timeout);
 
  private:
   class Proxy : public org::freedesktop::ModemManager::Modem::Simple_proxy,
                 public DBus::ObjectProxy {
    public:
-    Proxy(DBus::Connection *connection,
+    Proxy(ModemSimpleProxyDelegate *delegate,
+          DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
 
    private:
+    // Signal callbacks inherited from ModemManager::Modem::Simple_proxy.
+    // [None]
+
+    // Method callbacks inherited from ModemManager::Modem::Simple_proxy.
+    virtual void GetStatusCallback(const DBusPropertiesMap &props,
+                                   const DBus::Error &dberror, void *data);
+    virtual void ConnectCallback(const DBus::Error &dberror, void *data);
+
+    ModemSimpleProxyDelegate *delegate_;
+
     DISALLOW_COPY_AND_ASSIGN(Proxy);
   };
 
diff --git a/modem_simple_proxy_interface.h b/modem_simple_proxy_interface.h
index fbf32c6..c4a75a6 100644
--- a/modem_simple_proxy_interface.h
+++ b/modem_simple_proxy_interface.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,14 +9,33 @@
 
 namespace shill {
 
+class AsyncCallHandler;
+class Error;
+
 // These are the methods that a ModemManager.Modem.Simple proxy must
 // support. The interface is provided so that it can be mocked in tests.
+// All calls are made asynchronously. Call completion is signalled through
+// the corresponding 'OnXXXCallback' method in the ProxyDelegate interface.
 class ModemSimpleProxyInterface {
  public:
   virtual ~ModemSimpleProxyInterface() {}
 
-  virtual DBusPropertiesMap GetStatus() = 0;
-  virtual void Connect(const DBusPropertiesMap &properties) = 0;
+  virtual void GetModemStatus(AsyncCallHandler *call_handler, int timeout) = 0;
+  virtual void Connect(const DBusPropertiesMap &properties,
+                       AsyncCallHandler *call_handler, int timeout) = 0;
+};
+
+// ModemManager.Modem.Simple method reply callback and signal
+// delegate to be associated with the proxy.
+class ModemSimpleProxyDelegate {
+ public:
+  virtual ~ModemSimpleProxyDelegate() {}
+
+  virtual void OnGetModemStatusCallback(const DBusPropertiesMap &props,
+                                        const Error &error,
+                                        AsyncCallHandler *call_handler) = 0;
+  virtual void OnConnectCallback(const Error &error,
+                                 AsyncCallHandler *call_handler) = 0;
 };
 
 }  // namespace shill
diff --git a/proxy_factory.cc b/proxy_factory.cc
index 387ad3d..346df34 100644
--- a/proxy_factory.cc
+++ b/proxy_factory.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -61,9 +61,10 @@
 }
 
 ModemSimpleProxyInterface *ProxyFactory::CreateModemSimpleProxy(
+    ModemSimpleProxyDelegate *delegate,
     const string &path,
     const string &service) {
-  return new ModemSimpleProxy(connection(), path, service);
+  return new ModemSimpleProxy(delegate, connection(), path, service);
 }
 
 ModemCDMAProxyInterface *ProxyFactory::CreateModemCDMAProxy(
diff --git a/proxy_factory.h b/proxy_factory.h
index 186f00a..e58ceaf 100644
--- a/proxy_factory.h
+++ b/proxy_factory.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -29,6 +29,7 @@
 class ModemManagerProxyInterface;
 class ModemProxyDelegate;
 class ModemProxyInterface;
+class ModemSimpleProxyDelegate;
 class ModemSimpleProxyInterface;
 class PowerManagerProxyDelegate;
 class PowerManagerProxyInterface;
@@ -60,6 +61,7 @@
                                                 const std::string &service);
 
   virtual ModemSimpleProxyInterface *CreateModemSimpleProxy(
+      ModemSimpleProxyDelegate *delegate,
       const std::string &path,
       const std::string &service);
 
diff --git a/service.cc b/service.cc
index 21a8a13..3829d65 100644
--- a/service.cc
+++ b/service.cc
@@ -193,11 +193,12 @@
   explicitly_disconnected_ = true;
 }
 
-void Service::ActivateCellularModem(const string &/*carrier*/, Error *error) {
-  const string kMessage = "Service doesn't support cellular modem activation.";
-  LOG(ERROR) << kMessage;
-  CHECK(error);
-  error->Populate(Error::kInvalidArguments, kMessage);
+void Service::ActivateCellularModem(const string &/*carrier*/,
+                                    ReturnerInterface *returner) {
+  Error error;
+  Error::PopulateAndLog( &error, Error::kNotSupported,
+                         "Service doesn't support cellular modem activation.");
+  returner->ReturnError(error);
 }
 
 bool Service::TechnologyIs(const Technology::Identifier /*type*/) const {
diff --git a/service.h b/service.h
index 944ce6b..1ba48c1 100644
--- a/service.h
+++ b/service.h
@@ -30,6 +30,7 @@
 class KeyValueStore;
 class Manager;
 class Metrics;
+class ReturnerInterface;
 class ServiceAdaptorInterface;
 class StoreInterface;
 
@@ -113,8 +114,9 @@
   // auto-connect until a subsequent call to Connect, or Load.
   virtual void Disconnect(Error *error);
 
-  // The default implementation sets |error| to kInvalidArguments.
-  virtual void ActivateCellularModem(const std::string &carrier, Error *error);
+  // The default implementation returns the error kInvalidArguments.
+  virtual void ActivateCellularModem(const std::string &carrier,
+                                     ReturnerInterface *returner);
 
   // Base method always returns false.
   virtual bool TechnologyIs(const Technology::Identifier type) const;
diff --git a/service_dbus_adaptor.cc b/service_dbus_adaptor.cc
index 70c372c..7ed9d84 100644
--- a/service_dbus_adaptor.cc
+++ b/service_dbus_adaptor.cc
@@ -99,9 +99,10 @@
 
 void ServiceDBusAdaptor::ActivateCellularModem(const string &carrier,
                                                ::DBus::Error &error) {
-  Error e;
-  service_->ActivateCellularModem(carrier, &e);
-  e.ToDBusError(&error);
+  VLOG(2) << __func__;
+  Returner *returner = Returner::Create(this);
+  service_->ActivateCellularModem(carrier, returner);
+  returner->DelayOrReturn(&error);
 }
 
 }  // namespace shill
diff --git a/service_unittest.cc b/service_unittest.cc
index 51f9d12..be325c4 100644
--- a/service_unittest.cc
+++ b/service_unittest.cc
@@ -252,9 +252,11 @@
 }
 
 TEST_F(ServiceTest, ActivateCellularModem) {
+  MockReturner returner;
+  EXPECT_CALL(returner, Return()).Times(0);
+  EXPECT_CALL(returner, ReturnError(_));
   Error error;
-  service_->ActivateCellularModem("Carrier", &error);
-  EXPECT_EQ(Error::kInvalidArguments, error.type());
+  service_->ActivateCellularModem("Carrier", &returner);
 }
 
 TEST_F(ServiceTest, MakeFavorite) {