Make Enable/Disable work using new callbacks for async support.

Use new-style callbacks to implement the Manager EnableTechnology
and DisableTechnology operations asynchronously. This allows
devices to be enabled and disabled from the UI ,and for the UI
to display available networks once the device is enabled.

Removed the behavior whereby setting the Device.Powered property
had the side effect of enabling or disabling the device. To
replace this, I added new Device.Enable and Device.Disable calls
for enabling and disabling individual devices.

Also separated the in-memory value of the Powered property from
the persisted value. Whenever a client requests that a device
be enabled or disabled, the desired power state is immediately
saved in the profile, but the in-memory value isn't updated until
the operation completes. On startup, shill now automatically
starts any devices for which the persistent Powered property
is set, and does not start devices for which it is not set.

BUG=chromium-os:23319,chromium-os:27814
TEST=Manual testing on device + unit tests passing.

Change-Id: Id676be3fc662cfd5efb730c67687edfd16b2dc6b
Reviewed-on: https://gerrit.chromium.org/gerrit/18123
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 ad496d8..5171aa9 100644
--- a/Makefile
+++ b/Makefile
@@ -4,8 +4,8 @@
 
 CXX ?= g++
 CXXFLAGS ?= -fno-strict-aliasing
-CXXFLAGS += -Wall -Wextra -Wno-unused-parameter -Werror -Wuninitialized \
-	    -Woverloaded-virtual
+CXXFLAGS += -Wall -Wextra -Wno-unused-parameter -Wno-unused-result \
+	    -Werror -Wuninitialized -Woverloaded-virtual
 CXXFLAGS += $(EXTRA_CXXFLAGS)
 CPPFLAGS ?= -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
 PKG_CONFIG ?= pkg-config
@@ -16,9 +16,6 @@
 LIBDIR = /usr/lib
 SCRIPTDIR = $(LIBDIR)/flimflam/scripts
 CPPFLAGS += -DSCRIPTDIR=\"$(SCRIPTDIR)\"
-
-# libevent, gdk and gtk-2.0 are needed to leverage chrome's MessageLoop
-# TODO(cmasone): explore if newer versions of libbase let us avoid this.
 BASE_LIBS = -lcares -lmobile-provider -lmetrics
 BASE_INCLUDE_DIRS = -iquote.. -iquote $(BUILDDIR)
 BASE_LIB_DIRS =
@@ -102,7 +99,6 @@
 	$(addprefix $(BUILD_DBUS_BINDINGS_DIR)/, $(DBUS_PROXY_HEADERS))
 
 SHILL_OBJS = $(addprefix $(BUILDDIR)/, \
-	async_call_handler.o \
 	async_connection.o \
 	byte_string.o \
 	cellular.o \
diff --git a/adaptor_interfaces.h b/adaptor_interfaces.h
index f0a24ee..c754f76 100644
--- a/adaptor_interfaces.h
+++ b/adaptor_interfaces.h
@@ -137,18 +137,5 @@
                                     const Stringmap &value) = 0;
 };
 
-// A ReturnerInterface instance (along with its ownership) is passed by the
-// adaptor to the method handler. The handler releases ownership and initiates
-// an RPC return by calling one of the Return* methods.
-class ReturnerInterface {
- public:
-  virtual void Return() = 0;
-  virtual void ReturnError(const Error &error) = 0;
-
- protected:
-  // Destruction happens through the Return* methods.
-  virtual ~ReturnerInterface() {}
-};
-
 }  // namespace shill
 #endif  // SHILL_ADAPTOR_INTERFACES_
diff --git a/async_call_handler.cc b/async_call_handler.cc
deleted file mode 100644
index 83da75e..0000000
--- a/async_call_handler.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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
deleted file mode 100644
index af88be0..0000000
--- a/async_call_handler.h
+++ /dev/null
@@ -1,66 +0,0 @@
-// 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/callbacks.h b/callbacks.h
new file mode 100644
index 0000000..4081054
--- /dev/null
+++ b/callbacks.h
@@ -0,0 +1,31 @@
+// 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_CALLBACKS_
+#define SHILL_CALLBACKS_
+
+#include <base/callback.h>
+
+#include "shill/dbus_properties.h"
+
+namespace shill {
+
+class Error;
+// Convenient typedefs for some commonly used callbacks.
+typedef base::Callback<void(const Error &)> ResultCallback;
+typedef base::Callback<void(const Error &)> EnabledStateChangedCallback;
+typedef base::Callback<void(const DBusPropertiesMap &,
+                            const Error &)> DBusPropertyMapCallback;
+typedef base::Callback<void(const std::vector<DBusPropertiesMap> &,
+                            const Error &)> DBusPropertyMapsCallback;
+typedef base::Callback<void(const DBus::Path &,
+                            const Error &)> DBusPathCallback;
+typedef base::Callback<void(
+    const std::vector<DBus::Path> &, const Error &)> DBusPathsCallback;
+typedef base::Callback<void(const std::string &, const Error &)> StringCallback;
+typedef base::Callback<void(
+    uint32, uint32, const DBusPropertiesMap &)> ActivationStateSignalCallback;
+
+}  // namespace shill
+#endif  // SHILL_CALLBACKS_
diff --git a/cellular.cc b/cellular.cc
index b524e2e..bf49d2f 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -35,6 +35,7 @@
 #include "shill/technology.h"
 
 using base::Bind;
+using base::Closure;
 using std::map;
 using std::string;
 using std::vector;
@@ -137,29 +138,48 @@
   state_ = state;
 }
 
-void Cellular::Start() {
-  LOG(INFO) << __func__ << ": " << GetStateString(state_);
+void Cellular::Start(Error *error,
+                     const EnabledStateChangedCallback &callback) {
+  VLOG(2) << __func__ << ": " << GetStateString(state_);
   if (state_ != kStateDisabled) {
     return;
   }
-  Device::Start();
   if (modem_state_ == kModemStateEnabled) {
-    // Modem already enabled.
-    OnModemEnabled();
+    // Modem already enabled. Make sure shill
+    // state matches ModemManager state.
+    SetState(kStateEnabled);
     return;
   }
-  // TODO(ers): this should not be done automatically. It should
-  // require an explicit Enable request to start the modem.
-  capability_->StartModem();
+  capability_->StartModem(error,
+                          Bind(&Cellular::OnModemStarted, this, callback));
 }
 
-void Cellular::Stop() {
-  DestroyService();
-  DisconnectModem();
+void Cellular::Stop(Error *error,
+                    const EnabledStateChangedCallback &callback) {
+  VLOG(2) << __func__ << ": " << GetStateString(state_);
+  if (service_) {
+    // TODO(ers): See whether we can/should do DestroyService() here.
+    manager()->DeregisterService(service_);
+    service_ = NULL;
+  }
+  capability_->StopModem(error,
+                         Bind(&Cellular::OnModemStopped, this, callback));
+}
+
+void Cellular::OnModemStarted(const EnabledStateChangedCallback &callback,
+                              const Error &error) {
+  VLOG(2) << __func__ << ": " << GetStateString(state_);
+  if (state_ == kStateDisabled)
+    SetState(kStateEnabled);
+  callback.Run(error);
+}
+
+void Cellular::OnModemStopped(const EnabledStateChangedCallback &callback,
+                              const Error &error) {
+  VLOG(2) << __func__ << ": " << GetStateString(state_);
   if (state_ != kStateDisabled)
-    capability_->DisableModem(NULL);
-  capability_->StopModem();
-  Device::Stop();
+    SetState(kStateDisabled);
+  callback.Run(error);
 }
 
 void Cellular::InitCapability(Type type, ProxyFactory *proxy_factory) {
@@ -177,66 +197,49 @@
   }
 }
 
-void Cellular::OnModemEnabled() {
-  VLOG(2) << __func__ << ": " << GetStateString(state_);
-  if (state_ == kStateDisabled) {
-    SetState(kStateEnabled);
-  }
-}
-
-void Cellular::OnModemDisabled() {
-  VLOG(2) << __func__ << ": " << GetStateString(state_);
-  if (state_ != kStateDisabled) {
-    SetState(kStateDisabled);
-  }
-}
-
 void Cellular::Activate(const std::string &carrier,
-                        ReturnerInterface *returner) {
-  capability_->Activate(carrier, new AsyncCallHandler(returner));
+                        Error *error, const ResultCallback &callback) {
+  capability_->Activate(carrier, error, callback);
 }
 
 void Cellular::RegisterOnNetwork(const string &network_id,
-                                 ReturnerInterface *returner) {
-  capability_->RegisterOnNetwork(network_id, new AsyncCallHandler(returner));
+                                 Error *error,
+                                 const ResultCallback &callback) {
+  capability_->RegisterOnNetwork(network_id, error, callback);
 }
 
-void Cellular::RequirePIN(
-    const string &pin, bool require, ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  capability_->RequirePIN(pin, require, new AsyncCallHandler(returner));
+void Cellular::RequirePIN(const string &pin, bool require,
+                          Error *error, const ResultCallback &callback) {
+  VLOG(2) << __func__ << "(" << require << ")";
+  capability_->RequirePIN(pin, require, error, callback);
 }
 
-void Cellular::EnterPIN(const string &pin, ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  capability_->EnterPIN(pin, new AsyncCallHandler(returner));
+void Cellular::EnterPIN(const string &pin,
+                        Error *error, const ResultCallback &callback) {
+  VLOG(2) << __func__;
+  capability_->EnterPIN(pin, error, callback);
 }
 
 void Cellular::UnblockPIN(const string &unblock_code,
                           const string &pin,
-                          ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  capability_->UnblockPIN(unblock_code, pin, new AsyncCallHandler(returner));
+                          Error *error, const ResultCallback &callback) {
+  VLOG(2) << __func__;
+  capability_->UnblockPIN(unblock_code, pin, error, callback);
 }
 
-void Cellular::ChangePIN(
-    const string &old_pin, const string &new_pin,
-    ReturnerInterface *returner) {
-  VLOG(2) << __func__ << "(" << returner << ")";
-  capability_->ChangePIN(old_pin, new_pin, new AsyncCallHandler(returner));
+void Cellular::ChangePIN(const string &old_pin, const string &new_pin,
+                         Error *error, const ResultCallback &callback) {
+  VLOG(2) << __func__;
+  capability_->ChangePIN(old_pin, new_pin, error, callback);
 }
 
-void Cellular::Scan(Error * /*error*/) {
-  // TODO(ers): for now report immediate success.
-  capability_->Scan(NULL);
+void Cellular::Scan(Error *error) {
+  // TODO(ers): for now report immediate success or failure.
+  capability_->Scan(error, ResultCallback());
 }
 
 void Cellular::HandleNewRegistrationState() {
-  dispatcher()->PostTask(Bind(&Cellular::HandleNewRegistrationStateTask, this));
-}
-
-void Cellular::HandleNewRegistrationStateTask() {
-  VLOG(2) << __func__;
+  VLOG(2) << __func__ << ": " << GetStateString(state_);
   if (!capability_->IsRegistered()) {
     DestroyService();
     if (state_ == kStateLinked ||
@@ -246,6 +249,12 @@
     }
     return;
   }
+  // In Disabled state, defer creating a service until fully
+  // enabled. UI will ignore the appearance of a new service
+  // on a disabled device.
+  if (state_ == kStateDisabled) {
+    return;
+  }
   if (state_ == kStateEnabled) {
     SetState(kStateRegistered);
   }
@@ -309,7 +318,8 @@
 
   DBusPropertiesMap properties;
   capability_->SetupConnectProperties(&properties);
-  capability_->Connect(properties);
+  // TODO(ers): use null callback until Connect is made fully asynchronous
+  capability_->Connect(properties, error, ResultCallback());
 }
 
 void Cellular::OnConnected() {
@@ -329,32 +339,22 @@
 
 void Cellular::Disconnect(Error *error) {
   VLOG(2) << __func__;
-  if (state_ != kStateConnected &&
-      state_ != kStateLinked) {
+  if (state_ != kStateConnected && state_ != kStateLinked) {
     Error::PopulateAndLog(
-        error, Error::kInProgress, "Not connected; request ignored.");
+        error, Error::kNotConnected, "Not connected; request ignored.");
     return;
   }
-  capability_->Disconnect();
+  // TODO(ers): use null callback until Disconnect is made fully asynchronous
+  capability_->Disconnect(error, ResultCallback());
 }
 
 void Cellular::OnDisconnected() {
   VLOG(2) << __func__;
-  SetState(kStateRegistered);
-}
-
-void Cellular::DisconnectModem() {
-  VLOG(2) << __func__;
-  if (state_ != kStateConnected &&
-      state_ != kStateLinked) {
-    return;
-  }
-  // 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.
-  capability_->Disconnect();
+  if (state_ == kStateConnected || state_ == kStateLinked)
+    SetState(kStateRegistered);
+  else
+    LOG(WARNING) << "Disconnect occurred while in state "
+                 << GetStateString(state_);
 }
 
 void Cellular::OnDisconnectFailed() {
diff --git a/cellular.h b/cellular.h
index f10214d..8d115c8 100644
--- a/cellular.h
+++ b/cellular.h
@@ -112,7 +112,8 @@
   void Disconnect(Error *error);
 
   // Asynchronously activates the modem. Returns an error on failure.
-  void Activate(const std::string &carrier, ReturnerInterface *returner);
+  void Activate(const std::string &carrier, Error *error,
+                const ResultCallback &callback);
 
   const CellularServiceRefPtr &service() const { return service_; }
 
@@ -142,25 +143,29 @@
   void OnModemManagerPropertiesChanged(const DBusPropertiesMap &properties);
 
   // Inherited from Device.
-  virtual void Start();
-  virtual void Stop();
+  virtual void Start(Error *error, const EnabledStateChangedCallback &callback);
+  virtual void Stop(Error *error, const EnabledStateChangedCallback &callback);
   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,
-                                 ReturnerInterface *returner);
-  virtual void RequirePIN(
-      const std::string &pin, bool require, ReturnerInterface *returner);
-  virtual void EnterPIN(const std::string &pin, ReturnerInterface *returner);
+                                 Error *error,
+                                 const ResultCallback &callback);
+  virtual void RequirePIN(const std::string &pin, bool require,
+                          Error *error, const ResultCallback &callback);
+  virtual void EnterPIN(const std::string &pin,
+                        Error *error, const ResultCallback &callback);
   virtual void UnblockPIN(const std::string &unblock_code,
                           const std::string &pin,
-                          ReturnerInterface *returner);
+                          Error *error, const ResultCallback &callback);
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
-                         ReturnerInterface *returner);
+                         Error *error, const ResultCallback &callback);
 
-  void OnModemEnabled();
-  void OnModemDisabled();
+  void OnModemStarted(const EnabledStateChangedCallback &callback,
+                      const Error &error);
+  void OnModemStopped(const EnabledStateChangedCallback &callback,
+                      const Error &error);
   void OnConnected();
   void OnConnectFailed();
   void OnDisconnected();
@@ -173,12 +178,13 @@
   friend class CellularCapabilityGSMTest;
   friend class ModemTest;
   FRIEND_TEST(CellularCapabilityCDMATest, CreateFriendlyServiceName);
+  FRIEND_TEST(CellularCapabilityCDMATest, GetRegistrationState);
   FRIEND_TEST(CellularCapabilityGSMTest, CreateFriendlyServiceName);
   FRIEND_TEST(CellularServiceTest, FriendlyName);
   FRIEND_TEST(CellularTest, CreateService);
   FRIEND_TEST(CellularTest, Connect);
   FRIEND_TEST(CellularTest, DisableModem);
-  FRIEND_TEST(CellularTest, DisconnectModem);
+  FRIEND_TEST(CellularTest, Disconnect);
   FRIEND_TEST(CellularTest, StartConnected);
   FRIEND_TEST(CellularTest, StartCDMARegister);
   FRIEND_TEST(CellularTest, StartGSMRegister);
@@ -186,23 +192,18 @@
   FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
   FRIEND_TEST(CellularCapabilityTest, EnableModemFail);
   FRIEND_TEST(CellularCapabilityTest, EnableModemSucceed);
+  FRIEND_TEST(CellularCapabilityTest, FinishEnable);
   FRIEND_TEST(CellularCapabilityTest, GetModemInfo);
   FRIEND_TEST(CellularCapabilityTest, GetModemStatus);
 
   void SetState(State state);
 
-  void ConnectTask(const DBusPropertiesMap &properties);
-  void DisconnectTask();
-  void DisconnectModem();
-
   // Invoked when the modem is connected to the cellular network to transition
   // to the network-connected state and bring the network interface up.
   void EstablishLink();
 
   void InitCapability(Type type, ProxyFactory *proxy_factory);
 
-  void HandleNewRegistrationStateTask();
-
   void CreateService();
   void DestroyService();
 
diff --git a/cellular_capability.cc b/cellular_capability.cc
index 183b26f..4dd3cd9 100644
--- a/cellular_capability.cc
+++ b/cellular_capability.cc
@@ -4,14 +4,15 @@
 
 #include "shill/cellular_capability.h"
 
+#include <base/bind.h>
 #include <chromeos/dbus/service_constants.h>
 
-#include "shill/adaptor_interfaces.h"
 #include "shill/cellular.h"
 #include "shill/error.h"
 #include "shill/property_accessor.h"
 #include "shill/proxy_factory.h"
 
+using base::Bind;
 using base::Callback;
 using base::Closure;
 using std::string;
@@ -20,51 +21,21 @@
 
 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(
-    const Closure &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;
-  Closure task = tasks_[0];
-  tasks_.erase(tasks_.begin());
-  dispatcher_->PostTask(task);
-}
+// All timeout values are in milliseconds
+const int CellularCapability::kTimeoutActivate = 120000;
+const int CellularCapability::kTimeoutConnect = 45000;
+const int CellularCapability::kTimeoutDefault = 5000;
+const int CellularCapability::kTimeoutEnable = 15000;
+const int CellularCapability::kTimeoutRegister = 90000;
+const int CellularCapability::kTimeoutScan = 120000;
 
 CellularCapability::CellularCapability(Cellular *cellular,
                                        ProxyFactory *proxy_factory)
     : allow_roaming_(false),
       scanning_supported_(false),
       cellular_(cellular),
-      proxy_factory_(proxy_factory) {
+      proxy_factory_(proxy_factory),
+      weak_ptr_factory_(this) {
   PropertyStore *store = cellular->mutable_store();
   store->RegisterConstString(flimflam::kCarrierProperty, &carrier_);
   HelpRegisterDerivedBool(flimflam::kCellularAllowRoamingProperty,
@@ -112,135 +83,173 @@
       flimflam::kCellularAllowRoamingProperty, value);
 }
 
-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::RunNextStep(CellularTaskList *tasks) {
+  CHECK(!tasks->empty());
+  VLOG(2) << __func__ << ": " << tasks->size() << " remaining tasks";
+  Closure task = (*tasks)[0];
+  tasks->erase(tasks->begin());
+  cellular()->dispatcher()->PostTask(task);
 }
 
-void CellularCapability::StopModem() {
+void CellularCapability::StepCompletedCallback(
+    const ResultCallback &callback,
+    CellularTaskList *tasks,
+    const Error &error) {
+  if (error.IsSuccess() && !tasks->empty()) {
+    RunNextStep(tasks);
+    return;
+  }
+  delete tasks;
+  callback.Run(error);
+}
+
+void CellularCapability::InitProxies() {
+  VLOG(2) << __func__;
+  proxy_.reset(proxy_factory()->CreateModemProxy(
+      cellular()->dbus_path(), cellular()->dbus_owner()));
+  simple_proxy_.reset(proxy_factory()->CreateModemSimpleProxy(
+      cellular()->dbus_path(), cellular()->dbus_owner()));
+  proxy_->set_state_changed_callback(
+      Bind(&CellularCapability::OnModemStateChangedSignal,
+           weak_ptr_factory_.GetWeakPtr()));
+}
+
+void CellularCapability::ReleaseProxies() {
   VLOG(2) << __func__;
   proxy_.reset();
   simple_proxy_.reset();
 }
 
-void CellularCapability::CompleteOperation(AsyncCallHandler *reply_handler) {
-  if (reply_handler && reply_handler->Complete())
-      delete reply_handler;
+void CellularCapability::FinishEnable(const ResultCallback &callback) {
+  callback.Run(Error());
+  GetRegistrationState();
+  GetSignalQuality();
 }
 
-void CellularCapability::CompleteOperation(AsyncCallHandler *reply_handler,
-                                           const Error &error) {
-  if (reply_handler && reply_handler->Complete(error))
-    delete reply_handler;
+void CellularCapability::FinishDisable(const ResultCallback &callback) {
+  ReleaseProxies();
+  callback.Run(Error());
 }
 
 void CellularCapability::OnUnsupportedOperation(
     const char *operation,
-    AsyncCallHandler *call_handler) {
-  Error error;
+    Error *error) {
   string message("The ");
   message.append(operation).append(" operation is not supported.");
-  Error::PopulateAndLog(&error, Error::kNotSupported, message);
-  CompleteOperation(call_handler, error);
+  Error::PopulateAndLog(error, Error::kNotSupported, message);
 }
 
-void CellularCapability::EnableModem(AsyncCallHandler *call_handler) {
+// always called from an async context
+void CellularCapability::EnableModem(const ResultCallback &callback) {
   VLOG(2) << __func__;
-  proxy_->Enable(true, call_handler, kTimeoutDefault);
+  CHECK(!callback.is_null());
+  Error error;
+  proxy_->Enable(true, &error, callback, kTimeoutEnable);
+  if (error.IsFailure())
+    callback.Run(error);
 }
 
-// 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 {
-    if (proxy_.get()) {
-      proxy_->Enable(false);
-    }
-    cellular()->OnModemDisabled();
-  } catch (const DBus::Error e) {
-    LOG(WARNING) << "Disable failed: " << e.what();
-  }
-}
-
-void CellularCapability::GetModemStatus(AsyncCallHandler *call_handler) {
+// always called from an async context
+void CellularCapability::DisableModem(const ResultCallback &callback) {
   VLOG(2) << __func__;
-  simple_proxy_->GetModemStatus(call_handler, kTimeoutDefault);
+  CHECK(!callback.is_null());
+  Error error;
+  proxy_->Enable(false, &error, callback, kTimeoutDefault);
+  if (error.IsFailure())
+      callback.Run(error);
 }
 
-void CellularCapability::GetModemInfo(AsyncCallHandler *call_handler) {
+// always called from an async context
+void CellularCapability::GetModemStatus(const ResultCallback &callback) {
   VLOG(2) << __func__;
-  proxy_->GetModemInfo(call_handler, kTimeoutDefault);
+  CHECK(!callback.is_null());
+  DBusPropertyMapCallback cb = Bind(&CellularCapability::OnGetModemStatusReply,
+                                    weak_ptr_factory_.GetWeakPtr(), callback);
+  Error error;
+  simple_proxy_->GetModemStatus(&error, cb, kTimeoutDefault);
+  if (error.IsFailure())
+      callback.Run(error);
 }
 
-// TODO(ers): make this async (supply an AsyncCallHandler arg)
-void CellularCapability::Connect(const DBusPropertiesMap &properties) {
+// always called from an async context
+void CellularCapability::GetModemInfo(const ResultCallback &callback) {
   VLOG(2) << __func__;
-  simple_proxy_->Connect(properties, NULL, kTimeoutConnect);
+  CHECK(!callback.is_null());
+  ModemInfoCallback cb = Bind(&CellularCapability::OnGetModemInfoReply,
+                              weak_ptr_factory_.GetWeakPtr(), callback);
+  Error error;
+  proxy_->GetModemInfo(&error, cb, kTimeoutDefault);
+  if (error.IsFailure())
+      callback.Run(error);
 }
 
-// TODO(ers): convert to async once the Stop() operation
-// has been converted to multi-step async.
-void CellularCapability::Disconnect() {
+void CellularCapability::Connect(const DBusPropertiesMap &properties,
+                                 Error *error,
+                                 const ResultCallback &callback) {
   VLOG(2) << __func__;
-  try {
-    proxy_->Disconnect();
-    cellular()->OnDisconnected();
-  } catch (const DBus::Error e) {
-    LOG(WARNING) << "Disconnect failed: " << e.what();
-  }
+  ResultCallback cb = Bind(&CellularCapability::OnConnectReply,
+                           weak_ptr_factory_.GetWeakPtr(),
+                           callback);
+  simple_proxy_->Connect(properties, error, cb, kTimeoutConnect);
+}
+
+void CellularCapability::Disconnect(Error *error,
+                                    const ResultCallback &callback) {
+  VLOG(2) << __func__;
+  ResultCallback cb = Bind(&CellularCapability::OnDisconnectReply,
+                           weak_ptr_factory_.GetWeakPtr(),
+                           callback);
+  proxy_->Disconnect(error, cb, kTimeoutDefault);
 }
 
 void CellularCapability::Activate(const string &/*carrier*/,
-                                  AsyncCallHandler *call_handler) {
-  OnUnsupportedOperation(__func__, call_handler);
+                                  Error *error,
+                                  const ResultCallback &/*callback*/) {
+  OnUnsupportedOperation(__func__, error);
 }
 
 void CellularCapability::RegisterOnNetwork(
-    const string &/*network_id*/, AsyncCallHandler *call_handler) {
-  OnUnsupportedOperation(__func__, call_handler);
+    const string &/*network_id*/,
+    Error *error, const ResultCallback &/*callback*/) {
+  OnUnsupportedOperation(__func__, error);
 }
 
-void CellularCapability::RequirePIN(
-    const string &/*pin*/, bool /*require*/, AsyncCallHandler *call_handler) {
-  OnUnsupportedOperation(__func__, call_handler);
+void CellularCapability::RequirePIN(const std::string &/*pin*/,
+                                    bool /*require*/,
+                                    Error *error,
+                                    const ResultCallback &/*callback*/) {
+  OnUnsupportedOperation(__func__, error);
 }
 
 void CellularCapability::EnterPIN(const string &/*pin*/,
-                                  AsyncCallHandler *call_handler) {
-  OnUnsupportedOperation(__func__, call_handler);
+                                  Error *error,
+                                  const ResultCallback &/*callback*/) {
+  OnUnsupportedOperation(__func__, error);
 }
 
 void CellularCapability::UnblockPIN(const string &/*unblock_code*/,
                                     const string &/*pin*/,
-                                    AsyncCallHandler *call_handler) {
-  OnUnsupportedOperation(__func__, call_handler);
+                                    Error *error,
+                                    const ResultCallback &/*callback*/) {
+  OnUnsupportedOperation(__func__, error);
 }
 
 void CellularCapability::ChangePIN(const string &/*old_pin*/,
                                    const string &/*new_pin*/,
-                                   AsyncCallHandler *call_handler) {
-  OnUnsupportedOperation(__func__, call_handler);
+                                   Error *error,
+                                   const ResultCallback &/*callback*/) {
+  OnUnsupportedOperation(__func__, error);
 }
 
-void CellularCapability::Scan(AsyncCallHandler *call_handler) {
-  OnUnsupportedOperation(__func__, call_handler);
+void CellularCapability::Scan(Error *error,
+                              const ResultCallback &callback) {
+  OnUnsupportedOperation(__func__, error);
 }
 
-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) {
+void CellularCapability::OnGetModemStatusReply(
+    const ResultCallback &callback,
+    const DBusPropertiesMap &props,
+    const Error &error) {
   VLOG(2) << __func__ << " " << props.size() << " props. error " << error;
   if (error.IsSuccess()) {
     DBusProperties::GetString(props, "carrier", &carrier_);
@@ -258,13 +267,13 @@
 
     UpdateStatus(props);
   }
-  CompleteOperation(call_handler, error);
+  callback.Run(error);
 }
 
-void CellularCapability::OnGetModemInfoCallback(
+void CellularCapability::OnGetModemInfoReply(
+    const ResultCallback &callback,
     const ModemHardwareInfo &info,
-    const Error &error,
-    AsyncCallHandler *call_handler) {
+    const Error &error) {
   VLOG(2) << __func__ << "(" << error << ")";
   if (error.IsSuccess()) {
     manufacturer_ = info._1;
@@ -273,30 +282,34 @@
     VLOG(2) << __func__ << ": " << info._1 << ", " << info._2 << ", "
             << info._3;
   }
-  CompleteOperation(call_handler, error);
+  callback.Run(error);
 }
 
-void CellularCapability::OnConnectCallback(const Error &error,
-                                           AsyncCallHandler *call_handler) {
+// TODO(ers): use the supplied callback when Connect is fully asynchronous
+void CellularCapability::OnConnectReply(const ResultCallback &callback,
+                                        const Error &error) {
   VLOG(2) << __func__ << "(" << error << ")";
   if (error.IsSuccess())
     cellular()->OnConnected();
   else
     cellular()->OnConnectFailed();
-  CompleteOperation(call_handler, error);
+  if (!callback.is_null())
+    callback.Run(error);
 }
 
-void CellularCapability::OnDisconnectCallback(const Error &error,
-                                              AsyncCallHandler *call_handler) {
+// TODO(ers): use the supplied callback when Disonnect is fully asynchronous
+void CellularCapability::OnDisconnectReply(const ResultCallback &callback,
+                                           const Error &error) {
   VLOG(2) << __func__ << "(" << error << ")";
   if (error.IsSuccess())
     cellular()->OnDisconnected();
   else
     cellular()->OnDisconnectFailed();
-  CompleteOperation(call_handler, error);
+  if (!callback.is_null())
+    callback.Run(error);
 }
 
-void CellularCapability::OnModemStateChanged(
+void CellularCapability::OnModemStateChangedSignal(
     uint32 old_state, uint32 new_state, uint32 reason) {
   VLOG(2) << __func__ << "(" << old_state << ", " << new_state << ", "
           << reason << ")";
diff --git a/cellular_capability.h b/cellular_capability.h
index fd1cb58..cde66eb 100644
--- a/cellular_capability.h
+++ b/cellular_capability.h
@@ -11,9 +11,9 @@
 #include <base/basictypes.h>
 #include <base/callback.h>
 #include <base/memory/scoped_ptr.h>
+#include <base/memory/weak_ptr.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"
@@ -25,14 +25,16 @@
 class EventDispatcher;
 class ProxyFactory;
 
+typedef std::vector<base::Closure> CellularTaskList;
+
 // Cellular devices instantiate subclasses of CellularCapability that
 // handle the specific modem technologies and capabilities.
-class CellularCapability : public ModemProxyDelegate,
-                           public ModemSimpleProxyDelegate {
+class CellularCapability {
  public:
   static const int kTimeoutActivate;
   static const int kTimeoutConnect;
   static const int kTimeoutDefault;
+  static const int kTimeoutEnable;
   static const int kTimeoutRegister;
   static const int kTimeoutScan;
 
@@ -64,104 +66,79 @@
   // 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();
+  virtual void StartModem(Error *error,
+                          const ResultCallback &callback) = 0;
+  // StopModem is also a multi-step operation consisting of several
+  // non-blocking calls.
+  virtual void StopModem(Error *error, const ResultCallback &callback) = 0;
+  virtual void Connect(const DBusPropertiesMap &properties, Error *error,
+                       const ResultCallback &callback);
+  virtual void Disconnect(Error *error, const ResultCallback &callback);
 
   // 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);
+                        Error *error, const ResultCallback &callback);
 
   // Network registration.
   virtual void RegisterOnNetwork(const std::string &network_id,
-                                 AsyncCallHandler *call_handler);
+                                 Error *error,
+                                 const ResultCallback &callback);
   virtual bool IsRegistered() = 0;
 
-  virtual void GetProperties(AsyncCallHandler *call_handler) = 0;
-
-  // Retrieves the current cellular signal strength.
-  virtual void GetSignalQuality() = 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, AsyncCallHandler *call_handler);
-  virtual void EnterPIN(const std::string &pin, AsyncCallHandler *call_handler);
+  virtual void RequirePIN(const std::string &pin, bool require,
+                          Error *error, const ResultCallback &callback);
+  virtual void EnterPIN(const std::string &pin,
+                        Error *error, const ResultCallback &callback);
   virtual void UnblockPIN(const std::string &unblock_code,
                           const std::string &pin,
-                          AsyncCallHandler *call_handler);
+                          Error *error, const ResultCallback &callback);
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
-                         AsyncCallHandler *call_handler);
+                         Error *error, const ResultCallback &callback);
 
   // Network scanning. The default implementation fails by invoking
   // the reply handler with an error.
-  virtual void Scan(AsyncCallHandler *call_handler);
+  virtual void Scan(Error *error, const ResultCallback &callback);
 
   // Returns an empty string if the network technology is unknown.
   virtual std::string GetNetworkTechnologyString() const = 0;
 
   virtual std::string GetRoamingStateString() const = 0;
 
+  virtual void GetSignalQuality() = 0;
+
   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();
+  // The following five methods are only ever called as
+  // callbacks (from the main loop), which is why they
+  // don't take an Error * argument.
+  virtual void EnableModem(const ResultCallback &callback);
+  virtual void DisableModem(const ResultCallback &callback);
+  virtual void GetModemStatus(const ResultCallback &callback);
+  virtual void GetModemInfo(const ResultCallback &callback);
+  virtual void GetProperties(const ResultCallback &callback) = 0;
 
-    // Override the non-error case
-    virtual bool CompleteOperation();
+  virtual void GetRegistrationState() = 0;
 
-    void AddTask(const base::Closure &task);
-    void PostNextTask();
+  void FinishEnable(const ResultCallback &callback);
+  void FinishDisable(const ResultCallback &callback);
+  virtual void InitProxies();
+  virtual void ReleaseProxies();
 
-   private:
-    EventDispatcher *dispatcher_;
-    std::vector<base::Closure> tasks_;
+  static void OnUnsupportedOperation(const char *operation, Error *error);
 
-    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);
-
+  // Run the next task in a list.
+  // Precondition: |tasks| is not empty.
+  void RunNextStep(CellularTaskList *tasks);
+  void StepCompletedCallback(const ResultCallback &callback,
+                             CellularTaskList *tasks, const Error &error);
   // Properties
   bool allow_roaming_;
   bool scanning_supported_;
@@ -183,6 +160,9 @@
   FRIEND_TEST(CellularCapabilityGSMTest, SetStorageIdentifier);
   FRIEND_TEST(CellularCapabilityGSMTest, UpdateStatus);
   FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
+  FRIEND_TEST(CellularCapabilityTest, EnableModemFail);
+  FRIEND_TEST(CellularCapabilityTest, EnableModemSucceed);
+  FRIEND_TEST(CellularCapabilityTest, FinishEnable);
   FRIEND_TEST(CellularCapabilityTest, GetModemInfo);
   FRIEND_TEST(CellularCapabilityTest, GetModemStatus);
   FRIEND_TEST(CellularServiceTest, FriendlyName);
@@ -191,7 +171,7 @@
   FRIEND_TEST(CellularTest, StartGSMRegister);
   FRIEND_TEST(CellularTest, StartLinked);
   FRIEND_TEST(CellularTest, Connect);
-  FRIEND_TEST(CellularTest, DisconnectModem);
+  FRIEND_TEST(CellularTest, Disconnect);
 
   void HelpRegisterDerivedBool(
       const std::string &name,
@@ -201,6 +181,22 @@
   bool GetAllowRoaming(Error */*error*/) { return allow_roaming_; }
   void SetAllowRoaming(const bool &value, Error *error);
 
+  // Method reply and signal callbacks from Modem interface
+  virtual void OnModemStateChangedSignal(
+      uint32 old_state, uint32 new_state, uint32 reason);
+  virtual void OnGetModemInfoReply(const ResultCallback &callback,
+                                   const ModemHardwareInfo &info,
+                                   const Error &error);
+
+  // Method reply callbacks from Modem.Simple interface
+  virtual void OnGetModemStatusReply(const ResultCallback &callback,
+                                     const DBusPropertiesMap &props,
+                                     const Error &error);
+  virtual void OnConnectReply(const ResultCallback &callback,
+                              const Error &error);
+  virtual void OnDisconnectReply(const ResultCallback &callback,
+                                 const Error &error);
+
   Cellular *cellular_;
 
   // Store cached copies of singletons for speed/ease of testing.
@@ -208,6 +204,7 @@
 
   scoped_ptr<ModemProxyInterface> proxy_;
   scoped_ptr<ModemSimpleProxyInterface> simple_proxy_;
+  base::WeakPtrFactory<CellularCapability> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(CellularCapability);
 };
diff --git a/cellular_capability_cdma.cc b/cellular_capability_cdma.cc
index 25b642d..cc60984 100644
--- a/cellular_capability_cdma.cc
+++ b/cellular_capability_cdma.cc
@@ -37,31 +37,64 @@
   store->RegisterConstUint16(flimflam::kPRLVersionProperty, &prl_version_);
 }
 
-
-void CellularCapabilityCDMA::StartModem()
-{
-  CellularCapability::StartModem();
+void CellularCapabilityCDMA::InitProxies() {
+  CellularCapability::InitProxies();
   proxy_.reset(proxy_factory()->CreateModemCDMAProxy(
-      this, cellular()->dbus_path(), cellular()->dbus_owner()));
-
-  MultiStepAsyncCallHandler *call_handler =
-      new MultiStepAsyncCallHandler(cellular()->dispatcher());
-  call_handler->AddTask(Bind(&CellularCapabilityCDMA::EnableModem,
-                              weak_ptr_factory_.GetWeakPtr(), call_handler));
-  call_handler->AddTask(Bind(&CellularCapabilityCDMA::GetModemStatus,
-                              weak_ptr_factory_.GetWeakPtr(), call_handler));
-  call_handler->AddTask(Bind(&CellularCapabilityCDMA::GetMEID,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-  call_handler->AddTask(Bind(&CellularCapabilityCDMA::GetModemInfo,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-  call_handler->AddTask(Bind(&CellularCapabilityCDMA::GetRegistrationState,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-  call_handler->PostNextTask();
+      cellular()->dbus_path(), cellular()->dbus_owner()));
+  proxy_->set_signal_quality_callback(
+      Bind(&CellularCapabilityCDMA::OnSignalQualitySignal,
+           weak_ptr_factory_.GetWeakPtr()));
+  proxy_->set_activation_state_callback(
+      Bind(&CellularCapabilityCDMA::OnActivationStateChangedSignal,
+           weak_ptr_factory_.GetWeakPtr()));
+  proxy_->set_registration_state_callback(
+      Bind(&CellularCapabilityCDMA::OnRegistrationStateChangedSignal,
+           weak_ptr_factory_.GetWeakPtr()));
 }
 
-void CellularCapabilityCDMA::StopModem() {
+void CellularCapabilityCDMA::StartModem(Error *error,
+                                        const ResultCallback &callback) {
   VLOG(2) << __func__;
-  CellularCapability::StopModem();
+  InitProxies();
+
+  CellularTaskList *tasks = new CellularTaskList();
+  ResultCallback cb =
+      Bind(&CellularCapabilityCDMA::StepCompletedCallback,
+           weak_ptr_factory_.GetWeakPtr(), callback, tasks);
+  tasks->push_back(Bind(&CellularCapabilityCDMA::EnableModem,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityCDMA::GetModemStatus,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityCDMA::GetMEID,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityCDMA::GetModemInfo,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityCDMA::FinishEnable,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+
+  RunNextStep(tasks);
+}
+
+void CellularCapabilityCDMA::StopModem(Error *error,
+                                        const ResultCallback &callback) {
+  VLOG(2) << __func__;
+  CellularTaskList *tasks = new CellularTaskList();
+  ResultCallback cb =
+      Bind(&CellularCapabilityCDMA::StepCompletedCallback,
+           weak_ptr_factory_.GetWeakPtr(), callback, tasks);
+  tasks->push_back(Bind(&CellularCapabilityCDMA::Disconnect,
+                        weak_ptr_factory_.GetWeakPtr(),
+                        static_cast<Error *>(NULL), cb));
+  tasks->push_back(Bind(&CellularCapabilityCDMA::DisableModem,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityCDMA::FinishDisable,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+
+  RunNextStep(tasks);
+}
+
+void CellularCapabilityCDMA::ReleaseProxies() {
+  CellularCapability::ReleaseProxies();
   proxy_.reset();
 }
 
@@ -107,18 +140,19 @@
 }
 
 void CellularCapabilityCDMA::Activate(const string &carrier,
-                                      AsyncCallHandler *call_handler) {
+                                      Error *error,
+                                      const ResultCallback &callback) {
   VLOG(2) << __func__ << "(" << carrier << ")";
   if (cellular()->state() != Cellular::kStateEnabled &&
       cellular()->state() != Cellular::kStateRegistered) {
-    Error error;
-    Error::PopulateAndLog(&error, Error::kInvalidArguments,
+    Error::PopulateAndLog(error, Error::kInvalidArguments,
                           "Unable to activate in " +
                           Cellular::GetStateString(cellular()->state()));
-    CompleteOperation(call_handler, error);
     return;
   }
-  proxy_->Activate(carrier, call_handler, kTimeoutActivate);
+  ActivationResultCallback cb = Bind(&CellularCapabilityCDMA::OnActivateReply,
+                                     weak_ptr_factory_.GetWeakPtr(), callback);
+  proxy_->Activate(carrier, error, cb, kTimeoutActivate);
 }
 
 void CellularCapabilityCDMA::HandleNewActivationState(uint32 error) {
@@ -165,19 +199,20 @@
   }
 }
 
-void CellularCapabilityCDMA::GetMEID(AsyncCallHandler *call_handler) {
+void CellularCapabilityCDMA::GetMEID(const ResultCallback &callback) {
   VLOG(2) << __func__;
   if (meid_.empty()) {
     // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
     meid_ = proxy_->MEID();
     VLOG(2) << "MEID: " << meid_;
   }
-  CompleteOperation(call_handler);
+  callback.Run(Error());
 }
 
-void CellularCapabilityCDMA::GetProperties(AsyncCallHandler */*call_handler*/) {
+void CellularCapabilityCDMA::GetProperties(const ResultCallback &callback) {
   VLOG(2) << __func__;
   // No properties.
+  callback.Run(Error());
 }
 
 bool CellularCapabilityCDMA::IsRegistered() {
@@ -216,21 +251,18 @@
 
 void CellularCapabilityCDMA::GetSignalQuality() {
   VLOG(2) << __func__;
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  uint32 strength = proxy_->GetSignalQuality();
-  cellular()->HandleNewSignalQuality(strength);
+  SignalQualityCallback callback =
+      Bind(&CellularCapabilityCDMA::OnGetSignalQualityReply,
+           weak_ptr_factory_.GetWeakPtr());
+  proxy_->GetSignalQuality(NULL, callback, kTimeoutDefault);
 }
 
-void CellularCapabilityCDMA::GetRegistrationState(
-    AsyncCallHandler *call_handler) {
+void CellularCapabilityCDMA::GetRegistrationState() {
   VLOG(2) << __func__;
-  // 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);
+  RegistrationStateCallback callback =
+      Bind(&CellularCapabilityCDMA::OnGetRegistrationStateReply,
+           weak_ptr_factory_.GetWeakPtr());
+  proxy_->GetRegistrationState(NULL, callback, kTimeoutDefault);
 }
 
 string CellularCapabilityCDMA::CreateFriendlyServiceName() {
@@ -248,23 +280,31 @@
   }
 }
 
-void CellularCapabilityCDMA::OnActivateCallback(
-    uint32 status,
-    const Error &error,
-    AsyncCallHandler *call_handler) {
-  if (error.IsFailure()) {
-    CompleteOperation(call_handler, error);
-    return;
+void CellularCapabilityCDMA::OnActivateReply(
+    const ResultCallback &callback, uint32 status, const Error &error) {
+  if (error.IsSuccess()) {
+    if (status == MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR) {
+      activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
+    }
+    HandleNewActivationState(status);
   }
-
-  if (status == MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR) {
-    activation_state_ = MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING;
-  }
-  HandleNewActivationState(status);
-  CompleteOperation(call_handler);
+  callback.Run(error);
 }
 
-void CellularCapabilityCDMA::OnCDMAActivationStateChanged(
+void CellularCapabilityCDMA::OnGetRegistrationStateReply(
+    uint32 state_1x, uint32 state_evdo, const Error &error) {
+  VLOG(2) << __func__;
+  if (error.IsSuccess())
+    OnRegistrationStateChangedSignal(state_1x, state_evdo);
+}
+
+void CellularCapabilityCDMA::OnGetSignalQualityReply(uint32 quality,
+                                                     const Error &error) {
+  if (error.IsSuccess())
+    OnSignalQualitySignal(quality);
+}
+
+void CellularCapabilityCDMA::OnActivationStateChangedSignal(
     uint32 activation_state,
     uint32 activation_error,
     const DBusPropertiesMap &status_changes) {
@@ -290,7 +330,7 @@
   HandleNewActivationState(activation_error);
 }
 
-void CellularCapabilityCDMA::OnCDMARegistrationStateChanged(
+void CellularCapabilityCDMA::OnRegistrationStateChangedSignal(
     uint32 state_1x, uint32 state_evdo) {
   VLOG(2) << __func__;
   registration_state_1x_ = state_1x;
@@ -301,7 +341,7 @@
 void CellularCapabilityCDMA::OnModemManagerPropertiesChanged(
     const DBusPropertiesMap &/*properties*/) {}
 
-void CellularCapabilityCDMA::OnCDMASignalQualityChanged(uint32 strength) {
+void CellularCapabilityCDMA::OnSignalQualitySignal(uint32 strength) {
   cellular()->HandleNewSignalQuality(strength);
 }
 
diff --git a/cellular_capability_cdma.h b/cellular_capability_cdma.h
index 5d34050..fae0dc0 100644
--- a/cellular_capability_cdma.h
+++ b/cellular_capability_cdma.h
@@ -15,38 +15,41 @@
 
 namespace shill {
 
-class CellularCapabilityCDMA : public CellularCapability,
-                               public ModemCDMAProxyDelegate {
-
+class CellularCapabilityCDMA : public CellularCapability {
  public:
   CellularCapabilityCDMA(Cellular *cellular, ProxyFactory *proxy_factory);
 
   // Inherited from CellularCapability.
-  virtual void StartModem();
-  virtual void StopModem();
+  virtual void StartModem(Error *error, const ResultCallback &callback);
+  virtual void StopModem(Error *error, const ResultCallback &callback);
   virtual void OnServiceCreated();
   virtual void UpdateStatus(const DBusPropertiesMap &properties);
   virtual void SetupConnectProperties(DBusPropertiesMap *properties);
-  virtual void Activate(const std::string &carrier,
-                        AsyncCallHandler *call_handler);
+  virtual void Activate(const std::string &carrier, Error *error,
+                        const ResultCallback &callback);
   virtual bool IsRegistered();
   virtual std::string CreateFriendlyServiceName();
   virtual std::string GetNetworkTechnologyString() const;
   virtual std::string GetRoamingStateString() const;
   virtual void GetSignalQuality();
-  virtual void GetRegistrationState(AsyncCallHandler *call_handler);
-  virtual void GetProperties(AsyncCallHandler *call_handler);
+  virtual void GetRegistrationState();
+  virtual void GetProperties(const ResultCallback &callback);
   virtual void OnModemManagerPropertiesChanged(
       const DBusPropertiesMap &properties);
 
-  virtual void GetMEID(AsyncCallHandler *call_handler);
+  virtual void GetMEID(const ResultCallback &callback);
 
   uint32 activation_state() const { return activation_state_; }
   uint32 registration_state_evdo() const { return registration_state_evdo_; }
   uint32 registration_state_1x() const { return registration_state_1x_; }
 
+ protected:
+  virtual void InitProxies();
+  virtual void ReleaseProxies();
+
  private:
   friend class CellularCapabilityCDMATest;
+  friend class CellularTest;
   FRIEND_TEST(CellularCapabilityCDMATest, Activate);
   FRIEND_TEST(CellularCapabilityCDMATest, ActivateError);
   FRIEND_TEST(CellularCapabilityCDMATest, CreateFriendlyServiceName);
@@ -64,16 +67,23 @@
   static std::string GetActivationStateString(uint32 state);
   static std::string GetActivationErrorString(uint32 error);
 
-  // Signal callbacks inherited from ModemCDMAProxyDelegate.
-  virtual void OnCDMAActivationStateChanged(
+  // Signal callbacks from the Modem.CDMA interface
+  virtual void OnActivationStateChangedSignal(
       uint32 activation_state,
       uint32 activation_error,
       const DBusPropertiesMap &status_changes);
-  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);
+  virtual void OnRegistrationStateChangedSignal(
+      uint32 state_1x, uint32 state_evdo);
+  virtual void OnSignalQualitySignal(uint32 strength);
+
+  // Method reply callbacks from the Modem.CDMA interface
+  virtual void OnActivateReply(const ResultCallback &callback,
+                               uint32 status, const Error &error);
+
+  virtual void OnGetRegistrationStateReply(uint32 state_1x, uint32 state_evdo,
+                                           const Error &error);
+  virtual void OnGetSignalQualityReply(uint32 strength,
+                                       const Error &error);
 
   scoped_ptr<ModemCDMAProxyInterface> proxy_;
   base::WeakPtrFactory<CellularCapabilityCDMA> weak_ptr_factory_;
diff --git a/cellular_capability_cdma_unittest.cc b/cellular_capability_cdma_unittest.cc
index 64f3714..1cf13dd 100644
--- a/cellular_capability_cdma_unittest.cc
+++ b/cellular_capability_cdma_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "shill/cellular_capability_cdma.h"
 
+#include <base/bind.h>
 #include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 #include <mm/mm-modem.h>
@@ -19,34 +20,15 @@
 #include "shill/mock_modem_cdma_proxy.h"
 #include "shill/nice_mock_control.h"
 
+using base::Bind;
+using base::Unretained;
+using std::string;
 using testing::_;
+using testing::Invoke;
 using testing::Return;
-using testing::SetArgumentPointee;
 
 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()
@@ -75,9 +57,35 @@
         dynamic_cast<CellularCapabilityCDMA *>(cellular_->capability_.get());
   }
 
+  void InvokeActivate(const string &carrier, Error *error,
+                      const ActivationResultCallback &callback,
+                      int timeout) {
+    callback.Run(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR, Error());
+  }
+  void InvokeActivateError(const string &carrier, Error *error,
+                           const ActivationResultCallback &callback,
+                           int timeout) {
+    callback.Run(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_SIGNAL, Error());
+  }
+  void InvokeGetSignalQuality(Error *error,
+                              const SignalQualityCallback &callback,
+                              int timeout) {
+    callback.Run(kStrength, Error());
+  }
+  void InvokeGetRegistrationState(Error *error,
+                                  const RegistrationStateCallback &callback,
+                                  int timeout) {
+    callback.Run(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
+                 MM_MODEM_CDMA_REGISTRATION_STATE_HOME,
+                 Error());
+  }
+
+  MOCK_METHOD1(TestCallback, void(const Error &error));
+
  protected:
   static const char kMEID[];
   static const char kTestCarrier[];
+  static const unsigned int kStrength;
 
   void SetRegistrationStateEVDO(uint32 state) {
     capability_->registration_state_evdo_ = state;
@@ -112,6 +120,7 @@
 
 const char CellularCapabilityCDMATest::kMEID[] = "D1234567EF8901";
 const char CellularCapabilityCDMATest::kTestCarrier[] = "The Cellular Carrier";
+const unsigned int CellularCapabilityCDMATest::kStrength = 90;
 
 TEST_F(CellularCapabilityCDMATest, PropertyStore) {
   EXPECT_TRUE(cellular_->store().Contains(flimflam::kPRLVersionProperty));
@@ -119,16 +128,15 @@
 
 TEST_F(CellularCapabilityCDMATest, Activate) {
   SetDeviceState(Cellular::kStateEnabled);
-  EXPECT_CALL(*proxy_,
-              Activate(kTestCarrier, _, CellularCapability::kTimeoutActivate));
-  MockReturner returner;
-  scoped_ptr<TestAsyncCallHandler> handler(new TestAsyncCallHandler(&returner));
+  EXPECT_CALL(*proxy_, Activate(kTestCarrier, _, _,
+                                CellularCapability::kTimeoutActivate))
+      .WillOnce(Invoke(this,
+                       &CellularCapabilityCDMATest::InvokeActivate));
+  EXPECT_CALL(*this, TestCallback(_));
   SetProxy();
-  capability_->Activate(kTestCarrier, handler.get());
   SetService();
-  capability_->OnActivateCallback(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_ERROR,
-                                  Error(), NULL);
-  EXPECT_TRUE(TestAsyncCallHandler::error().IsSuccess());
+  capability_->Activate(kTestCarrier, NULL,
+      Bind(&CellularCapabilityCDMATest::TestCallback, Unretained(this)));
   EXPECT_EQ(MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING,
             capability_->activation_state());
   EXPECT_EQ(flimflam::kActivationStateActivating,
@@ -137,21 +145,16 @@
 }
 
 TEST_F(CellularCapabilityCDMATest, ActivateError) {
-  MockReturner returner;
-  TestAsyncCallHandler *handler = new TestAsyncCallHandler(&returner);
-  capability_->Activate(kTestCarrier, handler);
-  EXPECT_EQ(Error::kInvalidArguments, TestAsyncCallHandler::error().type());
-
-  handler = new TestAsyncCallHandler(&returner);
-  SetDeviceState(Cellular::kStateRegistered);
-  EXPECT_CALL(*proxy_, Activate(kTestCarrier, handler,
-                                CellularCapability::kTimeoutActivate));
+  SetDeviceState(Cellular::kStateEnabled);
+  EXPECT_CALL(*proxy_, Activate(kTestCarrier, _, _,
+                                CellularCapability::kTimeoutActivate))
+      .WillOnce(Invoke(this,
+                       &CellularCapabilityCDMATest::InvokeActivateError));
+  EXPECT_CALL(*this, TestCallback(_));
   SetProxy();
   SetService();
-  capability_->Activate(kTestCarrier, handler);
-  capability_->OnActivateCallback(MM_MODEM_CDMA_ACTIVATION_ERROR_NO_SIGNAL,
-                                  Error(), handler);
-  EXPECT_TRUE(TestAsyncCallHandler::error().IsFailure());
+  capability_->Activate(kTestCarrier, NULL,
+      Bind(&CellularCapabilityCDMATest::TestCallback, Unretained(this)));
   EXPECT_EQ(MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED,
             capability_->activation_state());
   EXPECT_EQ(flimflam::kActivationStateNotActivated,
@@ -261,8 +264,10 @@
 }
 
 TEST_F(CellularCapabilityCDMATest, GetSignalQuality) {
-  const int kStrength = 90;
-  EXPECT_CALL(*proxy_, GetSignalQuality()).WillOnce(Return(kStrength));
+  EXPECT_CALL(*proxy_,
+              GetSignalQuality(NULL, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this,
+                       &CellularCapabilityCDMATest::InvokeGetSignalQuality));
   SetProxy();
   SetService();
   EXPECT_EQ(0, cellular_->service()->strength());
@@ -276,14 +281,16 @@
             capability_->registration_state_1x());
   EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
             capability_->registration_state_evdo());
-  EXPECT_CALL(*proxy_, GetRegistrationState(_, _))
-      .WillOnce(DoAll(
-          SetArgumentPointee<0>(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED),
-          SetArgumentPointee<1>(MM_MODEM_CDMA_REGISTRATION_STATE_HOME)));
-  EXPECT_CALL(*proxy_, GetSignalQuality()).WillOnce(Return(90));
+  EXPECT_CALL(*proxy_,
+              GetRegistrationState(NULL, _,
+                                   CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(
+          this,
+          &CellularCapabilityCDMATest::InvokeGetRegistrationState));
   SetProxy();
+  cellular_->state_ = Cellular::kStateEnabled;
   EXPECT_CALL(manager_, RegisterService(_));
-  capability_->GetRegistrationState(NULL);
+  capability_->GetRegistrationState();
   dispatcher_.DispatchPendingEvents();
   EXPECT_EQ(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
             capability_->registration_state_1x());
diff --git a/cellular_capability_gsm.cc b/cellular_capability_gsm.cc
index 63ebbff..6a7e88e 100644
--- a/cellular_capability_gsm.cc
+++ b/cellular_capability_gsm.cc
@@ -89,55 +89,79 @@
               this, get, set)));
 }
 
-void CellularCapabilityGSM::StartModem()
-{
-  CellularCapability::StartModem();
+void CellularCapabilityGSM::InitProxies() {
+  CellularCapability::InitProxies();
   card_proxy_.reset(
-      proxy_factory()->CreateModemGSMCardProxy(this,
-                                               cellular()->dbus_path(),
+      proxy_factory()->CreateModemGSMCardProxy(cellular()->dbus_path(),
                                                cellular()->dbus_owner()));
   network_proxy_.reset(
-      proxy_factory()->CreateModemGSMNetworkProxy(this,
-                                                  cellular()->dbus_path(),
+      proxy_factory()->CreateModemGSMNetworkProxy(cellular()->dbus_path(),
                                                   cellular()->dbus_owner()));
-  MultiStepAsyncCallHandler *call_handler =
-      new MultiStepAsyncCallHandler(cellular()->dispatcher());
-  call_handler->AddTask(Bind(&CellularCapabilityGSM::EnableModem,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-
-  call_handler->AddTask(Bind(&CellularCapabilityGSM::Register,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-
-  call_handler->AddTask(Bind(&CellularCapabilityGSM::GetModemStatus,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-
-  call_handler->AddTask(Bind(&CellularCapabilityGSM::GetIMEI,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-
-  call_handler->AddTask(Bind(&CellularCapabilityGSM::GetIMSI,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-
-  call_handler->AddTask(Bind(&CellularCapabilityGSM::GetSPN,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-
-  call_handler->AddTask(Bind(&CellularCapabilityGSM::GetMSISDN,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-
-  call_handler->AddTask(Bind(&CellularCapabilityGSM::GetProperties,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-
-  call_handler->AddTask(Bind(&CellularCapabilityGSM::GetModemInfo,
-                             weak_ptr_factory_.GetWeakPtr(), call_handler));
-
-  call_handler->AddTask(Bind(&CellularCapabilityGSM::GetRegistrationState,
-                             weak_ptr_factory_.GetWeakPtr(),
-                      call_handler));
-  call_handler->PostNextTask();
+  network_proxy_->set_signal_quality_callback(
+      Bind(&CellularCapabilityGSM::OnSignalQualitySignal,
+           weak_ptr_factory_.GetWeakPtr()));
+  network_proxy_->set_network_mode_callback(
+      Bind(&CellularCapabilityGSM::OnNetworkModeSignal,
+           weak_ptr_factory_.GetWeakPtr()));
+  network_proxy_->set_registration_info_callback(
+      Bind(&CellularCapabilityGSM::OnRegistrationInfoSignal,
+           weak_ptr_factory_.GetWeakPtr()));
 }
 
-void CellularCapabilityGSM::StopModem() {
+void CellularCapabilityGSM::StartModem(Error *error,
+                                       const ResultCallback &callback) {
+  InitProxies();
+
+  CellularTaskList *tasks = new CellularTaskList();
+  ResultCallback cb =
+      Bind(&CellularCapabilityGSM::StepCompletedCallback,
+           weak_ptr_factory_.GetWeakPtr(), callback, tasks);
+  tasks->push_back(Bind(&CellularCapabilityGSM::EnableModem,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityGSM::Register,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityGSM::GetModemStatus,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityGSM::GetIMEI,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityGSM::GetIMSI,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityGSM::GetSPN,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityGSM::GetMSISDN,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityGSM::GetProperties,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityGSM::GetModemInfo,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityGSM::FinishEnable,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+
+  RunNextStep(tasks);
+}
+
+void CellularCapabilityGSM::StopModem(Error *error,
+                                      const ResultCallback &callback) {
   VLOG(2) << __func__;
-  CellularCapability::StopModem();
+
+  CellularTaskList *tasks = new CellularTaskList();
+  ResultCallback cb =
+      Bind(&CellularCapabilityGSM::StepCompletedCallback,
+           weak_ptr_factory_.GetWeakPtr(), callback, tasks);
+  tasks->push_back(Bind(&CellularCapabilityGSM::Disconnect,
+                        weak_ptr_factory_.GetWeakPtr(),
+                        static_cast<Error *>(NULL), cb));
+  tasks->push_back(Bind(&CellularCapabilityGSM::DisableModem,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+  tasks->push_back(Bind(&CellularCapabilityGSM::FinishDisable,
+                        weak_ptr_factory_.GetWeakPtr(), cb));
+
+  RunNextStep(tasks);
+}
+
+void CellularCapabilityGSM::ReleaseProxies() {
+  VLOG(2) << __func__;
+  CellularCapability::ReleaseProxies();
   card_proxy_.reset();
   network_proxy_.reset();
 }
@@ -167,60 +191,94 @@
   // TODO(petkov): Setup apn and "home_only".
 }
 
-void CellularCapabilityGSM::GetIMEI(AsyncCallHandler *call_handler){
+// always called from an async context
+void CellularCapabilityGSM::GetIMEI(const ResultCallback &callback) {
   VLOG(2) << __func__;
+  CHECK(!callback.is_null());
+  Error error;
   if (imei_.empty()) {
-    card_proxy_->GetIMEI(call_handler, kTimeoutDefault);
+    GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMEIReply,
+                                    weak_ptr_factory_.GetWeakPtr(), callback);
+    card_proxy_->GetIMEI(&error, cb, kTimeoutDefault);
+    if (error.IsFailure())
+      callback.Run(error);
   } else {
     VLOG(2) << "Already have IMEI " << imei_;
-    CompleteOperation(call_handler);
+    callback.Run(error);
   }
 }
 
-void CellularCapabilityGSM::GetIMSI(AsyncCallHandler *call_handler){
+// always called from an async context
+void CellularCapabilityGSM::GetIMSI(const ResultCallback &callback) {
   VLOG(2) << __func__;
+  CHECK(!callback.is_null());
+  Error error;
   if (imsi_.empty()) {
-    card_proxy_->GetIMSI(call_handler, kTimeoutDefault);
+    GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetIMSIReply,
+                                    weak_ptr_factory_.GetWeakPtr(),
+                                    callback);
+    card_proxy_->GetIMSI(&error, cb, kTimeoutDefault);
+    if (error.IsFailure())
+      callback.Run(error);
   } else {
     VLOG(2) << "Already have IMSI " << imsi_;
-    CompleteOperation(call_handler);
+    callback.Run(error);
   }
 }
 
-void CellularCapabilityGSM::GetSPN(AsyncCallHandler *call_handler){
+// always called from an async context
+void CellularCapabilityGSM::GetSPN(const ResultCallback &callback) {
   VLOG(2) << __func__;
+  CHECK(!callback.is_null());
+  Error error;
   if (spn_.empty()) {
-    card_proxy_->GetSPN(call_handler, kTimeoutDefault);
+    GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetSPNReply,
+                                    weak_ptr_factory_.GetWeakPtr(),
+                                    callback);
+    card_proxy_->GetSPN(&error, cb, kTimeoutDefault);
+    if (error.IsFailure())
+      callback.Run(error);
   } else {
     VLOG(2) << "Already have SPN " << spn_;
-    CompleteOperation(call_handler);
+    callback.Run(error);
   }
 }
 
-void CellularCapabilityGSM::GetMSISDN(AsyncCallHandler *call_handler){
+// always called from an async context
+void CellularCapabilityGSM::GetMSISDN(const ResultCallback &callback) {
   VLOG(2) << __func__;
+  CHECK(!callback.is_null());
+  Error error;
   if (mdn_.empty()) {
-    card_proxy_->GetMSISDN(call_handler, kTimeoutDefault);
+    GSMIdentifierCallback cb = Bind(&CellularCapabilityGSM::OnGetMSISDNReply,
+                                    weak_ptr_factory_.GetWeakPtr(),
+                                    callback);
+    card_proxy_->GetMSISDN(&error, cb, kTimeoutDefault);
+    if (error.IsFailure())
+      callback.Run(error);
   } else {
     VLOG(2) << "Already have MSISDN " << mdn_;
-    CompleteOperation(call_handler);
+    callback.Run(error);
   }
 }
 
 void CellularCapabilityGSM::GetSignalQuality() {
   VLOG(2) << __func__;
-  // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
-  uint32 strength = network_proxy_->GetSignalQuality();
-  cellular()->HandleNewSignalQuality(strength);
+  SignalQualityCallback callback =
+      Bind(&CellularCapabilityGSM::OnGetSignalQualityReply,
+           weak_ptr_factory_.GetWeakPtr());
+  network_proxy_->GetSignalQuality(NULL, callback, kTimeoutDefault);
 }
 
-void CellularCapabilityGSM::GetRegistrationState(
-    AsyncCallHandler *call_handler) {
+void CellularCapabilityGSM::GetRegistrationState() {
   VLOG(2) << __func__;
-  network_proxy_->GetRegistrationInfo(call_handler, kTimeoutDefault);
+  RegistrationInfoCallback callback =
+      Bind(&CellularCapabilityGSM::OnGetRegistrationInfoReply,
+           weak_ptr_factory_.GetWeakPtr());
+  network_proxy_->GetRegistrationInfo(NULL, callback, kTimeoutDefault);
 }
 
-void CellularCapabilityGSM::GetProperties(AsyncCallHandler *call_handler) {
+void CellularCapabilityGSM::GetProperties(const ResultCallback &callback) {
   VLOG(2) << __func__;
 
   // TODO(petkov): Switch to asynchronous calls (crosbug.com/17583).
@@ -233,7 +291,7 @@
   sim_lock_status_.enabled = locks & MM_MODEM_GSM_FACILITY_SIM;
   VLOG(2) << "GSM EnabledFacilityLocks: " << locks;
 
-  CompleteOperation(call_handler);
+  callback.Run(Error());
 }
 
 string CellularCapabilityGSM::CreateFriendlyServiceName() {
@@ -364,28 +422,38 @@
       flimflam::kCellularApnListProperty, apn_list_);
 }
 
-void CellularCapabilityGSM::Register(AsyncCallHandler *call_handler) {
+// always called from an async context
+void CellularCapabilityGSM::Register(const ResultCallback &callback) {
   VLOG(2) << __func__ << " \"" << selected_network_ << "\"";
-  network_proxy_->Register(selected_network_, call_handler,
-                           kTimeoutRegister);
+  CHECK(!callback.is_null());
+  Error error;
+  ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
+                                weak_ptr_factory_.GetWeakPtr(), callback);
+  network_proxy_->Register(selected_network_, &error, cb, kTimeoutRegister);
+  if (error.IsFailure())
+    callback.Run(error);
 }
 
 void CellularCapabilityGSM::RegisterOnNetwork(
-    const string &network_id, AsyncCallHandler *call_handler) {
+    const string &network_id,
+    Error *error,
+    const ResultCallback &callback) {
   VLOG(2) << __func__ << "(" << network_id << ")";
+  CHECK(error);
   desired_network_ = network_id;
-  network_proxy_->Register(network_id, call_handler,
-                           kTimeoutRegister);
+  ResultCallback cb = Bind(&CellularCapabilityGSM::OnRegisterReply,
+                                weak_ptr_factory_.GetWeakPtr(), callback);
+  network_proxy_->Register(network_id, error, cb, kTimeoutRegister);
 }
 
-void CellularCapabilityGSM::OnRegisterCallback(const Error &error,
-                                               AsyncCallHandler *call_handler) {
+void CellularCapabilityGSM::OnRegisterReply(const ResultCallback &callback,
+                                            const Error &error) {
   VLOG(2) << __func__ << "(" << error << ")";
 
   if (error.IsSuccess()) {
     selected_network_ = desired_network_;
     desired_network_.clear();
-    CompleteOperation(call_handler);
+    callback.Run(error);
     return;
   }
   // If registration on the desired network failed,
@@ -393,10 +461,11 @@
   if (!desired_network_.empty()) {
     desired_network_.clear();
     selected_network_.clear();
-    Register(call_handler);
+    LOG(INFO) << "Couldn't register on selected network, trying home network";
+    Register(callback);
     return;
   }
-  CompleteOperation(call_handler, error);
+  callback.Run(error);
 }
 
 bool CellularCapabilityGSM::IsRegistered() {
@@ -405,45 +474,49 @@
 }
 
 void CellularCapabilityGSM::RequirePIN(
-    const string &pin, bool require, AsyncCallHandler *call_handler) {
-  VLOG(2) << __func__ << "(" << call_handler << ")";
-  card_proxy_->EnablePIN(pin, require, call_handler, kTimeoutDefault);
+    const std::string &pin, bool require,
+    Error *error, const ResultCallback &callback) {
+  CHECK(error);
+  card_proxy_->EnablePIN(pin, require, error, callback, kTimeoutDefault);
 }
 
 void CellularCapabilityGSM::EnterPIN(const string &pin,
-                                     AsyncCallHandler *call_handler) {
-  VLOG(2) << __func__ << "(" << call_handler << ")";
-  card_proxy_->SendPIN(pin, call_handler, kTimeoutDefault);
+                                     Error *error,
+                                     const ResultCallback &callback) {
+  CHECK(error);
+  card_proxy_->SendPIN(pin, error, callback, kTimeoutDefault);
 }
 
 void CellularCapabilityGSM::UnblockPIN(const string &unblock_code,
                                        const string &pin,
-                                       AsyncCallHandler *call_handler) {
-  VLOG(2) << __func__ << "(" << call_handler << ")";
-  card_proxy_->SendPUK(unblock_code, pin, call_handler,
-                       kTimeoutDefault);
+                                       Error *error,
+                                       const ResultCallback &callback) {
+  CHECK(error);
+  card_proxy_->SendPUK(unblock_code, pin, error, callback, kTimeoutDefault);
 }
 
 void CellularCapabilityGSM::ChangePIN(
     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);
+    Error *error, const ResultCallback &callback) {
+  CHECK(error);
+  card_proxy_->ChangePIN(old_pin, new_pin, error, callback, kTimeoutDefault);
 }
 
-void CellularCapabilityGSM::Scan(AsyncCallHandler *call_handler) {
+void CellularCapabilityGSM::Scan(Error *error, const ResultCallback &callback) {
   VLOG(2) << __func__;
   // TODO(petkov): Defer scan requests if a scan is in progress already.
-  network_proxy_->Scan(call_handler, kTimeoutScan);
+  CHECK(error);
+  ScanResultsCallback cb = Bind(&CellularCapabilityGSM::OnScanReply,
+                                weak_ptr_factory_.GetWeakPtr(), callback);
+  network_proxy_->Scan(error, cb, kTimeoutScan);
 }
 
-void CellularCapabilityGSM::OnScanCallback(const GSMScanResults &results,
-                                           const Error &error,
-                                           AsyncCallHandler *call_handler) {
+void CellularCapabilityGSM::OnScanReply(const ResultCallback &callback,
+                                        const GSMScanResults &results,
+                                        const Error &error) {
   VLOG(2) << __func__;
   if (error.IsFailure()) {
-    CompleteOperation(call_handler, error);
+    callback.Run(error);
     return;
   }
   found_networks_.clear();
@@ -453,7 +526,7 @@
   }
   cellular()->adaptor()->EmitStringmapsChanged(flimflam::kFoundNetworksProperty,
                                                found_networks_);
-  CompleteOperation(call_handler);
+  callback.Run(error);
 }
 
 Stringmap CellularCapabilityGSM::ParseScanResult(const GSMScanResult &result) {
@@ -596,46 +669,55 @@
   }
 }
 
-void CellularCapabilityGSM::OnGSMNetworkModeChanged(uint32 /*mode*/) {
+void CellularCapabilityGSM::OnNetworkModeSignal(uint32 /*mode*/) {
   // TODO(petkov): Implement this.
   NOTIMPLEMENTED();
 }
 
-void CellularCapabilityGSM::OnGSMRegistrationInfoChanged(
-    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::OnRegistrationInfoSignal(
+    uint32 status, const string &operator_code, const string &operator_name) {
+  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();
 }
 
-void CellularCapabilityGSM::OnGSMSignalQualityChanged(uint32 quality) {
+void CellularCapabilityGSM::OnSignalQualitySignal(uint32 quality) {
   cellular()->HandleNewSignalQuality(quality);
 }
 
-void CellularCapabilityGSM::OnGetIMEICallback(const string &imei,
-                                              const Error &error,
-                                              AsyncCallHandler *call_handler) {
+void CellularCapabilityGSM::OnGetRegistrationInfoReply(
+    uint32 status, const string &operator_code, const string &operator_name,
+    const Error &error) {
+  if (error.IsSuccess())
+    OnRegistrationInfoSignal(status, operator_code, operator_name);
+}
+
+void CellularCapabilityGSM::OnGetSignalQualityReply(uint32 quality,
+                                                    const Error &error) {
+  if (error.IsSuccess())
+    OnSignalQualitySignal(quality);
+}
+
+void CellularCapabilityGSM::OnGetIMEIReply(const ResultCallback &callback,
+                                           const string &imei,
+                                           const Error &error) {
   if (error.IsSuccess()) {
     VLOG(2) << "IMEI: " << imei;
     imei_ = imei;
   } else {
     VLOG(2) << "GetIMEI failed - " << error;
   }
-  CompleteOperation(call_handler, error);
+  callback.Run(error);
 }
 
-void CellularCapabilityGSM::OnGetIMSICallback(const string &imsi,
-                                              const Error &error,
-                                              AsyncCallHandler *call_handler) {
+void CellularCapabilityGSM::OnGetIMSIReply(const ResultCallback &callback,
+                                           const string &imsi,
+                                           const Error &error) {
   if (error.IsSuccess()) {
     VLOG(2) << "IMSI: " << imsi;
     imsi_ = imsi;
@@ -643,12 +725,12 @@
   } else {
     VLOG(2) << "GetIMSI failed - " << error;
   }
-  CompleteOperation(call_handler, error);
+  callback.Run(error);
 }
 
-void CellularCapabilityGSM::OnGetSPNCallback(const string &spn,
-                                             const Error &error,
-                                             AsyncCallHandler *call_handler) {
+void CellularCapabilityGSM::OnGetSPNReply(const ResultCallback &callback,
+                                          const string &spn,
+                                          const Error &error) {
   if (error.IsSuccess()) {
     VLOG(2) << "SPN: " << spn;
     spn_ = spn;
@@ -657,11 +739,12 @@
     VLOG(2) << "GetSPN failed - " << error;
   }
   // Ignore the error - it's not fatal.
-  CompleteOperation(call_handler);
+  callback.Run(Error());
 }
 
-void CellularCapabilityGSM::OnGetMSISDNCallback(
-    const string &msisdn, const Error &error, AsyncCallHandler *call_handler) {
+void CellularCapabilityGSM::OnGetMSISDNReply(const ResultCallback &callback,
+                                             const string &msisdn,
+                                             const Error &error) {
   if (error.IsSuccess()) {
     VLOG(2) << "MSISDN: " << msisdn;
     mdn_ = msisdn;
@@ -669,16 +752,7 @@
     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);
+  callback.Run(Error());
 }
 
 }  // namespace shill
diff --git a/cellular_capability_gsm.h b/cellular_capability_gsm.h
index c0277a8..916506b 100644
--- a/cellular_capability_gsm.h
+++ b/cellular_capability_gsm.h
@@ -19,50 +19,60 @@
 
 namespace shill {
 
-class CellularCapabilityGSM : public CellularCapability,
-                              public ModemGSMCardProxyDelegate,
-                              public ModemGSMNetworkProxyDelegate {
+class CellularCapabilityGSM : public CellularCapability {
  public:
   CellularCapabilityGSM(Cellular *cellular, ProxyFactory *proxy_factory);
 
   // Inherited from CellularCapability.
-  virtual void StartModem();
-  virtual void StopModem();
+  virtual void StartModem(Error *error, const ResultCallback &callback);
+  virtual void StopModem(Error *error, const ResultCallback &callback);
   virtual void OnServiceCreated();
   virtual void UpdateStatus(const DBusPropertiesMap &properties);
   virtual void SetupConnectProperties(DBusPropertiesMap *properties);
+  // The following two methods never need to report results
+  // via callbacks, which is why they don't take callbacks
+  // as arguments.
   virtual void GetSignalQuality();
-  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 GetRegistrationState();
+  // The following six methods are only ever called as
+  // callbacks (from the main loop), which is why they
+  // don't take an Error * argument.
+  virtual void GetProperties(const ResultCallback &callback);
+  virtual void GetIMEI(const ResultCallback &callback);
+  virtual void GetIMSI(const ResultCallback &callback);
+  virtual void GetSPN(const ResultCallback &callback);
+  virtual void GetMSISDN(const ResultCallback &callback);
+  virtual void Register(const ResultCallback &callback);
+
   virtual void RegisterOnNetwork(const std::string &network_id,
-                                 AsyncCallHandler *call_handler);
+                                 Error *error,
+                                 const ResultCallback &callback);
   virtual bool IsRegistered();
   virtual std::string CreateFriendlyServiceName();
-  virtual void RequirePIN(
-      const std::string &pin, bool require, AsyncCallHandler *call_handler);
-  virtual void EnterPIN(const std::string &pin, AsyncCallHandler *call_handler);
+  virtual void RequirePIN(const std::string &pin, bool require,
+                          Error *error, const ResultCallback &callback);
+  virtual void EnterPIN(const std::string &pin,
+                        Error *error, const ResultCallback &callback);
   virtual void UnblockPIN(const std::string &unblock_code,
                           const std::string &pin,
-                          AsyncCallHandler *call_handler);
+                          Error *error, const ResultCallback &callback);
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
-                         AsyncCallHandler *call_handler);
-  virtual void Scan(AsyncCallHandler *call_handler);
+                         Error *error, const ResultCallback &callback);
+  virtual void Scan(Error *error, const ResultCallback &callback);
   virtual std::string GetNetworkTechnologyString() const;
   virtual std::string GetRoamingStateString() const;
   virtual void OnModemManagerPropertiesChanged(
       const DBusPropertiesMap &properties);
 
+ protected:
+  virtual void InitProxies();
+  virtual void ReleaseProxies();
+
  private:
   friend class CellularTest;
   friend class CellularCapabilityGSMTest;
-  FRIEND_TEST(CellularTest, StartGSMRegister);
-  FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
+  friend class CellularCapabilityTest;
   FRIEND_TEST(CellularCapabilityGSMTest, CreateDeviceFromProperties);
   FRIEND_TEST(CellularCapabilityGSMTest, CreateFriendlyServiceName);
   FRIEND_TEST(CellularCapabilityGSMTest, GetIMEI);
@@ -83,6 +93,8 @@
   FRIEND_TEST(CellularCapabilityGSMTest, UpdateOperatorInfo);
   FRIEND_TEST(CellularCapabilityGSMTest, GetRegistrationState);
   FRIEND_TEST(CellularCapabilityGSMTest, OnModemManagerPropertiesChanged);
+  FRIEND_TEST(CellularCapabilityTest, AllowRoaming);
+  FRIEND_TEST(CellularTest, StartGSMRegister);
   FRIEND_TEST(ModemTest, CreateDeviceFromProperties);
 
   struct SimLockStatus {
@@ -132,35 +144,36 @@
       void(CellularCapabilityGSM::*set)(
           const KeyValueStore &value, Error *error));
 
-  // Signal callbacks inherited from ModemGSMNetworkProxyDelegate.
-  virtual void OnGSMNetworkModeChanged(uint32 mode);
-  virtual void OnGSMRegistrationInfoChanged(uint32 status,
-                                            const std::string &operator_code,
-                                            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);
+  // Signal callbacks
+  virtual void OnNetworkModeSignal(uint32 mode);
+  virtual void OnRegistrationInfoSignal(uint32 status,
+                                        const std::string &operator_code,
+                                        const std::string &operator_name);
+  virtual void OnSignalQualitySignal(uint32 quality);
+
+  // Method callbacks
+  virtual void OnGetRegistrationInfoReply(uint32 status,
+                                          const std::string &operator_code,
+                                          const std::string &operator_name,
+                                          const Error &error);
+  virtual void OnGetSignalQualityReply(uint32 quality, const Error &error);
+  virtual void OnRegisterReply(const ResultCallback &callback,
+                               const Error &error);
+  virtual void OnGetIMEIReply(const ResultCallback &callback,
+                              const std::string &imei,
+                              const Error &error);
+  virtual void OnGetIMSIReply(const ResultCallback &callback,
+                              const std::string &imsi,
+                              const Error &error);
+  virtual void OnGetSPNReply(const ResultCallback &callback,
+                             const std::string &spn,
+                             const Error &error);
+  virtual void OnGetMSISDNReply(const ResultCallback &callback,
+                                const std::string &msisdn,
+                                const Error &error);
+  virtual void OnScanReply(const ResultCallback &callback,
+                           const GSMScanResults &results,
+                           const Error &error);
 
   scoped_ptr<ModemGSMCardProxyInterface> card_proxy_;
   scoped_ptr<ModemGSMNetworkProxyInterface> network_proxy_;
diff --git a/cellular_capability_gsm_unittest.cc b/cellular_capability_gsm_unittest.cc
index c648819..5083736 100644
--- a/cellular_capability_gsm_unittest.cc
+++ b/cellular_capability_gsm_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "shill/cellular_capability_gsm.h"
 
+#include <base/bind.h>
 #include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 #include <mm/mm-modem.h>
@@ -19,34 +20,19 @@
 #include "shill/mock_modem_gsm_network_proxy.h"
 #include "shill/nice_mock_control.h"
 
+using base::Bind;
+using base::Unretained;
 using std::string;
 using testing::_;
+using testing::Invoke;
 using testing::NiceMock;
 using testing::Return;
 
 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_;
+MATCHER(IsSuccess, "") {
+  return arg.IsSuccess();
+}
 
 class CellularCapabilityGSMTest : public testing::Test {
  public:
@@ -83,6 +69,72 @@
         dynamic_cast<NiceMock<DeviceMockAdaptor> *>(cellular_->adaptor());
   }
 
+  void InvokeGetIMEI(Error *error, const GSMIdentifierCallback &callback,
+                     int timeout) {
+    callback.Run(kIMEI, Error());
+  }
+  void InvokeGetIMSI(Error *error, const GSMIdentifierCallback &callback,
+                     int timeout) {
+    callback.Run(kIMSI, Error());
+  }
+  void InvokeGetIMSI2(Error *error, const GSMIdentifierCallback &callback,
+                      int timeout) {
+    callback.Run("310240123456789", Error());
+  }
+  void InvokeGetMSISDN(Error *error, const GSMIdentifierCallback &callback,
+                       int timeout) {
+    callback.Run(kMSISDN, Error());
+  }
+  void InvokeGetSPN(Error *error, const GSMIdentifierCallback &callback,
+                    int timeout) {
+    callback.Run(kTestCarrier, Error());
+  }
+  void InvokeGetSignalQuality(Error *error,
+                              const SignalQualityCallback &callback,
+                              int timeout) {
+    callback.Run(kStrength, Error());
+  }
+  void InvokeGetRegistrationInfo(Error *error,
+                                 const RegistrationInfoCallback &callback,
+                                 int timeout) {
+    callback.Run(MM_MODEM_GSM_NETWORK_REG_STATUS_HOME,
+                 kTestNetwork, kTestCarrier, Error());
+  }
+  void InvokeRegister(const string &network_id,
+                      Error *error,
+                      const ResultCallback &callback,
+                      int timeout) {
+    callback.Run(Error());
+  }
+  void InvokeEnablePIN(const string &pin, bool enable,
+                       Error *error, const ResultCallback &callback,
+                       int timeout) {
+    callback.Run(Error());
+  }
+  void InvokeSendPIN(const string &pin, Error *error,
+                     const ResultCallback &callback, int timeout) {
+    callback.Run(Error());
+  }
+  void InvokeSendPUK(const string &puk, const string &pin, Error *error,
+                     const ResultCallback &callback, int timeout) {
+    callback.Run(Error());
+  }
+  void InvokeChangePIN(const string &old_pin, const string &pin, Error *error,
+                       const ResultCallback &callback, int timeout) {
+    callback.Run(Error());
+  }
+  void InvokeScan(Error *error, const ScanResultsCallback &callback,
+                  int timeout) {
+    GSMScanResults results;
+    results.push_back(GSMScanResult());
+    results[0][CellularCapabilityGSM::kNetworkPropertyID] = kScanID0;
+    results.push_back(GSMScanResult());
+    results[1][CellularCapabilityGSM::kNetworkPropertyID] = kScanID1;
+    callback.Run(results, Error());
+  }
+
+  MOCK_METHOD1(TestCallback, void(const Error &error));
+
  protected:
   static const char kAddress[];
   static const char kTestMobileProviderDBPath[];
@@ -93,6 +145,9 @@
   static const char kIMEI[];
   static const char kIMSI[];
   static const char kMSISDN[];
+  static const char kScanID0[];
+  static const char kScanID1[];
+  static const int kStrength;
 
   void SetCardProxy() {
     capability_->card_proxy_.reset(card_proxy_.release());
@@ -142,49 +197,75 @@
 const char CellularCapabilityGSMTest::kIMEI[] = "987654321098765";
 const char CellularCapabilityGSMTest::kIMSI[] = "310150123456789";
 const char CellularCapabilityGSMTest::kMSISDN[] = "12345678901";
+const char CellularCapabilityGSMTest::kScanID0[] = "123";
+const char CellularCapabilityGSMTest::kScanID1[] = "456";
+const int CellularCapabilityGSMTest::kStrength = 80;
 
 TEST_F(CellularCapabilityGSMTest, PropertyStore) {
   EXPECT_TRUE(cellular_->store().Contains(flimflam::kSIMLockStatusProperty));
 }
 
 TEST_F(CellularCapabilityGSMTest, GetIMEI) {
-  EXPECT_CALL(*card_proxy_, GetIMEI(NULL, _));
+  EXPECT_CALL(*card_proxy_, GetIMEI(_, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this,
+                       &CellularCapabilityGSMTest::InvokeGetIMEI));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
   SetCardProxy();
-  capability_->GetIMEI(NULL);
-  capability_->OnGetIMEICallback(kIMEI, Error(), NULL);
+  ASSERT_TRUE(capability_->imei_.empty());
+  capability_->GetIMEI(Bind(&CellularCapabilityGSMTest::TestCallback,
+                            Unretained(this)));
   EXPECT_EQ(kIMEI, capability_->imei_);
 }
 
 TEST_F(CellularCapabilityGSMTest, GetIMSI) {
-  EXPECT_CALL(*card_proxy_, GetIMSI(NULL, _));
+  EXPECT_CALL(*card_proxy_, GetIMSI(_, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this,
+                       &CellularCapabilityGSMTest::InvokeGetIMSI))
+      .WillOnce(Invoke(this,
+                       &CellularCapabilityGSMTest::InvokeGetIMSI2));
+  EXPECT_CALL(*this, TestCallback(IsSuccess())).Times(2);
   SetCardProxy();
-  capability_->GetIMSI(NULL);
-  capability_->OnGetIMSICallback(kIMSI, Error(), NULL);
+  ResultCallback callback = Bind(&CellularCapabilityGSMTest::TestCallback,
+                                 Unretained(this));
+  ASSERT_TRUE(capability_->imsi_.empty());
+  capability_->GetIMSI(callback);
   EXPECT_EQ(kIMSI, capability_->imsi_);
+  capability_->imsi_.clear();
   InitProviderDB();
-  capability_->OnGetIMSICallback("310240123456789", Error(), NULL);
+  capability_->GetIMSI(callback);
   EXPECT_EQ("T-Mobile", cellular_->home_provider().GetName());
 }
 
 TEST_F(CellularCapabilityGSMTest, GetMSISDN) {
-  EXPECT_CALL(*card_proxy_, GetMSISDN(NULL, _));
+  EXPECT_CALL(*card_proxy_, GetMSISDN(_, _,
+                                      CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this,
+                       &CellularCapabilityGSMTest::InvokeGetMSISDN));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
   SetCardProxy();
-  capability_->GetMSISDN(NULL);
-  capability_->OnGetMSISDNCallback(kMSISDN, Error(), NULL);
+  ASSERT_TRUE(capability_->mdn_.empty());
+  capability_->GetMSISDN(Bind(&CellularCapabilityGSMTest::TestCallback,
+                            Unretained(this)));
   EXPECT_EQ(kMSISDN, capability_->mdn_);
 }
 
 TEST_F(CellularCapabilityGSMTest, GetSPN) {
-  EXPECT_CALL(*card_proxy_, GetSPN(NULL, _));
+  EXPECT_CALL(*card_proxy_, GetSPN(_, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this,
+                       &CellularCapabilityGSMTest::InvokeGetSPN));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
   SetCardProxy();
-  capability_->GetSPN(NULL);
-  capability_->OnGetSPNCallback(kTestCarrier, Error(), NULL);
+  ASSERT_TRUE(capability_->spn_.empty());
+  capability_->GetSPN(Bind(&CellularCapabilityGSMTest::TestCallback,
+                            Unretained(this)));
   EXPECT_EQ(kTestCarrier, capability_->spn_);
 }
 
 TEST_F(CellularCapabilityGSMTest, GetSignalQuality) {
-  const int kStrength = 80;
-  EXPECT_CALL(*network_proxy_, GetSignalQuality()).WillOnce(Return(kStrength));
+  EXPECT_CALL(*network_proxy_,
+              GetSignalQuality(_, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this,
+                       &CellularCapabilityGSMTest::InvokeGetSignalQuality));
   SetNetworkProxy();
   SetService();
   EXPECT_EQ(0, cellular_->service()->strength());
@@ -193,12 +274,15 @@
 }
 
 TEST_F(CellularCapabilityGSMTest, RegisterOnNetwork) {
-  EXPECT_CALL(*network_proxy_, Register(kTestNetwork, NULL,
-                                        CellularCapability::kTimeoutRegister));
+  EXPECT_CALL(*network_proxy_, Register(kTestNetwork, _, _,
+                                        CellularCapability::kTimeoutRegister))
+      .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeRegister));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
   SetNetworkProxy();
-  capability_->RegisterOnNetwork(kTestNetwork, NULL);
-  dispatcher_.DispatchPendingEvents();
-  capability_->OnRegisterCallback(Error(), NULL);
+  Error error;
+  capability_->RegisterOnNetwork(kTestNetwork, &error,
+                                 Bind(&CellularCapabilityGSMTest::TestCallback,
+                                      Unretained(this)));
   EXPECT_EQ(kTestNetwork, capability_->selected_network_);
 }
 
@@ -221,68 +305,67 @@
 TEST_F(CellularCapabilityGSMTest, GetRegistrationState) {
   ASSERT_FALSE(capability_->IsRegistered());
   EXPECT_CALL(*network_proxy_,
-              GetRegistrationInfo(NULL, CellularCapability::kTimeoutDefault));
+              GetRegistrationInfo(_, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this,
+                       &CellularCapabilityGSMTest::InvokeGetRegistrationInfo));
   SetNetworkProxy();
-  capability_->GetRegistrationState(NULL);
-  capability_->OnGSMRegistrationInfoChanged(
-      MM_MODEM_GSM_NETWORK_REG_STATUS_HOME, kTestNetwork, kTestCarrier,
-      Error(), NULL);
+  capability_->GetRegistrationState();
   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,
-                                      _, CellularCapability::kTimeoutDefault));
-  MockReturner returner;
-  EXPECT_CALL(returner, Return());
-  EXPECT_CALL(returner, ReturnError(_)).Times(0);
-  GSMTestAsyncCallHandler *handler = new GSMTestAsyncCallHandler(&returner);
+  EXPECT_CALL(*card_proxy_, EnablePIN(kPIN, true, _, _,
+                                      CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeEnablePIN));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
   SetCardProxy();
-  capability_->RequirePIN(kPIN, true, handler);
-  capability_->OnPINOperationCallback(Error(), handler);
-  EXPECT_TRUE(GSMTestAsyncCallHandler::error().IsSuccess());
+  Error error;
+  capability_->RequirePIN(kPIN, true, &error,
+                          Bind(&CellularCapabilityGSMTest::TestCallback,
+                               Unretained(this)));
+  EXPECT_TRUE(error.IsSuccess());
 }
 
 TEST_F(CellularCapabilityGSMTest, EnterPIN) {
-  EXPECT_CALL(*card_proxy_,
-              SendPIN(kPIN, _, CellularCapability::kTimeoutDefault));
-  MockReturner returner;
-  EXPECT_CALL(returner, Return());
-  EXPECT_CALL(returner, ReturnError(_)).Times(0);
-  GSMTestAsyncCallHandler *handler = new GSMTestAsyncCallHandler(&returner);
+  EXPECT_CALL(*card_proxy_, SendPIN(kPIN, _, _,
+                                    CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeSendPIN));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
   SetCardProxy();
-  capability_->EnterPIN(kPIN, handler);
-  capability_->OnPINOperationCallback(Error(), handler);
-  EXPECT_TRUE(GSMTestAsyncCallHandler::error().IsSuccess());
+  Error error;
+  capability_->EnterPIN(kPIN, &error,
+                        Bind(&CellularCapabilityGSMTest::TestCallback,
+                             Unretained(this)));
+  EXPECT_TRUE(error.IsSuccess());
 }
 
 TEST_F(CellularCapabilityGSMTest, UnblockPIN) {
-  EXPECT_CALL(*card_proxy_,
-              SendPUK(kPUK, kPIN, _, CellularCapability::kTimeoutDefault));
-  MockReturner returner;
-  EXPECT_CALL(returner, Return());
-  EXPECT_CALL(returner, ReturnError(_)).Times(0);
-  GSMTestAsyncCallHandler *handler = new GSMTestAsyncCallHandler(&returner);
+  EXPECT_CALL(*card_proxy_, SendPUK(kPUK, kPIN, _, _,
+                                    CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeSendPUK));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
   SetCardProxy();
-  capability_->UnblockPIN(kPUK, kPIN, handler);
-  capability_->OnPINOperationCallback(Error(), handler);
-  EXPECT_TRUE(GSMTestAsyncCallHandler::error().IsSuccess());
+  Error error;
+  capability_->UnblockPIN(kPUK, kPIN, &error,
+                          Bind(&CellularCapabilityGSMTest::TestCallback,
+                             Unretained(this)));
+  EXPECT_TRUE(error.IsSuccess());
 }
 
 TEST_F(CellularCapabilityGSMTest, ChangePIN) {
   static const char kOldPIN[] = "1111";
-  EXPECT_CALL(*card_proxy_, ChangePIN(kOldPIN, kPIN,
-                                      _, CellularCapability::kTimeoutDefault));
-  MockReturner returner;
-  EXPECT_CALL(returner, Return());
-  EXPECT_CALL(returner, ReturnError(_)).Times(0);
-  GSMTestAsyncCallHandler *handler = new GSMTestAsyncCallHandler(&returner);
+  EXPECT_CALL(*card_proxy_, ChangePIN(kOldPIN, kPIN, _, _,
+                                    CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeChangePIN));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
   SetCardProxy();
-  capability_->ChangePIN(kOldPIN, kPIN, handler);
-  capability_->OnPINOperationCallback(Error(), handler);
-  EXPECT_TRUE(GSMTestAsyncCallHandler::error().IsSuccess());
+  Error error;
+  capability_->ChangePIN(kOldPIN, kPIN, &error,
+                         Bind(&CellularCapabilityGSMTest::TestCallback,
+                             Unretained(this)));
+  EXPECT_TRUE(error.IsSuccess());
 }
 
 namespace {
@@ -294,26 +377,22 @@
 }  // namespace
 
 TEST_F(CellularCapabilityGSMTest, Scan) {
-  static const char kID0[] = "123";
-  static const char kID1[] = "456";
   Error error;
-  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;
+  EXPECT_CALL(*network_proxy_, Scan(_, _, CellularCapability::kTimeoutScan))
+      .WillOnce(Invoke(this, &CellularCapabilityGSMTest::InvokeScan));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
   capability_->found_networks_.resize(3, Stringmap());
   EXPECT_CALL(*device_adaptor_,
               EmitStringmapsChanged(flimflam::kFoundNetworksProperty,
                                     SizeIs2()));
-  capability_->OnScanCallback(results, Error(), NULL);
+  SetNetworkProxy();
+  capability_->Scan(&error, Bind(&CellularCapabilityGSMTest::TestCallback,
+                                 Unretained(this)));
+  EXPECT_TRUE(error.IsSuccess());
   EXPECT_EQ(2, capability_->found_networks_.size());
-  EXPECT_EQ(kID0,
+  EXPECT_EQ(kScanID0,
             capability_->found_networks_[0][flimflam::kNetworkIdProperty]);
-  EXPECT_EQ(kID1,
+  EXPECT_EQ(kScanID1,
             capability_->found_networks_[1][flimflam::kNetworkIdProperty]);
 }
 
diff --git a/cellular_capability_unittest.cc b/cellular_capability_unittest.cc
index 3e18772..ae5bead 100644
--- a/cellular_capability_unittest.cc
+++ b/cellular_capability_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "shill/cellular_capability_gsm.h"
 
+#include <base/bind.h>
 #include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 #include <mm/mm-modem.h>
@@ -14,27 +15,42 @@
 #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"
 #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/mock_rtnl_handler.h"
 #include "shill/nice_mock_control.h"
 #include "shill/proxy_factory.h"
 
+using base::Bind;
+using base::Unretained;
 using std::string;
 using testing::InSequence;
 using testing::NiceMock;
+using testing::_;
 
 namespace shill {
 
+MATCHER(IsSuccess, "") {
+  return arg.IsSuccess();
+}
+MATCHER(IsFailure, "") {
+  return arg.IsFailure();
+}
+
 class CellularCapabilityTest : public testing::Test {
  public:
   CellularCapabilityTest()
-      : cellular_(new Cellular(&control_,
+      : manager_(&control_, &dispatcher_, &metrics_, &glib_),
+        cellular_(new Cellular(&control_,
                                &dispatcher_,
                                NULL,
-                               NULL,
+                               &manager_,
                                "",
                                "",
                                0,
@@ -58,6 +74,7 @@
   }
 
   virtual void SetUp() {
+    static_cast<Device *>(cellular_)->rtnl_handler_ = &rtnl_handler_;
     capability_ = cellular_->capability_.get();
     device_adaptor_ =
         dynamic_cast<NiceMock<DeviceMockAdaptor> *>(cellular_->adaptor());
@@ -67,43 +84,72 @@
     capability_->proxy_factory_ = NULL;
   }
 
+  void InvokeEnable(bool enable, Error *error,
+                    const ResultCallback &callback, int timeout) {
+    callback.Run(Error());
+  }
+  void InvokeEnableFail(bool enable, Error *error,
+                        const ResultCallback &callback, int timeout) {
+    callback.Run(Error(Error::kOperationFailed));
+  }
+  void InvokeDisconnect(Error *error, const ResultCallback &callback,
+                        int timeout) {
+    callback.Run(Error());
+  }
+  void InvokeGetModemStatus(Error *error,
+                            const DBusPropertyMapCallback &callback,
+                            int timeout) {
+    DBusPropertiesMap props;
+    props["carrier"].writer().append_string(kTestCarrier);
+    props["unknown-property"].writer().append_string("irrelevant-value");
+    callback.Run(props, Error());
+  }
+  void InvokeGetModemInfo(Error *error, const ModemInfoCallback &callback,
+                          int timeout) {
+    ModemHardwareInfo info;
+    info._1 = kManufacturer;
+    info._2 = kModelID;
+    info._3 = kHWRev;
+    callback.Run(info, Error());
+  }
+
+  MOCK_METHOD1(TestCallback, void(const Error &error));
+
  protected:
   static const char kTestCarrier[];
+  static const char kManufacturer[];
+  static const char kModelID[];
+  static const char kHWRev[];
 
   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();
@@ -121,6 +167,12 @@
     capability_->simple_proxy_.reset(simple_proxy_.release());
   }
 
+  void SetGSMNetworkProxy() {
+    CellularCapabilityGSM *gsm_capability =
+        dynamic_cast<CellularCapabilityGSM *>(cellular_->capability_.get());
+    gsm_capability->network_proxy_.reset(gsm_network_proxy_.release());
+  }
+
   void SetCellularType(Cellular::Type type) {
     cellular_->InitCapability(type, &proxy_factory_);
     capability_ = cellular_->capability_.get();
@@ -128,7 +180,11 @@
 
   NiceMockControl control_;
   EventDispatcher dispatcher_;
+  MockMetrics metrics_;
+  MockGLib glib_;
+  MockManager manager_;
   CellularRefPtr cellular_;
+  MockRTNLHandler rtnl_handler_;
   scoped_ptr<MockModemProxy> proxy_;
   scoped_ptr<MockModemSimpleProxy> simple_proxy_;
   scoped_ptr<MockModemCDMAProxy> cdma_proxy_;
@@ -140,54 +196,76 @@
 };
 
 const char CellularCapabilityTest::kTestCarrier[] = "The Cellular Carrier";
+const char CellularCapabilityTest::kManufacturer[] = "Company";
+const char CellularCapabilityTest::kModelID[] = "Gobi 2000";
+const char CellularCapabilityTest::kHWRev[] = "A00B1234";
 
 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));
+              GetModemStatus(_, _, CellularCapability::kTimeoutDefault)).
+      WillOnce(Invoke(this, &CellularCapabilityTest::InvokeGetModemStatus));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
   SetSimpleProxy();
-  capability_->GetModemStatus(NULL);
-  capability_->OnGetModemStatusCallback(props, Error(), NULL);
+  ResultCallback callback =
+      Bind(&CellularCapabilityTest::TestCallback, Unretained(this));
+  capability_->GetModemStatus(callback);
   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));
+  EXPECT_CALL(*proxy_, GetModemInfo(_, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularCapabilityTest::InvokeGetModemInfo));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
   SetProxy();
-  capability_->GetModemInfo(NULL);
-  capability_->OnGetModemInfoCallback(info, Error(), NULL);
+  ResultCallback callback =
+      Bind(&CellularCapabilityTest::TestCallback, Unretained(this));
+  capability_->GetModemInfo(callback);
   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_);
+  EXPECT_CALL(*proxy_, Enable(true, _, _, CellularCapability::kTimeoutEnable))
+      .WillOnce(Invoke(this, &CellularCapabilityTest::InvokeEnable));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
+  ResultCallback callback =
+      Bind(&CellularCapabilityTest::TestCallback, Unretained(this));
   SetProxy();
-  capability_->EnableModem(NULL);
-  capability_->OnModemEnableCallback(Error(), NULL);
-  EXPECT_EQ(Cellular::kStateEnabled, cellular_->state_);
+  capability_->EnableModem(callback);
 }
 
 TEST_F(CellularCapabilityTest, EnableModemFail) {
-  EXPECT_CALL(*proxy_, Enable(true, NULL, CellularCapability::kTimeoutDefault));
-  ASSERT_EQ(Cellular::kStateDisabled, cellular_->state_);
+  EXPECT_CALL(*proxy_, Enable(true, _, _, CellularCapability::kTimeoutEnable))
+      .WillOnce(Invoke(this, &CellularCapabilityTest::InvokeEnableFail));
+  EXPECT_CALL(*this, TestCallback(IsFailure()));
+  ResultCallback callback =
+      Bind(&CellularCapabilityTest::TestCallback, Unretained(this));
   SetProxy();
-  capability_->EnableModem(NULL);
-  capability_->OnModemEnableCallback(Error(Error::kOperationFailed), NULL);
-  EXPECT_EQ(Cellular::kStateDisabled, cellular_->state_);
+  capability_->EnableModem(callback);
+}
+
+TEST_F(CellularCapabilityTest, FinishEnable) {
+  EXPECT_CALL(*gsm_network_proxy_,
+              GetRegistrationInfo(NULL, _,
+                                  CellularCapability::kTimeoutDefault));
+  EXPECT_CALL(*gsm_network_proxy_,
+              GetSignalQuality(NULL, _, CellularCapability::kTimeoutDefault));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
+  SetGSMNetworkProxy();
+  capability_->FinishEnable(
+      Bind(&CellularCapabilityTest::TestCallback, Unretained(this)));
+}
+
+TEST_F(CellularCapabilityTest, UnsupportedOperation) {
+  Error error;
+  EXPECT_CALL(*this, TestCallback(IsSuccess())).Times(0);
+  capability_->CellularCapability::Scan(&error,
+      Bind(&CellularCapabilityTest::TestCallback, Unretained(this)));
+  EXPECT_TRUE(error.IsFailure());
+  EXPECT_EQ(Error::kNotSupported, error.type());
 }
 
 TEST_F(CellularCapabilityTest, AllowRoaming) {
@@ -210,7 +288,8 @@
   EXPECT_TRUE(capability_->GetAllowRoaming(NULL));
   EXPECT_EQ(Cellular::kStateConnected, cellular_->state_);
 
-  EXPECT_CALL(*proxy_, Disconnect());
+  EXPECT_CALL(*proxy_, Disconnect(_, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularCapabilityTest::InvokeDisconnect));
   SetProxy();
   cellular_->state_ = Cellular::kStateConnected;
   capability_->SetAllowRoaming(false, NULL);
diff --git a/cellular_service.cc b/cellular_service.cc
index 897dea2..4c39a3e 100644
--- a/cellular_service.cc
+++ b/cellular_service.cc
@@ -109,8 +109,9 @@
 }
 
 void CellularService::ActivateCellularModem(const string &carrier,
-                                            ReturnerInterface *returner) {
-  cellular_->Activate(carrier, returner);
+                                            Error *error,
+                                            const ResultCallback &callback) {
+  cellular_->Activate(carrier, error, callback);
 }
 
 bool CellularService::TechnologyIs(const Technology::Identifier type) const {
diff --git a/cellular_service.h b/cellular_service.h
index 7da9778..8e2a3a1 100644
--- a/cellular_service.h
+++ b/cellular_service.h
@@ -61,7 +61,8 @@
   virtual void Connect(Error *error);
   virtual void Disconnect(Error *error);
   virtual void ActivateCellularModem(const std::string &carrier,
-                                     ReturnerInterface *returner);
+                                     Error *error,
+                                     const ResultCallback &callback);
   virtual bool TechnologyIs(const Technology::Identifier type) const;
 
   virtual std::string GetStorageIdentifier() const;
diff --git a/cellular_unittest.cc b/cellular_unittest.cc
index 4ea7524..c309b45 100644
--- a/cellular_unittest.cc
+++ b/cellular_unittest.cc
@@ -8,6 +8,7 @@
 #include <linux/if.h>
 #include <linux/netlink.h>  // Needs typedefs from sys/socket.h.
 
+#include <base/bind.h>
 #include <chromeos/dbus/service_constants.h>
 #include <mm/mm-modem.h>
 #include <mobile_provider.h>
@@ -32,6 +33,8 @@
 #include "shill/property_store_unittest.h"
 #include "shill/proxy_factory.h"
 
+using base::Bind;
+using base::Unretained;
 using std::map;
 using std::string;
 using testing::_;
@@ -44,6 +47,10 @@
 
 namespace shill {
 
+MATCHER(IsSuccess, "") {
+  return arg.IsSuccess();
+}
+
 class CellularPropertyTest : public PropertyStoreTest {
  public:
   CellularPropertyTest()
@@ -142,29 +149,31 @@
   }
 
   virtual void TearDown() {
+    device_->DestroyIPConfig();
     device_->state_ = Cellular::kStateDisabled;
-    device_->capability_->proxy_.reset(new MockModemProxy());
-    device_->Stop();
+    device_->capability_->ReleaseProxies();
     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 InvokeEnable(bool enable, Error *error,
+                    const ResultCallback &callback, int timeout) {
+    callback.Run(Error());
   }
-
-  void InvokeGetModemStatus(void *data, int /*timeout*/) {
-    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+  void InvokeGetSignalQuality(Error *error,
+                              const SignalQualityCallback &callback,
+                              int timeout) {
+    callback.Run(kStrength, Error());
+  }
+  void InvokeGetModemStatus(Error *error,
+                            const DBusPropertyMapCallback &callback,
+                            int timeout) {
     DBusPropertiesMap props;
     props["carrier"].writer().append_string(kTestCarrier);
     props["unknown-property"].writer().append_string("irrelevant-value");
-    device_->capability_->OnGetModemStatusCallback(props, Error(), handler);
-    dispatcher_.DispatchPendingEvents();
+    callback.Run(props, Error());
   }
-
-  void InvokeGetModemInfo(void *data, int /*timeout*/) {
-    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
+  void InvokeGetModemInfo(Error *error, const ModemInfoCallback &callback,
+                            int timeout) {
     static const char kManufacturer[] = "Company";
     static const char kModelID[] = "Gobi 2000";
     static const char kHWRev[] = "A00B1234";
@@ -172,58 +181,66 @@
     info._1 = kManufacturer;
     info._2 = kModelID;
     info._3 = kHWRev;
-    device_->capability_->OnGetModemInfoCallback(info, Error(), handler);
-    dispatcher_.DispatchPendingEvents();
+    callback.Run(info, Error());
   }
-
-  void InvokeGetIMEI(void *data, int /*timeout*/) {
-    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
-    GetCapabilityGSM()->OnGetIMEICallback(kIMEI, Error(), handler);
-    dispatcher_.DispatchPendingEvents();
+  void InvokeGetRegistrationState1X(Error *error,
+                                    const RegistrationStateCallback &callback,
+                                    int timeout) {
+    callback.Run(MM_MODEM_CDMA_REGISTRATION_STATE_HOME,
+                 MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
+                 Error());
   }
-
-  void InvokeGetIMSI(void *data, int /*timeout*/) {
-    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
-    GetCapabilityGSM()->OnGetIMSICallback(kIMSI, Error(), handler);
-    dispatcher_.DispatchPendingEvents();
+  void InvokeGetIMEI(Error *error, const GSMIdentifierCallback &callback,
+                     int timeout) {
+    callback.Run(kIMEI, Error());
   }
-  void InvokeGetSPN(void *data, int /*timeout*/) {
-    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
-    GetCapabilityGSM()->OnGetSPNCallback(kTestCarrier, Error(), handler);
-    dispatcher_.DispatchPendingEvents();
+  void InvokeGetIMSI(Error *error, const GSMIdentifierCallback &callback,
+                     int timeout) {
+    callback.Run(kIMSI, Error());
   }
-  void InvokeGetMSISDN(void *data, int /*timeout*/) {
-    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
-    GetCapabilityGSM()->OnGetMSISDNCallback(kMSISDN, Error(), handler);
-    dispatcher_.DispatchPendingEvents();
+  void InvokeGetMSISDN(Error *error, const GSMIdentifierCallback &callback,
+                       int timeout) {
+    callback.Run(kMSISDN, Error());
   }
-  void InvokeRegister(const std::string &/*network_id*/,
-                      void *data, int /*timeout*/) {
-    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
-    GetCapabilityGSM()->OnRegisterCallback(Error(), handler);
-    dispatcher_.DispatchPendingEvents();
+  void InvokeGetSPN(Error *error, const GSMIdentifierCallback &callback,
+                    int timeout) {
+    callback.Run(kTestCarrier, Error());
   }
-  void InvokeGetRegistrationInfo(void *data, int /*timeout*/) {
-    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
-    GSMRegistrationInfo info;
+  void InvokeGetRegistrationInfo(Error *error,
+                                 const RegistrationInfoCallback &callback,
+                                 int timeout) {
     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();
+    callback.Run(MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING,
+                 kNetworkID, kTestCarrier, Error());
   }
-  void InvokeConnect(DBusPropertiesMap /*props*/, void *data, int /*timeout*/) {
-    AsyncCallHandler *handler = reinterpret_cast<AsyncCallHandler *>(data);
-    device_->capability_->OnConnectCallback(Error(), handler);
-    dispatcher_.DispatchPendingEvents();
+  void InvokeRegister(const string &network_id,
+                      Error *error,
+                      const ResultCallback &callback,
+                      int timeout) {
+    callback.Run(Error());
   }
+  void InvokeGetRegistrationState(Error *error,
+                                  const RegistrationStateCallback &callback,
+                                  int timeout) {
+    callback.Run(MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED,
+                 MM_MODEM_CDMA_REGISTRATION_STATE_HOME,
+                 Error());
+  }
+  void InvokeConnect(DBusPropertiesMap props, Error *error,
+                     const ResultCallback &callback, int timeout) {
+    callback.Run(Error());
+  }
+  void InvokeDisconnect(Error *error, const ResultCallback &callback,
+                        int timeout) {
+    if (!callback.is_null())
+      callback.Run(Error());
+  }
+
+  MOCK_METHOD1(TestCallback, void(const Error &error));
 
  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[];
@@ -232,41 +249,37 @@
   static const char kIMSI[];
   static const char kMSISDN[];
   static const char kTestMobileProviderDBPath[];
+  static const int kStrength;
 
   class TestProxyFactory : public ProxyFactory {
    public:
     explicit TestProxyFactory(CellularTest *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();
@@ -322,6 +335,7 @@
 const char CellularTest::kMSISDN[] = "12345678901";
 const char CellularTest::kTestMobileProviderDBPath[] =
     "provider_db_unittest.bfd";
+const int CellularTest::kStrength = 90;
 
 TEST_F(CellularTest, GetStateString) {
   EXPECT_EQ("CellularStateDisabled",
@@ -337,22 +351,28 @@
 }
 
 TEST_F(CellularTest, StartCDMARegister) {
-  const int kStrength = 90;
   SetCellularType(Cellular::kTypeCDMA);
-  EXPECT_CALL(*proxy_, Enable(true, _, CellularCapability::kTimeoutDefault))
+  EXPECT_CALL(*proxy_, Enable(true, _, _, CellularCapability::kTimeoutEnable))
       .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
   EXPECT_CALL(*simple_proxy_,
-              GetModemStatus(_, CellularCapability::kTimeoutDefault))
+              GetModemStatus(_, _, CellularCapability::kTimeoutDefault))
       .WillOnce(Invoke(this, &CellularTest::InvokeGetModemStatus));
   EXPECT_CALL(*cdma_proxy_, MEID()).WillOnce(Return(kMEID));
-  EXPECT_CALL(*proxy_, GetModemInfo(_, CellularCapability::kTimeoutDefault))
+  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_CALL(*cdma_proxy_,
+              GetRegistrationState(NULL, _,
+                                   CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this,
+                     &CellularTest::InvokeGetRegistrationState1X));
+  EXPECT_CALL(*cdma_proxy_,
+              GetSignalQuality(NULL, _, CellularCapability::kTimeoutDefault))
+      .Times(2)
+      .WillRepeatedly(Invoke(this,
+                             &CellularTest::InvokeGetSignalQuality));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
+  Error error;
+  device_->Start(&error, Bind(&CellularTest::TestCallback, Unretained(this)));
   dispatcher_.DispatchPendingEvents();
   EXPECT_EQ(kMEID, device_->capability_->meid_);
   EXPECT_EQ(kTestCarrier, device_->capability_->carrier_);
@@ -368,37 +388,44 @@
   provider_db_ = mobile_provider_open_db(kTestMobileProviderDBPath);
   ASSERT_TRUE(provider_db_);
   device_->provider_db_ = provider_db_;
-  const int kStrength = 70;
-  EXPECT_CALL(*proxy_, Enable(true, _, CellularCapability::kTimeoutDefault))
+  EXPECT_CALL(*proxy_, Enable(true, _, _, CellularCapability::kTimeoutEnable))
       .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
   EXPECT_CALL(*gsm_network_proxy_,
-              Register("", _, CellularCapability::kTimeoutRegister))
+              Register("", _, _, CellularCapability::kTimeoutRegister))
       .WillOnce(Invoke(this, &CellularTest::InvokeRegister));
   EXPECT_CALL(*simple_proxy_,
-              GetModemStatus(_, CellularCapability::kTimeoutDefault))
+              GetModemStatus(_, _, CellularCapability::kTimeoutDefault))
       .WillOnce(Invoke(this, &CellularTest::InvokeGetModemStatus));
-  EXPECT_CALL(*gsm_card_proxy_, GetIMEI(_, CellularCapability::kTimeoutDefault))
+  EXPECT_CALL(*gsm_card_proxy_,
+              GetIMEI(_, _, CellularCapability::kTimeoutDefault))
       .WillOnce(Invoke(this, &CellularTest::InvokeGetIMEI));
-  EXPECT_CALL(*gsm_card_proxy_, GetIMSI(_, CellularCapability::kTimeoutDefault))
+  EXPECT_CALL(*gsm_card_proxy_,
+              GetIMSI(_, _, CellularCapability::kTimeoutDefault))
       .WillOnce(Invoke(this, &CellularTest::InvokeGetIMSI));
-  EXPECT_CALL(*gsm_card_proxy_, GetSPN(_, CellularCapability::kTimeoutDefault))
+  EXPECT_CALL(*gsm_card_proxy_,
+              GetSPN(_, _, CellularCapability::kTimeoutDefault))
       .WillOnce(Invoke(this, &CellularTest::InvokeGetSPN));
   EXPECT_CALL(*gsm_card_proxy_,
-              GetMSISDN(_, CellularCapability::kTimeoutDefault))
+              GetMSISDN(_, _, CellularCapability::kTimeoutDefault))
       .WillOnce(Invoke(this, &CellularTest::InvokeGetMSISDN));
   EXPECT_CALL(*gsm_network_proxy_, AccessTechnology())
       .WillOnce(Return(MM_MODEM_GSM_ACCESS_TECH_EDGE));
   EXPECT_CALL(*gsm_card_proxy_, EnabledFacilityLocks())
       .WillOnce(Return(MM_MODEM_GSM_FACILITY_SIM));
-  EXPECT_CALL(*proxy_, GetModemInfo(_, CellularCapability::kTimeoutDefault))
+  EXPECT_CALL(*proxy_, GetModemInfo(_, _, CellularCapability::kTimeoutDefault))
       .WillOnce(Invoke(this, &CellularTest::InvokeGetModemInfo));
   static const char kNetworkID[] = "22803";
   EXPECT_CALL(*gsm_network_proxy_,
-              GetRegistrationInfo(_, CellularCapability::kTimeoutDefault))
+              GetRegistrationInfo(_, _, CellularCapability::kTimeoutDefault))
       .WillOnce(Invoke(this, &CellularTest::InvokeGetRegistrationInfo));
-  EXPECT_CALL(*gsm_network_proxy_, GetSignalQuality())
-      .WillOnce(Return(kStrength));
-  device_->Start();
+  EXPECT_CALL(*gsm_network_proxy_, GetSignalQuality(NULL, _, _))
+      .Times(2)
+      .WillRepeatedly(Invoke(this,
+                             &CellularTest::InvokeGetSignalQuality));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
+  Error error;
+  device_->Start(&error, Bind(&CellularTest::TestCallback, Unretained(this)));
+  EXPECT_TRUE(error.IsSuccess());
   dispatcher_.DispatchPendingEvents();
   EXPECT_EQ(kIMEI, device_->capability_->imei_);
   EXPECT_EQ(kIMSI, device_->capability_->imsi_);
@@ -422,19 +449,24 @@
   SetCellularType(Cellular::kTypeCDMA);
   device_->set_modem_state(Cellular::kModemStateConnected);
   device_->capability_->meid_ = kMEID;
-  EXPECT_CALL(*proxy_, Enable(true, _, CellularCapability::kTimeoutDefault))
+  EXPECT_CALL(*proxy_, Enable(true, _, _, CellularCapability::kTimeoutEnable))
       .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
   EXPECT_CALL(*simple_proxy_,
-              GetModemStatus(_, CellularCapability::kTimeoutDefault))
+              GetModemStatus(_, _, CellularCapability::kTimeoutDefault))
       .WillOnce(Invoke(this, &CellularTest::InvokeGetModemStatus));
-  EXPECT_CALL(*proxy_, GetModemInfo(_, CellularCapability::kTimeoutDefault))
+  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),
-          SetArgumentPointee<1>(MM_MODEM_CDMA_REGISTRATION_STATE_HOME)));
-  EXPECT_CALL(*cdma_proxy_, GetSignalQuality()).WillOnce(Return(90));
-  device_->Start();
+  EXPECT_CALL(*cdma_proxy_, GetRegistrationState(NULL, _, _))
+      .WillOnce(Invoke(this,
+                     &CellularTest::InvokeGetRegistrationState));
+  EXPECT_CALL(*cdma_proxy_, GetSignalQuality(NULL, _, _))
+      .Times(2)
+      .WillRepeatedly(Invoke(this,
+                             &CellularTest::InvokeGetSignalQuality));
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
+  Error error;
+  device_->Start(&error, Bind(&CellularTest::TestCallback, Unretained(this)));
+  EXPECT_TRUE(error.IsSuccess());
   dispatcher_.DispatchPendingEvents();
   EXPECT_EQ(Cellular::kStateConnected, device_->state_);
 }
@@ -445,23 +477,28 @@
   SetCellularType(Cellular::kTypeCDMA);
   device_->set_modem_state(Cellular::kModemStateConnected);
   device_->capability_->meid_ = kMEID;
-  EXPECT_CALL(*proxy_, Enable(true, _, CellularCapability::kTimeoutDefault))
+  EXPECT_CALL(*proxy_, Enable(true, _, _, CellularCapability::kTimeoutEnable))
       .WillOnce(Invoke(this, &CellularTest::InvokeEnable));
   EXPECT_CALL(*simple_proxy_,
-              GetModemStatus(_, CellularCapability::kTimeoutDefault))
+              GetModemStatus(_, _, CellularCapability::kTimeoutDefault))
       .WillOnce(Invoke(this, &CellularTest::InvokeGetModemStatus));
-  EXPECT_CALL(*proxy_, GetModemInfo(_, CellularCapability::kTimeoutDefault))
+  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),
-          SetArgumentPointee<1>(MM_MODEM_CDMA_REGISTRATION_STATE_HOME)));
-  EXPECT_CALL(*cdma_proxy_, GetSignalQuality()).WillOnce(Return(90));
+  EXPECT_CALL(*cdma_proxy_, GetRegistrationState(NULL, _, _))
+      .WillOnce(Invoke(this,
+                     &CellularTest::InvokeGetRegistrationState));
+  EXPECT_CALL(*cdma_proxy_, GetSignalQuality(NULL, _, _))
+      .Times(2)
+      .WillRepeatedly(Invoke(this,
+                             &CellularTest::InvokeGetSignalQuality));
   EXPECT_CALL(dhcp_provider_, CreateConfig(kTestDeviceName, _))
       .WillOnce(Return(dhcp_config_));
   EXPECT_CALL(*dhcp_config_, RequestIP()).WillOnce(Return(true));
   EXPECT_CALL(manager_, UpdateService(_)).Times(2);
-  device_->Start();
+  EXPECT_CALL(*this, TestCallback(IsSuccess()));
+  Error error;
+  device_->Start(&error, Bind(&CellularTest::TestCallback, Unretained(this)));
+  EXPECT_TRUE(error.IsSuccess());
   dispatcher_.DispatchPendingEvents();
   EXPECT_EQ(Cellular::kStateLinked, device_->state_);
   EXPECT_EQ(Service::kStateConfiguring, device_->service_->state());
@@ -489,7 +526,7 @@
   return ContainsKey(arg, CellularCapability::kConnectPropertyPhoneNumber);
 }
 
-}  // namespace {}
+}  // namespace
 
 TEST_F(CellularTest, Connect) {
   Error error;
@@ -516,7 +553,7 @@
 
   error.Populate(Error::kSuccess);
   EXPECT_CALL(*simple_proxy_,
-              Connect(ContainsPhoneNumber(), _,
+              Connect(ContainsPhoneNumber(), _, _,
                       CellularCapability::kTimeoutConnect))
                 .Times(2)
                 .WillRepeatedly(Invoke(this, &CellularTest::InvokeConnect));
@@ -537,12 +574,21 @@
   EXPECT_EQ(Cellular::kStateConnected, device_->state_);
 }
 
-TEST_F(CellularTest, DisconnectModem) {
-  device_->DisconnectModem();
-  EXPECT_CALL(*proxy_, Disconnect()).Times(1);
-  device_->capability_->proxy_.reset(proxy_.release());
+TEST_F(CellularTest, Disconnect) {
+  Error error;
+  device_->state_ = Cellular::kStateRegistered;
+  device_->Disconnect(&error);
+  EXPECT_EQ(Error::kNotConnected, error.type());
+  error.Reset();
+
   device_->state_ = Cellular::kStateConnected;
-  device_->DisconnectModem();
+  EXPECT_CALL(*proxy_,
+              Disconnect(_, _, CellularCapability::kTimeoutDefault))
+      .WillOnce(Invoke(this, &CellularTest::InvokeDisconnect));
+  device_->capability_->proxy_.reset(proxy_.release());
+  device_->Disconnect(&error);
+  EXPECT_TRUE(error.IsSuccess());
+  EXPECT_EQ(Cellular::kStateRegistered, device_->state_);
 }
 
 }  // namespace shill
diff --git a/dbus_adaptor.cc b/dbus_adaptor.cc
index 084de41..90352cf 100644
--- a/dbus_adaptor.cc
+++ b/dbus_adaptor.cc
@@ -6,6 +6,8 @@
 #include <string>
 #include <vector>
 
+#include <base/bind.h>
+#include <base/callback.h>
 #include <base/logging.h>
 #include <dbus-c++/dbus.h>
 
@@ -15,6 +17,8 @@
 #include "shill/key_value_store.h"
 #include "shill/property_store.h"
 
+using base::Bind;
+using base::Owned;
 using std::map;
 using std::string;
 using std::vector;
@@ -425,90 +429,46 @@
   return signature == ::DBus::type<map<string, ::DBus::Variant> >::sig();
 }
 
-// static
-DBusAdaptor::Returner *DBusAdaptor::Returner::Create(DBusAdaptor *adaptor) {
-  return new Returner(adaptor);
+void DBusAdaptor::DeferReply(const DBus::Tag *tag) {
+  return_later(tag);
 }
 
-DBusAdaptor::Returner::Returner(DBusAdaptor *adaptor)
-    : adaptor_(adaptor),
-      state_(kStateInitialized) {
-  VLOG(2) << __func__ << " @ " << this;
+void DBusAdaptor::ReplyNow(const DBus::Tag *tag) {
+  Continuation *cont = find_continuation(tag);
+  CHECK(cont);
+  return_now(cont);
 }
 
-DBusAdaptor::Returner::~Returner() {
-  CHECK(state_ != kStateDestroyed);
-  VLOG(2) << "Destroying returner @ " << this << " state: " << state_;
-  adaptor_ = NULL;
-  state_ = kStateDestroyed;
+void DBusAdaptor::ReplyNowWithError(const DBus::Tag *tag,
+                                    const DBus::Error &error) {
+  Continuation *cont = find_continuation(tag);
+  CHECK(cont);
+  return_error(cont, error);
 }
 
-void DBusAdaptor::Returner::Return() {
-  VLOG(2) << __func__ << " @ " << this << " state: " << state_;
-  switch (state_) {
-    case kStateInitialized:
-      // Service method is returning right away, without any continuation.
-      state_ = kStateReturned;
-      return;
-    case kStateDelayed: {
-      // This return happens in the continuation.
-      DBus::ObjectAdaptor::Continuation *cont =
-          adaptor_->find_continuation(this);
-      CHECK(cont);
-      adaptor_->return_now(cont);
-      delete this;
-      return;
-    }
-    default:
-      NOTREACHED() << "Unexpected state: " << state_;
-      break;
+ResultCallback DBusAdaptor::GetMethodReplyCallback(
+    const DBus::Tag *tag) {
+  return Bind(&DBusAdaptor::MethodReplyCallback, AsWeakPtr(), Owned(tag));
+}
+
+void DBusAdaptor::ReturnResultOrDefer(const DBus::Tag *tag,
+                                      const Error &error,
+                                      DBus::Error *dberror) {
+  if (error.IsOngoing()) {
+    DeferReply(tag);
+  } else if (error.IsFailure()) {
+    error.ToDBusError(dberror);
   }
 }
 
-void DBusAdaptor::Returner::ReturnError(const Error &error) {
-  VLOG(2) << __func__ << " @ " << this << " state: " << state_;
-  switch (state_) {
-    case kStateInitialized:
-      // Service method is returning right away, without any continuation.
-      error_.CopyFrom(error);
-      state_ = kStateReturned;
-      return;
-    case kStateDelayed: {
-      // This return happens in the continuation.
-      DBus::Error dbus_error;
-      error.ToDBusError(&dbus_error);
-      DBus::ObjectAdaptor::Continuation *cont =
-          adaptor_->find_continuation(this);
-      CHECK(cont);
-      adaptor_->return_error(cont, dbus_error);
-      delete this;
-      return;
-    }
-    default:
-      NOTREACHED() << "Unexpected state: " << state_;
-      break;
-  }
-}
-
-void DBusAdaptor::Returner::DelayOrReturn(DBus::Error *error) {
-  VLOG(2) << __func__ << " @ " << this << " state: " << state_;
-  switch (state_) {
-    case kStateInitialized:
-      // Service method needs continuation so delay the return.
-      state_ = kStateDelayed;
-
-      // return_later does not return. It unwinds the stack up to the dbus-c++
-      // message handler by throwing an exception.
-      adaptor_->return_later(this);
-      return;
-    case kStateReturned:
-      // Service method has returned right away, without any continuation.
-      error_.ToDBusError(error);
-      delete this;
-      return;
-    default:
-      NOTREACHED() << "Unexpected state: " << state_;
-      break;
+void DBusAdaptor::MethodReplyCallback(const DBus::Tag *tag,
+                                      const Error &error) {
+  if (error.IsFailure()) {
+    DBus::Error dberror;
+    error.ToDBusError(&dberror);
+    ReplyNowWithError(tag, dberror);
+  } else {
+    ReplyNow(tag);
   }
 }
 
diff --git a/dbus_adaptor.h b/dbus_adaptor.h
index 7385ddd..c43b080 100644
--- a/dbus_adaptor.h
+++ b/dbus_adaptor.h
@@ -10,10 +10,13 @@
 #include <vector>
 
 #include <base/basictypes.h>
+#include <base/callback.h>
+#include <base/memory/weak_ptr.h>
 #include <dbus-c++/dbus.h>
 
 #include "shill/accessor_interface.h"
 #include "shill/adaptor_interfaces.h"
+#include "shill/callbacks.h"
 #include "shill/error.h"
 
 namespace shill {
@@ -26,7 +29,8 @@
 
 // Superclass for all DBus-backed Adaptor objects
 class DBusAdaptor : public DBus::ObjectAdaptor,
-                    public DBus::IntrospectableAdaptor {
+                    public DBus::IntrospectableAdaptor,
+                    public base::SupportsWeakPtr<DBusAdaptor> {
  public:
   DBusAdaptor(DBus::Connection* conn, const std::string &object_path);
   virtual ~DBusAdaptor();
@@ -85,48 +89,35 @@
   static bool IsKeyValueStore(::DBus::Signature signature);
 
  protected:
-  class Returner : public DBus::Tag,
-                   public ReturnerInterface {
-   public:
-    // Creates a new returner instance associated with |adaptor|.
-    static Returner *Create(DBusAdaptor *adaptor);
-
-    // Used by the adaptor to initiate or delay the return, depending on the
-    // state of the returner. A call to this method should be the last statement
-    // in the adaptor method. If none of the interface Return* methods has been
-    // called yet, DelayOrReturn exits to the dbus-c++ message handler by
-    // throwing an exception. Otherwise, it initializes |error|, completes the
-    // RPC call right away and destroys |this|.
-    void DelayOrReturn(DBus::Error *error);
-
-    // Inherited from ReturnerInterface. These methods complete the RPC call
-    // right away and destroy the object if DelayOrReturn has been called
-    // already. Otherwise, they allow DelayOrReturn to complete the call.
-    virtual void Return();
-    virtual void ReturnError(const Error &error);
-
-   private:
-    // The returner transitions through the following states:
-    //
-    // Initialized -> [Delayed|Returned] -> Destroyed.
-    enum State {
-      kStateInitialized,  // No *Return* methods called yet.
-      kStateDelayed,  // DelayOrReturn called, Return* not.
-      kStateReturned,  // Return* called, DelayOrReturn not.
-      kStateDestroyed  // Return complete, returner destroyed.
-    };
-
-    explicit Returner(DBusAdaptor *adaptor);
-
-    // Destruction happens through the *Return* methods.
-    virtual ~Returner();
-
-    DBusAdaptor *adaptor_;
-    Error error_;
-    State state_;
-
-    DISALLOW_COPY_AND_ASSIGN(Returner);
-  };
+  ResultCallback GetMethodReplyCallback(const DBus::Tag *tag);
+  // Adaptors call this method just before returning. If |error|
+  // indicates that the operation has completed, with no asynchronously
+  // delivered result expected, then a DBus method reply is immediately
+  // sent to the client that initiated the method invocation. Otherwise,
+  // the operation is ongoing, and the result will be sent to the client
+  // when the operation completes at some later time.
+  //
+  // Adaptors should always construct an Error initialized to the value
+  // Error::kOperationInitiated. A pointer to this Error is passed down
+  // through the call stack. Any layer that determines that the operation
+  // has completed, either because of a failure that prevents carrying it
+  // out, or because it was possible to complete it without sending a request
+  // to an external server, should call error.Reset() to indicate success,
+  // or to some error type to reflect the kind of failure that occurred.
+  // Otherwise, they should leave the Error alone.
+  //
+  // The general structure of an adaptor method is
+  //
+  // void XXXXDBusAdaptor::SomeMethod(<args...>, DBus::Error &error) {
+  //   Error e(Error::kOperationInitiated);
+  //   DBus::Tag *tag = new DBus::Tag();
+  //   xxxx_->SomeMethod(<args...>, &e, GetMethodReplyCallback(tag));
+  //   ReturnResultOrDefer(tag, e, &error);
+  // }
+  //
+  void ReturnResultOrDefer(const DBus::Tag *tag,
+                           const Error &error,
+                           DBus::Error *dberror);
 
  private:
   static const char kByteArraysSig[];
@@ -135,6 +126,11 @@
   static const char kStringmapsSig[];
   static const char kStringsSig[];
 
+  void MethodReplyCallback(const DBus::Tag *tag, const Error &error);
+  void DeferReply(const DBus::Tag *tag);
+  void ReplyNow(const DBus::Tag *tag);
+  void ReplyNowWithError(const DBus::Tag *tag, const DBus::Error &error);
+
   DISALLOW_COPY_AND_ASSIGN(DBusAdaptor);
 };
 
diff --git a/dbus_bindings/org.chromium.flimflam.Device.xml b/dbus_bindings/org.chromium.flimflam.Device.xml
index 6546d20..da6c95c 100644
--- a/dbus_bindings/org.chromium.flimflam.Device.xml
+++ b/dbus_bindings/org.chromium.flimflam.Device.xml
@@ -19,6 +19,8 @@
 		<method name="ClearProperty">
 			<arg type="s" direction="in"/>
 		</method>
+                <method name="Enable"/>
+                <method name="Disable"/>
 		<method name="ProposeScan"/>
 		<method name="AddIPConfig">
 			<arg type="s" direction="in"/>
diff --git a/dbus_objectmanager_proxy.cc b/dbus_objectmanager_proxy.cc
index 5f83e49..3eedb61 100644
--- a/dbus_objectmanager_proxy.cc
+++ b/dbus_objectmanager_proxy.cc
@@ -12,36 +12,59 @@
 namespace shill {
 
 DBusObjectManagerProxy::DBusObjectManagerProxy(
-    DBusObjectManagerProxyDelegate *delegate,
     DBus::Connection *connection,
     const string &path,
     const string &service)
-    : proxy_(delegate, connection, path, service) {}
-
+    : proxy_(connection, path, service) {}
 DBusObjectManagerProxy::~DBusObjectManagerProxy() {}
 
-void DBusObjectManagerProxy::GetManagedObjects(AsyncCallHandler *call_handler,
-                                               int timeout) {
-  return proxy_.GetManagedObjects(call_handler, timeout);
+void DBusObjectManagerProxy::GetManagedObjects(
+    Error *error,
+    const ManagedObjectsCallback &callback,
+    int timeout) {
+  scoped_ptr<ManagedObjectsCallback> cb(new ManagedObjectsCallback(callback));
+  try {
+    proxy_.GetManagedObjects(cb.get(), timeout);
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
+}
+
+void DBusObjectManagerProxy::set_interfaces_added_callback(
+      const InterfacesAddedSignalCallback &callback) {
+  proxy_.set_interfaces_added_callback(callback);
+}
+
+void DBusObjectManagerProxy::set_interfaces_removed_callback(
+      const InterfacesRemovedSignalCallback &callback) {
+  proxy_.set_interfaces_removed_callback(callback);
 }
 
 // Inherited from DBusObjectManagerProxyInterface.
-DBusObjectManagerProxy::Proxy::Proxy(DBusObjectManagerProxyDelegate *delegate,
-                                     DBus::Connection *connection,
+DBusObjectManagerProxy::Proxy::Proxy(DBus::Connection *connection,
                                      const std::string &path,
                                      const std::string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()),
-      delegate_(delegate) {}
-
+    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
 
 DBusObjectManagerProxy::Proxy::~Proxy() {}
 
+void DBusObjectManagerProxy::Proxy::set_interfaces_added_callback(
+      const InterfacesAddedSignalCallback &callback) {
+  interfaces_added_callback_ = callback;
+}
+
+void DBusObjectManagerProxy::Proxy::set_interfaces_removed_callback(
+      const InterfacesRemovedSignalCallback &callback) {
+  interfaces_removed_callback_ = callback;
+}
+
 // Signal callback
 void DBusObjectManagerProxy::Proxy::InterfacesAdded(
     const ::DBus::Path &object_path,
     const DBusInterfaceToProperties &interface_to_properties) {
   VLOG(2) << __func__ << "(" << object_path << ")";
-  delegate_->OnInterfacesAdded(object_path, interface_to_properties);
+  interfaces_added_callback_.Run(object_path, interface_to_properties);
 }
 
 // Signal callback
@@ -49,7 +72,7 @@
     const ::DBus::Path &object_path,
     const std::vector< std::string > &interfaces) {
   VLOG(2) << __func__ << "(" << object_path << ")";
-  delegate_->OnInterfacesRemoved(object_path, interfaces);
+  interfaces_removed_callback_.Run(object_path, interfaces);
 }
 
 // Method callback
@@ -60,12 +83,10 @@
   VLOG(2) << __func__;
   shill::Error error;
   CellularError::FromDBusError(dberror, &error);
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ManagedObjectsCallback> callback(
+      reinterpret_cast<ManagedObjectsCallback *>(data));
 
-  delegate_->OnGetManagedObjectsCallback(
-      objects_with_properties,
-      error,
-      call_handler);
+  callback->Run(objects_with_properties, error);
 }
 
 }  // namespace shill
diff --git a/dbus_objectmanager_proxy.h b/dbus_objectmanager_proxy.h
index d3324db..6a5eb38 100644
--- a/dbus_objectmanager_proxy.h
+++ b/dbus_objectmanager_proxy.h
@@ -14,28 +14,37 @@
 class DBusObjectManagerProxy : public DBusObjectManagerProxyInterface {
  public:
   // Constructs a org.freedesktop.DBus.ObjectManager DBus object proxy
-  // at |path| owned by |service|. Caught signals and asynchronous
-  // method replies will be dispatched to |delegate|.
+  // at |path| owned by |service|.
 
-  DBusObjectManagerProxy(DBusObjectManagerProxyDelegate *delegate,
-                         DBus::Connection *connection,
+  DBusObjectManagerProxy(DBus::Connection *connection,
                          const std::string &path,
                          const std::string &service);
   virtual ~DBusObjectManagerProxy();
 
   // Inherited methods from DBusObjectManagerProxyInterface.
-  virtual void GetManagedObjects(AsyncCallHandler *call_handler, int timeout);
+  virtual void GetManagedObjects(Error *error,
+                                 const ManagedObjectsCallback &callback,
+                                 int timeout);
+
+  virtual void set_interfaces_added_callback(
+      const InterfacesAddedSignalCallback &callback);
+  virtual void set_interfaces_removed_callback(
+      const InterfacesRemovedSignalCallback &callback);
 
  private:
   class Proxy : public org::freedesktop::DBus::ObjectManager_proxy,
                 public DBus::ObjectProxy {
    public:
-    Proxy(DBusObjectManagerProxyDelegate *delegate,
-          DBus::Connection *connection,
+    Proxy(DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
 
+    virtual void set_interfaces_added_callback(
+        const InterfacesAddedSignalCallback &callback);
+    virtual void set_interfaces_removed_callback(
+        const InterfacesRemovedSignalCallback &callback);
+
    private:
     // Signal callbacks
     virtual void InterfacesAdded(
@@ -50,7 +59,8 @@
         const DBus::Error &error,
         void *call_handler);
 
-    DBusObjectManagerProxyDelegate *delegate_;
+    InterfacesAddedSignalCallback interfaces_added_callback_;
+    InterfacesRemovedSignalCallback interfaces_removed_callback_;
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
   };
diff --git a/dbus_objectmanager_proxy_interface.h b/dbus_objectmanager_proxy_interface.h
index 7584f2e..deb175a 100644
--- a/dbus_objectmanager_proxy_interface.h
+++ b/dbus_objectmanager_proxy_interface.h
@@ -6,48 +6,42 @@
 #include <string>
 
 #include <base/basictypes.h>
+#include <base/callback.h>
 
 #include "shill/dbus_properties.h"  // For DBusPropertiesMap
 
 namespace shill {
 
-class AsyncCallHandler;
 class Error;
 
 typedef std::map<std::string, DBusPropertiesMap> DBusInterfaceToProperties;
-typedef
-   std::map< ::DBus::Path, DBusInterfaceToProperties> DBusObjectsWithProperties;
+typedef std::map< ::DBus::Path, DBusInterfaceToProperties>
+    DBusObjectsWithProperties;
+typedef base::Callback<void(const DBusObjectsWithProperties &, const Error &)>
+    ManagedObjectsCallback;
+typedef base::Callback<void(const DBusInterfaceToProperties &, const Error &)>
+    InterfaceAndPropertiesCallback;
+typedef base::Callback<void(const DBus::Path &,
+                            const DBusInterfaceToProperties &)>
+    InterfacesAddedSignalCallback;
+typedef base::Callback<void(const DBus::Path &,
+                            const std::vector<std::string> &)>
+    InterfacesRemovedSignalCallback;
 
 // These are the methods that a org.freedesktop.DBus.ObjectManager
 // 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.
-
+// mocked in tests.  All calls are made asynchronously. Call completion
+// is signalled via the callbacks passed to the methods.
 class DBusObjectManagerProxyInterface {
  public:
   virtual ~DBusObjectManagerProxyInterface() {}
-  virtual void GetManagedObjects(
-      AsyncCallHandler *call_handler, int timeout) = 0;
-};
-
-class DBusObjectManagerProxyDelegate {
- public:
-  virtual ~DBusObjectManagerProxyDelegate() {}
-
-  // Signals
-  virtual void OnInterfacesAdded(
-      const ::DBus::Path &object_path,
-      const DBusInterfaceToProperties &interface_to_properties) = 0;
-  virtual void OnInterfacesRemoved(
-      const ::DBus::Path &object_path,
-      const std::vector<std::string> &interfaces) = 0;
-
-  // Async method callbacks
-  virtual void OnGetManagedObjectsCallback(
-      const DBusObjectsWithProperties &objects_with_properties,
-      const shill::Error &error,
-      AsyncCallHandler *call_handler) = 0;
+  virtual void GetManagedObjects(Error *error,
+                                 const ManagedObjectsCallback &callback,
+                                 int timeout) = 0;
+  virtual void set_interfaces_added_callback(
+      const InterfacesAddedSignalCallback &callback) = 0;
+  virtual void set_interfaces_removed_callback(
+      const InterfacesRemovedSignalCallback &callback) = 0;
 };
 
 }  // namespace shill
diff --git a/device.cc b/device.cc
index 2042c5d..7082583 100644
--- a/device.cc
+++ b/device.cc
@@ -38,7 +38,6 @@
 #include "shill/technology.h"
 
 using base::Bind;
-using base::Unretained;
 using base::StringPrintf;
 using std::string;
 using std::vector;
@@ -78,7 +77,9 @@
                const string &address,
                int interface_index,
                Technology::Identifier technology)
-    : powered_(true),
+    : enabled_(false),
+      enabled_persistent_(true),
+      enabled_pending_(enabled_),
       reconnect_(true),
       hardware_address_(address),
       interface_index_(interface_index),
@@ -89,9 +90,10 @@
       dispatcher_(dispatcher),
       metrics_(metrics),
       manager_(manager),
+      weak_ptr_factory_(this),
       adaptor_(control_interface->CreateDeviceAdaptor(this)),
-      portal_detector_callback_(
-          Bind(&Device::PortalDetectorCallback, Unretained(this))),
+      portal_detector_callback_(Bind(&Device::PortalDetectorCallback,
+                                     weak_ptr_factory_.GetWeakPtr())),
       technology_(technology),
       portal_attempts_to_online_(0),
       dhcp_provider_(DHCPProvider::GetInstance()),
@@ -128,7 +130,7 @@
                              &Device::AvailableIPConfigs,
                              NULL);
   store_.RegisterConstString(flimflam::kNameProperty, &link_name_);
-  store_.RegisterBool(flimflam::kPoweredProperty, &powered_);
+  store_.RegisterConstBool(flimflam::kPoweredProperty, &enabled_);
   HelpRegisterDerivedString(flimflam::kTypeProperty,
                             &Device::GetTechnologyString,
                             NULL);
@@ -150,29 +152,6 @@
   VLOG(2) << "Device " << link_name_ << " destroyed.";
 }
 
-void Device::Start() {
-  running_ = true;
-  VLOG(2) << "Device " << link_name_ << " starting.";
-  routing_table_->FlushRoutes(interface_index());
-  adaptor_->UpdateEnabled();
-}
-
-void Device::Stop() {
-  VLOG(2) << "Device " << link_name_ << " stopping.";
-  running_ = false;
-  DestroyIPConfig();         // breaks a reference cycle
-  SelectService(NULL);       // breaks a reference cycle
-  adaptor_->UpdateEnabled();
-  rtnl_handler_->SetInterfaceFlags(interface_index(), 0, IFF_UP);
-
-  VLOG(3) << "Device " << link_name_ << " ipconfig_ "
-          << (ipconfig_.get() ? "is set." : "is not set.");
-  VLOG(3) << "Device " << link_name_ << " connection_ "
-          << (connection_.get() ? "is set." : "is not set.");
-  VLOG(3) << "Device " << link_name_ << " selected_service_ "
-          << (selected_service_.get() ? "is set." : "is not set.");
-}
-
 bool Device::TechnologyIs(const Technology::Identifier /*type*/) const {
   return false;
 }
@@ -190,49 +169,41 @@
                         "Device doesn't support scan.");
 }
 
-void Device::RegisterOnNetwork(const std::string &/*network_id*/,
-                               ReturnerInterface *returner) {
-  Error error;
-  Error::PopulateAndLog(&error, Error::kNotSupported,
+void Device::RegisterOnNetwork(const std::string &/*network_id*/, Error *error,
+                                 const ResultCallback &/*callback*/) {
+  Error::PopulateAndLog(error, Error::kNotSupported,
                         "Device doesn't support network registration.");
-  returner->ReturnError(error);
 }
 
 void Device::RequirePIN(
-    const string &/*pin*/, bool /*require*/, ReturnerInterface *returner) {
+    const string &/*pin*/, bool /*require*/,
+    Error *error, const ResultCallback &/*callback*/) {
   VLOG(2) << __func__;
-  Error error;
-  Error::PopulateAndLog(
-      &error, Error::kNotSupported, "Device doesn't support RequirePIN.");
-  returner->ReturnError(error);
+  Error::PopulateAndLog(error, Error::kNotSupported,
+                        "Device doesn't support RequirePIN.");
 }
 
-void Device::EnterPIN(const string &/*pin*/, ReturnerInterface *returner) {
+void Device::EnterPIN(const string &/*pin*/,
+                      Error *error, const ResultCallback &/*callback*/) {
   VLOG(2) << __func__;
-  Error error;
-  Error::PopulateAndLog(
-      &error, Error::kNotSupported, "Device doesn't support EnterPIN.");
-  returner->ReturnError(error);
+  Error::PopulateAndLog(error, Error::kNotSupported,
+                        "Device doesn't support EnterPIN.");
 }
 
 void Device::UnblockPIN(const string &/*unblock_code*/,
                         const string &/*pin*/,
-                        ReturnerInterface *returner) {
+                        Error *error, const ResultCallback &/*callback*/) {
   VLOG(2) << __func__;
-  Error error;
-  Error::PopulateAndLog(
-      &error, Error::kNotSupported, "Device doesn't support UnblockPIN.");
-  returner->ReturnError(error);
+  Error::PopulateAndLog(error, Error::kNotSupported,
+                        "Device doesn't support UnblockPIN.");
 }
 
 void Device::ChangePIN(const string &/*old_pin*/,
                        const string &/*new_pin*/,
-                       ReturnerInterface *returner) {
+                       Error *error, const ResultCallback &/*callback*/) {
   VLOG(2) << __func__;
-  Error error;
-  Error::PopulateAndLog(
-      &error, Error::kNotSupported, "Device doesn't support ChangePIN.");
-  returner->ReturnError(error);
+  Error::PopulateAndLog(error, Error::kNotSupported,
+                        "Device doesn't support ChangePIN.");
 }
 
 void Device::DisableIPv6() {
@@ -297,14 +268,15 @@
     LOG(WARNING) << "Device is not available in the persistent store: " << id;
     return false;
   }
-  storage->GetBool(id, kStoragePowered, &powered_);
+  enabled_persistent_ = true;
+  storage->GetBool(id, kStoragePowered, &enabled_persistent_);
   // TODO(cmasone): What does it mean to load an IPConfig identifier??
   return true;
 }
 
 bool Device::Save(StoreInterface *storage) {
   const string id = GetStorageIdentifier();
-  storage->SetBool(id, kStoragePowered, powered_);
+  storage->SetBool(id, kStoragePowered, enabled_persistent_);
   if (ipconfig_.get()) {
     // The _0 is an index into the list of IPConfigs that this device might
     // have.  We only have one IPConfig right now, and I hope to never have
@@ -330,7 +302,8 @@
   DestroyIPConfig();
   EnableIPv6();
   ipconfig_ = dhcp_provider_->CreateConfig(link_name_, manager_->GetHostName());
-  ipconfig_->RegisterUpdateCallback(Bind(&Device::OnIPConfigUpdated, this));
+  ipconfig_->RegisterUpdateCallback(Bind(&Device::OnIPConfigUpdated,
+                                         weak_ptr_factory_.GetWeakPtr()));
   return ipconfig_->RequestIP();
 }
 
@@ -345,8 +318,8 @@
 
 void Device::HelpRegisterDerivedStrings(
     const string &name,
-    Strings(Device::*get)(Error *),
-    void(Device::*set)(const Strings&, Error *)) {
+    Strings(Device::*get)(Error *error),
+    void(Device::*set)(const Strings &value, Error *error)) {
   store_.RegisterDerivedStrings(
       name,
       StringsAccessor(new CustomAccessor<Device, Strings>(this, get, set)));
@@ -626,4 +599,76 @@
   return adaptor_->GetRpcConnectionIdentifier();
 }
 
+// callback
+void Device::OnEnabledStateChanged(const ResultCallback &callback,
+                                   const Error &error) {
+  VLOG(2) << __func__ << "(" << enabled_pending_ << ")";
+  if (error.IsSuccess()) {
+    enabled_ = enabled_pending_;
+    manager_->UpdateEnabledTechnologies();
+    adaptor_->EmitBoolChanged(flimflam::kPoweredProperty, enabled_);
+    adaptor_->UpdateEnabled();
+  }
+  if (!callback.is_null())
+    callback.Run(error);
+}
+
+void Device::SetEnabled(bool enable) {
+  VLOG(2) << __func__ << "(" << enable << ")";
+  SetEnabledInternal(enable, false, NULL, ResultCallback());
+}
+
+void Device::SetEnabledPersistent(bool enable,
+                                  Error *error,
+                                  const ResultCallback &callback) {
+  SetEnabledInternal(enable, true, error, callback);
+}
+
+void Device::SetEnabledInternal(bool enable,
+                                bool persist,
+                                Error *error,
+                                const ResultCallback &callback) {
+  VLOG(2) << "Device " << link_name_ << " "
+          << (enable ? "starting" : "stopping");
+  if (enable == enabled_) {
+    if (error)
+      error->Reset();
+    return;
+  }
+
+  if (enabled_pending_ == enable) {
+    if (error)
+      error->Populate(Error::kInProgress,
+                      "Enable operation already in progress");
+    return;
+  }
+
+  if (persist) {
+    enabled_persistent_ = enable;
+    manager_->SaveActiveProfile();
+  }
+
+  enabled_pending_ = enable;
+  EnabledStateChangedCallback enabled_callback =
+      Bind(&Device::OnEnabledStateChanged,
+           weak_ptr_factory_.GetWeakPtr(), callback);
+  if (enable) {
+    running_ = true;
+    routing_table_->FlushRoutes(interface_index());
+    Start(error, enabled_callback);
+  } else {
+    running_ = false;
+    DestroyIPConfig();         // breaks a reference cycle
+    SelectService(NULL);       // breaks a reference cycle
+    rtnl_handler_->SetInterfaceFlags(interface_index(), 0, IFF_UP);
+    VLOG(3) << "Device " << link_name_ << " ipconfig_ "
+            << (ipconfig_ ? "is set." : "is not set.");
+    VLOG(3) << "Device " << link_name_ << " connection_ "
+            << (connection_ ? "is set." : "is not set.");
+    VLOG(3) << "Device " << link_name_ << " selected_service_ "
+            << (selected_service_ ? "is set." : "is not set.");
+    Stop(error, enabled_callback);
+  }
+}
+
 }  // namespace shill
diff --git a/device.h b/device.h
index dde6970..2dadd3d 100644
--- a/device.h
+++ b/device.h
@@ -9,11 +9,13 @@
 #include <vector>
 
 #include <base/basictypes.h>
-#include <base/callback.h>
 #include <base/memory/ref_counted.h>
 #include <base/memory/scoped_ptr.h>
+#include <base/memory/weak_ptr.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
+#include "shill/adaptor_interfaces.h"
+#include "shill/callbacks.h"
 #include "shill/event_dispatcher.h"
 #include "shill/ip_address.h"
 #include "shill/ipconfig.h"
@@ -33,7 +35,6 @@
 class EventDispatcher;
 class Manager;
 class Metrics;
-class ReturnerInterface;
 class RoutingTable;
 class RTNLHandler;
 
@@ -52,13 +53,14 @@
          Technology::Identifier technology);
   virtual ~Device();
 
-  virtual void Start();
-
-  // Clear running state, especially any fields that hold a reference back
-  // to us. After a call to Stop(), the Device may be restarted (with a call
-  // to Start()), or destroyed (if its refcount falls to zero).
-  virtual void Stop();
-
+  // Enable or disable the device.
+  virtual void SetEnabled(bool enable);
+  // Enable or disable the device, and save the setting in the profile.
+  // The setting is persisted before the enable or disable operation
+  // starts, so that even if it fails, the user's intent is still recorded
+  // for the next time shill restarts.
+  void SetEnabledPersistent(bool enable,
+                            Error *error, const ResultCallback &callback);
 
   // TODO(gauravsh): We do not really need this since technology() can be used
   //                 to get a device's technology for direct comparison.
@@ -69,17 +71,18 @@
 
   // The default implementation sets |error| to kNotSupported.
   virtual void Scan(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);
+  virtual void RegisterOnNetwork(const std::string &network_id, Error *error,
+                                 const ResultCallback &callback);
+  virtual void RequirePIN(const std::string &pin, bool require,
+                          Error *error, const ResultCallback &callback);
+  virtual void EnterPIN(const std::string &pin,
+                        Error *error, const ResultCallback &callback);
   virtual void UnblockPIN(const std::string &unblock_code,
                           const std::string &pin,
-                          ReturnerInterface *returner);
+                          Error *error, const ResultCallback &callback);
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
-                         ReturnerInterface *returner);
+                         Error *error, const ResultCallback &callback);
   virtual void DisableIPv6();
   virtual void EnableIPv6();
   virtual void EnableIPv6Privacy();
@@ -108,7 +111,8 @@
   const std::string &link_name() const { return link_name_; }
   int interface_index() const { return interface_index_; }
   const ConnectionRefPtr &connection() const { return connection_; }
-  bool powered() const { return powered_; }
+  bool enabled() const { return enabled_; }
+  bool enabled_persistent() const { return enabled_persistent_; }
   virtual Technology::Identifier technology() const { return technology_; }
   std::string GetTechnologyString(Error *error);
 
@@ -148,11 +152,51 @@
   FRIEND_TEST(DeviceTest, SelectedService);
   FRIEND_TEST(DeviceTest, SetServiceConnectedState);
   FRIEND_TEST(DeviceTest, Stop);
+  FRIEND_TEST(DeviceTest, OnEnabledStateChanged);
   FRIEND_TEST(ManagerTest, DeviceRegistrationAndStart);
   FRIEND_TEST(ManagerTest, ConnectedTechnologies);
   FRIEND_TEST(ManagerTest, DefaultTechnology);
   FRIEND_TEST(WiFiMainTest, Connect);
 
+  // Each device must implement this method to do the work needed to
+  // enable the device to operate for establishing network connections.
+  // The |error| argument, if not NULL,
+  // will refer to an Error that starts out with the value
+  // Error::kOperationInitiated. This reflects the assumption that
+  // enable (and disable) operations will usually be non-blocking,
+  // and their completion will be indicated by means of an asynchronous
+  // reply sometime later. There are two circumstances in which a
+  // device's Start() method may overwrite |error|:
+  //
+  // 1. If an early failure is detected, such that the non-blocking
+  //    part of the operation never takes place, then |error| should
+  //    be set to the appropriate value corresponding to the type
+  //    of failure. This is the "immediate failure" case.
+  // 2. If the device is enabled without performing any non-blocking
+  //    steps, then |error| should be Reset, i.e., its value set
+  //    to Error::kSuccess. This is the "immediate success" case.
+  //
+  // In these two cases, because completion is immediate, |callback|
+  // is not used. If neither of these two conditions holds, then |error|
+  // should not be modified, and |callback| should be passed to the
+  // method that will initiate the non-blocking operation.
+  virtual void Start(Error *error,
+                     const EnabledStateChangedCallback &callback) = 0;
+
+  // Each device must implement this method to do the work needed to
+  // disable the device, i.e., clear any running state, and make the
+  // device no longer capable of establishing network connections.
+  // The discussion for Start() regarding the use of |error| and
+  // |callback| apply to Stop() as well.
+  virtual void Stop(Error *error,
+                    const EnabledStateChangedCallback &callback) = 0;
+
+  // The EnabledStateChangedCallback that gets passed to the device's
+  // Start() and Stop() methods is bound to this method. |callback|
+  // is the callback that was passed to SetEnabled().
+  void OnEnabledStateChanged(const ResultCallback &callback,
+                             const Error &error);
+
   // If there's an IP configuration in |ipconfig_|, releases the IP address and
   // destroys the configuration instance.
   void DestroyIPConfig();
@@ -202,9 +246,11 @@
       const std::string &name,
       std::string(Device::*get)(Error *),
       void(Device::*set)(const std::string&, Error *));
-  void HelpRegisterDerivedStrings(const std::string &name,
-                                  Strings(Device::*get)(Error *),
-                                  void(Device::*set)(const Strings&, Error *));
+
+  void HelpRegisterDerivedStrings(
+      const std::string &name,
+      Strings(Device::*get)(Error *error),
+      void(Device::*set)(const Strings &value, Error *error));
 
   // Property getters reserved for subclasses
   ControlInterface *control_interface() const { return control_interface_; }
@@ -217,6 +263,7 @@
   friend class DevicePortalDetectionTest;
   friend class DeviceTest;
   friend class CellularTest;
+  friend class CellularCapabilityTest;
   friend class WiFiMainTest;
 
   static const char kIPFlagTemplate[];
@@ -231,6 +278,9 @@
   static const char kStoragePowered[];
   static const char kStorageIPConfigs[];
 
+  void SetEnabledInternal(bool enable, bool persist,
+                          Error *error, const ResultCallback &callback);
+
   // Right now, Devices reference IPConfigs directly when persisted to disk
   // It's not clear that this makes sense long-term, but that's how it is now.
   // This call generates a string in the right format for this persisting.
@@ -246,8 +296,34 @@
   std::vector<std::string> AvailableIPConfigs(Error *error);
   std::string GetRpcConnectionIdentifier();
 
-  // Properties
-  bool powered_;  // indicates whether the device is configured to operate
+  // |enabled_persistent_| is the value of the Powered property, as
+  // read from the profile. If it is not found in the profile, it
+  // defaults to true. |enabled_| reflects the real-time state of
+  // the device, i.e., enabled or disabled. |enabled_pending_| reflects
+  // the target state of the device while an enable or disable operation
+  // is occurring.
+  //
+  // Some typical sequences for these state variables are shown below.
+  //
+  // Shill starts up, profile has been read:
+  //  |enabled_persistent_|=true   |enabled_|=false   |enabled_pending_|=false
+  //
+  // Shill acts on the value of |enabled_persistent_|, calls SetEnabled(true):
+  //  |enabled_persistent_|=true   |enabled_|=false   |enabled_pending_|=true
+  //
+  // SetEnabled completes successfully, device is enabled:
+  //  |enabled_persistent_|=true   |enabled_|=true    |enabled_pending_|=true
+  //
+  // User presses "Disable" button, SetEnabled(false) is called:
+  //  |enabled_persistent_|=false   |enabled_|=true    |enabled_pending_|=false
+  //
+  // SetEnabled completes successfully, device is disabled:
+  //  |enabled_persistent_|=false   |enabled_|=false    |enabled_pending_|=false
+  bool enabled_;
+  bool enabled_persistent_;
+  bool enabled_pending_;
+
+  // Other properties
   bool reconnect_;
   const std::string hardware_address_;
 
@@ -263,6 +339,7 @@
   Manager *manager_;
   IPConfigRefPtr ipconfig_;
   ConnectionRefPtr connection_;
+  base::WeakPtrFactory<Device> weak_ptr_factory_;
   scoped_ptr<DeviceAdaptorInterface> adaptor_;
   scoped_ptr<PortalDetector> portal_detector_;
   base::Callback<void(const PortalDetector::Result &)>
diff --git a/device_dbus_adaptor.cc b/device_dbus_adaptor.cc
index 49dad4c..8ad6763 100644
--- a/device_dbus_adaptor.cc
+++ b/device_dbus_adaptor.cc
@@ -2,15 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "shill/device.h"
 #include "shill/device_dbus_adaptor.h"
 
 #include <map>
 #include <string>
 
+#include <base/bind.h>
+
 #include "shill/device.h"
 #include "shill/error.h"
 
+using base::Bind;
 using std::map;
 using std::string;
 
@@ -83,6 +85,20 @@
   DBusAdaptor::ClearProperty(device_->mutable_store(), name, &error);
 }
 
+void DeviceDBusAdaptor::Enable(::DBus::Error &error) {
+  Error e(Error::kOperationInitiated);
+  DBus::Tag *tag = new DBus::Tag();
+  device_->SetEnabledPersistent(true, &e, GetMethodReplyCallback(tag));
+  ReturnResultOrDefer(tag, e, &error);
+}
+
+void DeviceDBusAdaptor::Disable(::DBus::Error &error) {
+  Error e(Error::kOperationInitiated);
+  DBus::Tag *tag = new DBus::Tag();
+  device_->SetEnabledPersistent(false, &e, GetMethodReplyCallback(tag));
+  ReturnResultOrDefer(tag, e, &error);
+}
+
 void DeviceDBusAdaptor::ProposeScan(::DBus::Error &error) {
   Error e;
   device_->Scan(&e);
@@ -97,40 +113,41 @@
 void DeviceDBusAdaptor::Register(const string &network_id,
                                  ::DBus::Error &error) {
   VLOG(2) << __func__ << "(" << network_id << ")";
-  Returner *returner = Returner::Create(this);
-  device_->RegisterOnNetwork(network_id, returner);
-  returner->DelayOrReturn(&error);
+  Error e(Error::kOperationInitiated);
+  DBus::Tag *tag = new DBus::Tag();
+  device_->RegisterOnNetwork(network_id, &e, GetMethodReplyCallback(tag));
+  ReturnResultOrDefer(tag, e, &error);
 }
 
 void DeviceDBusAdaptor::RequirePin(
     const string &pin, const bool &require, DBus::Error &error) {
-  VLOG(2) << __func__;
-  Returner *returner = Returner::Create(this);
-  device_->RequirePIN(pin, require, returner);
-  returner->DelayOrReturn(&error);
+  Error e(Error::kOperationInitiated);
+  DBus::Tag *tag = new DBus::Tag();
+  device_->RequirePIN(pin, require, &e, GetMethodReplyCallback(tag));
+  ReturnResultOrDefer(tag, e, &error);
 }
 
 void DeviceDBusAdaptor::EnterPin(const string &pin, DBus::Error &error) {
-  VLOG(2) << __func__;
-  Returner *returner = Returner::Create(this);
-  device_->EnterPIN(pin, returner);
-  returner->DelayOrReturn(&error);
+  Error e(Error::kOperationInitiated);
+  DBus::Tag *tag = new DBus::Tag();
+  device_->EnterPIN(pin, &e, GetMethodReplyCallback(tag));
+  ReturnResultOrDefer(tag, e, &error);
 }
 
 void DeviceDBusAdaptor::UnblockPin(
     const string &unblock_code, const string &pin, DBus::Error &error) {
-  VLOG(2) << __func__;
-  Returner *returner = Returner::Create(this);
-  device_->UnblockPIN(unblock_code, pin, returner);
-  returner->DelayOrReturn(&error);
+  Error e(Error::kOperationInitiated);
+  DBus::Tag *tag = new DBus::Tag();
+  device_->UnblockPIN(unblock_code, pin, &e, GetMethodReplyCallback(tag));
+  ReturnResultOrDefer(tag, e, &error);
 }
 
 void DeviceDBusAdaptor::ChangePin(
     const string &old_pin, const string &new_pin, DBus::Error &error) {
-  VLOG(2) << __func__;
-  Returner *returner = Returner::Create(this);
-  device_->ChangePIN(old_pin, new_pin, returner);
-  returner->DelayOrReturn(&error);
+  Error e;
+  DBus::Tag *tag = new DBus::Tag();
+  device_->ChangePIN(old_pin, new_pin, &e, GetMethodReplyCallback(tag));
+  ReturnResultOrDefer(tag, e, &error);
 }
 
 }  // namespace shill
diff --git a/device_dbus_adaptor.h b/device_dbus_adaptor.h
index abb373f..e295d99 100644
--- a/device_dbus_adaptor.h
+++ b/device_dbus_adaptor.h
@@ -52,6 +52,8 @@
                            const ::DBus::Variant& value,
                            ::DBus::Error &error);
   virtual void ClearProperty(const std::string& , ::DBus::Error &error);
+  virtual void Enable(::DBus::Error &error);
+  virtual void Disable(::DBus::Error &error);
   virtual void ProposeScan(::DBus::Error &error);
   virtual ::DBus::Path AddIPConfig(const std::string& , ::DBus::Error &error);
   virtual void Register(const std::string &network_id, ::DBus::Error &error);
diff --git a/device_info.cc b/device_info.cc
index 3800319..366f74f 100644
--- a/device_info.cc
+++ b/device_info.cc
@@ -193,7 +193,7 @@
   string driver_name(driver_path.BaseName().value());
   // See if driver for this interface is in a list of known modem driver names
   for (int modem_idx = 0; kModemDrivers[modem_idx] != NULL; ++modem_idx) {
-    // TODO(ers) should have additional checks to make sure a cdc_ether
+    // TODO(ers): should have additional checks to make sure a cdc_ether
     // device is really a modem. flimflam uses udev to make such checks,
     // looking to see whether a ttyACM or ttyUSB device is associated.
     if (driver_name == kModemDrivers[modem_idx]) {
diff --git a/device_stub.h b/device_stub.h
index 4588379..b3e3b8c 100644
--- a/device_stub.h
+++ b/device_stub.h
@@ -37,14 +37,15 @@
              Technology::Identifier technology)
       : Device(control_interface, dispatcher, metrics, manager, link_name,
                address, interface_index, technology) {}
-  void Start() {}
-  void Stop() {}
+  void Start(Error */*error*/,
+             const EnabledStateChangedCallback &/*callback*/) {}
+  void Stop(Error */*error*/,
+            const EnabledStateChangedCallback &/*callback*/) {}
   bool TechnologyIs(const Technology::Identifier type) const {
     return type == technology();
   }
 
  private:
-
   DISALLOW_COPY_AND_ASSIGN(DeviceStub);
 };
 
diff --git a/device_unittest.cc b/device_unittest.cc
index 2f4dd17..de7a1e6 100644
--- a/device_unittest.cc
+++ b/device_unittest.cc
@@ -53,17 +53,36 @@
 
 namespace shill {
 
+class TestDevice : public Device {
+ public:
+  TestDevice(ControlInterface *control_interface,
+             EventDispatcher *dispatcher,
+             Metrics *metrics,
+             Manager *manager,
+             const std::string &link_name,
+             const std::string &address,
+             int interface_index,
+             Technology::Identifier technology)
+      : Device(control_interface, dispatcher, metrics, manager, link_name,
+               address, interface_index, technology) {}
+  ~TestDevice() {}
+  virtual void Start(Error *error,
+                     const EnabledStateChangedCallback &callback) {}
+  virtual void Stop(Error *error,
+                    const EnabledStateChangedCallback &callback) {}
+};
+
 class DeviceTest : public PropertyStoreTest {
  public:
   DeviceTest()
-      : device_(new Device(control_interface(),
-                           dispatcher(),
-                           NULL,
-                           manager(),
-                           kDeviceName,
-                           kDeviceAddress,
-                           kDeviceInterfaceIndex,
-                           Technology::kUnknown)),
+      : device_(new TestDevice(control_interface(),
+                               dispatcher(),
+                               NULL,
+                               manager(),
+                               kDeviceName,
+                               kDeviceAddress,
+                               kDeviceInterfaceIndex,
+                               Technology::kUnknown)),
         device_info_(control_interface(), NULL, NULL, NULL) {
     DHCPProvider::GetInstance()->glib_ = glib();
     DHCPProvider::GetInstance()->control_interface_ = control_interface();
@@ -113,33 +132,17 @@
 TEST_F(DeviceTest, GetProperties) {
   map<string, ::DBus::Variant> props;
   Error error(Error::kInvalidProperty, "");
-  {
-    ::DBus::Error dbus_error;
-    bool expected = true;
-    device_->mutable_store()->SetBoolProperty(flimflam::kPoweredProperty,
-                                              expected,
-                                              &error);
-    DBusAdaptor::GetProperties(device_->store(), &props, &dbus_error);
-    ASSERT_FALSE(props.find(flimflam::kPoweredProperty) == props.end());
-    EXPECT_EQ(props[flimflam::kPoweredProperty].reader().get_bool(),
-              expected);
-  }
-  {
-    ::DBus::Error dbus_error;
-    DBusAdaptor::GetProperties(device_->store(), &props, &dbus_error);
-    ASSERT_FALSE(props.find(flimflam::kNameProperty) == props.end());
-    EXPECT_EQ(props[flimflam::kNameProperty].reader().get_string(),
-              string(kDeviceName));
-  }
+  ::DBus::Error dbus_error;
+  DBusAdaptor::GetProperties(device_->store(), &props, &dbus_error);
+  ASSERT_FALSE(props.find(flimflam::kNameProperty) == props.end());
+  EXPECT_EQ(props[flimflam::kNameProperty].reader().get_string(),
+            string(kDeviceName));
 }
 
-TEST_F(DeviceTest, SetProperty) {
+// Note: there are currently no writeable Device properties that
+// aren't registered in a subclass.
+TEST_F(DeviceTest, SetReadOnlyProperty) {
   ::DBus::Error error;
-  EXPECT_TRUE(DBusAdaptor::SetProperty(device_->mutable_store(),
-                                       flimflam::kPoweredProperty,
-                                       PropertyStoreTest::kBoolV,
-                                       &error));
-
   // Ensure that an attempt to write a R/O property returns InvalidArgs error.
   EXPECT_FALSE(DBusAdaptor::SetProperty(device_->mutable_store(),
                                         flimflam::kAddressProperty,
@@ -148,22 +151,6 @@
   EXPECT_EQ(invalid_args(), error.name());
 }
 
-TEST_F(DeviceTest, ClearProperty) {
-  ::DBus::Error error;
-  EXPECT_TRUE(device_->powered());
-
-  EXPECT_TRUE(DBusAdaptor::SetProperty(device_->mutable_store(),
-                                       flimflam::kPoweredProperty,
-                                       PropertyStoreTest::kBoolV,
-                                       &error));
-  EXPECT_FALSE(device_->powered());
-
-  EXPECT_TRUE(DBusAdaptor::ClearProperty(device_->mutable_store(),
-                                         flimflam::kPoweredProperty,
-                                         &error));
-  EXPECT_TRUE(device_->powered());
-}
-
 TEST_F(DeviceTest, ClearReadOnlyProperty) {
   ::DBus::Error error;
   EXPECT_FALSE(DBusAdaptor::SetProperty(device_->mutable_store(),
@@ -311,10 +298,12 @@
 
 TEST_F(DeviceTest, Start) {
   EXPECT_CALL(routing_table_, FlushRoutes(kDeviceInterfaceIndex));
-  device_->Start();
+  device_->SetEnabled(true);
 }
 
 TEST_F(DeviceTest, Stop) {
+  device_->enabled_ = true;
+  device_->enabled_pending_ = true;
   device_->ipconfig_ = new IPConfig(&control_interface_, kDeviceName);
   scoped_refptr<MockService> service(
       new NiceMock<MockService>(&control_interface_,
@@ -327,8 +316,11 @@
       WillRepeatedly(Return(Service::kStateConnected));
   EXPECT_CALL(*dynamic_cast<DeviceMockAdaptor *>(device_->adaptor_.get()),
               UpdateEnabled());
+  EXPECT_CALL(*dynamic_cast<DeviceMockAdaptor *>(device_->adaptor_.get()),
+              EmitBoolChanged(flimflam::kPoweredProperty, false));
   EXPECT_CALL(rtnl_handler_, SetInterfaceFlags(_, 0, IFF_UP));
-  device_->Stop();
+  device_->SetEnabled(false);
+  device_->OnEnabledStateChanged(ResultCallback(), Error());
 
   EXPECT_FALSE(device_->ipconfig_.get());
   EXPECT_FALSE(device_->selected_service_.get());
diff --git a/dhcp_config.cc b/dhcp_config.cc
index 2511e2f..4053508 100644
--- a/dhcp_config.cc
+++ b/dhcp_config.cc
@@ -7,7 +7,6 @@
 #include <arpa/inet.h>
 #include <sys/wait.h>
 
-#include <base/bind.h>
 #include <base/file_util.h>
 #include <base/logging.h>
 #include <base/stringprintf.h>
@@ -20,7 +19,6 @@
 #include "shill/ip_address.h"
 #include "shill/proxy_factory.h"
 
-using base::Bind;
 using std::string;
 using std::vector;
 
@@ -114,15 +112,6 @@
 }
 
 void DHCPConfig::InitProxy(const string &service) {
-  // Defer proxy creation because dbus-c++ doesn't allow registration of new
-  // D-Bus objects in the context of a D-Bus signal handler.
-  if (!proxy_.get()) {
-    dispatcher_->PostTask(
-        Bind(&DHCPConfig::InitProxyTask, AsWeakPtr(), service));
-  }
-}
-
-void DHCPConfig::InitProxyTask(const string &service) {
   if (!proxy_.get()) {
     VLOG(2) << "Init DHCP Proxy: " << device_name() << " at " << service;
     proxy_.reset(proxy_factory_->CreateDHCPProxy(service));
diff --git a/dhcp_config.h b/dhcp_config.h
index d49600f..2afa200 100644
--- a/dhcp_config.h
+++ b/dhcp_config.h
@@ -7,7 +7,6 @@
 
 #include <base/file_path.h>
 #include <base/memory/scoped_ptr.h>
-#include <base/memory/weak_ptr.h>
 #include <dbus-c++/types.h>
 #include <glib.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
@@ -23,7 +22,7 @@
 class GLib;
 class ProxyFactory;
 
-class DHCPConfig : public IPConfig, public base::SupportsWeakPtr<DHCPConfig> {
+class DHCPConfig : public IPConfig {
  public:
   typedef std::map<std::string, DBus::Variant> Configuration;
 
@@ -93,10 +92,6 @@
 
   static const char kType[];
 
-  // If |proxy_| is not initialized already, sets it to a new D-Bus proxy to
-  // |service|.
-  void InitProxyTask(const std::string &service);
-
   // Starts dhcpcd, returns true on success and false otherwise.
   bool Start();
 
diff --git a/dhcp_config_unittest.cc b/dhcp_config_unittest.cc
index db6ca29..ed39b2c 100644
--- a/dhcp_config_unittest.cc
+++ b/dhcp_config_unittest.cc
@@ -83,11 +83,9 @@
 
 TEST_F(DHCPConfigTest, InitProxy) {
   static const char kService[] = ":1.200";
-  config_->InitProxy(kService);
-
   EXPECT_TRUE(proxy_.get());
   EXPECT_FALSE(config_->proxy_.get());
-  dispatcher()->DispatchPendingEvents();
+  config_->InitProxy(kService);
   EXPECT_FALSE(proxy_.get());
   EXPECT_TRUE(config_->proxy_.get());
 
diff --git a/dns_client.cc b/dns_client.cc
index cbed636..b357e02 100644
--- a/dns_client.cc
+++ b/dns_client.cc
@@ -67,8 +67,10 @@
       running_(false),
       resolver_state_(NULL),
       weak_ptr_factory_(this),
-      read_callback_(Bind(&DNSClient::HandleDNSRead, Unretained(this))),
-      write_callback_(Bind(&DNSClient::HandleDNSWrite, Unretained(this))),
+      read_callback_(Bind(&DNSClient::HandleDNSRead,
+                          weak_ptr_factory_.GetWeakPtr())),
+      write_callback_(Bind(&DNSClient::HandleDNSWrite,
+                           weak_ptr_factory_.GetWeakPtr())),
       ares_(Ares::GetInstance()),
       time_(Time::GetInstance()) {}
 
diff --git a/dns_client_unittest.cc b/dns_client_unittest.cc
index 6a15c2d..5097756 100644
--- a/dns_client_unittest.cc
+++ b/dns_client_unittest.cc
@@ -205,7 +205,8 @@
   void ExpectReset() {
     EXPECT_TRUE(dns_client_->address_.family() == IPAddress::kFamilyIPv4);
     EXPECT_TRUE(dns_client_->address_.IsDefault());
-    EXPECT_FALSE(dns_client_->weak_ptr_factory_.HasWeakPtrs());
+    EXPECT_FALSE(dns_client_->read_callback_.is_null());
+    EXPECT_FALSE(dns_client_->write_callback_.is_null());
     EXPECT_FALSE(dns_client_->resolver_state_.get());
   }
 
diff --git a/error.cc b/error.cc
index a61f736..23aa4ff 100644
--- a/error.cc
+++ b/error.cc
@@ -20,6 +20,7 @@
   { "Failure", "Operation failed (no other information)" },
   { "AlreadyConnected", "Already connected" },
   { "AlreadyExists", "Already exists" },
+  { "OperationInitiated", "Operation initiated" },
   { "InProgress", "In progress" },
   { "InternalError", "Internal error" },
   { "InvalidArguments", "Invalid arguments" },
diff --git a/error.h b/error.h
index ea387ff..001be8a 100644
--- a/error.h
+++ b/error.h
@@ -22,6 +22,7 @@
     kOperationFailed,  // failure, otherwise unspecified
     kAlreadyConnected,
     kAlreadyExists,
+    kOperationInitiated,
     kInProgress,
     kInternalError,
     kInvalidArguments,
@@ -65,7 +66,8 @@
   const std::string &message() const { return message_; }
 
   bool IsSuccess() const { return type_ == kSuccess; }
-  bool IsFailure() const { return !IsSuccess(); }
+  bool IsFailure() const { return !IsSuccess() && !IsOngoing(); }
+  bool IsOngoing() const { return type_ == kOperationInitiated; }
 
   static std::string GetName(Type type);
   static std::string GetDefaultMessage(Type type);
diff --git a/ethernet.cc b/ethernet.cc
index d636960..903fa4b 100644
--- a/ethernet.cc
+++ b/ethernet.cc
@@ -13,6 +13,7 @@
 
 #include <base/logging.h>
 
+#include "shill/adaptor_interfaces.h"
 #include "shill/control_interface.h"
 #include "shill/device.h"
 #include "shill/device_info.h"
@@ -47,26 +48,31 @@
 }
 
 Ethernet::~Ethernet() {
-  Stop();
+  Stop(NULL, EnabledStateChangedCallback());
 }
 
-void Ethernet::Start() {
+void Ethernet::Start(Error *error,
+                     const EnabledStateChangedCallback &callback) {
   service_ = new EthernetService(control_interface(),
                                  dispatcher(),
                                  metrics(),
                                  manager(),
                                  this);
-  Device::Start();
   RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
                                                 IFF_UP);
+  OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
+  if (error)
+    error->Reset();       // indicate immediate completion
 }
 
-void Ethernet::Stop() {
+void Ethernet::Stop(Error *error, const EnabledStateChangedCallback &callback) {
   if (service_) {
     manager()->DeregisterService(service_);
     service_ = NULL;
   }
-  Device::Stop();
+  OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
+  if (error)
+    error->Reset();       // indicate immediate completion
 }
 
 bool Ethernet::TechnologyIs(const Technology::Identifier type) const {
@@ -78,13 +84,15 @@
   if ((flags & IFF_LOWER_UP) != 0 && !link_up_) {
     LOG(INFO) << link_name() << " is up; should start L3!";
     link_up_ = true;
-    manager()->RegisterService(service_);
-    if (service_->auto_connect()) {
-      if (AcquireIPConfig()) {
-        SelectService(service_);
-        SetServiceState(Service::kStateConfiguring);
-      } else {
-        LOG(ERROR) << "Unable to acquire DHCP config.";
+    if (service_) {
+      manager()->RegisterService(service_);
+      if (service_->auto_connect()) {
+        if (AcquireIPConfig()) {
+          SelectService(service_);
+          SetServiceState(Service::kStateConfiguring);
+        } else {
+          LOG(ERROR) << "Unable to acquire DHCP config.";
+        }
       }
     }
   } else if ((flags & IFF_LOWER_UP) == 0 && link_up_) {
diff --git a/ethernet.h b/ethernet.h
index e50a28d..2652125 100644
--- a/ethernet.h
+++ b/ethernet.h
@@ -24,8 +24,8 @@
            int interface_index);
   ~Ethernet();
 
-  void Start();
-  void Stop();
+  void Start(Error *error, const EnabledStateChangedCallback &callback);
+  void Stop(Error *error, const EnabledStateChangedCallback &callback);
   bool TechnologyIs(Technology::Identifier type) const;
   void LinkEvent(unsigned int flags, unsigned int change);
 
diff --git a/http_proxy.cc b/http_proxy.cc
index 5aa1f9e..3d2fd0c 100644
--- a/http_proxy.cc
+++ b/http_proxy.cc
@@ -29,7 +29,6 @@
 
 using base::Bind;
 using base::StringPrintf;
-using base::Unretained;
 using std::string;
 using std::vector;
 
@@ -56,14 +55,20 @@
     : state_(kStateIdle),
       connection_(connection),
       weak_ptr_factory_(this),
-      accept_callback_(Bind(&HTTPProxy::AcceptClient, Unretained(this))),
+      accept_callback_(Bind(&HTTPProxy::AcceptClient,
+                            weak_ptr_factory_.GetWeakPtr())),
       connect_completion_callback_(Bind(&HTTPProxy::OnConnectCompletion,
-                                        Unretained(this))),
-      dns_client_callback_(Bind(&HTTPProxy::GetDNSResult, Unretained(this))),
-      read_client_callback_(Bind(&HTTPProxy::ReadFromClient, Unretained(this))),
-      read_server_callback_(Bind(&HTTPProxy::ReadFromServer, Unretained(this))),
-      write_client_callback_(Bind(&HTTPProxy::WriteToClient, Unretained(this))),
-      write_server_callback_(Bind(&HTTPProxy::WriteToServer, Unretained(this))),
+                                        weak_ptr_factory_.GetWeakPtr())),
+      dns_client_callback_(Bind(&HTTPProxy::GetDNSResult,
+                                weak_ptr_factory_.GetWeakPtr())),
+      read_client_callback_(Bind(&HTTPProxy::ReadFromClient,
+                                 weak_ptr_factory_.GetWeakPtr())),
+      read_server_callback_(Bind(&HTTPProxy::ReadFromServer,
+                                 weak_ptr_factory_.GetWeakPtr())),
+      write_client_callback_(Bind(&HTTPProxy::WriteToClient,
+                                  weak_ptr_factory_.GetWeakPtr())),
+      write_server_callback_(Bind(&HTTPProxy::WriteToServer,
+                                  weak_ptr_factory_.GetWeakPtr())),
       dispatcher_(NULL),
       dns_client_(NULL),
       proxy_port_(-1),
diff --git a/http_request.cc b/http_request.cc
index 3e3f798..b718831 100644
--- a/http_request.cc
+++ b/http_request.cc
@@ -22,7 +22,6 @@
 
 using base::Bind;
 using base::Callback;
-using base::Unretained;
 using base::StringPrintf;
 using std::string;
 
@@ -45,12 +44,14 @@
       sockets_(sockets),
       weak_ptr_factory_(this),
       connect_completion_callback_(
-          Bind(&HTTPRequest::OnConnectCompletion, Unretained(this))),
-      dns_client_callback_(Bind(&HTTPRequest::GetDNSResult, Unretained(this))),
+          Bind(&HTTPRequest::OnConnectCompletion,
+               weak_ptr_factory_.GetWeakPtr())),
+      dns_client_callback_(Bind(&HTTPRequest::GetDNSResult,
+                                weak_ptr_factory_.GetWeakPtr())),
       read_server_callback_(Bind(&HTTPRequest::ReadFromServer,
-                                 Unretained(this))),
+                                 weak_ptr_factory_.GetWeakPtr())),
       write_server_callback_(Bind(&HTTPRequest::WriteToServer,
-                                  Unretained(this))),
+                                  weak_ptr_factory_.GetWeakPtr())),
       dns_client_(
           new DNSClient(IPAddress::kFamilyIPv4,
                         connection->interface_name(),
diff --git a/http_request_unittest.cc b/http_request_unittest.cc
index 6e8338e..0625367 100644
--- a/http_request_unittest.cc
+++ b/http_request_unittest.cc
@@ -153,7 +153,10 @@
     EXPECT_EQ(&sockets_, request_->sockets_);
     EXPECT_TRUE(request_->result_callback_.is_null());
     EXPECT_TRUE(request_->read_event_callback_.is_null());
-    EXPECT_FALSE(request_->weak_ptr_factory_.HasWeakPtrs());
+    EXPECT_FALSE(request_->connect_completion_callback_.is_null());
+    EXPECT_FALSE(request_->dns_client_callback_.is_null());
+    EXPECT_FALSE(request_->read_server_callback_.is_null());
+    EXPECT_FALSE(request_->write_server_callback_.is_null());
     EXPECT_FALSE(request_->read_server_handler_.get());
     EXPECT_FALSE(request_->write_server_handler_.get());
     EXPECT_EQ(dns_client_, request_->dns_client_.get());
diff --git a/manager.cc b/manager.cc
index 36aa099..ab33206 100644
--- a/manager.cc
+++ b/manager.cc
@@ -408,6 +408,12 @@
           ActiveProfile().get() == profile.get());
 }
 
+void Manager::SaveActiveProfile() {
+  if (!profiles_.empty()) {
+    ActiveProfile()->Save();
+  }
+}
+
 bool Manager::MoveServiceToProfile(const ServiceRefPtr &to_move,
                                    const ProfileRefPtr &destination) {
   const ProfileRefPtr from = to_move->profile();
@@ -442,7 +448,56 @@
                         "Unknown Profile requested for Service");
 }
 
+void Manager::EnableTechnology(const std::string &technology_name,
+                               Error *error,
+                               const ResultCallback &callback) {
+  Technology::Identifier id = Technology::IdentifierFromName(technology_name);
+  if (id == Technology::kUnknown) {
+    error->Populate(Error::kInvalidArguments, "Unknown technology");
+    return;
+  }
+  for (vector<DeviceRefPtr>::iterator it = devices_.begin();
+       it != devices_.end(); ++it) {
+    DeviceRefPtr device = *it;
+    if (device->technology() == id && !device->enabled()) {
+      device->SetEnabledPersistent(true, error, callback);
+      // Continue with other devices even if one fails
+      // TODO(ers): Decide whether an error should be returned
+      // for the overall EnableTechnology operation if some
+      // devices succeed and some fail.
+    }
+  }
+}
+
+void Manager::DisableTechnology(const std::string &technology_name,
+                                Error *error,
+                                const ResultCallback &callback) {
+  Technology::Identifier id = Technology::IdentifierFromName(technology_name);
+  if (id == Technology::kUnknown) {
+    error->Populate(Error::kInvalidArguments, "Unknown technology");
+    return;
+  }
+  for (vector<DeviceRefPtr>::iterator it = devices_.begin();
+       it != devices_.end(); ++it) {
+    DeviceRefPtr device = *it;
+    if (device->technology() == id && device->enabled()) {
+      device->SetEnabledPersistent(false, error, callback);
+      // Continue with other devices even if one fails
+      // TODO(ers): Decide whether an error should be returned
+      // for the overall DisableTechnology operation if some
+      // devices succeed and some fail.
+    }
+  }
+}
+
+void Manager::UpdateEnabledTechnologies() {
+  Error error;
+  adaptor_->EmitStringsChanged(flimflam::kEnabledTechnologiesProperty,
+                               EnabledTechnologies(&error));
+}
+
 void Manager::RegisterDevice(const DeviceRefPtr &to_manage) {
+  VLOG(2) << __func__ << "(" << to_manage->FriendlyName() << ")";
   vector<DeviceRefPtr>::iterator it;
   for (it = devices_.begin(); it != devices_.end(); ++it) {
     if (to_manage.get() == it->get())
@@ -467,8 +522,8 @@
 
   // In normal usage, running_ will always be true when we are here, however
   // unit tests sometimes do things in otherwise invalid states.
-  if (running_ && to_manage->powered())
-    to_manage->Start();
+  if (running_ && to_manage->enabled_persistent())
+    to_manage->SetEnabled(true);
 
   Error error;
   adaptor_->EmitStringsChanged(flimflam::kAvailableTechnologiesProperty,
@@ -478,11 +533,12 @@
 }
 
 void Manager::DeregisterDevice(const DeviceRefPtr &to_forget) {
+  VLOG(2) << __func__ << "(" << to_forget->FriendlyName() << ")";
   vector<DeviceRefPtr>::iterator it;
   for (it = devices_.begin(); it != devices_.end(); ++it) {
     if (to_forget.get() == it->get()) {
       VLOG(2) << "Deregistered device: " << to_forget->UniqueName();
-      to_forget->Stop();
+      to_forget->SetEnabled(false);
       devices_.erase(it);
       Error error;
       adaptor_->EmitStringsChanged(flimflam::kAvailableTechnologiesProperty,
@@ -742,10 +798,15 @@
       services_[0]->GetTechnologyString(error) : "";
 }
 
-vector<string> Manager::EnabledTechnologies(Error *error) {
-  // TODO(gauravsh): This must be wired up to the RPC interface to handle
-  // enabled/disabled devices as set by the user. crosbug.com/23319
-  return AvailableTechnologies(error);
+vector<string> Manager::EnabledTechnologies(Error */*error*/) {
+  set<string> unique_technologies;
+  for (vector<DeviceRefPtr>::iterator it = devices_.begin();
+       it != devices_.end(); ++it) {
+    if ((*it)->enabled())
+      unique_technologies.insert(
+          Technology::NameFromIdentifier((*it)->technology()));
+  }
+  return vector<string>(unique_technologies.begin(), unique_technologies.end());
 }
 
 vector<string> Manager::EnumerateDevices(Error */*error*/) {
diff --git a/manager.h b/manager.h
index c1e2491..d47d894 100644
--- a/manager.h
+++ b/manager.h
@@ -63,6 +63,7 @@
 
   const ProfileRefPtr &ActiveProfile() const;
   bool IsActiveProfile(const ProfileRefPtr &profile) const;
+  virtual void SaveActiveProfile();
   bool MoveServiceToProfile(const ServiceRefPtr &to_move,
                             const ProfileRefPtr &destination);
 
@@ -132,6 +133,14 @@
   // If there is no such connection, this function returns a reference to NULL.
   virtual ServiceRefPtr GetDefaultService() const;
 
+  // Enable all devices for the named technology.
+  void EnableTechnology(const std::string &technology_name,
+                        Error *error,
+                        const ResultCallback &callback);
+  // Disable all devices for the named technology.
+  void DisableTechnology(const std::string &technology_name,
+                         Error *error,
+                         const ResultCallback &callback);
   // Return whether a technology is marked as enabled for portal detection.
   virtual bool IsPortalDetectionEnabled(Technology::Identifier tech);
 
@@ -158,6 +167,7 @@
   }
   virtual const std::string &GetHostName() { return props_.host_name; }
 
+  virtual void UpdateEnabledTechnologies();
   PowerManager *power_manager() const { return power_manager_.get(); }
 
  private:
diff --git a/manager_dbus_adaptor.cc b/manager_dbus_adaptor.cc
index 75127d6..57749f6 100644
--- a/manager_dbus_adaptor.cc
+++ b/manager_dbus_adaptor.cc
@@ -146,12 +146,26 @@
   e.ToDBusError(&error);
 }
 
-void ManagerDBusAdaptor::EnableTechnology(const string &,
-                                          ::DBus::Error &/*error*/) {
+void ManagerDBusAdaptor::EnableTechnology(const string &technology_name,
+                                          ::DBus::Error &error) {
+  Error e(Error::kOperationInitiated);
+  DBus::Tag *tag = new DBus::Tag();
+  manager_->EnableTechnology(technology_name, &e, GetMethodReplyCallback(tag));
+  ReturnResultOrDefer(tag, e, &error);
+  // TODO(ers): A reply will be sent to the client as soon as the first
+  // device of a given technology has finished being enabled. It would
+  // seem arguably more correct to wait until all the devices were enabled.
 }
 
-void ManagerDBusAdaptor::DisableTechnology(const string &,
-                                           ::DBus::Error &/*error*/) {
+void ManagerDBusAdaptor::DisableTechnology(const string &technology_name,
+                                           ::DBus::Error &error) {
+  Error e(Error::kOperationInitiated);
+  DBus::Tag *tag = new DBus::Tag();
+  manager_->DisableTechnology(technology_name, &e, GetMethodReplyCallback(tag));
+  ReturnResultOrDefer(tag, e, &error);
+  // TODO(ers): A reply will be sent to the client as soon as the first
+  // device of a given technology has finished being disabled. It would
+  // seem arguably more correct to wait until all the devices were enabled.
 }
 
 // Called, e.g., to get WiFiService handle for a hidden SSID.
diff --git a/manager_unittest.cc b/manager_unittest.cc
index 32a16a5..8d35b2e 100644
--- a/manager_unittest.cc
+++ b/manager_unittest.cc
@@ -227,11 +227,11 @@
 
 TEST_F(ManagerTest, DeviceRegistrationAndStart) {
   manager()->running_ = true;
-  mock_devices_[0]->powered_ = true;
-  mock_devices_[1]->powered_ = false;
-  EXPECT_CALL(*mock_devices_[0].get(), Start())
+  mock_devices_[0]->enabled_persistent_ = true;
+  mock_devices_[1]->enabled_persistent_ = false;
+  EXPECT_CALL(*mock_devices_[0].get(), SetEnabled(true))
       .Times(1);
-  EXPECT_CALL(*mock_devices_[1].get(), Start())
+  EXPECT_CALL(*mock_devices_[1].get(), SetEnabled(_))
       .Times(0);
   manager()->RegisterDevice(mock_devices_[0]);
   manager()->RegisterDevice(mock_devices_[1]);
@@ -258,11 +258,11 @@
   ASSERT_TRUE(IsDeviceRegistered(mock_devices_[0], Technology::kEthernet));
   ASSERT_TRUE(IsDeviceRegistered(mock_devices_[1], Technology::kWifi));
 
-  EXPECT_CALL(*mock_devices_[0].get(), Stop());
+  EXPECT_CALL(*mock_devices_[0].get(), SetEnabled(false));
   manager()->DeregisterDevice(mock_devices_[0]);
   EXPECT_FALSE(IsDeviceRegistered(mock_devices_[0], Technology::kEthernet));
 
-  EXPECT_CALL(*mock_devices_[1].get(), Stop());
+  EXPECT_CALL(*mock_devices_[1].get(), SetEnabled(false));
   manager()->DeregisterDevice(mock_devices_[1]);
   EXPECT_FALSE(IsDeviceRegistered(mock_devices_[1], Technology::kWifi));
 }
diff --git a/mm1_modem_modem3gpp_proxy.cc b/mm1_modem_modem3gpp_proxy.cc
index 966bd97..85ac8e6 100644
--- a/mm1_modem_modem3gpp_proxy.cc
+++ b/mm1_modem_modem3gpp_proxy.cc
@@ -14,22 +14,39 @@
 namespace mm1 {
 
 ModemModem3gppProxy::ModemModem3gppProxy(
-    ModemModem3gppProxyDelegate *delegate,
     DBus::Connection *connection,
     const string &path,
     const string &service)
-    : proxy_(delegate, connection, path, service) {}
+    : proxy_(connection, path, service) {}
 
 ModemModem3gppProxy::~ModemModem3gppProxy() {}
 
 void ModemModem3gppProxy::Register(const std::string &operator_id,
-                                   AsyncCallHandler *call_handler,
+                                   Error *error,
+                                   const ResultCallback &callback,
                                    int timeout) {
-  proxy_.Register(operator_id, call_handler, timeout);
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.Register(operator_id, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-void ModemModem3gppProxy::Scan(AsyncCallHandler *call_handler, int timeout) {
-  proxy_.Scan(call_handler, timeout);
+void ModemModem3gppProxy::Scan(Error *error,
+                               const DBusPropertyMapsCallback &callback,
+                               int timeout) {
+  scoped_ptr<DBusPropertyMapsCallback> cb(
+      new DBusPropertyMapsCallback(callback));
+  try {
+    proxy_.Scan(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 // Inherited properties from ModemModem3gppProxyInterface.
@@ -50,12 +67,10 @@
 };
 
 // ModemModem3gppProxy::Proxy
-ModemModem3gppProxy::Proxy::Proxy(ModemModem3gppProxyDelegate *delegate,
-                                  DBus::Connection *connection,
+ModemModem3gppProxy::Proxy::Proxy(DBus::Connection *connection,
                                   const std::string &path,
                                   const std::string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()),
-      delegate_(delegate) {}
+    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
 
 ModemModem3gppProxy::Proxy::~Proxy() {}
 
@@ -63,19 +78,20 @@
 // org::freedesktop::ModemManager1::Modem::ModemModem3gppProxy
 void ModemModem3gppProxy::Proxy::RegisterCallback(const ::DBus::Error& dberror,
                                                   void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnRegisterCallback(error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 void ModemModem3gppProxy::Proxy::ScanCallback(
     const std::vector<DBusPropertiesMap> &results,
     const ::DBus::Error& dberror, void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<DBusPropertyMapsCallback> callback(
+      reinterpret_cast<DBusPropertyMapsCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnScanCallback(results, error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(results, error);
 }
 
 }  // namespace mm1
diff --git a/mm1_modem_modem3gpp_proxy.h b/mm1_modem_modem3gpp_proxy.h
index 2cd72e5..17c015f 100644
--- a/mm1_modem_modem3gpp_proxy.h
+++ b/mm1_modem_modem3gpp_proxy.h
@@ -17,18 +17,19 @@
 class ModemModem3gppProxy : public ModemModem3gppProxyInterface {
  public:
   // Constructs a org.freedesktop.ModemManager1.Modem.Modem3gpp DBus
-  // object proxy at |path| owned by |service|. Caught signals and
-  // asynchronous method replies will be dispatched to |delegate|.
-  ModemModem3gppProxy(ModemModem3gppProxyDelegate *delegate,
-                      DBus::Connection *connection,
+  // object proxy at |path| owned by |service|.
+  ModemModem3gppProxy(DBus::Connection *connection,
                       const std::string &path,
                       const std::string &service);
   virtual ~ModemModem3gppProxy();
   // Inherited methods from ModemModem3gppProxyInterface.
   virtual void Register(const std::string &operator_id,
-                        AsyncCallHandler *call_handler,
+                        Error *error,
+                        const ResultCallback &callback,
                         int timeout);
-  virtual void Scan(AsyncCallHandler *call_handler, int timeout);
+  virtual void Scan(Error *error,
+                    const DBusPropertyMapsCallback &callback,
+                    int timeout);
 
   // Inherited properties from Modem3gppProxyInterface.
   virtual std::string Imei();
@@ -41,8 +42,7 @@
   class Proxy : public org::freedesktop::ModemManager1::Modem::Modem3gpp_proxy,
                 public DBus::ObjectProxy {
    public:
-    Proxy(ModemModem3gppProxyDelegate *delegate,
-          DBus::Connection *connection,
+    Proxy(DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
@@ -54,7 +54,6 @@
     virtual void ScanCallback(
         const std::vector<DBusPropertiesMap> &results,
         const ::DBus::Error& dberror, void *data);
-    ModemModem3gppProxyDelegate *delegate_;
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
   };
diff --git a/mm1_modem_modem3gpp_proxy_interface.h b/mm1_modem_modem3gpp_proxy_interface.h
index 3e875ea..c3a0b22 100644
--- a/mm1_modem_modem3gpp_proxy_interface.h
+++ b/mm1_modem_modem3gpp_proxy_interface.h
@@ -9,28 +9,29 @@
 
 #include <base/basictypes.h>
 
-#include "shill/dbus_properties.h"
+#include "shill/callbacks.h"
 
 namespace shill {
-class AsyncCallHandler;
 class Error;
 
 namespace mm1 {
 
 // These are the methods that a
 // org.freedesktop.ModemManager1.Modem.Modem3gpp 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.
+// All calls are made asynchronously. Call completion is signalled via
+// the callbacks passed to the methods.
 class ModemModem3gppProxyInterface {
  public:
   virtual ~ModemModem3gppProxyInterface() {}
 
   virtual void Register(const std::string &operator_id,
-                        AsyncCallHandler *call_handler,
+                        Error *error,
+                        const ResultCallback &callback,
                         int timeout) = 0;
-  virtual void Scan(AsyncCallHandler *call_handler, int timeout) = 0;
+  virtual void Scan(Error *error,
+                    const DBusPropertyMapsCallback &callback,
+                    int timeout) = 0;
 
   // Properties.
   virtual std::string Imei() = 0;
@@ -40,21 +41,6 @@
   virtual uint32_t EnabledFacilityLocks() = 0;
 };
 
-// ModemManager1.Modem.Modem3gpp signal delegate to be associated with
-// the proxy.
-class ModemModem3gppProxyDelegate {
- public:
-  virtual ~ModemModem3gppProxyDelegate() {}
-
-  // Handle async callbacks
-  virtual void OnRegisterCallback(const Error &error,
-                                  AsyncCallHandler *call_handler) = 0;
-  virtual void OnScanCallback(
-      const std::vector<DBusPropertiesMap> &results,
-      const Error &error,
-      AsyncCallHandler *call_handler) = 0;
-};
-
 }  // namespace mm1
 }  // namespace shill
 
diff --git a/mm1_modem_modemcdma_proxy.cc b/mm1_modem_modemcdma_proxy.cc
index ecc82f3..d3046cd 100644
--- a/mm1_modem_modemcdma_proxy.cc
+++ b/mm1_modem_modemcdma_proxy.cc
@@ -13,25 +13,45 @@
 namespace shill {
 namespace mm1 {
 
-ModemModemCdmaProxy::ModemModemCdmaProxy(ModemModemCdmaProxyDelegate *delegate,
-                                         DBus::Connection *connection,
+ModemModemCdmaProxy::ModemModemCdmaProxy(DBus::Connection *connection,
                                          const string &path,
                                          const string &service)
-    : proxy_(delegate, connection, path, service) {}
+    : proxy_(connection, path, service) {}
 
 ModemModemCdmaProxy::~ModemModemCdmaProxy() {}
 
 void ModemModemCdmaProxy::Activate(const std::string &carrier,
-                                   AsyncCallHandler *call_handler,
-                                   int timeout) {
-  proxy_.Activate(carrier, call_handler, timeout);
+                        Error *error,
+                        const ResultCallback &callback,
+                        int timeout) {
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.Activate(carrier, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemModemCdmaProxy::ActivateManual(
     const DBusPropertiesMap &properties,
-    AsyncCallHandler *call_handler,
+    Error *error,
+    const ResultCallback &callback,
     int timeout) {
-  proxy_.ActivateManual(properties, call_handler, timeout);
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.ActivateManual(properties, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
+}
+
+void ModemModemCdmaProxy::set_activation_state_callback(
+    const ActivationStateSignalCallback &callback) {
+  proxy_.set_activation_state_callback(callback);
 }
 
 // Inherited properties from ModemModemCdmaProxyInterface.
@@ -55,42 +75,45 @@
 };
 
 // ModemModemCdmaProxy::Proxy
-ModemModemCdmaProxy::Proxy::Proxy(ModemModemCdmaProxyDelegate *delegate,
-                                  DBus::Connection *connection,
+ModemModemCdmaProxy::Proxy::Proxy(DBus::Connection *connection,
                                   const std::string &path,
                                   const std::string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()),
-      delegate_(delegate) {}
+    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
 
 ModemModemCdmaProxy::Proxy::~Proxy() {}
 
+void ModemModemCdmaProxy::Proxy::set_activation_state_callback(
+    const ActivationStateSignalCallback &callback) {
+  activation_state_callback_ = callback;
+}
+
 // Signal callbacks inherited from Proxy
 void ModemModemCdmaProxy::Proxy::ActivationStateChanged(
     const uint32_t &activation_state,
     const uint32_t &activation_error,
     const DBusPropertiesMap &status_changes) {
-  delegate_->OnActivationStateChanged(activation_state,
-                                      activation_error,
-                                      status_changes);
+  activation_state_callback_.Run(activation_state,
+                                 activation_error,
+                                 status_changes);
 }
 
 // Method callbacks inherited from
 // org::freedesktop::ModemManager1::Modem::ModemModemCdmaProxy
 void ModemModemCdmaProxy::Proxy::ActivateCallback(const ::DBus::Error& dberror,
                                                   void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnActivateCallback(error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 void ModemModemCdmaProxy::Proxy::ActivateManualCallback(
     const ::DBus::Error& dberror,
     void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnActivateManualCallback(error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 }  // namespace mm1
diff --git a/mm1_modem_modemcdma_proxy.h b/mm1_modem_modemcdma_proxy.h
index e4dadb7..9cc03bd 100644
--- a/mm1_modem_modemcdma_proxy.h
+++ b/mm1_modem_modemcdma_proxy.h
@@ -12,29 +12,31 @@
 #include "shill/mm1_modem_modemcdma_proxy_interface.h"
 
 namespace shill {
-class AsyncCallHandler;
 namespace mm1 {
 
 class ModemModemCdmaProxy : public ModemModemCdmaProxyInterface {
  public:
   // Constructs a org.freedesktop.ModemManager1.Modem.ModemCdma DBus
-  // object proxy at |path| owned by |service|. Caught signals and
-  // asynchronous method replies will be dispatched to |delegate|.
-  ModemModemCdmaProxy(ModemModemCdmaProxyDelegate *delegate,
-                      DBus::Connection *connection,
+  // object proxy at |path| owned by |service|.
+  ModemModemCdmaProxy(DBus::Connection *connection,
                       const std::string &path,
                       const std::string &service);
   virtual ~ModemModemCdmaProxy();
 
   // Inherited methods from ModemModemCdmaProxyInterface.
   virtual void Activate(const std::string &carrier,
-                        AsyncCallHandler *call_handler,
+                        Error *error,
+                        const ResultCallback &callback,
                         int timeout);
   virtual void ActivateManual(
       const DBusPropertiesMap &properties,
-      AsyncCallHandler *call_handler,
+      Error *error,
+      const ResultCallback &callback,
       int timeout);
 
+  virtual void set_activation_state_callback(
+      const ActivationStateSignalCallback &callback);
+
   // Inherited properties from ModemCdmaProxyInterface.
   virtual std::string Meid();
   virtual std::string Esn();
@@ -47,12 +49,14 @@
   class Proxy : public org::freedesktop::ModemManager1::Modem::ModemCdma_proxy,
                 public DBus::ObjectProxy {
    public:
-    Proxy(ModemModemCdmaProxyDelegate *delegate,
-          DBus::Connection *connection,
+    Proxy(DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
 
+    virtual void set_activation_state_callback(
+        const ActivationStateSignalCallback &callback);
+
    private:
     // Signal callbacks inherited from Proxy
     // handle signals
@@ -66,7 +70,7 @@
     virtual void ActivateCallback(const ::DBus::Error& dberror, void *data);
     virtual void ActivateManualCallback(const ::DBus::Error& dberror,
                                         void *data);
-    ModemModemCdmaProxyDelegate *delegate_;
+    ActivationStateSignalCallback activation_state_callback_;
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
   };
diff --git a/mm1_modem_modemcdma_proxy_interface.h b/mm1_modem_modemcdma_proxy_interface.h
index ee18506..7ca0aed 100644
--- a/mm1_modem_modemcdma_proxy_interface.h
+++ b/mm1_modem_modemcdma_proxy_interface.h
@@ -9,10 +9,9 @@
 
 #include <base/basictypes.h>
 
-#include "shill/dbus_properties.h"
+#include "shill/callbacks.h"
 
 namespace shill {
-class AsyncCallHandler;
 class Error;
 
 namespace mm1 {
@@ -20,20 +19,24 @@
 // These are the methods that a
 // org.freedesktop.ModemManager1.Modem.ModemCdma 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.
+// calls are made asynchronously. Call completion is signalled via
+// the callbacks passed to the methods.
 class ModemModemCdmaProxyInterface {
  public:
   virtual ~ModemModemCdmaProxyInterface() {}
 
   virtual void Activate(const std::string &carrier,
-                        AsyncCallHandler *call_handler,
+                        Error *error,
+                        const ResultCallback &callback,
                         int timeout) = 0;
   virtual void ActivateManual(
       const DBusPropertiesMap &properties,
-      AsyncCallHandler *call_handler, int timeout) = 0;
+      Error *error,
+      const ResultCallback &callback,
+      int timeout) = 0;
 
+  virtual void set_activation_state_callback(
+      const ActivationStateSignalCallback &callback) = 0;
 
   // Properties.
   virtual std::string Meid() = 0;
@@ -44,27 +47,6 @@
   virtual uint32_t EvdoRegistrationState() = 0;
 };
 
-
-// ModemManager1.Modem.ModemCdma signal delegate to be associated with
-// the proxy.
-class ModemModemCdmaProxyDelegate {
- public:
-  virtual ~ModemModemCdmaProxyDelegate() {}
-
-  // handle signals
-  virtual void OnActivationStateChanged(
-      const uint32_t &activation_state,
-      const uint32_t &activation_error,
-      const DBusPropertiesMap &status_changes) = 0;
-
-  // Handle async callbacks
-  virtual void OnActivateCallback(const Error &error,
-                                  AsyncCallHandler *call_handler) = 0;
-  virtual void OnActivateManualCallback(
-      const Error &error,
-      AsyncCallHandler *call_handler) = 0;
-};
-
 }  // namespace mm1
 }  // namespace shill
 
diff --git a/mm1_modem_proxy.cc b/mm1_modem_proxy.cc
index 1ff47bf..2cbaccd 100644
--- a/mm1_modem_proxy.cc
+++ b/mm1_modem_proxy.cc
@@ -13,66 +13,144 @@
 namespace shill {
 namespace mm1 {
 
-ModemProxy::ModemProxy(ModemProxyDelegate *delegate,
-                       DBus::Connection *connection,
+ModemProxy::ModemProxy(DBus::Connection *connection,
                        const string &path,
                        const string &service)
-    : proxy_(delegate, connection, path, service) {}
+    : proxy_(connection, path, service) {}
 
 ModemProxy::~ModemProxy() {}
 
-void ModemProxy::Enable(const bool &enable,
-                        AsyncCallHandler *call_handler,
-                        int timeout) {
-  proxy_.Enable(enable, call_handler, timeout);
+void ModemProxy::set_state_changed_callback(
+      const ModemStateChangedSignalCallback &callback) {
+  proxy_.set_state_changed_callback(callback);
 }
 
-void ModemProxy::ListBearers(AsyncCallHandler *call_handler,
+void ModemProxy::Enable(bool enable,
+                        Error *error,
+                        const ResultCallback &callback,
+                        int timeout) {
+  VLOG(2) << __func__ << "(" << enable << ", " << timeout << ")";
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.Enable(enable, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
+}
+
+void ModemProxy::ListBearers(Error *error,
+                             const DBusPathsCallback &callback,
                              int timeout) {
-  proxy_.ListBearers(call_handler, timeout);
+  scoped_ptr<DBusPathsCallback> cb(new DBusPathsCallback(callback));
+  try {
+    proxy_.ListBearers(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemProxy::CreateBearer(
     const DBusPropertiesMap &properties,
-    AsyncCallHandler *call_handler,
+    Error *error,
+    const DBusPathCallback &callback,
     int timeout) {
-  proxy_.CreateBearer(properties, call_handler, timeout);
+  scoped_ptr<DBusPathCallback> cb(new DBusPathCallback(callback));
+  try {
+  proxy_.CreateBearer(properties, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemProxy::DeleteBearer(const ::DBus::Path &bearer,
-                              AsyncCallHandler *call_handler,
+                              Error *error,
+                              const ResultCallback &callback,
                               int timeout) {
-  proxy_.DeleteBearer(bearer, call_handler, timeout);
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+  proxy_.DeleteBearer(bearer, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-void ModemProxy::Reset(AsyncCallHandler *call_handler, int timeout) {
-  proxy_.Reset(call_handler, timeout);
+void ModemProxy::Reset(Error *error,
+                       const ResultCallback &callback,
+                       int timeout) {
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+  proxy_.Reset(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemProxy::FactoryReset(const std::string &code,
-                              AsyncCallHandler *call_handler,
+                              Error *error,
+                              const ResultCallback &callback,
                               int timeout) {
-  proxy_.FactoryReset(code, call_handler, timeout);
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.FactoryReset(code, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemProxy::SetAllowedModes(const uint32_t &modes,
                                  const uint32_t &preferred,
-                                 AsyncCallHandler *call_handler,
+                                 Error *error,
+                                 const ResultCallback &callback,
                                  int timeout) {
-  proxy_.SetAllowedModes(modes, preferred, call_handler, timeout);
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.SetAllowedModes(modes, preferred, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemProxy::SetBands(const std::vector< uint32_t > &bands,
-                          AsyncCallHandler *call_handler,
+                          Error *error,
+                          const ResultCallback &callback,
                           int timeout) {
-  proxy_.SetBands(bands, call_handler, timeout);
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.SetBands(bands, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemProxy::Command(const std::string &cmd,
                          const uint32_t &user_timeout,
-                         AsyncCallHandler *call_handler,
+                         Error *error,
+                         const StringCallback &callback,
                          int timeout) {
-  proxy_.Command(cmd, user_timeout, call_handler, timeout);
+  scoped_ptr<StringCallback> cb(new StringCallback(callback));
+  try {
+    proxy_.Command(cmd, user_timeout, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 // Inherited properties from ModemProxyInterface.
@@ -146,99 +224,103 @@
   return proxy_.Bands();
 };
 
-ModemProxy::Proxy::Proxy(ModemProxyDelegate *delegate,
-                         DBus::Connection *connection,
+ModemProxy::Proxy::Proxy(DBus::Connection *connection,
                          const std::string &path,
                          const std::string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()),
-      delegate_(delegate) {}
+    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
 
 ModemProxy::Proxy::~Proxy() {}
 
+void ModemProxy::Proxy::set_state_changed_callback(
+      const ModemStateChangedSignalCallback &callback) {
+  state_changed_callback_ = callback;
+}
+
 // Signal callbacks inherited from Proxy
 void ModemProxy::Proxy::StateChanged(const uint32_t &old,
                                      const uint32_t &_new,
                                      const uint32_t &reason) {
-  delegate_->OnStateChanged(old, _new, reason);
+  state_changed_callback_.Run(old, _new, reason);
 }
 
 // Method callbacks inherited from
 // org::freedesktop::ModemManager1::ModemProxy
 void ModemProxy::Proxy::EnableCallback(const ::DBus::Error& dberror,
                                        void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnEnableCallback(error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 void ModemProxy::Proxy::ListBearersCallback(
     const std::vector< ::DBus::Path > &bearers,
     const ::DBus::Error& dberror,
     void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<DBusPathsCallback> callback(
+      reinterpret_cast<DBusPathsCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnListBearersCallback(bearers, error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(bearers, error);
 }
 
 void ModemProxy::Proxy::CreateBearerCallback(const ::DBus::Path &path,
                                              const ::DBus::Error& dberror,
                                              void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnCreateBearerCallback(path, error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 void ModemProxy::Proxy::DeleteBearerCallback(const ::DBus::Error& dberror,
                                              void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnDeleteBearerCallback(error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 void ModemProxy::Proxy::ResetCallback(const ::DBus::Error& dberror,
                                       void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnResetCallback(error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 void ModemProxy::Proxy::FactoryResetCallback(const ::DBus::Error& dberror,
                                              void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnFactoryResetCallback(error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 void ModemProxy::Proxy::SetAllowedModesCallback(
     const ::DBus::Error& dberror,
     void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnSetAllowedModesCallback(error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 void ModemProxy::Proxy::SetBandsCallback(const ::DBus::Error& dberror,
                                          void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnSetBandsCallback(error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 void ModemProxy::Proxy::CommandCallback(const std::string &response,
                                         const ::DBus::Error& dberror,
                                         void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnCommandCallback(response, error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 }  // namespace mm1
diff --git a/mm1_modem_proxy.h b/mm1_modem_proxy.h
index 3758423..f7da74d 100644
--- a/mm1_modem_proxy.h
+++ b/mm1_modem_proxy.h
@@ -12,48 +12,58 @@
 #include "shill/mm1_modem_proxy_interface.h"
 
 namespace shill {
-class AsyncCallHandler;
-
 namespace mm1 {
 
 class ModemProxy : public ModemProxyInterface {
  public:
   // Constructs a org.freedesktop.ModemManager1.Modem DBus object
-  // proxy at |path| owned by |service|. Caught signals and
-  // asynchronous method replies will be dispatched to |delegate|.
-  ModemProxy(ModemProxyDelegate *delegate,
-             DBus::Connection *connection,
+  // proxy at |path| owned by |service|.
+  ModemProxy(DBus::Connection *connection,
              const std::string &path,
              const std::string &service);
   virtual ~ModemProxy();
 
   // Inherited methods from ModemProxyInterface.
-  virtual void Enable(const bool &enable,
-                      AsyncCallHandler *call_handler,
+  virtual void Enable(bool enable,
+                      Error *error,
+                      const ResultCallback &callback,
                       int timeout);
-  virtual void ListBearers(AsyncCallHandler *call_handler, int timeout);
-  virtual void CreateBearer(
-      const DBusPropertiesMap &properties,
-      AsyncCallHandler *call_handler,
-      int timeout);
+  virtual void ListBearers(Error *error,
+                           const DBusPathsCallback &callback,
+                           int timeout);
+  virtual void CreateBearer(const DBusPropertiesMap &properties,
+                            Error *error,
+                            const DBusPathCallback &callback,
+                            int timeout);
   virtual void DeleteBearer(const ::DBus::Path &bearer,
-                            AsyncCallHandler *call_handler,
+                            Error *error,
+                            const ResultCallback &callback,
                             int timeout);
-  virtual void Reset(AsyncCallHandler *call_handler, int timeout);
+  virtual void Reset(Error *error,
+                     const ResultCallback &callback,
+                     int timeout);
   virtual void FactoryReset(const std::string &code,
-                            AsyncCallHandler *call_handler,
+                            Error *error,
+                            const ResultCallback &callback,
                             int timeout);
-  virtual void SetAllowedModes(const uint32_t &modes, const uint32_t &preferred,
-                               AsyncCallHandler *call_handler,
+  virtual void SetAllowedModes(const uint32_t &modes,
+                               const uint32_t &preferred,
+                               Error *error,
+                               const ResultCallback &callback,
                                int timeout);
   virtual void SetBands(const std::vector< uint32_t > &bands,
-                        AsyncCallHandler *call_handler,
+                        Error *error,
+                        const ResultCallback &callback,
                         int timeout);
   virtual void Command(const std::string &cmd,
                        const uint32_t &user_timeout,
-                       AsyncCallHandler *call_handler,
+                       Error *error,
+                       const StringCallback &callback,
                        int timeout);
 
+  virtual void set_state_changed_callback(
+      const ModemStateChangedSignalCallback &callback);
+
   // Inherited properties from ModemProxyInterface.
   virtual const ::DBus::Path Sim();
   virtual uint32_t ModemCapabilities();
@@ -83,12 +93,14 @@
   class Proxy : public org::freedesktop::ModemManager1::Modem_proxy,
                 public DBus::ObjectProxy {
    public:
-    Proxy(ModemProxyDelegate *delegate,
-          DBus::Connection *connection,
+    Proxy(DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
 
+    void set_state_changed_callback(
+        const ModemStateChangedSignalCallback &callback);
+
    private:
     // Signal callbacks inherited from Proxy
     // handle signals
@@ -114,7 +126,7 @@
                                  const ::DBus::Error &dberror,
                                  void *data);
 
-    ModemProxyDelegate *delegate_;
+    ModemStateChangedSignalCallback state_changed_callback_;
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
   };
diff --git a/mm1_modem_proxy_interface.h b/mm1_modem_proxy_interface.h
index c12717a..479abda 100644
--- a/mm1_modem_proxy_interface.h
+++ b/mm1_modem_proxy_interface.h
@@ -9,51 +9,65 @@
 
 #include <base/basictypes.h>
 
-#include "shill/dbus_properties.h"
+#include "shill/callbacks.h"
 
 namespace shill {
-class AsyncCallHandler;
 class Error;
 
 namespace mm1 {
 
+typedef base::Callback<void(uint32,
+                            uint32, uint32)> ModemStateChangedSignalCallback;
+
 // These are the methods that a org.freedesktop.ModemManager1.Modem
-// 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.
+// proxy must support. The interface is provided so that it can be
+// mocked in tests. All calls are made asynchronously. Call completion
+// is signalled via the callbacks passed to the methods.
 class ModemProxyInterface {
  public:
   virtual ~ModemProxyInterface() {}
 
-  virtual void Enable(const bool &enable,
-                      AsyncCallHandler *call_handler,
+  virtual void Enable(bool enable,
+                      Error *error,
+                      const ResultCallback &callback,
                       int timeout) = 0;
-  virtual void ListBearers(AsyncCallHandler *call_handler,
+  virtual void ListBearers(Error *error,
+                           const DBusPathsCallback &callback,
                            int timeout) = 0;
-  virtual void CreateBearer(
-      const DBusPropertiesMap &properties,
-      AsyncCallHandler *call_handler,
-      int timeout) = 0;
-  virtual void DeleteBearer(const ::DBus::Path &bearer,
-                            AsyncCallHandler *call_handler,
+  virtual void CreateBearer(const DBusPropertiesMap &properties,
+                            Error *error,
+                            const DBusPathCallback &callback,
                             int timeout) = 0;
-  virtual void Reset(AsyncCallHandler *call_handler, int timeout) = 0;
+  virtual void DeleteBearer(const ::DBus::Path &bearer,
+                            Error *error,
+                            const ResultCallback &callback,
+                            int timeout) = 0;
+  virtual void Reset(Error *error,
+                     const ResultCallback &callback,
+                     int timeout) = 0;
   virtual void FactoryReset(const std::string &code,
-                            AsyncCallHandler *call_handler,
+                            Error *error,
+                            const ResultCallback &callback,
                             int timeout) = 0;
   virtual void SetAllowedModes(const uint32_t &modes,
                                const uint32_t &preferred,
-                               AsyncCallHandler *call_handler,
+                               Error *error,
+                               const ResultCallback &callback,
                                int timeout) = 0;
   virtual void SetBands(const std::vector< uint32_t > &bands,
-                        AsyncCallHandler *call_handler,
+                        Error *error,
+                        const ResultCallback &callback,
                         int timeout) = 0;
   virtual void Command(const std::string &cmd,
                        const uint32_t &user_timeout,
-                       AsyncCallHandler *call_handler,
+                       Error *error,
+                       const StringCallback &callback,
                        int timeout) = 0;
 
+
+  virtual void set_state_changed_callback(
+      const ModemStateChangedSignalCallback &callback) = 0;
+
   // Properties.
   virtual const ::DBus::Path Sim() = 0;
   virtual uint32_t ModemCapabilities() = 0;
@@ -80,42 +94,6 @@
   virtual const std::vector< uint32_t > Bands() = 0;
 };
 
-
-// ModemManager1.Modem signal delegate to be associated with the proxy.
-class ModemProxyDelegate {
- public:
-  virtual ~ModemProxyDelegate() {}
-
-  // handle signals
-  virtual void OnStateChanged(const uint32_t &old,
-                              const uint32_t &_new,
-                              const uint32_t &reason) = 0;
-
-  // Handle async callbacks
-  virtual void OnEnableCallback(const Error &error,
-                                AsyncCallHandler *call_handler) = 0;
-  virtual void OnListBearersCallback(
-      const std::vector< ::DBus::Path > &bearers,
-      const Error &error,
-      AsyncCallHandler *call_handler) = 0;
-  virtual void OnCreateBearerCallback(const ::DBus::Path &bearer,
-                                      const Error &error,
-                                      AsyncCallHandler *call_handler) = 0;
-  virtual void OnDeleteBearerCallback(const Error &error,
-                                      AsyncCallHandler *call_handler) = 0;
-  virtual void OnResetCallback(const Error &error,
-                               AsyncCallHandler *call_handler) = 0;
-  virtual void OnFactoryResetCallback(const Error &error,
-                                      AsyncCallHandler *call_handler) = 0;
-  virtual void OnSetAllowedModesCallback(const Error &error,
-                                         AsyncCallHandler *call_handler) = 0;
-  virtual void OnSetBandsCallback(const Error &error,
-                                  AsyncCallHandler *call_handler) = 0;
-  virtual void OnCommandCallback(const std::string &response,
-                                 const Error &error,
-                                 AsyncCallHandler *call_handler) = 0;
-};
-
 }  // namespace mm1
 }  // namespace shill
 
diff --git a/mm1_modem_simple_proxy.cc b/mm1_modem_simple_proxy.cc
index 077d982..de3adc1 100644
--- a/mm1_modem_simple_proxy.cc
+++ b/mm1_modem_simple_proxy.cc
@@ -7,46 +7,67 @@
 #include <base/logging.h>
 
 #include "cellular_error.h"
-#include "async_call_handler.h"
 
 using std::string;
 
 namespace shill {
 namespace mm1 {
 
-ModemSimpleProxy::ModemSimpleProxy(ModemSimpleProxyDelegate *delegate,
-                                   DBus::Connection *connection,
+ModemSimpleProxy::ModemSimpleProxy(DBus::Connection *connection,
                                    const string &path,
                                    const string &service)
-    : proxy_(delegate, connection, path, service) {}
+    : proxy_(connection, path, service) {}
 
 ModemSimpleProxy::~ModemSimpleProxy() {}
 
 void ModemSimpleProxy::Connect(
     const DBusPropertiesMap &properties,
-    AsyncCallHandler *call_handler,
+    Error *error,
+    const DBusPathCallback &callback,
     int timeout) {
-  proxy_.Connect(properties, call_handler, timeout);
+  scoped_ptr<DBusPathCallback> cb(new DBusPathCallback(callback));
+  try {
+    proxy_.Connect(properties, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemSimpleProxy::Disconnect(const ::DBus::Path &bearer,
-                                  AsyncCallHandler *call_handler,
+                                  Error *error,
+                                  const ResultCallback &callback,
                                   int timeout) {
-  proxy_.Disconnect(bearer, call_handler, timeout);
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.Disconnect(bearer, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-void ModemSimpleProxy::GetStatus(AsyncCallHandler *call_handler, int timeout) {
-  proxy_.GetStatus(call_handler, timeout);
+void ModemSimpleProxy::GetStatus(Error *error,
+                                 const DBusPropertyMapCallback &callback,
+                                 int timeout) {
+  scoped_ptr<DBusPropertyMapCallback> cb(new DBusPropertyMapCallback(callback));
+  try  {
+    proxy_.GetStatus(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 
 // ModemSimpleProxy::Proxy
-ModemSimpleProxy::Proxy::Proxy(ModemSimpleProxyDelegate *delegate,
-                               DBus::Connection *connection,
+ModemSimpleProxy::Proxy::Proxy(DBus::Connection *connection,
                                const std::string &path,
                                const std::string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()),
-      delegate_(delegate) {}
+    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
 
 ModemSimpleProxy::Proxy::~Proxy() {}
 
@@ -55,28 +76,30 @@
 void ModemSimpleProxy::Proxy::ConnectCallback(const ::DBus::Path &bearer,
                                               const ::DBus::Error &dberror,
                                               void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<DBusPathCallback> callback(
+      reinterpret_cast<DBusPathCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnConnectCallback(bearer, error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(bearer, error);
 }
 
 void ModemSimpleProxy::Proxy::DisconnectCallback(const ::DBus::Error &dberror,
                                                  void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnDisconnectCallback(error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(error);
 }
 
 void ModemSimpleProxy::Proxy::GetStatusCallback(
     const DBusPropertiesMap &properties,
     const ::DBus::Error &dberror,
     void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<DBusPropertyMapCallback> callback(
+      reinterpret_cast<DBusPropertyMapCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-      delegate_->OnGetStatusCallback(properties, error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(properties, error);
 }
 
 }  // namespace mm1
diff --git a/mm1_modem_simple_proxy.h b/mm1_modem_simple_proxy.h
index a438df0..8136855 100644
--- a/mm1_modem_simple_proxy.h
+++ b/mm1_modem_simple_proxy.h
@@ -17,10 +17,8 @@
 class ModemSimpleProxy : public ModemSimpleProxyInterface {
  public:
   // Constructs a org.freedesktop.ModemManager1.Modem.Simple DBus
-  // object proxy at |path| owned by |service|. Caught signals and
-  // asynchronous method replies will be dispatched to |delegate|.
-  ModemSimpleProxy(ModemSimpleProxyDelegate *delegate,
-                   DBus::Connection *connection,
+  // object proxy at |path| owned by |service|.
+  ModemSimpleProxy(DBus::Connection *connection,
                    const std::string &path,
                    const std::string &service);
   virtual ~ModemSimpleProxy();
@@ -28,19 +26,22 @@
   // Inherited methods from SimpleProxyInterface.
   virtual void Connect(
       const DBusPropertiesMap &properties,
-      AsyncCallHandler *call_handler,
+      Error *error,
+      const DBusPathCallback &callback,
       int timeout);
   virtual void Disconnect(const ::DBus::Path &bearer,
-                          AsyncCallHandler *call_handler,
+                          Error *error,
+                          const ResultCallback &callback,
                           int timeout);
-  virtual void GetStatus(AsyncCallHandler *call_handler, int timeout);
+  virtual void GetStatus(Error *error,
+                         const DBusPropertyMapCallback &callback,
+                         int timeout);
 
  private:
   class Proxy : public org::freedesktop::ModemManager1::Modem::Simple_proxy,
                 public DBus::ObjectProxy {
    public:
-    Proxy(ModemSimpleProxyDelegate *delegate,
-          DBus::Connection *connection,
+    Proxy(DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
@@ -56,7 +57,6 @@
         const DBusPropertiesMap &bearer,
         const ::DBus::Error &dberror,
         void *data);
-    ModemSimpleProxyDelegate *delegate_;
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
   };
diff --git a/mm1_modem_simple_proxy_interface.h b/mm1_modem_simple_proxy_interface.h
index 99bdafc..9ec59c3 100644
--- a/mm1_modem_simple_proxy_interface.h
+++ b/mm1_modem_simple_proxy_interface.h
@@ -9,10 +9,9 @@
 
 #include <base/basictypes.h>
 
-#include "shill/dbus_properties.h"
+#include "shill/callbacks.h"
 
 namespace shill {
-class AsyncCallHandler;
 class Error;
 
 namespace mm1 {
@@ -20,42 +19,25 @@
 // These are the methods that a
 // org.freedesktop.ModemManager1.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.
+// are made asynchronously. Call completion is signalled via the callbacks
+// passed to the methods.
 class ModemSimpleProxyInterface {
  public:
   virtual ~ModemSimpleProxyInterface() {}
 
-  virtual void Connect(
-      const DBusPropertiesMap &properties,
-      AsyncCallHandler *call_handler,
-      int timeout) = 0;
+  virtual void Connect(const DBusPropertiesMap &properties,
+                       Error *error,
+                       const DBusPathCallback &callback,
+                       int timeout) = 0;
   virtual void Disconnect(const ::DBus::Path &bearer,
-                          AsyncCallHandler *call_handler,
+                          Error *error,
+                          const ResultCallback &callback,
                           int timeout) = 0;
-  virtual void GetStatus(AsyncCallHandler *call_handler,
+  virtual void GetStatus(Error *error,
+                         const DBusPropertyMapCallback &callback,
                          int timeout) = 0;
 };
 
-// ModemManager1.Modem.Simple signal delegate to be associated with
-// the proxy.
-class ModemSimpleProxyDelegate {
- public:
-  virtual ~ModemSimpleProxyDelegate() {}
-
-  // Handle async callbacks
-  virtual void OnConnectCallback(const ::DBus::Path &bearer,
-                                 const Error &error,
-                                 AsyncCallHandler *call_handler) = 0;
-  virtual void OnDisconnectCallback(const Error &error,
-                                    AsyncCallHandler *call_handler) = 0;
-  virtual void OnGetStatusCallback(
-      const DBusPropertiesMap &bearer,
-      const Error &error,
-      AsyncCallHandler *call_handler) = 0;
-};
-
 }  // namespace mm1
 }  // namespace shill
 
diff --git a/mock_adaptors.cc b/mock_adaptors.cc
index e94bb9e..86988f6 100644
--- a/mock_adaptors.cc
+++ b/mock_adaptors.cc
@@ -84,8 +84,4 @@
 
 const string &ServiceMockAdaptor::GetRpcIdentifier() { return rpc_id_; }
 
-MockReturner::MockReturner() {}
-
-MockReturner::~MockReturner() {}
-
 }  // namespace shill
diff --git a/mock_adaptors.h b/mock_adaptors.h
index 2380733..0fda580 100644
--- a/mock_adaptors.h
+++ b/mock_adaptors.h
@@ -146,14 +146,5 @@
   const std::string rpc_id_;
 };
 
-class MockReturner : public ReturnerInterface {
- public:
-  MockReturner();
-  virtual ~MockReturner();
-
-  MOCK_METHOD0(Return, void());
-  MOCK_METHOD1(ReturnError, void(const Error &error));
-};
-
 }  // namespace shill
 #endif  // SHILL_MOCK_ADAPTORS_
diff --git a/mock_dbus_objectmanager_proxy.h b/mock_dbus_objectmanager_proxy.h
index ce481e7..fa6530e 100644
--- a/mock_dbus_objectmanager_proxy.h
+++ b/mock_dbus_objectmanager_proxy.h
@@ -17,8 +17,13 @@
   MockDBusObjectManagerProxy();
   virtual ~MockDBusObjectManagerProxy();
 
-  MOCK_METHOD2(GetManagedObjects, void(AsyncCallHandler *call_handler,
+  MOCK_METHOD3(GetManagedObjects, void(Error *error,
+                                       const ManagedObjectsCallback &callback,
                                        int timeout));
+  MOCK_METHOD1(set_interfaces_added_callback,
+      void(const InterfacesAddedSignalCallback &callback));
+  MOCK_METHOD1(set_interfaces_removed_callback,
+      void(const InterfacesRemovedSignalCallback &callback));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockDBusObjectManagerProxy);
diff --git a/mock_device.h b/mock_device.h
index 96a522c..0d2a473 100644
--- a/mock_device.h
+++ b/mock_device.h
@@ -25,13 +25,16 @@
              int interface_index);
   virtual ~MockDevice();
 
-  MOCK_METHOD0(Start, void());
-  MOCK_METHOD0(Stop, void());
+  MOCK_METHOD2(Start, void(Error *error,
+                           const EnabledStateChangedCallback &callback));
+  MOCK_METHOD2(Stop, void(Error *error,
+                          const EnabledStateChangedCallback &callback));
+  MOCK_METHOD1(SetEnabled, void(bool));
   MOCK_METHOD1(Scan, void(Error *error));
   MOCK_CONST_METHOD1(TechnologyIs,
                      bool(const Technology::Identifier technology));
-  MOCK_METHOD1(Load, bool(StoreInterface*));
-  MOCK_METHOD1(Save, bool(StoreInterface*));
+  MOCK_METHOD1(Load, bool(StoreInterface *storage));
+  MOCK_METHOD1(Save, bool(StoreInterface *storage));
   MOCK_METHOD0(DisableIPv6, void());
   MOCK_METHOD0(EnableIPv6, void());
   MOCK_METHOD0(EnableIPv6Privacy, void());
diff --git a/mock_manager.h b/mock_manager.h
index 4e66453..9060a21 100644
--- a/mock_manager.h
+++ b/mock_manager.h
@@ -32,6 +32,7 @@
                bool (const ProfileRefPtr &profile,
                      const std::string &entry_name));
   MOCK_CONST_METHOD0(GetDefaultService, ServiceRefPtr());
+  MOCK_METHOD0(UpdateEnabledTechnologies, void());
   MOCK_METHOD1(IsPortalDetectionEnabled, bool(Technology::Identifier tech));
   MOCK_CONST_METHOD0(GetPortalCheckURL, const std::string &());
   MOCK_CONST_METHOD0(GetPortalCheckInterval, int());
diff --git a/mock_modem_cdma_proxy.h b/mock_modem_cdma_proxy.h
index e3c0453..915f1b1 100644
--- a/mock_modem_cdma_proxy.h
+++ b/mock_modem_cdma_proxy.h
@@ -19,12 +19,22 @@
   MockModemCDMAProxy();
   virtual ~MockModemCDMAProxy();
 
-  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());
+  MOCK_METHOD4(Activate, void(const std::string &carrier, Error *error,
+                              const ActivationResultCallback &callback,
+                              int timeout));
+  MOCK_METHOD3(GetRegistrationState,
+               void(Error *error, const RegistrationStateCallback &callback,
+                    int timeout));
+  MOCK_METHOD3(GetSignalQuality, void(Error *error,
+                                      const SignalQualityCallback &callback,
+                                      int timeout));
   MOCK_METHOD0(MEID, const std::string());
+  MOCK_METHOD1(set_activation_state_callback,
+      void(const ActivationStateSignalCallback &callback));
+  MOCK_METHOD1(set_signal_quality_callback,
+      void(const SignalQualitySignalCallback &callback));
+  MOCK_METHOD1(set_registration_state_callback,
+      void(const RegistrationStateSignalCallback &callback));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockModemCDMAProxy);
diff --git a/mock_modem_gsm_card_proxy.h b/mock_modem_gsm_card_proxy.h
index 70d99e4..1e2cbdf 100644
--- a/mock_modem_gsm_card_proxy.h
+++ b/mock_modem_gsm_card_proxy.h
@@ -19,20 +19,32 @@
   MockModemGSMCardProxy();
   virtual ~MockModemGSMCardProxy();
 
-  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_METHOD3(GetIMEI, void(Error *error,
+                             const GSMIdentifierCallback &callback,
+                             int timeout));
+  MOCK_METHOD3(GetIMSI, void(Error *error,
+                             const GSMIdentifierCallback &callback,
+                             int timeout));
+  MOCK_METHOD3(GetSPN, void(Error *error,
+                            const GSMIdentifierCallback &callback,
+                            int timeout));
+  MOCK_METHOD3(GetMSISDN, void(Error *error,
+                               const GSMIdentifierCallback &callback,
+                               int timeout));
 
-  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,
+  MOCK_METHOD5(EnablePIN, void(const std::string &pin, bool enabled,
+                               Error *error, const ResultCallback &callback,
+                               int timeout));
+  MOCK_METHOD4(SendPIN, void(const std::string &pin,
+                             Error *error, const ResultCallback &callback,
+                             int timeout));
+  MOCK_METHOD5(SendPUK, void(const std::string &puk, const std::string &pin,
+                             Error *error, const ResultCallback &callback,
+                             int timeout));
+  MOCK_METHOD5(ChangePIN, void(const std::string &old_pin,
                                const std::string &new_pin,
-                               AsyncCallHandler *call_handler, int timeout));
+                               Error *error, const ResultCallback &callback,
+                               int timeout));
   MOCK_METHOD0(EnabledFacilityLocks, uint32());
 
  private:
diff --git a/mock_modem_gsm_network_proxy.h b/mock_modem_gsm_network_proxy.h
index c10f469..0b623db 100644
--- a/mock_modem_gsm_network_proxy.h
+++ b/mock_modem_gsm_network_proxy.h
@@ -19,13 +19,23 @@
   MockModemGSMNetworkProxy();
   virtual ~MockModemGSMNetworkProxy();
 
-  MOCK_METHOD2(GetRegistrationInfo, void(AsyncCallHandler *call_handler,
-                                         int timeout));
-  MOCK_METHOD0(GetSignalQuality, uint32());
-  MOCK_METHOD3(Register, void(const std::string &network_id,
-                              AsyncCallHandler *call_handler, int timeout));
-  MOCK_METHOD2(Scan, void(AsyncCallHandler *call_handler, int timeout));
+  MOCK_METHOD3(GetRegistrationInfo,
+               void(Error *error, const RegistrationInfoCallback &callback,
+                    int timeout));
+  MOCK_METHOD3(GetSignalQuality, void(Error *error,
+                                      const SignalQualityCallback &callback,
+                                      int timeout));
+  MOCK_METHOD4(Register, void(const std::string &network_id, Error *error,
+                              const ResultCallback &callback, int timeout));
+  MOCK_METHOD3(Scan, void(Error *error, const ScanResultsCallback &callback,
+                          int timeout));
   MOCK_METHOD0(AccessTechnology, uint32());
+  MOCK_METHOD1(set_signal_quality_callback,
+      void(const SignalQualitySignalCallback &callback));
+  MOCK_METHOD1(set_network_mode_callback,
+      void(const NetworkModeSignalCallback &callback));
+  MOCK_METHOD1(set_registration_info_callback,
+      void(const RegistrationInfoSignalCallback &callback));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockModemGSMNetworkProxy);
diff --git a/mock_modem_proxy.h b/mock_modem_proxy.h
index e30676b..5d38290 100644
--- a/mock_modem_proxy.h
+++ b/mock_modem_proxy.h
@@ -12,18 +12,20 @@
 
 namespace shill {
 
-class AsyncCallHandler;
-
 class MockModemProxy : public ModemProxyInterface {
  public:
   MockModemProxy();
   virtual ~MockModemProxy();
 
-  MOCK_METHOD3(Enable, void(bool enable, AsyncCallHandler *call_handler,
-                            int timeout));
-  MOCK_METHOD1(Enable, void(bool enable));
-  MOCK_METHOD0(Disconnect, void());
-  MOCK_METHOD2(GetModemInfo, void(AsyncCallHandler *call_handler, int timeout));
+  MOCK_METHOD4(Enable, void(bool enable, Error *error,
+                            const ResultCallback &callback, int timeout));
+  MOCK_METHOD3(Disconnect, void(Error *error, const ResultCallback &callback,
+                                int timeout));
+  MOCK_METHOD3(GetModemInfo, void(Error *error,
+                                  const ModemInfoCallback &callback,
+                                  int timeout));
+  MOCK_METHOD1(set_state_changed_callback,
+               void(const ModemStateChangedSignalCallback &callback));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockModemProxy);
diff --git a/mock_modem_simple_proxy.h b/mock_modem_simple_proxy.h
index 5f17629..6a2adf7 100644
--- a/mock_modem_simple_proxy.h
+++ b/mock_modem_simple_proxy.h
@@ -17,10 +17,12 @@
   MockModemSimpleProxy();
   virtual ~MockModemSimpleProxy();
 
-  MOCK_METHOD2(GetModemStatus, void(AsyncCallHandler *call_handler,
+  MOCK_METHOD3(GetModemStatus, void(Error *error,
+                                    const DBusPropertyMapCallback &callback,
                                     int timeout));
-  MOCK_METHOD3(Connect, void(const DBusPropertiesMap &properties,
-                             AsyncCallHandler *call_handler, int timeout));
+  MOCK_METHOD4(Connect, void(const DBusPropertiesMap &properties,
+                             Error *error, const ResultCallback &callback,
+                             int timeout));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockModemSimpleProxy);
diff --git a/mock_vpn.h b/mock_vpn.h
index 943ddc1..2d853db 100644
--- a/mock_vpn.h
+++ b/mock_vpn.h
@@ -21,9 +21,11 @@
           int interface_index);
   virtual ~MockVPN();
 
-  MOCK_METHOD0(Stop, void());
+  MOCK_METHOD2(Stop, void(Error *error,
+                          const EnabledStateChangedCallback &callback));
   MOCK_METHOD1(UpdateIPConfig, void(const IPConfig::Properties &properties));
   MOCK_METHOD0(OnDisconnected, void());
+  MOCK_METHOD1(SetEnabled, void(bool));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockVPN);
diff --git a/mock_wifi.h b/mock_wifi.h
index 78b1b9f..5318fc9 100644
--- a/mock_wifi.h
+++ b/mock_wifi.h
@@ -34,8 +34,10 @@
            int interface_index);
   virtual ~MockWiFi();
 
-  MOCK_METHOD0(Start, void());
-  MOCK_METHOD0(Stop, void());
+  MOCK_METHOD2(Start, void(Error *error,
+                           const EnabledStateChangedCallback &callback));
+  MOCK_METHOD2(Stop, void(Error *error,
+                          const EnabledStateChangedCallback &callback));
   MOCK_METHOD1(Scan, void(Error *error));
   MOCK_METHOD2(GetService,
                WiFiServiceRefPtr(const KeyValueStore &args, Error *error));
diff --git a/modem_cdma_proxy.cc b/modem_cdma_proxy.cc
index 472e6d1..05e126e 100644
--- a/modem_cdma_proxy.cc
+++ b/modem_cdma_proxy.cc
@@ -6,32 +6,60 @@
 
 #include <base/logging.h>
 
-#include "cellular_error.h"
+#include "shill/cellular_error.h"
 
 using std::string;
 
 namespace shill {
 
-ModemCDMAProxy::ModemCDMAProxy(ModemCDMAProxyDelegate *delegate,
+ModemCDMAProxy::ModemCDMAProxy(
                                DBus::Connection *connection,
                                const string &path,
                                const string &service)
-    : proxy_(delegate, connection, path, service) {}
+    : proxy_(connection, path, service) {}
 
 ModemCDMAProxy::~ModemCDMAProxy() {}
 
-void ModemCDMAProxy::Activate(const string &carrier,
-                                AsyncCallHandler *call_handler, int timeout) {
-  proxy_.Activate(carrier, call_handler, timeout);
+void ModemCDMAProxy::Activate(const string &carrier, Error *error,
+                              const ActivationResultCallback &callback,
+                              int timeout) {
+  scoped_ptr<ActivationResultCallback>
+      cb(new ActivationResultCallback(callback));
+  try {
+    proxy_.Activate(carrier, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-void ModemCDMAProxy::GetRegistrationState(uint32 *cdma_1x_state,
-                                          uint32 *evdo_state) {
-  proxy_.GetRegistrationState(*cdma_1x_state, *evdo_state);
+void ModemCDMAProxy::GetRegistrationState(
+    Error *error,
+    const RegistrationStateCallback &callback,
+    int timeout) {
+  scoped_ptr<RegistrationStateCallback>
+      cb(new RegistrationStateCallback(callback));
+  try {
+    proxy_.GetRegistrationState(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-uint32 ModemCDMAProxy::GetSignalQuality() {
-  return proxy_.GetSignalQuality();
+void ModemCDMAProxy::GetSignalQuality(Error *error,
+                                      const SignalQualityCallback &callback,
+                                      int timeout) {
+  scoped_ptr<SignalQualityCallback> cb(new SignalQualityCallback(callback));
+  try {
+    proxy_.GetSignalQuality(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 const string ModemCDMAProxy::MEID() {
@@ -39,45 +67,98 @@
   return proxy_.Meid();
 }
 
-ModemCDMAProxy::Proxy::Proxy(ModemCDMAProxyDelegate *delegate,
-                             DBus::Connection *connection,
+void ModemCDMAProxy::set_activation_state_callback(
+    const ActivationStateSignalCallback &callback) {
+  proxy_.set_activation_state_callback(callback);
+}
+
+void ModemCDMAProxy::set_signal_quality_callback(
+    const SignalQualitySignalCallback &callback) {
+  proxy_.set_signal_quality_callback(callback);
+}
+
+void ModemCDMAProxy::set_registration_state_callback(
+    const RegistrationStateSignalCallback &callback) {
+  proxy_.set_registration_state_callback(callback);
+}
+
+ModemCDMAProxy::Proxy::Proxy(DBus::Connection *connection,
                              const string &path,
                              const string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()),
-      delegate_(delegate) {}
+    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
 
 ModemCDMAProxy::Proxy::~Proxy() {}
 
+void ModemCDMAProxy::Proxy::set_activation_state_callback(
+    const ActivationStateSignalCallback &callback) {
+  activation_state_callback_ = callback;
+}
+
+void ModemCDMAProxy::Proxy::set_signal_quality_callback(
+    const SignalQualitySignalCallback &callback) {
+  signal_quality_callback_ = callback;
+}
+
+void ModemCDMAProxy::Proxy::set_registration_state_callback(
+    const RegistrationStateSignalCallback &callback) {
+  registration_state_callback_ = callback;
+}
+
 void ModemCDMAProxy::Proxy::ActivationStateChanged(
     const uint32 &activation_state,
     const uint32 &activation_error,
     const DBusPropertiesMap &status_changes) {
   VLOG(2) << __func__ << "(" << activation_state << ", " << activation_error
           << ")";
-  delegate_->OnCDMAActivationStateChanged(
-      activation_state, activation_error, status_changes);
+  activation_state_callback_.Run(activation_state,
+                                  activation_error,
+                                  status_changes);
 }
 
 void ModemCDMAProxy::Proxy::SignalQuality(const uint32 &quality) {
   VLOG(2) << __func__ << "(" << quality << ")";
-  delegate_->OnCDMASignalQualityChanged(quality);
+  signal_quality_callback_.Run(quality);
 }
 
 void ModemCDMAProxy::Proxy::RegistrationStateChanged(
     const uint32 &cdma_1x_state,
     const uint32 &evdo_state) {
   VLOG(2) << __func__ << "(" << cdma_1x_state << ", " << evdo_state << ")";
-  delegate_->OnCDMARegistrationStateChanged(cdma_1x_state, evdo_state);
+  registration_state_callback_.Run(cdma_1x_state, evdo_state);
 }
 
-void ModemCDMAProxy::Proxy::ActivateCallback(const uint32 &status,
+void ModemCDMAProxy::Proxy::ActivateCallback(const uint32_t &status,
                                              const DBus::Error &dberror,
                                              void *data) {
   VLOG(2) << __func__ << "(" << status << ")";
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ActivationResultCallback> callback(
+      reinterpret_cast<ActivationResultCallback *>(data));
   Error error;
-  CellularError::FromDBusError(dberror, &error),
-  delegate_->OnActivateCallback(status, error, call_handler);
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(status, error);
+}
+
+void ModemCDMAProxy::Proxy::GetRegistrationStateCallback(
+    const uint32 &state_1x, const uint32 &state_evdo,
+    const DBus::Error &dberror, void *data) {
+  VLOG(2) << __func__ << "(" << state_1x << ", " << state_evdo << ")";
+  scoped_ptr<RegistrationStateCallback> callback(
+      reinterpret_cast<RegistrationStateCallback *>(data));
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(state_1x, state_evdo, error);
+}
+
+
+void ModemCDMAProxy::Proxy::GetSignalQualityCallback(const uint32 &quality,
+                                                     const DBus::Error &dberror,
+                                                     void *data) {
+  VLOG(2) << __func__ << "(" << quality << ")";
+  scoped_ptr<SignalQualityCallback> callback(
+      reinterpret_cast<SignalQualityCallback *>(data));
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(quality, error);
 }
 
 }  // namespace shill
diff --git a/modem_cdma_proxy.h b/modem_cdma_proxy.h
index ed1240e..1c9922d 100644
--- a/modem_cdma_proxy.h
+++ b/modem_cdma_proxy.h
@@ -16,31 +16,47 @@
 class ModemCDMAProxy : public ModemCDMAProxyInterface {
  public:
   // Constructs a ModemManager.Modem.CDMA DBus object proxy at |path| owned by
-  // |service|. Caught signals and asynchronous method replies will be
-  // dispatched to |delegate|.
-  ModemCDMAProxy(ModemCDMAProxyDelegate *delegate,
-                 DBus::Connection *connection,
+  // |service|.
+  ModemCDMAProxy(DBus::Connection *connection,
                  const std::string &path,
                  const std::string &service);
   virtual ~ModemCDMAProxy();
 
   // Inherited from ModemCDMAProxyInterface.
-  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 void Activate(const std::string &carrier, Error *error,
+                        const ActivationResultCallback &callback, int timeout);
+  virtual void GetRegistrationState(Error *error,
+                                    const RegistrationStateCallback &callback,
+                                    int timeout);
+  virtual void GetSignalQuality(Error *error,
+                                const SignalQualityCallback &callback,
+                                int timeout);
   virtual const std::string MEID();
 
- private:
-  class Proxy : public org::freedesktop::ModemManager::Modem::Cdma_proxy,
-                public DBus::ObjectProxy {
+  virtual void set_activation_state_callback(
+      const ActivationStateSignalCallback &callback);
+  virtual void set_signal_quality_callback(
+      const SignalQualitySignalCallback &callback);
+  virtual void set_registration_state_callback(
+      const RegistrationStateSignalCallback &callback);
+
+ public:
+  class Proxy
+      : public org::freedesktop::ModemManager::Modem::Cdma_proxy,
+        public DBus::ObjectProxy {
    public:
-    Proxy(ModemCDMAProxyDelegate *delegate,
-          DBus::Connection *connection,
+    Proxy(DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
 
+    void set_activation_state_callback(
+        const ActivationStateSignalCallback &callback);
+    void set_signal_quality_callback(
+        const SignalQualitySignalCallback &callback);
+    void set_registration_state_callback(
+        const RegistrationStateSignalCallback &callback);
+
    private:
     // Signal callbacks inherited from ModemManager::Modem::Cdma_proxy.
     virtual void ActivationStateChanged(
@@ -54,8 +70,17 @@
     // Method callbacks inherited from ModemManager::Modem::Cdma_proxy.
     virtual void ActivateCallback(const uint32 &status,
                                   const DBus::Error &dberror, void *data);
+    virtual void GetRegistrationStateCallback(const uint32 &state_1x,
+                                              const uint32 &state_evdo,
+                                              const DBus::Error &error,
+                                              void *data);
+    virtual void GetSignalQualityCallback(const uint32 &quality,
+                                          const DBus::Error &dberror,
+                                          void *data);
 
-    ModemCDMAProxyDelegate *delegate_;
+    ActivationStateSignalCallback activation_state_callback_;
+    SignalQualitySignalCallback signal_quality_callback_;
+    RegistrationStateSignalCallback registration_state_callback_;
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
   };
diff --git a/modem_cdma_proxy_interface.h b/modem_cdma_proxy_interface.h
index 3329b7b..921a499 100644
--- a/modem_cdma_proxy_interface.h
+++ b/modem_cdma_proxy_interface.h
@@ -9,45 +9,48 @@
 
 #include <base/basictypes.h>
 
+#include "shill/callbacks.h"
 #include "shill/dbus_properties.h"
 
 namespace shill {
 
-class AsyncCallHandler;
 class Error;
 
+typedef base::Callback<void(uint32)> SignalQualitySignalCallback;
+typedef base::Callback<void(uint32, uint32)> RegistrationStateSignalCallback;
+
+typedef base::Callback<void(uint32, const Error &)> ActivationResultCallback;
+typedef base::Callback<void(uint32, const Error &)> SignalQualityCallback;
+typedef base::Callback<void(uint32, uint32,
+                            const Error &)> RegistrationStateCallback;
+
 // 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.
+// All calls are made asynchronously. Call completion is signalled via
+// the callbacks passed to the methods.
 class ModemCDMAProxyInterface {
  public:
   virtual ~ModemCDMAProxyInterface() {}
 
-  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;
+  virtual void Activate(const std::string &carrier, Error *error,
+                        const ActivationResultCallback &callback,
+                        int timeout) = 0;
+  virtual void GetRegistrationState(Error *error,
+                                    const RegistrationStateCallback &callback,
+                                    int timeout) = 0;
+  virtual void GetSignalQuality(Error *error,
+                                const SignalQualityCallback &callback,
+                                int timeout) = 0;
 
   // Properties.
   virtual const std::string MEID() = 0;
-};
 
-// ModemManager.Modem.CDMA signal delegate to be associated with the proxy.
-class ModemCDMAProxyDelegate {
- public:
-  virtual ~ModemCDMAProxyDelegate() {}
-
-  virtual void OnCDMAActivationStateChanged(
-      uint32 activation_state,
-      uint32 activation_error,
-      const DBusPropertiesMap &status_changes) = 0;
-  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;
+  virtual void set_activation_state_callback(
+      const ActivationStateSignalCallback &callback) = 0;
+  virtual void set_signal_quality_callback(
+      const SignalQualitySignalCallback &callback) = 0;
+  virtual void set_registration_state_callback(
+      const RegistrationStateSignalCallback &callback) = 0;
 };
 
 }  // namespace shill
diff --git a/modem_gsm_card_proxy.cc b/modem_gsm_card_proxy.cc
index b6b142b..f6c8cd3 100644
--- a/modem_gsm_card_proxy.cc
+++ b/modem_gsm_card_proxy.cc
@@ -4,135 +4,202 @@
 
 #include "shill/modem_gsm_card_proxy.h"
 
+#include <base/bind.h>
+
 #include "shill/cellular_error.h"
 #include "shill/error.h"
 
+using base::Bind;
+using base::Callback;
 using std::string;
 
 namespace shill {
 
-ModemGSMCardProxy::ModemGSMCardProxy(ModemGSMCardProxyDelegate *delegate,
-                                     DBus::Connection *connection,
+ModemGSMCardProxy::ModemGSMCardProxy(DBus::Connection *connection,
                                      const string &path,
                                      const string &service)
-    : proxy_(delegate, connection, path, service) {}
+    : proxy_(connection, path, service) {}
 
 ModemGSMCardProxy::~ModemGSMCardProxy() {}
 
-void ModemGSMCardProxy::GetIMEI(AsyncCallHandler *call_handler, int timeout) {
-  proxy_.GetImei(call_handler, timeout);
+void ModemGSMCardProxy::GetIMEI(Error *error,
+                                const GSMIdentifierCallback &callback,
+                                int timeout) {
+  scoped_ptr<GSMIdentifierCallback> cb(new GSMIdentifierCallback(callback));
+  try {
+    proxy_.GetImei(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-void ModemGSMCardProxy::GetIMSI(AsyncCallHandler *call_handler, int timeout) {
-  proxy_.GetImsi(call_handler, timeout);
+void ModemGSMCardProxy::GetIMSI(Error *error,
+                                const GSMIdentifierCallback &callback,
+                                int timeout) {
+  scoped_ptr<GSMIdentifierCallback> cb(new GSMIdentifierCallback(callback));
+  try {
+    proxy_.GetImsi(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-void ModemGSMCardProxy::GetSPN(AsyncCallHandler *call_handler, int timeout) {
-  proxy_.GetSpn(call_handler, timeout);
+void ModemGSMCardProxy::GetSPN(Error *error,
+                               const GSMIdentifierCallback &callback,
+                               int timeout) {
+  scoped_ptr<GSMIdentifierCallback> cb(new GSMIdentifierCallback(callback));
+  try {
+    proxy_.GetSpn(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-void ModemGSMCardProxy::GetMSISDN(AsyncCallHandler *call_handler, int timeout) {
-  proxy_.GetMsIsdn(call_handler, timeout);
+void ModemGSMCardProxy::GetMSISDN(Error *error,
+                                  const GSMIdentifierCallback &callback,
+                                  int timeout) {
+  scoped_ptr<GSMIdentifierCallback> cb(new GSMIdentifierCallback(callback));
+  try {
+    proxy_.GetMsIsdn(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemGSMCardProxy::EnablePIN(const string &pin, bool enabled,
-                                  AsyncCallHandler *call_handler, int timeout) {
-  proxy_.EnablePin(pin, enabled, call_handler, timeout);
+                                  Error *error,
+                                  const ResultCallback &callback,
+                                  int timeout) {
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.EnablePin(pin, enabled, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemGSMCardProxy::SendPIN(const string &pin,
-                                AsyncCallHandler *call_handler, int timeout) {
-  proxy_.SendPin(pin, call_handler, timeout);
+                                Error *error,
+                                const ResultCallback &callback,
+                                int timeout) {
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.SendPin(pin, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemGSMCardProxy::SendPUK(const string &puk, const string &pin,
-                                AsyncCallHandler *call_handler, int timeout) {
-  proxy_.SendPuk(puk, pin, call_handler, timeout);
+                                Error *error,
+                                const ResultCallback &callback,
+                                int timeout) {
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.SendPuk(puk, pin, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemGSMCardProxy::ChangePIN(const string &old_pin,
                                   const string &new_pin,
-                                  AsyncCallHandler *call_handler, int timeout) {
-  proxy_.ChangePin(old_pin, new_pin, call_handler, timeout);
+                                  Error *error,
+                                  const ResultCallback &callback,
+                                  int timeout) {
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.ChangePin(old_pin, new_pin, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 uint32 ModemGSMCardProxy::EnabledFacilityLocks() {
   return proxy_.EnabledFacilityLocks();
 }
 
-ModemGSMCardProxy::Proxy::Proxy(ModemGSMCardProxyDelegate *delegate,
-                                DBus::Connection *connection,
+ModemGSMCardProxy::Proxy::Proxy(DBus::Connection *connection,
                                 const string &path,
                                 const string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()),
-      delegate_(delegate) {}
+    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
+
+void ModemGSMCardProxy::Proxy::GetIdCallback(const string &id,
+                                             const DBus::Error &dberror,
+                                             void *data) {
+  scoped_ptr<GSMIdentifierCallback> callback(
+      reinterpret_cast<GSMIdentifierCallback *>(data));
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(id, error);
+}
 
 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);
+  GetIdCallback(imei, dberror, data);
 }
 
 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);
+  GetIdCallback(imsi, dberror, data);
 }
 
 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);
+  GetIdCallback(spn, dberror, data);
 }
 
-void ModemGSMCardProxy::Proxy::GetMsIsdnCallback(const string &misisdn,
+void ModemGSMCardProxy::Proxy::GetMsIsdnCallback(const string &msisdn,
                                                  const DBus::Error &dberror,
                                                  void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  GetIdCallback(msisdn, dberror, data);
+}
+
+void ModemGSMCardProxy::Proxy::PinCallback(const DBus::Error &dberror,
+                                           void *data) {
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
   CellularError::FromDBusError(dberror, &error);
-  delegate_->OnGetMSISDNCallback(misisdn, error, call_handler);
+  callback->Run(error);
 }
 
 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);
+  PinCallback(dberror, data);
 }
 
 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);
+  PinCallback(dberror, data);
 }
 
 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);
+  PinCallback(dberror, data);
 }
 
 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);
+  PinCallback(dberror, data);
 }
 
 ModemGSMCardProxy::Proxy::~Proxy() {}
diff --git a/modem_gsm_card_proxy.h b/modem_gsm_card_proxy.h
index 944403a..b6196d1 100644
--- a/modem_gsm_card_proxy.h
+++ b/modem_gsm_card_proxy.h
@@ -16,28 +16,35 @@
 
 class ModemGSMCardProxy : public ModemGSMCardProxyInterface {
  public:
-  // Constructs a ModemManager.Modem.Gsm.Card DBus object proxy at |path| owned
-  // by |service|. Callbacks will be dispatched to |delegate|.
-  ModemGSMCardProxy(ModemGSMCardProxyDelegate *delegate,
-                    DBus::Connection *connection,
+  // Constructs a ModemManager.Modem.Gsm.Card DBus
+  // object proxy at |path| owned by |service|.
+  ModemGSMCardProxy(DBus::Connection *connection,
                     const std::string &path,
                     const std::string &service);
   virtual ~ModemGSMCardProxy();
 
   // Inherited from ModemGSMCardProxyInterface.
-  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 GetIMEI(Error *error, const GSMIdentifierCallback &callback,
+                       int timeout);
+  virtual void GetIMSI(Error *error, const GSMIdentifierCallback &callback,
+                       int timeout);
+  virtual void GetSPN(Error *error, const GSMIdentifierCallback &callback,
+                      int timeout);
+  virtual void GetMSISDN(Error *error, const GSMIdentifierCallback &callback,
+                         int timeout);
   virtual void EnablePIN(const std::string &pin, bool enabled,
-                         AsyncCallHandler *call_handler, int timeout);
+                         Error *error, const ResultCallback &callback,
+                         int timeout);
   virtual void SendPIN(const std::string &pin,
-                       AsyncCallHandler *call_handler, int timeout);
+                       Error *error, const ResultCallback &callback,
+                       int timeout);
   virtual void SendPUK(const std::string &puk, const std::string &pin,
-                       AsyncCallHandler *call_handler, int timeout);
+                       Error *error, const ResultCallback &callback,
+                       int timeout);
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
-                         AsyncCallHandler *call_handler, int timeout);
+                         Error *error, const ResultCallback &callback,
+                         int timeout);
   virtual uint32 EnabledFacilityLocks();
 
  private:
@@ -45,8 +52,7 @@
       : public org::freedesktop::ModemManager::Modem::Gsm::Card_proxy,
         public DBus::ObjectProxy {
    public:
-    Proxy(ModemGSMCardProxyDelegate *delegate,
-          DBus::Connection *connection,
+    Proxy(DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
@@ -62,14 +68,16 @@
                                  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,
+    virtual void GetMsIsdnCallback(const std::string &msisdn,
                                  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_;
+    virtual void GetIdCallback(const std::string &id,
+                               const DBus::Error &dberror, void *data);
+    virtual void PinCallback(const DBus::Error &dberror, void *data);
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
   };
diff --git a/modem_gsm_card_proxy_interface.h b/modem_gsm_card_proxy_interface.h
index a047c02..1285a3c 100644
--- a/modem_gsm_card_proxy_interface.h
+++ b/modem_gsm_card_proxy_interface.h
@@ -7,62 +7,47 @@
 
 #include <string>
 
+#include "shill/callbacks.h"
+
 namespace shill {
 
-class AsyncCallHandler;
 class Error;
+typedef base::Callback<void(const std::string &,
+                            const Error &)> GSMIdentifierCallback;
 
 // 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.
+// All calls are made asynchronously.
 class ModemGSMCardProxyInterface {
  public:
   virtual ~ModemGSMCardProxyInterface() {}
 
-  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 GetIMEI(Error *error, const GSMIdentifierCallback &callback,
+                       int timeout) = 0;
+  virtual void GetIMSI(Error *error, const GSMIdentifierCallback &callback,
+                       int timeout) = 0;
+  virtual void GetSPN(Error *error, const GSMIdentifierCallback &callback,
+                      int timeout) = 0;
+  virtual void GetMSISDN(Error *error, const GSMIdentifierCallback &callback,
+                         int timeout) = 0;
   virtual void EnablePIN(const std::string &pin, bool enabled,
-                         AsyncCallHandler *call_handler, int timeout) = 0;
+                         Error *error, const ResultCallback &callback,
+                         int timeout) = 0;
   virtual void SendPIN(const std::string &pin,
-                       AsyncCallHandler *call_handler, int timeout) = 0;
+                       Error *error, const ResultCallback &callback,
+                       int timeout) = 0;
   virtual void SendPUK(const std::string &puk, const std::string &pin,
-                       AsyncCallHandler *call_handler, int timeout) = 0;
+                       Error *error, const ResultCallback &callback,
+                       int timeout) = 0;
   virtual void ChangePIN(const std::string &old_pin,
                          const std::string &new_pin,
-                         AsyncCallHandler *call_handler, int timeout) = 0;
+                         Error *error, const ResultCallback &callback,
+                         int timeout) = 0;
 
   // Properties.
   virtual uint32 EnabledFacilityLocks() = 0;
 };
 
-// ModemManager.Modem.Gsm.Card callback delegate to be associated with the
-// proxy.
-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
 
 #endif  // SHILL_MODEM_GSM_CARD_PROXY_INTERFACE_
diff --git a/modem_gsm_network_proxy.cc b/modem_gsm_network_proxy.cc
index ca7bbaa..22bcac6 100644
--- a/modem_gsm_network_proxy.cc
+++ b/modem_gsm_network_proxy.cc
@@ -9,54 +9,120 @@
 #include "shill/cellular_error.h"
 #include "shill/error.h"
 
+using base::Callback;
 using std::string;
 
 namespace shill {
 
 ModemGSMNetworkProxy::ModemGSMNetworkProxy(
-    ModemGSMNetworkProxyDelegate *delegate,
     DBus::Connection *connection,
     const string &path,
     const string &service)
-    : proxy_(delegate, connection, path, service) {}
+    : proxy_(connection, path, service) {}
 
 ModemGSMNetworkProxy::~ModemGSMNetworkProxy() {}
 
-void ModemGSMNetworkProxy::GetRegistrationInfo(AsyncCallHandler *call_handler,
-                                               int timeout) {
-  proxy_.GetRegistrationInfo(call_handler, timeout);
+void ModemGSMNetworkProxy::GetRegistrationInfo(
+    Error *error,
+    const RegistrationInfoCallback &callback,
+    int timeout) {
+  scoped_ptr<RegistrationInfoCallback>
+      cb(new RegistrationInfoCallback(callback));
+  try {
+    proxy_.GetRegistrationInfo(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-uint32 ModemGSMNetworkProxy::GetSignalQuality() {
-  return proxy_.GetSignalQuality();
+void ModemGSMNetworkProxy::GetSignalQuality(
+    Error *error,
+    const SignalQualityCallback &callback,
+    int timeout) {
+  scoped_ptr<SignalQualityCallback> cb(new SignalQualityCallback(callback));
+  try {
+    proxy_.GetSignalQuality(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemGSMNetworkProxy::Register(const string &network_id,
-                                    AsyncCallHandler *call_handler,
+                                    Error *error,
+                                    const ResultCallback &callback,
                                     int timeout) {
-  proxy_.Register(network_id, call_handler, timeout);
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.Register(network_id, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-void ModemGSMNetworkProxy::Scan(AsyncCallHandler *call_handler, int timeout) {
-  proxy_.Scan(call_handler, timeout);
+void ModemGSMNetworkProxy::Scan(Error *error,
+                                const ScanResultsCallback &callback,
+                                int timeout) {
+  scoped_ptr<ScanResultsCallback> cb(new ScanResultsCallback(callback));
+  try {
+    proxy_.Scan(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 uint32 ModemGSMNetworkProxy::AccessTechnology() {
   return proxy_.AccessTechnology();
 }
 
-ModemGSMNetworkProxy::Proxy::Proxy(ModemGSMNetworkProxyDelegate *delegate,
-                                   DBus::Connection *connection,
+void ModemGSMNetworkProxy::set_signal_quality_callback(
+    const SignalQualitySignalCallback &callback) {
+  proxy_.set_signal_quality_callback(callback);
+}
+
+void ModemGSMNetworkProxy::set_network_mode_callback(
+    const NetworkModeSignalCallback &callback) {
+  proxy_.set_network_mode_callback(callback);
+}
+
+void ModemGSMNetworkProxy::set_registration_info_callback(
+    const RegistrationInfoSignalCallback &callback) {
+  proxy_.set_registration_info_callback(callback);
+}
+
+ModemGSMNetworkProxy::Proxy::Proxy(DBus::Connection *connection,
                                    const string &path,
                                    const string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()),
-      delegate_(delegate) {}
+    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
 
 ModemGSMNetworkProxy::Proxy::~Proxy() {}
 
+void ModemGSMNetworkProxy::Proxy::set_signal_quality_callback(
+    const SignalQualitySignalCallback &callback) {
+  signal_quality_callback_ = callback;
+}
+
+void ModemGSMNetworkProxy::Proxy::set_network_mode_callback(
+    const NetworkModeSignalCallback &callback) {
+  network_mode_callback_ = callback;
+}
+
+void ModemGSMNetworkProxy::Proxy::set_registration_info_callback(
+    const RegistrationInfoSignalCallback &callback) {
+  registration_info_callback_ = callback;
+}
+
 void ModemGSMNetworkProxy::Proxy::SignalQuality(const uint32 &quality) {
   VLOG(2) << __func__ << "(" << quality << ")";
-  delegate_->OnGSMSignalQualityChanged(quality);
+  if (!signal_quality_callback_.is_null())
+    signal_quality_callback_.Run(quality);
 }
 
 void ModemGSMNetworkProxy::Proxy::RegistrationInfo(
@@ -65,39 +131,51 @@
     const string &operator_name) {
   VLOG(2) << __func__ << "(" << status << ", " << operator_code << ", "
           << operator_name << ")";
-  delegate_->OnGSMRegistrationInfoChanged(status, operator_code, operator_name,
-                                          Error(), NULL);
+  if (!registration_info_callback_.is_null())
+    registration_info_callback_.Run(status, operator_code, operator_name);
 }
 
 void ModemGSMNetworkProxy::Proxy::NetworkMode(const uint32_t &mode) {
   VLOG(2) << __func__ << "(" << mode << ")";
-  delegate_->OnGSMNetworkModeChanged(mode);
+  if (!network_mode_callback_.is_null())
+    network_mode_callback_.Run(mode);
 }
 
 void ModemGSMNetworkProxy::Proxy::RegisterCallback(const DBus::Error &dberror,
                                                    void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
   CellularError::FromDBusError(dberror, &error);
-  delegate_->OnRegisterCallback(error, call_handler);
+  callback->Run(error);
 }
 
 void ModemGSMNetworkProxy::Proxy::GetRegistrationInfoCallback(
     const GSMRegistrationInfo &info, const DBus::Error &dberror, void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<RegistrationInfoCallback> callback(
+      reinterpret_cast<RegistrationInfoCallback *>(data));
   Error error;
   CellularError::FromDBusError(dberror, &error);
-  delegate_->OnGSMRegistrationInfoChanged(info._1, info._2, info._3,
-                                          error, call_handler);
+  callback->Run(info._1, info._2, info._3, error);
+}
+
+void ModemGSMNetworkProxy::Proxy::GetSignalQualityCallback(
+    const uint32 &quality, const DBus::Error &dberror, void *data) {
+  VLOG(2) << __func__ << "(" << quality << ")";
+  scoped_ptr<SignalQualityCallback> callback(
+      reinterpret_cast<SignalQualityCallback *>(data));
+  Error error;
+  CellularError::FromDBusError(dberror, &error);
+  callback->Run(quality, error);
 }
 
 void ModemGSMNetworkProxy::Proxy::ScanCallback(const GSMScanResults &results,
                                                const DBus::Error &dberror,
                                                void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ScanResultsCallback> callback(
+      reinterpret_cast<ScanResultsCallback *>(data));
   Error error;
   CellularError::FromDBusError(dberror, &error);
-  delegate_->OnScanCallback(results, error, call_handler);
+  callback->Run(results, error);
 }
 
 }  // namespace shill
diff --git a/modem_gsm_network_proxy.h b/modem_gsm_network_proxy.h
index 4b0dc50..f714585 100644
--- a/modem_gsm_network_proxy.h
+++ b/modem_gsm_network_proxy.h
@@ -15,32 +15,50 @@
 class ModemGSMNetworkProxy : public ModemGSMNetworkProxyInterface {
  public:
   // Constructs a ModemManager.Modem.Gsm.Network DBus object proxy at |path|
-  // owned by |service|. Caught signals will be dispatched to |delegate|.
-  ModemGSMNetworkProxy(ModemGSMNetworkProxyDelegate *delegate,
-                       DBus::Connection *connection,
+  // owned by |service|.
+  ModemGSMNetworkProxy(DBus::Connection *connection,
                        const std::string &path,
                        const std::string &service);
   virtual ~ModemGSMNetworkProxy();
 
   // Inherited from ModemGSMNetworkProxyInterface.
-  virtual void GetRegistrationInfo(AsyncCallHandler *call_handler, int timeout);
-  virtual uint32 GetSignalQuality();
+  virtual void GetRegistrationInfo(Error *error,
+                                   const RegistrationInfoCallback &callback,
+                                   int timeout);
+  virtual void GetSignalQuality(Error *error,
+                                const SignalQualityCallback &callback,
+                                int timeout);
   virtual void Register(const std::string &network_id,
-                        AsyncCallHandler *call_handler, int timeout);
-  virtual void Scan(AsyncCallHandler *call_handler, int timeout);
+                        Error *error, const ResultCallback &callback,
+                        int timeout);
+  virtual void Scan(Error *error, const ScanResultsCallback &callback,
+                    int timeout);
   virtual uint32 AccessTechnology();
 
+  virtual void set_signal_quality_callback(
+      const SignalQualitySignalCallback &callback);
+  virtual void set_network_mode_callback(
+      const NetworkModeSignalCallback &callback);
+  virtual void set_registration_info_callback(
+      const RegistrationInfoSignalCallback &callback);
+
  private:
   class Proxy
       : public org::freedesktop::ModemManager::Modem::Gsm::Network_proxy,
         public DBus::ObjectProxy {
    public:
-    Proxy(ModemGSMNetworkProxyDelegate *delegate,
-          DBus::Connection *connection,
+    Proxy(DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
 
+    virtual void set_signal_quality_callback(
+        const SignalQualitySignalCallback &callback);
+    virtual void set_network_mode_callback(
+        const NetworkModeSignalCallback &callback);
+    virtual void set_registration_info_callback(
+        const RegistrationInfoSignalCallback &callback);
+
    private:
     // Signal callbacks inherited from ModemManager::Modem::Gsm::Network_proxy.
     virtual void SignalQuality(const uint32 &quality);
@@ -54,10 +72,15 @@
     virtual void GetRegistrationInfoCallback(const GSMRegistrationInfo &info,
                                              const DBus::Error &dberror,
                                              void *data);
+    virtual void GetSignalQualityCallback(const uint32 &quality,
+                                          const DBus::Error &dberror,
+                                          void *data);
     virtual void ScanCallback(const GSMScanResults &results,
                               const DBus::Error &dberror, void *data);
 
-    ModemGSMNetworkProxyDelegate *delegate_;
+    SignalQualitySignalCallback signal_quality_callback_;
+    RegistrationInfoSignalCallback registration_info_callback_;
+    NetworkModeSignalCallback network_mode_callback_;
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
   };
diff --git a/modem_gsm_network_proxy_interface.h b/modem_gsm_network_proxy_interface.h
index e3ab494..a4e77ac 100644
--- a/modem_gsm_network_proxy_interface.h
+++ b/modem_gsm_network_proxy_interface.h
@@ -12,9 +12,10 @@
 #include <base/basictypes.h>
 #include <dbus-c++/types.h>
 
+#include "shill/callbacks.h"
+
 namespace shill {
 
-class AsyncCallHandler;
 class Error;
 
 typedef DBus::Struct<unsigned int, std::string, std::string>
@@ -22,48 +23,50 @@
 typedef std::map<std::string, std::string> GSMScanResult;
 typedef std::vector<GSMScanResult> GSMScanResults;
 
+typedef base::Callback<void(uint32)> SignalQualitySignalCallback;
+typedef base::Callback<void(
+    uint32_t,
+    const std::string &,
+    const std::string &)> RegistrationInfoSignalCallback;
+typedef base::Callback<void(uint32_t)> NetworkModeSignalCallback;
+
+typedef base::Callback<void(uint32, const Error &)> SignalQualityCallback;
+typedef base::Callback<void(uint32_t,
+                            const std::string &,
+                            const std::string &,
+                            const Error &)> RegistrationInfoCallback;
+typedef base::Callback<void(const GSMScanResults &,
+                            const Error &)> ScanResultsCallback;
+
 // 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.
+// All calls are made asynchronously.
+// XXX fixup comment to reflect new reality
 class ModemGSMNetworkProxyInterface {
  public:
   virtual ~ModemGSMNetworkProxyInterface() {}
 
-  virtual void GetRegistrationInfo(AsyncCallHandler *call_handler,
+  virtual void GetRegistrationInfo(Error *error,
+                                   const RegistrationInfoCallback &callback,
                                    int timeout) = 0;
-  virtual uint32 GetSignalQuality() = 0;
+  virtual void GetSignalQuality(Error *error,
+                                const SignalQualityCallback &callback,
+                                int timeout) = 0;
   virtual void Register(const std::string &network_id,
-                        AsyncCallHandler *call_handler, int timeout) = 0;
-  virtual void Scan(AsyncCallHandler *call_handler, int timeout) = 0;
+                        Error *error, const ResultCallback &callback,
+                        int timeout) = 0;
+  virtual void Scan(Error *error, const ScanResultsCallback &callback,
+                    int timeout) = 0;
 
   // Properties.
   virtual uint32 AccessTechnology() = 0;
-};
-
-// 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,
-      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;
+  // Signal callbacks
+  virtual void set_signal_quality_callback(
+      const SignalQualitySignalCallback &callback) = 0;
+  virtual void set_network_mode_callback(
+      const NetworkModeSignalCallback &callback) = 0;
+  virtual void set_registration_info_callback(
+      const RegistrationInfoSignalCallback &callback) = 0;
 };
 
 }  // namespace shill
diff --git a/modem_manager.cc b/modem_manager.cc
index aef86cb..9aedd83 100644
--- a/modem_manager.cc
+++ b/modem_manager.cc
@@ -6,14 +6,16 @@
 
 #include <algorithm>
 
+#include <base/bind.h>
 #include <base/logging.h>
 #include <base/stl_util.h>
 
-#include "shill/async_call_handler.h"
+#include "shill/error.h"
 #include "shill/modem.h"
 #include "shill/modem_manager_proxy.h"
 #include "shill/proxy_factory.h"
 
+using base::Bind;
 using std::string;
 using std::tr1::shared_ptr;
 using std::vector;
@@ -174,25 +176,36 @@
                              Metrics *metrics,
                              Manager *manager,
                              GLib *glib,
-                             mobile_provider_db *provider_db) :
-    ModemManager(service,
-                 path,
-                 control_interface,
-                 dispatcher,
-                 metrics,
-                 manager,
-                 glib,
-                 provider_db) {}
+                             mobile_provider_db *provider_db)
+    : ModemManager(service,
+                   path,
+                   control_interface,
+                   dispatcher,
+                   metrics,
+                   manager,
+                   glib,
+                   provider_db),
+      weak_ptr_factory_(this) {}
 
 ModemManager1::~ModemManager1() {}
 
 void ModemManager1::Connect(const string &supplied_owner) {
   ModemManager::Connect(supplied_owner);
   proxy_.reset(
-      proxy_factory()->CreateDBusObjectManagerProxy(this, path(), owner()));
+      proxy_factory()->CreateDBusObjectManagerProxy(path(), owner()));
+  proxy_->set_interfaces_added_callback(
+      Bind(&ModemManager1::OnInterfacesAddedSignal,
+           weak_ptr_factory_.GetWeakPtr()));
+  proxy_->set_interfaces_removed_callback(
+      Bind(&ModemManager1::OnInterfacesRemovedSignal,
+           weak_ptr_factory_.GetWeakPtr()));
 
   // TODO(rochberg):  Make global kDBusDefaultTimeout and use it here
-  proxy_->GetManagedObjects(new AsyncCallHandler(NULL), 5000);
+  Error error;
+  proxy_->GetManagedObjects(&error,
+                            Bind(&ModemManager1::OnGetManagedObjectsReply,
+                                 weak_ptr_factory_.GetWeakPtr()),
+                            5000);
 }
 
 void ModemManager1::Disconnect() {
@@ -204,9 +217,9 @@
   LOG(ERROR) << __func__;
 }
 
-// DBusObjectManagerProxyDelegate signal methods
-// Also called by OnGetManagedObjectsCallback
-void ModemManager1::OnInterfacesAdded(
+// signal methods
+// Also called by OnGetManagedObjectsReply
+void ModemManager1::OnInterfacesAddedSignal(
     const ::DBus::Path &object_path,
     const DBusInterfaceToProperties &interface_to_properties) {
   if (ContainsKey(interface_to_properties, kDBusInterfaceModem)) {
@@ -216,7 +229,7 @@
   }
 }
 
-void ModemManager1::OnInterfacesRemoved(
+void ModemManager1::OnInterfacesRemovedSignal(
     const ::DBus::Path &object_path,
     const vector<string> &interfaces) {
   LOG(INFO) << "MM1:  Removing interfaces from " << object_path;
@@ -232,13 +245,14 @@
 }
 
 // DBusObjectManagerProxy async method call
-void ModemManager1::OnGetManagedObjectsCallback(
+void ModemManager1::OnGetManagedObjectsReply(
     const DBusObjectsWithProperties &objects,
-    const Error &error,
-    AsyncCallHandler * /* call_handler */) {
-  DBusObjectsWithProperties::const_iterator m;
-  for (m = objects.begin(); m != objects.end(); ++m) {
-    OnInterfacesAdded(m->first, m->second);
+    const Error &error) {
+  if (error.IsSuccess()) {
+    DBusObjectsWithProperties::const_iterator m;
+    for (m = objects.begin(); m != objects.end(); ++m) {
+      OnInterfacesAddedSignal(m->first, m->second);
+    }
   }
 }
 
diff --git a/modem_manager.h b/modem_manager.h
index e1e4a16..91a746b 100644
--- a/modem_manager.h
+++ b/modem_manager.h
@@ -12,6 +12,7 @@
 
 #include <base/basictypes.h>
 #include <base/memory/scoped_ptr.h>
+#include <base/memory/weak_ptr.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "shill/dbus_objectmanager_proxy_interface.h"
@@ -22,7 +23,6 @@
 namespace shill {
 
 class ControlInterface;
-class DBusObjectManagerProxyDelegate;
 class DBusObjectManagerProxyInterface;
 class EventDispatcher;
 class Manager;
@@ -150,8 +150,7 @@
   DISALLOW_COPY_AND_ASSIGN(ModemManagerClassic);
 };
 
-class ModemManager1 : public ModemManager,
-                      public DBusObjectManagerProxyDelegate {
+class ModemManager1 : public ModemManager {
  public:
   ModemManager1(const std::string &service,
                 const std::string &path,
@@ -173,24 +172,24 @@
   virtual void InitModem(std::tr1::shared_ptr<Modem> modem);
 
   // DBusObjectManagerProxyDelegate signal methods
-  virtual void OnInterfacesAdded(
+  virtual void OnInterfacesAddedSignal(
       const ::DBus::Path &object_path,
       const DBusInterfaceToProperties &interface_to_properties);
-  virtual void OnInterfacesRemoved(
+  virtual void OnInterfacesRemovedSignal(
       const ::DBus::Path &object_path,
       const std::vector<std::string> &interfaces);
 
   // DBusObjectManagerProxyDelegate method callbacks
-  virtual void OnGetManagedObjectsCallback(
+  virtual void OnGetManagedObjectsReply(
       const DBusObjectsWithProperties &objects_with_properties,
-      const Error &error,
-      AsyncCallHandler *call_handler);
+      const Error &error);
 
  private:
   FRIEND_TEST(ModemManager1Test, Connect);
   FRIEND_TEST(ModemManager1Test, AddRemoveInterfaces);
 
   scoped_ptr<DBusObjectManagerProxyInterface> proxy_;
+  base::WeakPtrFactory<ModemManager1> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ModemManager1);
 };
diff --git a/modem_manager_unittest.cc b/modem_manager_unittest.cc
index 22ba368..3db25ff 100644
--- a/modem_manager_unittest.cc
+++ b/modem_manager_unittest.cc
@@ -269,7 +269,6 @@
     explicit TestProxyFactory(ModemManager1Test *test) : test_(test) {}
 
     virtual DBusObjectManagerProxyInterface *CreateDBusObjectManagerProxy(
-        DBusObjectManagerProxyDelegate */*manager*/,
         const string &/*path*/,
         const string &/*service*/) {
       return test_->proxy_.release();
@@ -307,10 +306,10 @@
 TEST_F(ModemManager1Test, Connect) {
   Error e;
 
-  EXPECT_CALL(*proxy_, GetManagedObjects(_, _));
+  EXPECT_CALL(*proxy_, GetManagedObjects(_, _, _));
 
   modem_manager_.Connect(kOwner);
-  modem_manager_.OnGetManagedObjectsCallback(GetModemWithProperties(), e, NULL);
+  modem_manager_.OnGetManagedObjectsReply(GetModemWithProperties(), e);
   EXPECT_EQ(1, modem_manager_.modems_.size());
   EXPECT_TRUE(ContainsKey(modem_manager_.modems_, kModemPath));
   // TODO(rochberg): As mm1::connect gets more interesting, this will
@@ -318,37 +317,35 @@
 }
 
 TEST_F(ModemManager1Test, AddRemoveInterfaces) {
-  EXPECT_CALL(*proxy_, GetManagedObjects(_, _));
+  EXPECT_CALL(*proxy_, GetManagedObjects(_, _, _));
   modem_manager_.Connect(kOwner);
 
   // Have nothing come back from GetManagedObjects
-  modem_manager_.OnGetManagedObjectsCallback(DBusObjectsWithProperties(),
-                                             Error(),
-                                             NULL);
+  modem_manager_.OnGetManagedObjectsReply(DBusObjectsWithProperties(), Error());
   EXPECT_EQ(0, modem_manager_.modems_.size());
 
   // Add an object that doesn't have a modem interface.  Nothing should be added
-  modem_manager_.OnInterfacesAdded(kModemPath,
-                                   DBusInterfaceToProperties());
+  modem_manager_.OnInterfacesAddedSignal(kModemPath,
+                                         DBusInterfaceToProperties());
   EXPECT_EQ(0, modem_manager_.modems_.size());
 
   // Actually add a modem
-  modem_manager_.OnInterfacesAdded(kModemPath,
-                                   GetModemWithProperties()[kModemPath]);
+  modem_manager_.OnInterfacesAddedSignal(kModemPath,
+                                         GetModemWithProperties()[kModemPath]);
   EXPECT_EQ(1, modem_manager_.modems_.size());
 
   // Remove an irrelevant interface
   vector<string> not_including_modem_interface;
   not_including_modem_interface.push_back("not.a.modem.interface");
-  modem_manager_.OnInterfacesRemoved(kModemPath,
-                                     not_including_modem_interface);
+  modem_manager_.OnInterfacesRemovedSignal(kModemPath,
+                                           not_including_modem_interface);
   EXPECT_EQ(1, modem_manager_.modems_.size());
 
   // Remove the modem
   vector<string> with_modem_interface;
   with_modem_interface.push_back(ModemManager1::kDBusInterfaceModem);
-  modem_manager_.OnInterfacesRemoved(kModemPath,
-                                     with_modem_interface);
+  modem_manager_.OnInterfacesRemovedSignal(kModemPath,
+                                           with_modem_interface);
   EXPECT_EQ(0, modem_manager_.modems_.size());
 }
 }  // namespace shill
diff --git a/modem_proxy.cc b/modem_proxy.cc
index 0a7b85a..e17b871 100644
--- a/modem_proxy.cc
+++ b/modem_proxy.cc
@@ -4,86 +4,113 @@
 
 #include "shill/modem_proxy.h"
 
+#include <base/bind.h>
 #include <base/logging.h>
 
 #include "shill/cellular_error.h"
 #include "shill/error.h"
 
+using base::Bind;
+using base::Callback;
 using std::string;
 
 namespace shill {
 
-ModemProxy::ModemProxy(ModemProxyDelegate *delegate,
-                       DBus::Connection *connection,
+typedef Callback<void(const ModemHardwareInfo &,
+                      const Error &)> ModemInfoCallback;
+
+ModemProxy::ModemProxy(DBus::Connection *connection,
                        const string &path,
                        const string &service)
-    : proxy_(delegate, connection, path, service) {}
+    : proxy_(connection, path, service) {}
 
 ModemProxy::~ModemProxy() {}
 
-// 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) {
+void ModemProxy::set_state_changed_callback(
+      const ModemStateChangedSignalCallback &callback) {
+  proxy_.set_state_changed_callback(callback);
+}
+
+void ModemProxy::Enable(bool enable, Error *error,
+                        const ResultCallback &callback, int timeout) {
   VLOG(2) << __func__ << "(" << enable << ", " << timeout << ")";
-  proxy_.Enable(enable, call_handler, timeout);
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.Enable(enable, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-// 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);
+void ModemProxy::Disconnect(Error *error, const ResultCallback &callback,
+                            int timeout) {
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.Disconnect(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-void ModemProxy::Disconnect() {
-  proxy_.Disconnect();
+void ModemProxy::GetModemInfo(Error *error,
+                              const ModemInfoCallback &callback,
+                              int timeout) {
+  scoped_ptr<ModemInfoCallback> cb(new ModemInfoCallback(callback));
+  try {
+    proxy_.GetInfo(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-void ModemProxy::GetModemInfo(AsyncCallHandler *call_handler, int timeout) {
-  proxy_.GetInfo(call_handler, timeout);
-}
-
-ModemProxy::Proxy::Proxy(ModemProxyDelegate *delegate,
-                         DBus::Connection *connection,
+ModemProxy::Proxy::Proxy(DBus::Connection *connection,
                          const string &path,
                          const string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()),
-      delegate_(delegate) {}
+    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
 
 ModemProxy::Proxy::~Proxy() {}
 
+void ModemProxy::Proxy::set_state_changed_callback(
+      const ModemStateChangedSignalCallback &callback) {
+  state_changed_callback_ = callback;
+}
+
 void ModemProxy::Proxy::StateChanged(
     const uint32 &old, const uint32 &_new, const uint32 &reason) {
   VLOG(2) << __func__ << "(" << old << ", " << _new << ", " << reason << ")";
-  delegate_->OnModemStateChanged(old, _new, reason);
+  state_changed_callback_.Run(old, _new, reason);
 }
 
 void ModemProxy::Proxy::EnableCallback(const DBus::Error &dberror, void *data) {
   VLOG(2) << __func__;
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
   CellularError::FromDBusError(dberror, &error);
-  delegate_->OnModemEnableCallback(error, call_handler);
+  callback->Run(error);
 }
 
 void ModemProxy::Proxy::GetInfoCallback(const ModemHardwareInfo &info,
                                         const DBus::Error &dberror,
                                         void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ModemInfoCallback> callback(
+      reinterpret_cast<ModemInfoCallback *>(data));
   Error error;
   CellularError::FromDBusError(dberror, &error);
-  delegate_->OnGetModemInfoCallback(info, error, call_handler);
+  callback->Run(info, error);
 }
 
 void ModemProxy::Proxy::DisconnectCallback(const DBus::Error &dberror,
                                            void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
   CellularError::FromDBusError(dberror, &error);
-  delegate_->OnDisconnectCallback(error, call_handler);
+  callback->Run(error);
 }
 
 }  // namespace shill
diff --git a/modem_proxy.h b/modem_proxy.h
index d5021f6..8e21bf0 100644
--- a/modem_proxy.h
+++ b/modem_proxy.h
@@ -18,31 +18,35 @@
 class ModemProxy : public ModemProxyInterface {
  public:
   // Constructs a ModemManager.Modem DBus object proxy at |path| owned by
-  // |service|. Caught signals will be dispatched to |delegate|.
-  ModemProxy(ModemProxyDelegate *delegate,
-             DBus::Connection *connection,
+  // |service|.
+  ModemProxy(DBus::Connection *connection,
              const std::string &path,
              const std::string &service);
   virtual ~ModemProxy();
 
   // Inherited from ModemProxyInterface.
-  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 void GetModemInfo(AsyncCallHandler *call_handler, int timeout);
+  virtual void Enable(bool enable, Error *error,
+                      const ResultCallback &callback, int timeout);
+  virtual void Disconnect(Error *error, const ResultCallback &callback,
+                          int timeout);
+  virtual void GetModemInfo(Error *error, const ModemInfoCallback &callback,
+                            int timeout);
+
+  virtual void set_state_changed_callback(
+      const ModemStateChangedSignalCallback &callback);
 
  private:
   class Proxy : public org::freedesktop::ModemManager::Modem_proxy,
                 public DBus::ObjectProxy {
    public:
-    Proxy(ModemProxyDelegate *delegate,
-          DBus::Connection *connection,
+    Proxy(DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
 
+    void set_state_changed_callback(
+        const ModemStateChangedSignalCallback &callback);
+
    private:
     // Signal callbacks inherited from ModemManager::Modem_proxy.
     virtual void StateChanged(
@@ -54,7 +58,7 @@
                                  const DBus::Error &dberror, void *data);
     virtual void DisconnectCallback(const DBus::Error &dberror, void *data);
 
-    ModemProxyDelegate *delegate_;
+    ModemStateChangedSignalCallback state_changed_callback_;
 
     DISALLOW_COPY_AND_ASSIGN(Proxy);
   };
diff --git a/modem_proxy_interface.h b/modem_proxy_interface.h
index 0005087..1d89c0f 100644
--- a/modem_proxy_interface.h
+++ b/modem_proxy_interface.h
@@ -9,49 +9,37 @@
 
 #include <dbus-c++/types.h>
 
+#include "shill/callbacks.h"
 #include "shill/dbus_properties.h"
 
 namespace shill {
 
-class AsyncCallHandler;
+class CallContext;
 class Error;
 
 typedef DBus::Struct<std::string, std::string, std::string> ModemHardwareInfo;
 
+typedef base::Callback<void(uint32,
+                            uint32, uint32)> ModemStateChangedSignalCallback;
+typedef base::Callback<void(const ModemHardwareInfo &,
+                            const Error &)> ModemInfoCallback;
+
 // These are the methods that a ModemManager.Modem 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.
+// made asynchronously.
 class ModemProxyInterface {
  public:
   virtual ~ModemProxyInterface() {}
 
-  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 void GetModemInfo(AsyncCallHandler *call_handler, int timeout) = 0;
-};
+  virtual void Enable(bool enable, Error *error,
+                      const ResultCallback &callback, int timeout) = 0;
+  virtual void Disconnect(Error *error, const ResultCallback &callback,
+                          int timeout) = 0;
+  virtual void GetModemInfo(Error *error, const ModemInfoCallback &callback,
+                            int timeout) = 0;
 
-// 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;
-
-  // 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;
+  virtual void set_state_changed_callback(
+      const ModemStateChangedSignalCallback &callback) = 0;
 };
 
 }  // namespace shill
diff --git a/modem_simple_proxy.cc b/modem_simple_proxy.cc
index a902b16..3470e29 100644
--- a/modem_simple_proxy.cc
+++ b/modem_simple_proxy.cc
@@ -4,55 +4,77 @@
 
 #include "shill/modem_simple_proxy.h"
 
+#include <base/bind.h>
+
 #include "shill/cellular_error.h"
 #include "shill/error.h"
 
+using base::Bind;
+using base::Callback;
 using std::string;
 
 namespace shill {
 
-ModemSimpleProxy::ModemSimpleProxy(ModemSimpleProxyDelegate *delegate,
-                                   DBus::Connection *connection,
+typedef Callback<void(const DBusPropertiesMap &,
+                      const Error &)> ModemStatusCallback;
+
+ModemSimpleProxy::ModemSimpleProxy(DBus::Connection *connection,
                                    const string &path,
                                    const string &service)
-    : proxy_(delegate, connection, path, service) {}
+    : proxy_(connection, path, service) {}
 
 ModemSimpleProxy::~ModemSimpleProxy() {}
 
-void ModemSimpleProxy::GetModemStatus(AsyncCallHandler *call_handler,
+void ModemSimpleProxy::GetModemStatus(Error *error,
+                                      const DBusPropertyMapCallback &callback,
                                       int timeout) {
-  proxy_.GetStatus(call_handler, timeout);
+  scoped_ptr<DBusPropertyMapCallback> cb(new DBusPropertyMapCallback(callback));
+  try {
+    proxy_.GetStatus(cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
 void ModemSimpleProxy::Connect(const DBusPropertiesMap &properties,
-                               AsyncCallHandler *call_handler, int timeout) {
-  proxy_.Connect(properties, call_handler, timeout);
+                               Error *error,
+                               const ResultCallback &callback,
+                               int timeout) {
+  scoped_ptr<ResultCallback> cb(new ResultCallback(callback));
+  try {
+    proxy_.Connect(properties, cb.get(), timeout);
+    cb.release();
+  } catch (DBus::Error e) {
+    if (error)
+      CellularError::FromDBusError(e, error);
+  }
 }
 
-ModemSimpleProxy::Proxy::Proxy(ModemSimpleProxyDelegate *delegate,
-                               DBus::Connection *connection,
+ModemSimpleProxy::Proxy::Proxy(DBus::Connection *connection,
                                const string &path,
                                const string &service)
-    : DBus::ObjectProxy(*connection, path, service.c_str()),
-      delegate_(delegate) {}
+    : DBus::ObjectProxy(*connection, path, service.c_str()) {}
 
 ModemSimpleProxy::Proxy::~Proxy() {}
 
 void ModemSimpleProxy::Proxy::GetStatusCallback(const DBusPropertiesMap &props,
                                                 const DBus::Error &dberror,
                                                 void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<DBusPropertyMapCallback> callback(
+      reinterpret_cast<DBusPropertyMapCallback *>(data));
   Error error;
   CellularError::FromDBusError(dberror, &error);
-  delegate_->OnGetModemStatusCallback(props, error, call_handler);
+  callback->Run(props, error);
 }
 
 void ModemSimpleProxy::Proxy::ConnectCallback(const DBus::Error &dberror,
                                               void *data) {
-  AsyncCallHandler *call_handler = reinterpret_cast<AsyncCallHandler *>(data);
+  scoped_ptr<ResultCallback> callback(reinterpret_cast<ResultCallback *>(data));
   Error error;
   CellularError::FromDBusError(dberror, &error);
-  delegate_->OnConnectCallback(error, call_handler);
+  callback->Run(error);
 }
 
 }  // namespace shill
diff --git a/modem_simple_proxy.h b/modem_simple_proxy.h
index 3193366..ff99d65 100644
--- a/modem_simple_proxy.h
+++ b/modem_simple_proxy.h
@@ -17,23 +17,25 @@
 // A proxy to ModemManager.Modem.Simple.
 class ModemSimpleProxy : public ModemSimpleProxyInterface {
  public:
-  ModemSimpleProxy(ModemSimpleProxyDelegate *delegate,
-                   DBus::Connection *connection,
+  ModemSimpleProxy(DBus::Connection *connection,
                    const std::string &path,
                    const std::string &service);
   virtual ~ModemSimpleProxy();
 
   // Inherited from ModemSimpleProxyInterface.
-  virtual void GetModemStatus(AsyncCallHandler *call_handler, int timeout);
+  virtual void GetModemStatus(Error *error,
+                              const DBusPropertyMapCallback &callback,
+                              int timeout);
   virtual void Connect(const DBusPropertiesMap &properties,
-                       AsyncCallHandler *call_handler, int timeout);
+                       Error *error,
+                       const ResultCallback &callback,
+                       int timeout);
 
  private:
   class Proxy : public org::freedesktop::ModemManager::Modem::Simple_proxy,
                 public DBus::ObjectProxy {
    public:
-    Proxy(ModemSimpleProxyDelegate *delegate,
-          DBus::Connection *connection,
+    Proxy(DBus::Connection *connection,
           const std::string &path,
           const std::string &service);
     virtual ~Proxy();
@@ -47,8 +49,6 @@
                                    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 c4a75a6..80dcb3b 100644
--- a/modem_simple_proxy_interface.h
+++ b/modem_simple_proxy_interface.h
@@ -5,39 +5,27 @@
 #ifndef SHILL_MODEM_SIMPLE_PROXY_INTERFACE_
 #define SHILL_MODEM_SIMPLE_PROXY_INTERFACE_
 
+#include "shill/callbacks.h"
 #include "shill/dbus_properties.h"
 
 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.
+// All calls are made asynchronously.
 class ModemSimpleProxyInterface {
  public:
   virtual ~ModemSimpleProxyInterface() {}
 
-  virtual void GetModemStatus(AsyncCallHandler *call_handler, int timeout) = 0;
+  virtual void GetModemStatus(Error *error,
+                              const DBusPropertyMapCallback &callback,
+                              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;
+                       Error *error, const ResultCallback &callback,
+                       int timeout) = 0;
 };
 
 }  // namespace shill
-
 #endif  // SHILL_MODEM_SIMPLE_PROXY_INTERFACE_
diff --git a/openvpn_driver.cc b/openvpn_driver.cc
index 4e31029..1e92edf 100644
--- a/openvpn_driver.cc
+++ b/openvpn_driver.cc
@@ -134,7 +134,7 @@
   rpc_task_.reset();
   if (device_) {
     int interface_index = device_->interface_index();
-    device_->Stop();
+    device_->SetEnabled(false);
     device_ = NULL;
     device_info_->DeleteInterface(interface_index);
   }
@@ -209,8 +209,10 @@
   CHECK(!device_);
   device_ = new VPN(control_, dispatcher_, metrics_, manager_,
                     link_name, interface_index);
-  device_->Start();
+
+  device_->SetEnabled(true);
   device_->SelectService(service_);
+
   rpc_task_.reset(new RPCTask(control_, this));
   if (!SpawnOpenVPN()) {
     Cleanup(Service::kStateFailure);
diff --git a/openvpn_driver_unittest.cc b/openvpn_driver_unittest.cc
index e4ede64..016fb04 100644
--- a/openvpn_driver_unittest.cc
+++ b/openvpn_driver_unittest.cc
@@ -405,7 +405,7 @@
   driver_->service_ = service_;
   EXPECT_CALL(glib_, SourceRemove(kTag));
   EXPECT_CALL(glib_, SpawnClosePID(kPID));
-  EXPECT_CALL(*device_, Stop());
+  EXPECT_CALL(*device_, SetEnabled(false));
   EXPECT_CALL(device_info_, DeleteInterface(kInterfaceIndex));
   EXPECT_CALL(*service_, SetState(Service::kStateFailure));
   driver_->Cleanup(Service::kStateFailure);
@@ -452,7 +452,7 @@
 TEST_F(OpenVPNDriverTest, Disconnect) {
   driver_->device_ = device_;
   driver_->service_ = service_;
-  EXPECT_CALL(*device_, Stop());
+  EXPECT_CALL(*device_, SetEnabled(false));
   EXPECT_CALL(device_info_, DeleteInterface(kInterfaceIndex));
   EXPECT_CALL(*service_, SetState(Service::kStateIdle));
   driver_->Disconnect();
diff --git a/portal_detector.cc b/portal_detector.cc
index a0a8fb2..2072efa 100644
--- a/portal_detector.cc
+++ b/portal_detector.cc
@@ -22,7 +22,6 @@
 
 using base::Bind;
 using base::Callback;
-using base::Unretained;
 using base::StringPrintf;
 using std::string;
 
@@ -58,9 +57,11 @@
       weak_ptr_factory_(this),
       portal_result_callback_(callback),
       request_read_callback_(
-          Bind(&PortalDetector::RequestReadCallback, Unretained(this))),
+          Bind(&PortalDetector::RequestReadCallback,
+               weak_ptr_factory_.GetWeakPtr())),
       request_result_callback_(
-          Bind(&PortalDetector::RequestResultCallback, Unretained(this))),
+          Bind(&PortalDetector::RequestResultCallback,
+               weak_ptr_factory_.GetWeakPtr())),
       time_(Time::GetInstance()) { }
 
 PortalDetector::~PortalDetector() {
diff --git a/proxy_factory.cc b/proxy_factory.cc
index 4a5c2ce..ba06e07 100644
--- a/proxy_factory.cc
+++ b/proxy_factory.cc
@@ -43,10 +43,9 @@
 }
 
 DBusObjectManagerProxyInterface *ProxyFactory::CreateDBusObjectManagerProxy(
-    DBusObjectManagerProxyDelegate *delegate,
     const string &path,
     const string &service) {
-  return new DBusObjectManagerProxy(delegate, connection(), path, service);
+  return new DBusObjectManagerProxy(connection(), path, service);
 }
 
 DBusPropertiesProxyInterface *ProxyFactory::CreateDBusPropertiesProxy(
@@ -64,38 +63,33 @@
 }
 
 ModemProxyInterface *ProxyFactory::CreateModemProxy(
-    ModemProxyDelegate *delegate,
     const string &path,
     const string &service) {
-  return new ModemProxy(delegate, connection(), path, service);
+  return new ModemProxy(connection(), path, service);
 }
 
 ModemSimpleProxyInterface *ProxyFactory::CreateModemSimpleProxy(
-    ModemSimpleProxyDelegate *delegate,
     const string &path,
     const string &service) {
-  return new ModemSimpleProxy(delegate, connection(), path, service);
+  return new ModemSimpleProxy(connection(), path, service);
 }
 
 ModemCDMAProxyInterface *ProxyFactory::CreateModemCDMAProxy(
-    ModemCDMAProxyDelegate *delegate,
     const string &path,
     const string &service) {
-  return new ModemCDMAProxy(delegate, connection(), path, service);
+  return new ModemCDMAProxy(connection(), path, service);
 }
 
 ModemGSMCardProxyInterface *ProxyFactory::CreateModemGSMCardProxy(
-    ModemGSMCardProxyDelegate *delegate,
     const string &path,
     const string &service) {
-  return new ModemGSMCardProxy(delegate, connection(), path, service);
+  return new ModemGSMCardProxy(connection(), path, service);
 }
 
 ModemGSMNetworkProxyInterface *ProxyFactory::CreateModemGSMNetworkProxy(
-    ModemGSMNetworkProxyDelegate *delegate,
     const string &path,
     const string &service) {
-  return new ModemGSMNetworkProxy(delegate, connection(), path, service);
+  return new ModemGSMNetworkProxy(connection(), path, service);
 }
 
 PowerManagerProxyInterface *ProxyFactory::CreatePowerManagerProxy(
diff --git a/proxy_factory.h b/proxy_factory.h
index 02db954..0e4da8e 100644
--- a/proxy_factory.h
+++ b/proxy_factory.h
@@ -16,22 +16,16 @@
 
 namespace shill {
 
-class DBusObjectManagerProxyDelegate;
 class DBusObjectManagerProxyInterface;
 class DBusPropertiesProxyDelegate;
 class DBusPropertiesProxyInterface;
 class DHCPProxyInterface;
-class ModemCDMAProxyDelegate;
 class ModemCDMAProxyInterface;
-class ModemGSMCardProxyDelegate;
 class ModemGSMCardProxyInterface;
-class ModemGSMNetworkProxyDelegate;
 class ModemGSMNetworkProxyInterface;
 class ModemManagerClassic;
 class ModemManagerProxyInterface;
-class ModemProxyDelegate;
 class ModemProxyInterface;
-class ModemSimpleProxyDelegate;
 class ModemSimpleProxyInterface;
 class PowerManagerProxyDelegate;
 class PowerManagerProxyInterface;
@@ -50,7 +44,6 @@
   virtual void Init();
 
   virtual DBusObjectManagerProxyInterface *CreateDBusObjectManagerProxy(
-      DBusObjectManagerProxyDelegate *delegate,
       const std::string &path,
       const std::string &service);
 
@@ -64,27 +57,22 @@
       const std::string &path,
       const std::string &service);
 
-  virtual ModemProxyInterface *CreateModemProxy(ModemProxyDelegate *delegate,
-                                                const std::string &path,
+  virtual ModemProxyInterface *CreateModemProxy(const std::string &path,
                                                 const std::string &service);
 
   virtual ModemSimpleProxyInterface *CreateModemSimpleProxy(
-      ModemSimpleProxyDelegate *delegate,
       const std::string &path,
       const std::string &service);
 
   virtual ModemCDMAProxyInterface *CreateModemCDMAProxy(
-      ModemCDMAProxyDelegate *delegate,
       const std::string &path,
       const std::string &service);
 
   virtual ModemGSMCardProxyInterface *CreateModemGSMCardProxy(
-      ModemGSMCardProxyDelegate *delegate,
       const std::string &path,
       const std::string &service);
 
   virtual ModemGSMNetworkProxyInterface *CreateModemGSMNetworkProxy(
-      ModemGSMNetworkProxyDelegate *delegate,
       const std::string &path,
       const std::string &service);
 
diff --git a/service.cc b/service.cc
index 4e7eba0..6de5cf8 100644
--- a/service.cc
+++ b/service.cc
@@ -222,11 +222,10 @@
 }
 
 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);
+                                    Error *error,
+                                    const ResultCallback &/*callback*/) {
+  Error::PopulateAndLog(error, Error::kNotSupported,
+                        "Service doesn't support cellular modem activation.");
 }
 
 bool Service::TechnologyIs(const Technology::Identifier /*type*/) const {
diff --git a/service.h b/service.h
index 968e3a2..e79a1d4 100644
--- a/service.h
+++ b/service.h
@@ -13,7 +13,9 @@
 #include <base/memory/scoped_ptr.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
+#include "shill/adaptor_interfaces.h"
 #include "shill/accessor_interface.h"
+#include "shill/callbacks.h"
 #include "shill/property_store.h"
 #include "shill/refptr_types.h"
 #include "shill/sockets.h"
@@ -34,7 +36,6 @@
 class KeyValueStore;
 class Manager;
 class Metrics;
-class ReturnerInterface;
 class ServiceAdaptorInterface;
 class StoreInterface;
 
@@ -151,7 +152,8 @@
 
   // The default implementation returns the error kInvalidArguments.
   virtual void ActivateCellularModem(const std::string &carrier,
-                                     ReturnerInterface *returner);
+                                     Error *error,
+                                     const ResultCallback &callback);
 
   // 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 15a35b3..92f19aa 100644
--- a/service_dbus_adaptor.cc
+++ b/service_dbus_adaptor.cc
@@ -104,9 +104,10 @@
 void ServiceDBusAdaptor::ActivateCellularModem(const string &carrier,
                                                ::DBus::Error &error) {
   VLOG(2) << __func__;
-  Returner *returner = Returner::Create(this);
-  service_->ActivateCellularModem(carrier, returner);
-  returner->DelayOrReturn(&error);
+  Error e(Error::kOperationInitiated);
+  DBus::Tag *tag = new DBus::Tag();
+  service_->ActivateCellularModem(carrier, &e, GetMethodReplyCallback(tag));
+  ReturnResultOrDefer(tag, e, &error);
 }
 
 }  // namespace shill
diff --git a/service_unittest.cc b/service_unittest.cc
index 6e98166..fc1a0b9 100644
--- a/service_unittest.cc
+++ b/service_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include <base/bind.h>
 #include <chromeos/dbus/service_constants.h>
 #include <dbus-c++/dbus.h>
 #include <gtest/gtest.h>
@@ -24,6 +25,8 @@
 #include "shill/property_store_unittest.h"
 #include "shill/service_under_test.h"
 
+using base::Bind;
+using base::Unretained;
 using std::map;
 using std::string;
 using std::vector;
@@ -52,7 +55,10 @@
 
   virtual ~ServiceTest() {}
 
+  MOCK_METHOD1(TestCallback, void(const Error &error));
+
  protected:
+
   MockManager mock_manager_;
   scoped_refptr<ServiceUnderTest> service_;
   string storage_id_;
@@ -271,11 +277,12 @@
 }
 
 TEST_F(ServiceTest, ActivateCellularModem) {
-  MockReturner returner;
-  EXPECT_CALL(returner, Return()).Times(0);
-  EXPECT_CALL(returner, ReturnError(_));
+  ResultCallback callback =
+      Bind(&ServiceTest::TestCallback, Unretained(this));
+  EXPECT_CALL(*this, TestCallback(_)).Times(0);
   Error error;
-  service_->ActivateCellularModem("Carrier", &returner);
+  service_->ActivateCellularModem("Carrier", &error, callback);
+  EXPECT_TRUE(error.IsFailure());
 }
 
 TEST_F(ServiceTest, MakeFavorite) {
diff --git a/vpn.cc b/vpn.cc
index 65af8b0..37d7b9a 100644
--- a/vpn.cc
+++ b/vpn.cc
@@ -25,10 +25,16 @@
 
 VPN::~VPN() {}
 
-void VPN::Start() {
-  Device::Start();
+void VPN::Start(Error *error, const EnabledStateChangedCallback &callback) {
   RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
                                                 IFF_UP);
+  if (error)
+    error->Reset();
+}
+
+void VPN::Stop(Error *error, const EnabledStateChangedCallback &callback) {
+  if (error)
+    error->Reset();
 }
 
 bool VPN::TechnologyIs(const Technology::Identifier type) const {
diff --git a/vpn.h b/vpn.h
index 4fea4b4..e852ba2 100644
--- a/vpn.h
+++ b/vpn.h
@@ -20,7 +20,8 @@
 
   virtual ~VPN();
 
-  void Start();
+  virtual void Start(Error *error, const EnabledStateChangedCallback &callback);
+  virtual void Stop(Error *error, const EnabledStateChangedCallback &callback);
 
   virtual bool TechnologyIs(const Technology::Identifier type) const;
 
diff --git a/wifi.cc b/wifi.cc
index 5dc0f56..929c2fa 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -36,6 +36,7 @@
 #include "shill/profile.h"
 #include "shill/property_accessor.h"
 #include "shill/proxy_factory.h"
+#include "shill/rtnl_handler.h"
 #include "shill/shill_time.h"
 #include "shill/store_interface.h"
 #include "shill/supplicant_interface_proxy_interface.h"
@@ -92,6 +93,7 @@
              address,
              interface_index,
              Technology::kWifi),
+      weak_ptr_factory_(this),
       proxy_factory_(ProxyFactory::GetInstance()),
       time_(Time::GetInstance()),
       supplicant_state_(kInterfaceStateUnknown),
@@ -133,7 +135,7 @@
 
 WiFi::~WiFi() {}
 
-void WiFi::Start() {
+void WiFi::Start(Error *error, const EnabledStateChangedCallback &callback) {
   ::DBus::Path interface_path;
 
   supplicant_process_proxy_.reset(
@@ -163,6 +165,8 @@
       proxy_factory_->CreateSupplicantInterfaceProxy(
           this, interface_path, wpa_supplicant::kDBusAddr));
 
+  RTNLHandler::GetInstance()->SetInterfaceFlags(interface_index(), IFF_UP,
+                                                IFF_UP);
   // TODO(quiche) Set ApScan=1 and BSSExpireAge=190, like flimflam does?
 
   // Clear out any networks that might previously have been configured
@@ -195,13 +199,15 @@
   // when the power state changes.
   manager()->power_manager()->AddStateChangeCallback(
       UniqueName(),
-      Bind(&WiFi::HandlePowerStateChange, this));
+      Bind(&WiFi::HandlePowerStateChange, weak_ptr_factory_.GetWeakPtr()));
 
   Scan(NULL);
-  Device::Start();
+  OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
+  if (error)
+    error->Reset();       // indicate immediate completion
 }
 
-void WiFi::Stop() {
+void WiFi::Stop(Error *error, const EnabledStateChangedCallback &callback) {
   VLOG(2) << "WiFi " << link_name() << " stopping.";
   // TODO(quiche): Remove interface from supplicant.
   supplicant_interface_proxy_.reset();  // breaks a reference cycle
@@ -221,7 +227,9 @@
   current_service_ = NULL;            // breaks a reference cycle
   pending_service_ = NULL;            // breaks a reference cycle
 
-  Device::Stop();
+  OnEnabledStateChanged(EnabledStateChangedCallback(), Error());
+  if (error)
+    error->Reset();       // indicate immediate completion
   // TODO(quiche): Anything else to do?
 
   VLOG(3) << "WiFi " << link_name() << " supplicant_process_proxy_ "
@@ -247,7 +255,7 @@
   // Needs to send a D-Bus message, but may be called from D-Bus
   // signal handler context (via Manager::RequestScan). So defer work
   // to event loop.
-  dispatcher()->PostTask(Bind(&WiFi::ScanTask, this));
+  dispatcher()->PostTask(Bind(&WiFi::ScanTask, weak_ptr_factory_.GetWeakPtr()));
 }
 
 bool WiFi::TechnologyIs(const Technology::Identifier type) const {
@@ -264,20 +272,24 @@
                     const map<string, ::DBus::Variant> &properties) {
   // Called from a D-Bus signal handler, and may need to send a D-Bus
   // message. So defer work to event loop.
-  dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask, this, path, properties));
+  dispatcher()->PostTask(Bind(&WiFi::BSSAddedTask,
+                              weak_ptr_factory_.GetWeakPtr(),
+                              path, properties));
 }
 
 void WiFi::BSSRemoved(const ::DBus::Path &path) {
   // Called from a D-Bus signal handler, and may need to send a D-Bus
   // message. So defer work to event loop.
-  dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask, this, path));
+  dispatcher()->PostTask(Bind(&WiFi::BSSRemovedTask,
+                              weak_ptr_factory_.GetWeakPtr(), path));
 }
 
 void WiFi::PropertiesChanged(const map<string, ::DBus::Variant> &properties) {
   LOG(INFO) << "In " << __func__ << "(): called";
   // Called from D-Bus signal handler, but may need to send a D-Bus
   // message. So defer work to event loop.
-  dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask, this, properties));
+  dispatcher()->PostTask(Bind(&WiFi::PropertiesChangedTask,
+                              weak_ptr_factory_.GetWeakPtr(), properties));
 }
 
 void WiFi::ScanDone() {
@@ -287,7 +299,8 @@
   // may require the the registration of new D-Bus objects. And such
   // registration can't be done in the context of a D-Bus signal
   // handler.
-  dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask, this));
+  dispatcher()->PostTask(Bind(&WiFi::ScanDoneTask,
+                              weak_ptr_factory_.GetWeakPtr()));
 }
 
 void WiFi::ConnectTo(WiFiService *service,
@@ -408,7 +421,8 @@
   // to event loop.
   if (!clear_cached_credentials_pending_) {
     clear_cached_credentials_pending_ = true;
-    dispatcher()->PostTask(Bind(&WiFi::ClearCachedCredentialsTask, this));
+    dispatcher()->PostTask(Bind(&WiFi::ClearCachedCredentialsTask,
+                                weak_ptr_factory_.GetWeakPtr()));
   }
 }
 
@@ -971,8 +985,9 @@
     scan_pending_ = true;
   } catch (const DBus::Error e) {  // NOLINT
     LOG(WARNING) << "Scan failed. Attempting to re-connect to the supplicant.";
-    Stop();
-    Start();
+    EnabledStateChangedCallback null_callback;
+    Stop(NULL, null_callback);
+    Start(NULL, null_callback);
   }
 }
 
diff --git a/wifi.h b/wifi.h
index 3363a98..3d37ab2 100644
--- a/wifi.h
+++ b/wifi.h
@@ -82,6 +82,8 @@
 #include <string>
 #include <vector>
 
+#include <base/callback_forward.h>
+#include <base/memory/weak_ptr.h>
 #include <dbus-c++/dbus.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
@@ -111,8 +113,8 @@
        int interface_index);
   virtual ~WiFi();
 
-  virtual void Start();
-  virtual void Stop();
+  virtual void Start(Error *error, const EnabledStateChangedCallback &callback);
+  virtual void Stop(Error *error, const EnabledStateChangedCallback &callback);
   virtual bool Load(StoreInterface *storage);
   virtual void Scan(Error *error);
   virtual bool TechnologyIs(const Technology::Identifier type) const;
@@ -229,6 +231,8 @@
   // scan is initiated.
   void HandlePowerStateChange(PowerManager::SuspendState new_state);
 
+  base::WeakPtrFactory<WiFi> weak_ptr_factory_;
+
   // Store cached copies of singletons for speed/ease of testing.
   ProxyFactory *proxy_factory_;
   Time *time_;
diff --git a/wifi_service.cc b/wifi_service.cc
index 8450823..7596206 100644
--- a/wifi_service.cc
+++ b/wifi_service.cc
@@ -7,7 +7,6 @@
 #include <string>
 #include <utility>
 
-#include <base/bind.h>
 #include <base/logging.h>
 #include <base/stringprintf.h>
 #include <base/string_number_conversions.h>
@@ -29,7 +28,6 @@
 #include "shill/wifi_endpoint.h"
 #include "shill/wpa_supplicant.h"
 
-using base::Bind;
 using std::set;
 using std::string;
 using std::vector;
@@ -130,28 +128,13 @@
     //    If we queued AutoConnects, we could build a long queue of
     //    useless work (one AutoConnect per Service), which blocks
     //    more timely work.
-    ConnectTask();
+    Connect(NULL);
   } else {
     LOG(INFO) << "Suppressed autoconnect to " << friendly_name() << " "
               << "(" << reason << ")";
   }
 }
 
-void WiFiService::Connect(Error */*error*/) {
-  LOG(INFO) << "In " << __func__ << "():";
-  // Defer handling, since dbus-c++ does not permit us to send an
-  // outbound request while processing an inbound one.
-  dispatcher()->PostTask(Bind(&WiFiService::ConnectTask, this));
-}
-
-void WiFiService::Disconnect(Error *error) {
-  LOG(INFO) << __func__;
-  Service::Disconnect(error);
-  // Defer handling, since dbus-c++ does not permit us to send an
-  // outbound request while processing an inbound one.
-  dispatcher()->PostTask(Bind(&WiFiService::DisconnectTask, this));
-}
-
 bool WiFiService::TechnologyIs(const Technology::Identifier type) const {
   return wifi_->TechnologyIs(type);
 }
@@ -398,7 +381,8 @@
               this, set, clear, default_value)));
 }
 
-void WiFiService::ConnectTask() {
+void WiFiService::Connect(Error */*error*/) {
+  LOG(INFO) << "In " << __func__ << "():";
   std::map<string, DBus::Variant> params;
   DBus::MessageIter writer;
 
@@ -456,7 +440,9 @@
   wifi_->ConnectTo(this, params);
 }
 
-void WiFiService::DisconnectTask() {
+void WiFiService::Disconnect(Error *error) {
+  LOG(INFO) << __func__;
+  Service::Disconnect(error);
   wifi_->DisconnectFrom(this);
 }
 
diff --git a/wifi_service.h b/wifi_service.h
index f1f04fc..33c78c6 100644
--- a/wifi_service.h
+++ b/wifi_service.h
@@ -129,9 +129,6 @@
       void(WiFiService::*clear)(Error *error),
       const std::string *default_value);
 
-  void ConnectTask();
-  void DisconnectTask();
-
   std::string GetDeviceRpcId(Error *error);
   void ClearPassphrase(Error *error);
   void UpdateConnectable();
diff --git a/wifi_service_unittest.cc b/wifi_service_unittest.cc
index a966052..1fbd064 100644
--- a/wifi_service_unittest.cc
+++ b/wifi_service_unittest.cc
@@ -335,7 +335,7 @@
                                                    false);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), WPASecurityArgs()));
-  wifi_service->ConnectTask();
+  wifi_service->Connect(NULL);
 }
 
 TEST_F(WiFiServiceTest, ConnectTaskRSN) {
@@ -351,7 +351,7 @@
                                                    false);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), WPASecurityArgs()));
-  wifi_service->ConnectTask();
+  wifi_service->Connect(NULL);
 }
 
 TEST_F(WiFiServiceTest, ConnectTaskPSK) {
@@ -367,7 +367,7 @@
                                                    false);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), WPASecurityArgs()));
-  wifi_service->ConnectTask();
+  wifi_service->Connect(NULL);
 }
 
 TEST_F(WiFiServiceTest, ConnectTask8021x) {
@@ -386,7 +386,7 @@
   wifi_service->set_eap(eap);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), EAPSecurityArgs()));
-  wifi_service->ConnectTask();
+  wifi_service->Connect(NULL);
 }
 
 MATCHER(WEPSecurityArgsKeyIndex0, "") {
@@ -436,27 +436,27 @@
   wifi_service->SetPassphrase("0:abcdefghijklm", &error);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), WEPSecurityArgsKeyIndex0()));
-  wifi_service->ConnectTask();
+  wifi_service->Connect(NULL);
 
   wifi_service->SetPassphrase("abcdefghijklm", &error);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), WEPSecurityArgsKeyIndex0()));
-  wifi_service->ConnectTask();
+  wifi_service->Connect(NULL);
 
   wifi_service->SetPassphrase("1:abcdefghijklm", &error);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), WEPSecurityArgsKeyIndex1()));
-  wifi_service->ConnectTask();
+  wifi_service->Connect(NULL);
 
   wifi_service->SetPassphrase("2:abcdefghijklm", &error);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), WEPSecurityArgsKeyIndex2()));
-  wifi_service->ConnectTask();
+  wifi_service->Connect(NULL);
 
   wifi_service->SetPassphrase("3:abcdefghijklm", &error);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), WEPSecurityArgsKeyIndex3()));
-  wifi_service->ConnectTask();
+  wifi_service->Connect(NULL);
 }
 
 
@@ -485,7 +485,7 @@
   wifi_service->set_eap(eap);
   EXPECT_CALL(*wifi(),
               ConnectTo(wifi_service.get(), DynamicWEPArgs()));
-  wifi_service->ConnectTask();
+  wifi_service->Connect(NULL);
 }
 
 TEST_F(WiFiServiceTest, LoadHidden) {
diff --git a/wifi_unittest.cc b/wifi_unittest.cc
index b26ce12..d589bc9 100644
--- a/wifi_unittest.cc
+++ b/wifi_unittest.cc
@@ -215,6 +215,7 @@
     EXPECT_CALL(*manager(), UpdateService(_)).Times(AnyNumber());
     EXPECT_CALL(*power_manager_, RemoveStateChangeCallback(wifi_->UniqueName()))
         .Times(AnyNumber());
+    wifi_->SelectService(NULL);
     if (supplicant_bss_proxy_.get()) {
       EXPECT_CALL(*supplicant_bss_proxy_, Die());
     }
@@ -222,7 +223,7 @@
     // must Stop WiFi instance, to clear its list of services.
     // otherwise, the WiFi instance will not be deleted. (because
     // services reference a WiFi instance, creating a cycle.)
-    wifi_->Stop();
+    wifi_->Stop(NULL, ResultCallback());
     wifi_->set_dhcp_provider(NULL);
   }
 
@@ -348,14 +349,13 @@
   void StartWiFi() {
     EXPECT_CALL(*power_manager_, AddStateChangeCallback(wifi_->UniqueName(), _))
         .WillOnce(SaveArg<1>(&power_state_callback_));
-    wifi_->Start();
+    wifi_->Start(NULL, ResultCallback());
   }
   void StopWiFi() {
     EXPECT_CALL(*power_manager_,
                 RemoveStateChangeCallback(wifi_->UniqueName()));
-    wifi_->Stop();
+    wifi_->Stop(NULL, ResultCallback());
   }
-
   void GetOpenService(const char *service_type,
                       const char *ssid,
                       const char *mode,