Sync update_engine from chromiumos/platform2.

The update_engine project was synced from chromium gerrit instance with
the latest changes.You can find the old version here:
https://chromium.googlesource.com/chromiumos/platform2/+/830d80acf26c8232d4b6e14916b7dad543bcf5ec/update_engine

BUG=b/23084607
diff --git a/action.h b/action.h
index 53786d1..3cbc26b 100644
--- a/action.h
+++ b/action.h
@@ -30,12 +30,12 @@
 // How it works:
 //
 // First off, there is only one thread and all I/O should be asynchronous.
-// A glib main loop blocks whenever there is no work to be done. This happens
+// A message loop blocks whenever there is no work to be done. This happens
 // where there is no CPU work to be done and no I/O ready to transfer in or
-// out. Two kinds of events can wake up the main loop: timer alarm or file
-// descriptors. If either of these happens, glib finds out the owner of what
-// fired and calls the appropriate code to handle it. As such, all the code
-// in the Action* classes and the code that is calls is non-blocking.
+// out. Two kinds of events can wake up the message loop: timer alarm or file
+// descriptors. If either of these happens, the message loop finds out the owner
+// of what fired and calls the appropriate code to handle it. As such, all the
+// code in the Action* classes and the code that is calls is non-blocking.
 //
 // An ActionProcessor contains a queue of Actions to perform. When
 // ActionProcessor::StartProcessing() is called, it executes the first action.
diff --git a/chrome_browser_proxy_resolver.cc b/chrome_browser_proxy_resolver.cc
index 116c67a..932fb9c 100644
--- a/chrome_browser_proxy_resolver.cc
+++ b/chrome_browser_proxy_resolver.cc
@@ -12,11 +12,7 @@
 #include <base/bind.h>
 #include <base/strings/string_tokenizer.h>
 #include <base/strings/string_util.h>
-#include <dbus/dbus-glib-lowlevel.h>
-#include <dbus/dbus-glib.h>
 
-#include "update_engine/dbus_constants.h"
-#include "update_engine/glib_utils.h"
 #include "update_engine/utils.h"
 
 namespace chromeos_update_engine {
@@ -29,22 +25,10 @@
 using std::pair;
 using std::string;
 
-#define LIB_CROS_PROXY_RESOLVE_NAME "ProxyResolved"
-#define LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE                 \
-  "org.chromium.UpdateEngineLibcrosProxyResolvedInterface"
 const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
-const char kLibCrosServicePath[] = "/org/chromium/LibCrosService";
-const char kLibCrosServiceInterface[] = "org.chromium.LibCrosServiceInterface";
-const char kLibCrosServiceResolveNetworkProxyMethodName[] =
-    "ResolveNetworkProxy";
-const char kLibCrosProxyResolveName[] = LIB_CROS_PROXY_RESOLVE_NAME;
+const char kLibCrosProxyResolveName[] = "ProxyResolved";
 const char kLibCrosProxyResolveSignalInterface[] =
-    LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE;
-const char kLibCrosProxyResolveSignalFilter[] = "type='signal', "
-    "interface='" LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE "', "
-    "member='" LIB_CROS_PROXY_RESOLVE_NAME "'";
-#undef LIB_CROS_PROXY_RESOLVE_SIGNAL_INTERFACE
-#undef LIB_CROS_PROXY_RESOLVE_NAME
+    "org.chromium.UpdateEngineLibcrosProxyResolvedInterface";
 
 namespace {
 
@@ -53,59 +37,21 @@
 }  // namespace
 
 ChromeBrowserProxyResolver::ChromeBrowserProxyResolver(
-    DBusWrapperInterface* dbus)
-    : dbus_(dbus), timeout_(kTimeout) {}
+    LibCrosProxy* libcros_proxy)
+    : libcros_proxy_(libcros_proxy), timeout_(kTimeout) {}
 
 bool ChromeBrowserProxyResolver::Init() {
-  if (proxy_)
-    return true;  // Already initialized.
-
-  // Set up signal handler. Code lifted from libcros.
-  GError* g_error = nullptr;
-  DBusGConnection* bus = dbus_->BusGet(DBUS_BUS_SYSTEM, &g_error);
-  TEST_AND_RETURN_FALSE(bus);
-  DBusConnection* connection = dbus_->ConnectionGetConnection(bus);
-  TEST_AND_RETURN_FALSE(connection);
-
-  DBusError dbus_error;
-  dbus_error_init(&dbus_error);
-  dbus_->DBusBusAddMatch(connection, kLibCrosProxyResolveSignalFilter,
-                         &dbus_error);
-  TEST_AND_RETURN_FALSE(!dbus_error_is_set(&dbus_error));
-  TEST_AND_RETURN_FALSE(dbus_->DBusConnectionAddFilter(
-      connection,
-      &ChromeBrowserProxyResolver::StaticFilterMessage,
-      this,
-      nullptr));
-
-  proxy_ = dbus_->ProxyNewForName(bus, kLibCrosServiceName, kLibCrosServicePath,
-                                  kLibCrosServiceInterface);
-  if (!proxy_) {
-    dbus_->DBusConnectionRemoveFilter(
-        connection,
-        &ChromeBrowserProxyResolver::StaticFilterMessage,
-        this);
-  }
-  TEST_AND_RETURN_FALSE(proxy_);  // For the error log
+  libcros_proxy_->ue_proxy_resolved_interface()
+      ->RegisterProxyResolvedSignalHandler(
+          base::Bind(&ChromeBrowserProxyResolver::OnProxyResolvedSignal,
+                     base::Unretained(this)),
+          base::Bind(&ChromeBrowserProxyResolver::OnSignalConnected,
+                     base::Unretained(this)));
   return true;
 }
 
 ChromeBrowserProxyResolver::~ChromeBrowserProxyResolver() {
-  // Remove DBus connection filters and Kill proxy object.
-  if (proxy_) {
-    GError* gerror = nullptr;
-    DBusGConnection* gbus = dbus_->BusGet(DBUS_BUS_SYSTEM, &gerror);
-    if (gbus) {
-      DBusConnection* connection = dbus_->ConnectionGetConnection(gbus);
-      dbus_->DBusConnectionRemoveFilter(
-          connection,
-          &ChromeBrowserProxyResolver::StaticFilterMessage,
-          this);
-    }
-    dbus_->ProxyUnref(proxy_);
-  }
-
-  // Kill outstanding timers
+  // Kill outstanding timers.
   for (auto& timer : timers_) {
     MessageLoop::current()->CancelTask(timer.second);
     timer.second = MessageLoop::kTaskIdNull;
@@ -115,26 +61,14 @@
 bool ChromeBrowserProxyResolver::GetProxiesForUrl(const string& url,
                                                   ProxiesResolvedFn callback,
                                                   void* data) {
-  GError* error = nullptr;
-  guint timeout = timeout_;
-  if (proxy_) {
-    if (!dbus_->ProxyCall_3_0(proxy_,
-                              kLibCrosServiceResolveNetworkProxyMethodName,
-                              &error,
-                              url.c_str(),
-                              kLibCrosProxyResolveSignalInterface,
-                              kLibCrosProxyResolveName)) {
-      if (error) {
-        LOG(WARNING) << "dbus_g_proxy_call failed, continuing with no proxy: "
-                     << utils::GetAndFreeGError(&error);
-      } else {
-        LOG(WARNING) << "dbus_g_proxy_call failed with no error string, "
-                        "continuing with no proxy.";
-      }
-      timeout = 0;
-    }
-  } else {
-    LOG(WARNING) << "dbus proxy object missing, continuing with no proxy.";
+  int timeout = timeout_;
+  chromeos::ErrorPtr error;
+  if (!libcros_proxy_->service_interface_proxy()->ResolveNetworkProxy(
+          url.c_str(),
+          kLibCrosProxyResolveSignalInterface,
+          kLibCrosProxyResolveName,
+          &error)) {
+    LOG(WARNING) << "Can't resolve the proxy. Continuing with no proxy.";
     timeout = 0;
   }
 
@@ -149,36 +83,6 @@
   return true;
 }
 
-DBusHandlerResult ChromeBrowserProxyResolver::FilterMessage(
-    DBusConnection* connection,
-    DBusMessage* message) {
-  // Code lifted from libcros.
-  if (!dbus_->DBusMessageIsSignal(message,
-                                  kLibCrosProxyResolveSignalInterface,
-                                  kLibCrosProxyResolveName)) {
-    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-  }
-  // Get args
-  char* source_url = nullptr;
-  char* proxy_list = nullptr;
-  char* error = nullptr;
-  DBusError arg_error;
-  dbus_error_init(&arg_error);
-  if (!dbus_->DBusMessageGetArgs_3(message, &arg_error,
-                                   &source_url,
-                                   &proxy_list,
-                                   &error)) {
-    LOG(ERROR) << "Error reading dbus signal.";
-    return DBUS_HANDLER_RESULT_HANDLED;
-  }
-  if (!source_url || !proxy_list) {
-    LOG(ERROR) << "Error getting url, proxy list from dbus signal.";
-    return DBUS_HANDLER_RESULT_HANDLED;
-  }
-  HandleReply(source_url, proxy_list);
-  return DBUS_HANDLER_RESULT_HANDLED;
-}
-
 bool ChromeBrowserProxyResolver::DeleteUrlState(
     const string& source_url,
     bool delete_timer,
@@ -202,11 +106,25 @@
   return true;
 }
 
-void ChromeBrowserProxyResolver::HandleReply(const string& source_url,
-                                             const string& proxy_list) {
+void ChromeBrowserProxyResolver::OnSignalConnected(const string& interface_name,
+                                                   const string& signal_name,
+                                                   bool successful) {
+  if (!successful) {
+    LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "."
+               << signal_name;
+  }
+}
+
+void ChromeBrowserProxyResolver::OnProxyResolvedSignal(
+    const string& source_url,
+    const string& proxy_info,
+    const string& error_message) {
   pair<ProxiesResolvedFn, void*> callback;
   TEST_AND_RETURN(DeleteUrlState(source_url, true, &callback));
-  (*callback.first)(ParseProxyString(proxy_list), callback.second);
+  if (!error_message.empty()) {
+    LOG(WARNING) << "ProxyResolved error: " << error_message;
+  }
+  (*callback.first)(ParseProxyString(proxy_info), callback.second);
 }
 
 void ChromeBrowserProxyResolver::HandleTimeout(string source_url) {
diff --git a/chrome_browser_proxy_resolver.h b/chrome_browser_proxy_resolver.h
index ab0a92c..aef87ee 100644
--- a/chrome_browser_proxy_resolver.h
+++ b/chrome_browser_proxy_resolver.h
@@ -10,44 +10,31 @@
 #include <string>
 #include <utility>
 
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-lowlevel.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include <chromeos/message_loops/message_loop.h>
 
-#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/libcros_proxy.h"
 #include "update_engine/proxy_resolver.h"
 
 namespace chromeos_update_engine {
 
 extern const char kLibCrosServiceName[];
-extern const char kLibCrosServicePath[];
-extern const char kLibCrosServiceInterface[];
-extern const char kLibCrosServiceResolveNetworkProxyMethodName[];
 extern const char kLibCrosProxyResolveName[];
 extern const char kLibCrosProxyResolveSignalInterface[];
-extern const char kLibCrosProxyResolveSignalFilter[];
 
 class ChromeBrowserProxyResolver : public ProxyResolver {
  public:
-  explicit ChromeBrowserProxyResolver(DBusWrapperInterface* dbus);
+  explicit ChromeBrowserProxyResolver(LibCrosProxy* libcros_proxy);
   ~ChromeBrowserProxyResolver() override;
+
+  // Initialize the ProxyResolver using the provided DBus proxies.
   bool Init();
 
   bool GetProxiesForUrl(const std::string& url,
                         ProxiesResolvedFn callback,
                         void* data) override;
-  void set_timeout(int seconds) { timeout_ = seconds; }
-
-  // Public for test
-  static DBusHandlerResult StaticFilterMessage(
-      DBusConnection* connection,
-      DBusMessage* message,
-      void* data) {
-    return reinterpret_cast<ChromeBrowserProxyResolver*>(data)->FilterMessage(
-        connection, message);
-  }
 
  private:
   FRIEND_TEST(ChromeBrowserProxyResolverTest, ParseTest);
@@ -56,12 +43,17 @@
       CallbacksMap;
   typedef std::multimap<std::string, chromeos::MessageLoop::TaskId> TimeoutsMap;
 
+  // Called when the signal in UpdateEngineLibcrosProxyResolvedInterface is
+  // connected.
+  void OnSignalConnected(const std::string& interface_name,
+                         const std::string& signal_name,
+                         bool successful);
+
   // Handle a reply from Chrome:
-  void HandleReply(const std::string& source_url,
-                   const std::string& proxy_list);
-  DBusHandlerResult FilterMessage(
-      DBusConnection* connection,
-      DBusMessage* message);
+  void OnProxyResolvedSignal(const std::string& source_url,
+                             const std::string& proxy_info,
+                             const std::string& error_message);
+
   // Handle no reply:
   void HandleTimeout(std::string source_url);
 
@@ -79,9 +71,11 @@
   // Shutdown the dbus proxy object.
   void Shutdown();
 
-  DBusWrapperInterface* dbus_;
-  DBusGProxy* proxy_{nullptr};
-  DBusGProxy* peer_proxy_{nullptr};
+  // DBus proxies to request a HTTP proxy resolution. The request is done in the
+  // service_interface_proxy() interface and the response is received as a
+  // signal in the ue_proxy_resolved_interface().
+  LibCrosProxy* libcros_proxy_;
+
   int timeout_;
   TimeoutsMap timers_;
   CallbacksMap callbacks_;
diff --git a/chrome_browser_proxy_resolver_unittest.cc b/chrome_browser_proxy_resolver_unittest.cc
index 3866358..62ee2e0 100644
--- a/chrome_browser_proxy_resolver_unittest.cc
+++ b/chrome_browser_proxy_resolver_unittest.cc
@@ -6,81 +6,171 @@
 
 #include <deque>
 #include <string>
+#include <vector>
 
 #include <gtest/gtest.h>
 
 #include <base/bind.h>
+#include <chromeos/make_unique_ptr.h>
 #include <chromeos/message_loops/fake_message_loop.h>
 
-#include "update_engine/mock_dbus_wrapper.h"
+#include "update_engine/dbus_mocks.h"
+#include "update_engine/dbus_test_utils.h"
 
 using ::testing::Return;
-using ::testing::SetArgPointee;
 using ::testing::StrEq;
 using ::testing::_;
 using chromeos::MessageLoop;
+using org::chromium::LibCrosServiceInterfaceProxyMock;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyMock;
 using std::deque;
 using std::string;
+using std::vector;
 
 namespace chromeos_update_engine {
 
 class ChromeBrowserProxyResolverTest : public ::testing::Test {
  protected:
+  ChromeBrowserProxyResolverTest()
+      : service_interface_mock_(new LibCrosServiceInterfaceProxyMock()),
+        ue_proxy_resolved_interface_mock_(
+            new UpdateEngineLibcrosProxyResolvedInterfaceProxyMock()),
+        libcros_proxy_(
+            chromeos::make_unique_ptr(service_interface_mock_),
+            chromeos::make_unique_ptr(ue_proxy_resolved_interface_mock_)) {}
+
   void SetUp() override {
     loop_.SetAsCurrent();
+    // The ProxyResolved signal should be subscribed to.
+    MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(
+        ue_proxy_resolved_signal_,
+        *ue_proxy_resolved_interface_mock_,
+        ProxyResolved);
+
+    EXPECT_TRUE(resolver_.Init());
+    // Run the loop once to dispatch the successfully registered signal handler.
+    EXPECT_TRUE(loop_.RunOnce(false));
   }
 
   void TearDown() override {
     EXPECT_FALSE(loop_.PendingTasks());
   }
 
+  // Send the signal to the callback passed during registration of the
+  // ProxyResolved.
+  void SendReplySignal(const string& source_url,
+                       const string& proxy_info,
+                       const string& error_message);
+
+  void RunTest(bool chrome_replies, bool chrome_alive);
+
  private:
   chromeos::FakeMessageLoop loop_{nullptr};
+
+  // Local pointers to the mocks. The instances are owned by the
+  // |libcros_proxy_|.
+  LibCrosServiceInterfaceProxyMock* service_interface_mock_;
+  UpdateEngineLibcrosProxyResolvedInterfaceProxyMock*
+      ue_proxy_resolved_interface_mock_;
+
+  // The registered signal handler for the signal
+  // UpdateEngineLibcrosProxyResolvedInterface.ProxyResolved.
+  chromeos_update_engine::dbus_test_utils::MockSignalHandler<
+      void(const string&, const string&, const string&)>
+      ue_proxy_resolved_signal_;
+
+  LibCrosProxy libcros_proxy_;
+  ChromeBrowserProxyResolver resolver_{&libcros_proxy_};
 };
 
+
+void ChromeBrowserProxyResolverTest::SendReplySignal(
+    const string& source_url,
+    const string& proxy_info,
+    const string& error_message) {
+  ASSERT_TRUE(ue_proxy_resolved_signal_.IsHandlerRegistered());
+  ue_proxy_resolved_signal_.signal_callback().Run(
+      source_url, proxy_info, error_message);
+}
+
+namespace {
+void CheckResponseResolved(const deque<string>& proxies,
+                           void* /* pirv_data */) {
+  EXPECT_EQ(2, proxies.size());
+  EXPECT_EQ("socks5://192.168.52.83:5555", proxies[0]);
+  EXPECT_EQ(kNoProxy, proxies[1]);
+  MessageLoop::current()->BreakLoop();
+}
+
+void CheckResponseNoReply(const deque<string>& proxies, void* /* pirv_data */) {
+  EXPECT_EQ(1, proxies.size());
+  EXPECT_EQ(kNoProxy, proxies[0]);
+  MessageLoop::current()->BreakLoop();
+}
+}  // namespace
+
+// chrome_replies should be set to whether or not we fake a reply from
+// chrome. If there's no reply, the resolver should time out.
+// If chrome_alive is false, assume that sending to chrome fails.
+void ChromeBrowserProxyResolverTest::RunTest(bool chrome_replies,
+                                             bool chrome_alive) {
+  char kUrl[] = "http://example.com/blah";
+  char kProxyConfig[] = "SOCKS5 192.168.52.83:5555;DIRECT";
+
+  EXPECT_CALL(*service_interface_mock_,
+              ResolveNetworkProxy(StrEq(kUrl),
+                                  StrEq(kLibCrosProxyResolveSignalInterface),
+                                  StrEq(kLibCrosProxyResolveName),
+                                  _,
+                                  _))
+      .WillOnce(Return(chrome_alive));
+
+  ProxiesResolvedFn get_proxies_response = &CheckResponseNoReply;
+  if (chrome_replies) {
+    get_proxies_response = &CheckResponseResolved;
+    MessageLoop::current()->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&ChromeBrowserProxyResolverTest::SendReplySignal,
+                   base::Unretained(this),
+                   kUrl,
+                   kProxyConfig,
+                   ""),
+        base::TimeDelta::FromSeconds(1));
+  }
+
+  EXPECT_TRUE(resolver_.GetProxiesForUrl(kUrl, get_proxies_response, nullptr));
+  MessageLoop::current()->Run();
+}
+
+
 TEST_F(ChromeBrowserProxyResolverTest, ParseTest) {
   // Test ideas from
   // http://src.chromium.org/svn/trunk/src/net/proxy/proxy_list_unittest.cc
-  const char* inputs[] = {
-    "PROXY foopy:10",
-    " DIRECT",  // leading space.
-    "PROXY foopy1 ; proxy foopy2;\t DIRECT",
-    "proxy foopy1 ; SOCKS foopy2",
-    "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
-    "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
-    "PROXY-foopy:10",
-    "PROXY",
-    "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
-    "HTTP foopy1; SOCKS5 foopy2"
-  };
-  deque<string> outputs[arraysize(inputs)];
-  outputs[0].push_back("http://foopy:10");
-  outputs[0].push_back(kNoProxy);
-  outputs[1].push_back(kNoProxy);
-  outputs[2].push_back("http://foopy1");
-  outputs[2].push_back("http://foopy2");
-  outputs[2].push_back(kNoProxy);
-  outputs[3].push_back("http://foopy1");
-  outputs[3].push_back("socks4://foopy2");
-  outputs[3].push_back(kNoProxy);
-  outputs[4].push_back(kNoProxy);
-  outputs[4].push_back("http://foopy1");
-  outputs[4].push_back(kNoProxy);
-  outputs[4].push_back("socks5://foopy2");
-  outputs[4].push_back(kNoProxy);
-  outputs[5].push_back(kNoProxy);
-  outputs[5].push_back("http://foopy1:80");
-  outputs[5].push_back(kNoProxy);
-  outputs[5].push_back(kNoProxy);
-  outputs[6].push_back(kNoProxy);
-  outputs[7].push_back(kNoProxy);
-  outputs[8].push_back("http://foopy1");
-  outputs[8].push_back("socks5://foopy2");
-  outputs[8].push_back(kNoProxy);
-  outputs[9].push_back("socks5://foopy2");
-  outputs[9].push_back(kNoProxy);
+  vector<string> inputs = {
+      "PROXY foopy:10",
+      " DIRECT",  // leading space.
+      "PROXY foopy1 ; proxy foopy2;\t DIRECT",
+      "proxy foopy1 ; SOCKS foopy2",
+      "DIRECT ; proxy foopy1 ; DIRECT ; SOCKS5 foopy2;DIRECT ",
+      "DIRECT ; proxy foopy1:80; DIRECT ; DIRECT",
+      "PROXY-foopy:10",
+      "PROXY",
+      "PROXY foopy1 ; JUNK ; JUNK ; SOCKS5 foopy2 ; ;",
+      "HTTP foopy1; SOCKS5 foopy2"};
+  vector<deque<string>> outputs = {
+      {"http://foopy:10", kNoProxy},
+      {kNoProxy},
+      {"http://foopy1", "http://foopy2", kNoProxy},
+      {"http://foopy1", "socks4://foopy2", kNoProxy},
+      {kNoProxy, "http://foopy1", kNoProxy, "socks5://foopy2", kNoProxy},
+      {kNoProxy, "http://foopy1:80", kNoProxy, kNoProxy},
+      {kNoProxy},
+      {kNoProxy},
+      {"http://foopy1", "socks5://foopy2", kNoProxy},
+      {"socks5://foopy2", kNoProxy}};
+  ASSERT_EQ(inputs.size(), outputs.size());
 
-  for (size_t i = 0; i < arraysize(inputs); i++) {
+  for (size_t i = 0; i < inputs.size(); i++) {
     deque<string> results =
         ChromeBrowserProxyResolver::ParseProxyString(inputs[i]);
     deque<string>& expected = outputs[i];
@@ -93,110 +183,6 @@
   }
 }
 
-namespace {
-void DBusWrapperTestResolved(const deque<string>& proxies,
-                             void* /* pirv_data */) {
-  EXPECT_EQ(2, proxies.size());
-  EXPECT_EQ("socks5://192.168.52.83:5555", proxies[0]);
-  EXPECT_EQ(kNoProxy, proxies[1]);
-  MessageLoop::current()->BreakLoop();
-}
-void DBusWrapperTestResolvedNoReply(const deque<string>& proxies,
-                                    void* /* pirv_data */) {
-  EXPECT_EQ(1, proxies.size());
-  EXPECT_EQ(kNoProxy, proxies[0]);
-  MessageLoop::current()->BreakLoop();
-}
-
-void SendReply(DBusConnection* connection,
-               DBusMessage* message,
-               ChromeBrowserProxyResolver* resolver) {
-  LOG(INFO) << "Calling SendReply";
-  ChromeBrowserProxyResolver::StaticFilterMessage(connection,
-                                                  message,
-                                                  resolver);
-}
-
-// chrome_replies should be set to whether or not we fake a reply from
-// chrome. If there's no reply, the resolver should time out.
-// If chrome_alive is false, assume that sending to chrome fails.
-void RunTest(bool chrome_replies, bool chrome_alive) {
-  intptr_t number = 1;
-  DBusGConnection* kMockSystemGBus =
-      reinterpret_cast<DBusGConnection*>(number++);
-  DBusConnection* kMockSystemBus =
-      reinterpret_cast<DBusConnection*>(number++);
-  DBusGProxy* kMockDbusProxy =
-      reinterpret_cast<DBusGProxy*>(number++);
-  DBusMessage* kMockDbusMessage =
-      reinterpret_cast<DBusMessage*>(number++);
-
-  char kUrl[] = "http://example.com/blah";
-  char kProxyConfig[] = "SOCKS5 192.168.52.83:5555;DIRECT";
-
-  testing::StrictMock<MockDBusWrapper> dbus_iface;
-
-  EXPECT_CALL(dbus_iface, BusGet(_, _))
-      .Times(2)
-      .WillRepeatedly(Return(kMockSystemGBus));
-  EXPECT_CALL(dbus_iface,
-              ConnectionGetConnection(kMockSystemGBus))
-      .Times(2)
-      .WillRepeatedly(Return(kMockSystemBus));
-  EXPECT_CALL(dbus_iface, DBusBusAddMatch(kMockSystemBus, _, _));
-  EXPECT_CALL(dbus_iface,
-              DBusConnectionAddFilter(kMockSystemBus, _, _, _))
-      .WillOnce(Return(1));
-  EXPECT_CALL(dbus_iface,
-              ProxyNewForName(kMockSystemGBus,
-                              StrEq(kLibCrosServiceName),
-                              StrEq(kLibCrosServicePath),
-                              StrEq(kLibCrosServiceInterface)))
-      .WillOnce(Return(kMockDbusProxy));
-  EXPECT_CALL(dbus_iface, ProxyUnref(kMockDbusProxy));
-
-  EXPECT_CALL(dbus_iface, ProxyCall_3_0(
-      kMockDbusProxy,
-      StrEq(kLibCrosServiceResolveNetworkProxyMethodName),
-      _,
-      StrEq(kUrl),
-      StrEq(kLibCrosProxyResolveSignalInterface),
-      StrEq(kLibCrosProxyResolveName)))
-      .WillOnce(Return(chrome_alive ? TRUE : FALSE));
-
-  EXPECT_CALL(dbus_iface,
-              DBusConnectionRemoveFilter(kMockSystemBus, _, _));
-
-  if (chrome_replies) {
-    EXPECT_CALL(dbus_iface,
-                DBusMessageIsSignal(kMockDbusMessage,
-                                    kLibCrosProxyResolveSignalInterface,
-                                    kLibCrosProxyResolveName))
-        .WillOnce(Return(1));
-    EXPECT_CALL(dbus_iface,
-                DBusMessageGetArgs_3(kMockDbusMessage, _, _, _, _))
-        .WillOnce(DoAll(SetArgPointee<2>(static_cast<char*>(kUrl)),
-                        SetArgPointee<3>(static_cast<char*>(kProxyConfig)),
-                        Return(TRUE)));
-  }
-
-  ChromeBrowserProxyResolver resolver(&dbus_iface);
-  EXPECT_EQ(true, resolver.Init());
-  resolver.set_timeout(1);
-  ProxiesResolvedFn get_proxies_response = &DBusWrapperTestResolvedNoReply;
-
-  if (chrome_replies) {
-    get_proxies_response = &DBusWrapperTestResolved;
-    MessageLoop::current()->PostTask(
-        FROM_HERE,
-        base::Bind(&SendReply, kMockSystemBus, kMockDbusMessage, &resolver));
-  }
-
-  EXPECT_TRUE(resolver.GetProxiesForUrl(kUrl, get_proxies_response, nullptr));
-  MessageLoop::current()->Run();
-}
-}  // namespace
-
 TEST_F(ChromeBrowserProxyResolverTest, SuccessTest) {
   RunTest(true, true);
 }
diff --git a/connection_manager.cc b/connection_manager.cc
index f6c0217..004eba0 100644
--- a/connection_manager.cc
+++ b/connection_manager.cc
@@ -10,14 +10,14 @@
 #include <base/stl_util.h>
 #include <base/strings/string_util.h>
 #include <chromeos/dbus/service_constants.h>
-#include <dbus/dbus-glib.h>
-#include <glib.h>
 #include <policy/device_policy.h>
 
 #include "update_engine/prefs.h"
 #include "update_engine/system_state.h"
 #include "update_engine/utils.h"
 
+using org::chromium::flimflam::ManagerProxyInterface;
+using org::chromium::flimflam::ServiceProxyInterface;
 using std::set;
 using std::string;
 
@@ -25,171 +25,46 @@
 
 namespace {
 
-// Gets the DbusGProxy for FlimFlam. Must be free'd with ProxyUnref()
-bool GetFlimFlamProxy(DBusWrapperInterface* dbus_iface,
-                      const char* path,
-                      const char* interface,
-                      DBusGProxy** out_proxy) {
-  DBusGConnection* bus;
-  DBusGProxy* proxy;
-  GError* error = nullptr;
-
-  bus = dbus_iface->BusGet(DBUS_BUS_SYSTEM, &error);
-  if (!bus) {
-    LOG(ERROR) << "Failed to get system bus";
-    return false;
+NetworkConnectionType ParseConnectionType(const string& type_str) {
+  if (type_str == shill::kTypeEthernet) {
+    return NetworkConnectionType::kEthernet;
+  } else if (type_str == shill::kTypeWifi) {
+    return NetworkConnectionType::kWifi;
+  } else if (type_str == shill::kTypeWimax) {
+    return NetworkConnectionType::kWimax;
+  } else if (type_str == shill::kTypeBluetooth) {
+    return NetworkConnectionType::kBluetooth;
+  } else if (type_str == shill::kTypeCellular) {
+    return NetworkConnectionType::kCellular;
   }
-  proxy = dbus_iface->ProxyNewForName(bus, shill::kFlimflamServiceName, path,
-                                      interface);
-  *out_proxy = proxy;
-  return true;
+  return NetworkConnectionType::kUnknown;
 }
 
-// On success, caller owns the GHashTable at out_hash_table.
-// Returns true on success.
-bool GetProperties(DBusWrapperInterface* dbus_iface,
-                   const char* path,
-                   const char* interface,
-                   GHashTable** out_hash_table) {
-  DBusGProxy* proxy;
-  GError* error = nullptr;
-
-  TEST_AND_RETURN_FALSE(GetFlimFlamProxy(dbus_iface,
-                                         path,
-                                         interface,
-                                         &proxy));
-
-  gboolean rc = dbus_iface->ProxyCall_0_1(proxy,
-                                          "GetProperties",
-                                          &error,
-                                          out_hash_table);
-  dbus_iface->ProxyUnref(proxy);
-  if (rc == FALSE) {
-    LOG(ERROR) << "dbus_g_proxy_call failed";
-    return false;
-  }
-
-  return true;
-}
-
-// Returns (via out_path) the default network path, or empty string if
-// there's no network up.
-// Returns true on success.
-bool GetDefaultServicePath(DBusWrapperInterface* dbus_iface, string* out_path) {
-  GHashTable* hash_table = nullptr;
-
-  TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
-                                      shill::kFlimflamServicePath,
-                                      shill::kFlimflamManagerInterface,
-                                      &hash_table));
-
-  GValue* value = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
-                                                                "Services"));
-  GPtrArray* array = nullptr;
-  bool success = false;
-  if (G_VALUE_HOLDS(value, DBUS_TYPE_G_OBJECT_PATH_ARRAY) &&
-      (array = reinterpret_cast<GPtrArray*>(g_value_get_boxed(value))) &&
-      (array->len > 0)) {
-    *out_path = static_cast<const char*>(g_ptr_array_index(array, 0));
-    success = true;
-  }
-
-  g_hash_table_unref(hash_table);
-  return success;
-}
-
-NetworkConnectionType ParseConnectionType(const char* type_str) {
-  if (!strcmp(type_str, shill::kTypeEthernet)) {
-    return kNetEthernet;
-  } else if (!strcmp(type_str, shill::kTypeWifi)) {
-    return kNetWifi;
-  } else if (!strcmp(type_str, shill::kTypeWimax)) {
-    return kNetWimax;
-  } else if (!strcmp(type_str, shill::kTypeBluetooth)) {
-    return kNetBluetooth;
-  } else if (!strcmp(type_str, shill::kTypeCellular)) {
-    return kNetCellular;
-  }
-  return kNetUnknown;
-}
-
-NetworkTethering ParseTethering(const char* tethering_str) {
-  if (!strcmp(tethering_str, shill::kTetheringNotDetectedState)) {
+NetworkTethering ParseTethering(const string& tethering_str) {
+  if (tethering_str == shill::kTetheringNotDetectedState) {
     return NetworkTethering::kNotDetected;
-  } else if (!strcmp(tethering_str, shill::kTetheringSuspectedState)) {
+  } else if (tethering_str == shill::kTetheringSuspectedState) {
     return NetworkTethering::kSuspected;
-  } else if (!strcmp(tethering_str, shill::kTetheringConfirmedState)) {
+  } else if (tethering_str == shill::kTetheringConfirmedState) {
     return NetworkTethering::kConfirmed;
   }
   LOG(WARNING) << "Unknown Tethering value: " << tethering_str;
   return NetworkTethering::kUnknown;
 }
 
-bool GetServicePathProperties(DBusWrapperInterface* dbus_iface,
-                              const string& path,
-                              NetworkConnectionType* out_type,
-                              NetworkTethering* out_tethering) {
-  GHashTable* hash_table = nullptr;
-
-  TEST_AND_RETURN_FALSE(GetProperties(dbus_iface,
-                                      path.c_str(),
-                                      shill::kFlimflamServiceInterface,
-                                      &hash_table));
-
-  // Populate the out_tethering.
-  GValue* value =
-      reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
-                                                    shill::kTetheringProperty));
-  const char* tethering_str = nullptr;
-
-  if (value != nullptr)
-    tethering_str = g_value_get_string(value);
-  if (tethering_str != nullptr) {
-    *out_tethering = ParseTethering(tethering_str);
-  } else {
-    // Set to Unknown if not present.
-    *out_tethering = NetworkTethering::kUnknown;
-  }
-
-  // Populate the out_type property.
-  value = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table,
-                                                        shill::kTypeProperty));
-  const char* type_str = nullptr;
-  bool success = false;
-  if (value != nullptr && (type_str = g_value_get_string(value)) != nullptr) {
-    success = true;
-    if (!strcmp(type_str, shill::kTypeVPN)) {
-      value = reinterpret_cast<GValue*>(
-          g_hash_table_lookup(hash_table, shill::kPhysicalTechnologyProperty));
-      if (value != nullptr &&
-          (type_str = g_value_get_string(value)) != nullptr) {
-        *out_type = ParseConnectionType(type_str);
-      } else {
-        LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
-                   << " connection (service: " << path << "). Returning default"
-                   << " kNetUnknown value.";
-        *out_type = kNetUnknown;
-      }
-    } else {
-      *out_type = ParseConnectionType(type_str);
-    }
-  }
-  g_hash_table_unref(hash_table);
-  return success;
-}
-
 }  // namespace
 
-ConnectionManager::ConnectionManager(SystemState *system_state)
-    :  system_state_(system_state) {}
+ConnectionManager::ConnectionManager(ShillProxyInterface* shill_proxy,
+                                     SystemState* system_state)
+    : shill_proxy_(shill_proxy), system_state_(system_state) {}
 
 bool ConnectionManager::IsUpdateAllowedOver(NetworkConnectionType type,
                                             NetworkTethering tethering) const {
   switch (type) {
-    case kNetBluetooth:
+    case NetworkConnectionType::kBluetooth:
       return false;
 
-    case kNetCellular: {
+    case NetworkConnectionType::kCellular: {
       set<string> allowed_types;
       const policy::DevicePolicy* device_policy =
           system_state_->device_policy();
@@ -246,52 +121,108 @@
         // Treat this connection as if it is a cellular connection.
         LOG(INFO) << "Current connection is confirmed tethered, using Cellular "
                      "setting.";
-        return IsUpdateAllowedOver(kNetCellular, NetworkTethering::kUnknown);
+        return IsUpdateAllowedOver(NetworkConnectionType::kCellular,
+                                   NetworkTethering::kUnknown);
       }
       return true;
   }
 }
 
+// static
 const char* ConnectionManager::StringForConnectionType(
-    NetworkConnectionType type) const {
-  static const char* const kValues[] = {shill::kTypeEthernet,
-                                        shill::kTypeWifi,
-                                        shill::kTypeWimax,
-                                        shill::kTypeBluetooth,
-                                        shill::kTypeCellular};
-  if (type < 0 || type >= static_cast<int>(arraysize(kValues))) {
-    return "Unknown";
-  }
-  return kValues[type];
-}
-
-const char* ConnectionManager::StringForTethering(
-    NetworkTethering tethering) const {
-  switch (tethering) {
-    case NetworkTethering::kNotDetected:
-      return shill::kTetheringNotDetectedState;
-    case NetworkTethering::kSuspected:
-      return shill::kTetheringSuspectedState;
-    case NetworkTethering::kConfirmed:
-      return shill::kTetheringConfirmedState;
-    case NetworkTethering::kUnknown:
+    NetworkConnectionType type) {
+  switch (type) {
+    case NetworkConnectionType::kEthernet:
+      return shill::kTypeEthernet;
+    case NetworkConnectionType::kWifi:
+      return shill::kTypeWifi;
+    case NetworkConnectionType::kWimax:
+      return shill::kTypeWimax;
+    case NetworkConnectionType::kBluetooth:
+      return shill::kTypeBluetooth;
+    case NetworkConnectionType::kCellular:
+      return shill::kTypeCellular;
+    case NetworkConnectionType::kUnknown:
       return "Unknown";
   }
-  // The program shouldn't reach this point, but the compiler isn't smart
-  // enough to infer that.
   return "Unknown";
 }
 
 bool ConnectionManager::GetConnectionProperties(
-    DBusWrapperInterface* dbus_iface,
     NetworkConnectionType* out_type,
-    NetworkTethering* out_tethering) const {
+    NetworkTethering* out_tethering) {
   string default_service_path;
-  TEST_AND_RETURN_FALSE(GetDefaultServicePath(dbus_iface,
-                                              &default_service_path));
-  TEST_AND_RETURN_FALSE(GetServicePathProperties(dbus_iface,
-                                                 default_service_path,
-                                                 out_type, out_tethering));
+  TEST_AND_RETURN_FALSE(GetDefaultServicePath(&default_service_path));
+  if (default_service_path.empty())
+    return false;
+  TEST_AND_RETURN_FALSE(
+      GetServicePathProperties(default_service_path, out_type, out_tethering));
+  return true;
+}
+
+bool ConnectionManager::GetDefaultServicePath(string* out_path) {
+  chromeos::VariantDictionary properties;
+  chromeos::ErrorPtr error;
+  ManagerProxyInterface* manager_proxy = shill_proxy_->GetManagerProxy();
+  if (!manager_proxy)
+    return false;
+  TEST_AND_RETURN_FALSE(manager_proxy->GetProperties(&properties, &error));
+
+  const auto& prop_default_service =
+      properties.find(shill::kDefaultServiceProperty);
+  if (prop_default_service == properties.end())
+    return false;
+
+  *out_path = prop_default_service->second.TryGet<dbus::ObjectPath>().value();
+  return !out_path->empty();
+}
+
+bool ConnectionManager::GetServicePathProperties(
+    const string& path,
+    NetworkConnectionType* out_type,
+    NetworkTethering* out_tethering) {
+  // We create and dispose the ServiceProxyInterface on every request.
+  std::unique_ptr<ServiceProxyInterface> service =
+      shill_proxy_->GetServiceForPath(path);
+
+  chromeos::VariantDictionary properties;
+  chromeos::ErrorPtr error;
+  TEST_AND_RETURN_FALSE(service->GetProperties(&properties, &error));
+
+  // Populate the out_tethering.
+  const auto& prop_tethering = properties.find(shill::kTetheringProperty);
+  if (prop_tethering == properties.end()) {
+    // Set to Unknown if not present.
+    *out_tethering = NetworkTethering::kUnknown;
+  } else {
+    // If the property doesn't contain a string value, the empty string will
+    // become kUnknown.
+    *out_tethering = ParseTethering(prop_tethering->second.TryGet<string>());
+  }
+
+  // Populate the out_type property.
+  const auto& prop_type = properties.find(shill::kTypeProperty);
+  if (prop_type == properties.end()) {
+    // Set to Unknown if not present.
+    *out_type = NetworkConnectionType::kUnknown;
+    return false;
+  }
+
+  string type_str = prop_type->second.TryGet<string>();
+  if (type_str == shill::kTypeVPN) {
+    const auto& prop_physical =
+        properties.find(shill::kPhysicalTechnologyProperty);
+    if (prop_physical == properties.end()) {
+      LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
+                 << " connection (service: " << path << "). Returning default"
+                 << " kUnknown value.";
+      *out_type = NetworkConnectionType::kUnknown;
+    } else {
+      *out_type = ParseConnectionType(prop_physical->second.TryGet<string>());
+    }
+  } else {
+    *out_type = ParseConnectionType(type_str);
+  }
   return true;
 }
 
diff --git a/connection_manager.h b/connection_manager.h
index f9973a3..870b088 100644
--- a/connection_manager.h
+++ b/connection_manager.h
@@ -5,64 +5,52 @@
 #ifndef UPDATE_ENGINE_CONNECTION_MANAGER_H_
 #define UPDATE_ENGINE_CONNECTION_MANAGER_H_
 
+#include <string>
+
 #include <base/macros.h>
 
-#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/connection_manager_interface.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/shill_proxy_interface.h"
 
 namespace chromeos_update_engine {
 
-enum NetworkConnectionType {
-  kNetEthernet = 0,
-  kNetWifi,
-  kNetWimax,
-  kNetBluetooth,
-  kNetCellular,
-  kNetUnknown
-};
-
-enum class NetworkTethering {
-  kNotDetected = 0,
-  kSuspected,
-  kConfirmed,
-  kUnknown
-};
-
 class SystemState;
 
-// This class exposes a generic interface to the connection manager
-// (e.g FlimFlam, Shill, etc.) to consolidate all connection-related
-// logic in update_engine.
-class ConnectionManager {
+// This class implements the concrete class that talks with the connection
+// manager (shill) over DBus.
+// TODO(deymo): Remove this class and use ShillProvider from the UpdateManager.
+class ConnectionManager : public ConnectionManagerInterface {
  public:
-  // Constructs a new ConnectionManager object initialized with the
-  // given system state.
-  explicit ConnectionManager(SystemState* system_state);
-  virtual ~ConnectionManager() = default;
-
-  // Populates |out_type| with the type of the network connection
-  // that we are currently connected and |out_tethering| with the estimate of
-  // whether that network is being tethered. The dbus_iface is used to query
-  // the real connection manager (e.g shill).
-  virtual bool GetConnectionProperties(DBusWrapperInterface* dbus_iface,
-                                       NetworkConnectionType* out_type,
-                                       NetworkTethering* out_tethering) const;
-
-  // Returns true if we're allowed to update the system when we're
-  // connected to the internet through the given network connection type and the
-  // given tethering state.
-  virtual bool IsUpdateAllowedOver(NetworkConnectionType type,
-                                   NetworkTethering tethering) const;
-
   // Returns the string representation corresponding to the given
   // connection type.
-  virtual const char* StringForConnectionType(NetworkConnectionType type) const;
+  static const char* StringForConnectionType(NetworkConnectionType type);
 
-  // Returns the string representation corresponding to the given tethering
-  // state.
-  virtual const char* StringForTethering(NetworkTethering tethering) const;
+  // Constructs a new ConnectionManager object initialized with the
+  // given system state.
+  ConnectionManager(ShillProxyInterface* shill_proxy,
+                    SystemState* system_state);
+  ~ConnectionManager() override = default;
+
+  // ConnectionManagerInterface overrides.
+  bool GetConnectionProperties(NetworkConnectionType* out_type,
+                               NetworkTethering* out_tethering) override;
+  bool IsUpdateAllowedOver(NetworkConnectionType type,
+                           NetworkTethering tethering) const override;
 
  private:
-  // The global context for update_engine
+  // Returns (via out_path) the default network path, or empty string if
+  // there's no network up. Returns true on success.
+  bool GetDefaultServicePath(std::string* out_path);
+
+  bool GetServicePathProperties(const std::string& path,
+                                NetworkConnectionType* out_type,
+                                NetworkTethering* out_tethering);
+
+  // The mockable interface to access the shill DBus proxies.
+  ShillProxyInterface* shill_proxy_;
+
+  // The global context for update_engine.
   SystemState* system_state_;
 
   DISALLOW_COPY_AND_ASSIGN(ConnectionManager);
diff --git a/connection_manager_interface.h b/connection_manager_interface.h
new file mode 100644
index 0000000..642f60f
--- /dev/null
+++ b/connection_manager_interface.h
@@ -0,0 +1,56 @@
+// Copyright 2015 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 UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
+#define UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
+
+#include <base/macros.h>
+
+namespace chromeos_update_engine {
+
+enum class NetworkConnectionType {
+  kEthernet,
+  kWifi,
+  kWimax,
+  kBluetooth,
+  kCellular,
+  kUnknown
+};
+
+enum class NetworkTethering {
+  kNotDetected,
+  kSuspected,
+  kConfirmed,
+  kUnknown
+};
+
+// This class exposes a generic interface to the connection manager
+// (e.g FlimFlam, Shill, etc.) to consolidate all connection-related
+// logic in update_engine.
+class ConnectionManagerInterface {
+ public:
+  virtual ~ConnectionManagerInterface() = default;
+
+  // Populates |out_type| with the type of the network connection
+  // that we are currently connected and |out_tethering| with the estimate of
+  // whether that network is being tethered.
+  virtual bool GetConnectionProperties(NetworkConnectionType* out_type,
+                                       NetworkTethering* out_tethering) = 0;
+
+  // Returns true if we're allowed to update the system when we're
+  // connected to the internet through the given network connection type and the
+  // given tethering state.
+  virtual bool IsUpdateAllowedOver(NetworkConnectionType type,
+                                   NetworkTethering tethering) const = 0;
+
+ protected:
+  ConnectionManagerInterface() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ConnectionManagerInterface);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_CONNECTION_MANAGER_INTERFACE_H_
diff --git a/connection_manager_unittest.cc b/connection_manager_unittest.cc
index 8ca1a00..1867108 100644
--- a/connection_manager_unittest.cc
+++ b/connection_manager_unittest.cc
@@ -8,45 +8,51 @@
 #include <string>
 
 #include <base/logging.h>
+#include <chromeos/any.h>
 #include <chromeos/dbus/service_constants.h>
+#include <chromeos/make_unique_ptr.h>
+#include <chromeos/message_loops/fake_message_loop.h>
+#include <chromeos/variant_dictionary.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "update_engine/fake_shill_proxy.h"
 #include "update_engine/fake_system_state.h"
-#include "update_engine/mock_dbus_wrapper.h"
 #include "update_engine/test_utils.h"
 
+using org::chromium::flimflam::ManagerProxyMock;
+using org::chromium::flimflam::ServiceProxyMock;
 using std::set;
 using std::string;
-using testing::A;
-using testing::AnyNumber;
 using testing::Return;
-using testing::SetArgumentPointee;
-using testing::StrEq;
+using testing::SetArgPointee;
 using testing::_;
 
 namespace chromeos_update_engine {
 
 class ConnectionManagerTest : public ::testing::Test {
  public:
-  ConnectionManagerTest()
-      : kMockFlimFlamManagerProxy_(nullptr),
-        kMockFlimFlamServiceProxy_(nullptr),
-        kServicePath_(nullptr),
-        cmut_(&fake_system_state_) {
+  void SetUp() override {
+    loop_.SetAsCurrent();
     fake_system_state_.set_connection_manager(&cmut_);
   }
 
- protected:
-  void SetupMocks(const char* service_path);
-  void SetManagerReply(const char* reply_value, const GType& reply_type);
+  void TearDown() override { EXPECT_FALSE(loop_.PendingTasks()); }
 
-  // Sets the |service_type| Type and the |physical_technology|
-  // PhysicalTechnology properties in the mocked service. If a null
-  // |physical_technology| is passed, the property is not set (not present).
-  void SetServiceReply(const char* service_type,
+ protected:
+  // Sets the default_service object path in the response from the
+  // ManagerProxyMock instance.
+  void SetManagerReply(const char* default_service, bool reply_succeeds);
+
+  // Sets the |service_type|, |physical_technology| and |service_tethering|
+  // properties in the mocked service |service_path|. If any of the three
+  // const char* is a nullptr, the corresponding property will not be included
+  // in the response.
+  void SetServiceReply(const string& service_path,
+                       const char* service_type,
                        const char* physical_technology,
                        const char* service_tethering);
+
   void TestWithServiceType(
       const char* service_type,
       const char* physical_technology,
@@ -55,171 +61,119 @@
       const char* service_tethering,
       NetworkTethering expected_tethering);
 
-  static const char* kGetPropertiesMethod;
-  DBusGProxy* kMockFlimFlamManagerProxy_;
-  DBusGProxy* kMockFlimFlamServiceProxy_;
-  DBusGConnection* kMockSystemBus_;
-  const char* kServicePath_;
-  testing::StrictMock<MockDBusWrapper> dbus_iface_;
-  ConnectionManager cmut_;  // ConnectionManager under test.
+  chromeos::FakeMessageLoop loop_{nullptr};
   FakeSystemState fake_system_state_;
+  FakeShillProxy fake_shill_proxy_;
+
+  // ConnectionManager under test.
+  ConnectionManager cmut_{&fake_shill_proxy_, &fake_system_state_};
 };
 
-// static
-const char* ConnectionManagerTest::kGetPropertiesMethod = "GetProperties";
+void ConnectionManagerTest::SetManagerReply(const char* default_service,
+                                            bool reply_succeeds) {
+  ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_.GetManagerProxy();
+  if (!reply_succeeds) {
+    EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+        .WillOnce(Return(false));
+    return;
+  }
 
-void ConnectionManagerTest::SetupMocks(const char* service_path) {
-  int number = 1;
-  kMockSystemBus_ = reinterpret_cast<DBusGConnection*>(number++);
-  kMockFlimFlamManagerProxy_ = reinterpret_cast<DBusGProxy*>(number++);
-  kMockFlimFlamServiceProxy_ = reinterpret_cast<DBusGProxy*>(number++);
-  ASSERT_NE(kMockSystemBus_, static_cast<DBusGConnection*>(nullptr));
+  // Create a dictionary of properties and optionally include the default
+  // service.
+  chromeos::VariantDictionary reply_dict;
+  reply_dict["SomeOtherProperty"] = 0xC0FFEE;
 
-  kServicePath_ = service_path;
-
-  ON_CALL(dbus_iface_, BusGet(DBUS_BUS_SYSTEM, _))
-      .WillByDefault(Return(kMockSystemBus_));
-  EXPECT_CALL(dbus_iface_, BusGet(DBUS_BUS_SYSTEM, _))
-      .Times(AnyNumber());
+  if (default_service) {
+    reply_dict[shill::kDefaultServiceProperty] =
+        dbus::ObjectPath(default_service);
+  }
+  EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
 }
 
-void ConnectionManagerTest::SetManagerReply(const char *reply_value,
-                                            const GType& reply_type) {
-  ASSERT_TRUE(dbus_g_type_is_collection(reply_type));
-
-  // Create the GPtrArray array holding the |reply_value| pointer. The
-  // |reply_value| string is duplicated because it should be mutable on the
-  // interface and is because dbus-glib collections will g_free() each element
-  // of the GPtrArray automatically when the |array_as_value| GValue is unset.
-  // The g_strdup() is not being leaked.
-  GPtrArray* array = g_ptr_array_new();
-  ASSERT_NE(nullptr, array);
-  g_ptr_array_add(array, g_strdup(reply_value));
-
-  GValue* array_as_value = g_new0(GValue, 1);
-  EXPECT_EQ(array_as_value, g_value_init(array_as_value, reply_type));
-  g_value_take_boxed(array_as_value, array);
-
-  // Initialize return value for D-Bus call to Manager object, which is a
-  // hash table of static strings (char*) in GValue* containing a single array.
-  GHashTable* manager_hash_table = g_hash_table_new_full(
-      g_str_hash, g_str_equal,
-      nullptr,  // no key_destroy_func because keys are static.
-      test_utils::GValueFree);  // value_destroy_func
-  g_hash_table_insert(manager_hash_table,
-                      const_cast<char*>("Services"),
-                      array_as_value);
-
-  // Plumb return value into mock object.
-  EXPECT_CALL(dbus_iface_, ProxyCall_0_1(kMockFlimFlamManagerProxy_,
-                                         StrEq(kGetPropertiesMethod),
-                                         _, A<GHashTable**>()))
-      .WillOnce(DoAll(SetArgumentPointee<3>(manager_hash_table), Return(TRUE)));
-
-  // Set other expectations.
-  EXPECT_CALL(dbus_iface_,
-              ProxyNewForName(kMockSystemBus_,
-                              StrEq(shill::kFlimflamServiceName),
-                              StrEq(shill::kFlimflamServicePath),
-                              StrEq(shill::kFlimflamManagerInterface)))
-      .WillOnce(Return(kMockFlimFlamManagerProxy_));
-  EXPECT_CALL(dbus_iface_, ProxyUnref(kMockFlimFlamManagerProxy_));
-  EXPECT_CALL(dbus_iface_, BusGet(DBUS_BUS_SYSTEM, _))
-      .RetiresOnSaturation();
-}
-
-void ConnectionManagerTest::SetServiceReply(const char* service_type,
+void ConnectionManagerTest::SetServiceReply(const string& service_path,
+                                            const char* service_type,
                                             const char* physical_technology,
                                             const char* service_tethering) {
-  // Initialize return value for D-Bus call to Service object, which is a
-  // hash table of static strings (char*) in GValue*.
-  GHashTable* service_hash_table = g_hash_table_new_full(
-      g_str_hash, g_str_equal,
-      nullptr,  // no key_destroy_func because keys are static.
-      test_utils::GValueFree);  // value_destroy_func
-  GValue* service_type_value = test_utils::GValueNewString(service_type);
-  g_hash_table_insert(service_hash_table,
-                      const_cast<char*>("Type"),
-                      service_type_value);
+  chromeos::VariantDictionary reply_dict;
+  reply_dict["SomeOtherProperty"] = 0xC0FFEE;
+
+  if (service_type)
+    reply_dict[shill::kTypeProperty] = string(service_type);
 
   if (physical_technology) {
-    GValue* physical_technology_value =
-        test_utils::GValueNewString(physical_technology);
-    g_hash_table_insert(service_hash_table,
-                        const_cast<char*>("PhysicalTechnology"),
-                        physical_technology_value);
+    reply_dict[shill::kPhysicalTechnologyProperty] =
+        string(physical_technology);
   }
 
-  if (service_tethering) {
-    GValue* service_tethering_value =
-        test_utils::GValueNewString(service_tethering);
-    g_hash_table_insert(service_hash_table,
-                        const_cast<char*>("Tethering"),
-                        service_tethering_value);
-  }
+  if (service_tethering)
+    reply_dict[shill::kTetheringProperty] = string(service_tethering);
+
+  std::unique_ptr<ServiceProxyMock> service_proxy_mock(new ServiceProxyMock());
 
   // Plumb return value into mock object.
-  EXPECT_CALL(dbus_iface_, ProxyCall_0_1(kMockFlimFlamServiceProxy_,
-                                         StrEq(kGetPropertiesMethod),
-                                         _, A<GHashTable**>()))
-      .WillOnce(DoAll(SetArgumentPointee<3>(service_hash_table), Return(TRUE)));
+  EXPECT_CALL(*service_proxy_mock.get(), GetProperties(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
 
-  // Set other expectations.
-  EXPECT_CALL(dbus_iface_,
-              ProxyNewForName(kMockSystemBus_,
-                              StrEq(shill::kFlimflamServiceName),
-                              StrEq(kServicePath_),
-                              StrEq(shill::kFlimflamServiceInterface)))
-      .WillOnce(Return(kMockFlimFlamServiceProxy_));
-  EXPECT_CALL(dbus_iface_, ProxyUnref(kMockFlimFlamServiceProxy_));
-  EXPECT_CALL(dbus_iface_, BusGet(DBUS_BUS_SYSTEM, _))
-      .RetiresOnSaturation();
+  fake_shill_proxy_.SetServiceForPath(service_path,
+                                      std::move(service_proxy_mock));
 }
 
 void ConnectionManagerTest::TestWithServiceType(
     const char* service_type,
     const char* physical_technology,
     NetworkConnectionType expected_type) {
-
-  SetupMocks("/service/guest-network");
-  SetManagerReply(kServicePath_, DBUS_TYPE_G_OBJECT_PATH_ARRAY);
-  SetServiceReply(service_type, physical_technology,
+  SetManagerReply("/service/guest-network", true);
+  SetServiceReply("/service/guest-network",
+                  service_type,
+                  physical_technology,
                   shill::kTetheringNotDetectedState);
 
   NetworkConnectionType type;
   NetworkTethering tethering;
-  EXPECT_TRUE(cmut_.GetConnectionProperties(&dbus_iface_, &type, &tethering));
+  EXPECT_TRUE(cmut_.GetConnectionProperties(&type, &tethering));
   EXPECT_EQ(expected_type, type);
-  testing::Mock::VerifyAndClearExpectations(&dbus_iface_);
+  testing::Mock::VerifyAndClearExpectations(
+      fake_shill_proxy_.GetManagerProxy());
 }
 
 void ConnectionManagerTest::TestWithServiceTethering(
     const char* service_tethering,
     NetworkTethering expected_tethering) {
-
-  SetupMocks("/service/guest-network");
-  SetManagerReply(kServicePath_, DBUS_TYPE_G_OBJECT_PATH_ARRAY);
-  SetServiceReply(shill::kTypeWifi, nullptr, service_tethering);
+  SetManagerReply("/service/guest-network", true);
+  SetServiceReply(
+      "/service/guest-network", shill::kTypeWifi, nullptr, service_tethering);
 
   NetworkConnectionType type;
   NetworkTethering tethering;
-  EXPECT_TRUE(cmut_.GetConnectionProperties(&dbus_iface_, &type, &tethering));
+  EXPECT_TRUE(cmut_.GetConnectionProperties(&type, &tethering));
   EXPECT_EQ(expected_tethering, tethering);
+  testing::Mock::VerifyAndClearExpectations(
+      fake_shill_proxy_.GetManagerProxy());
 }
 
 TEST_F(ConnectionManagerTest, SimpleTest) {
-  TestWithServiceType(shill::kTypeEthernet, nullptr, kNetEthernet);
-  TestWithServiceType(shill::kTypeWifi, nullptr, kNetWifi);
-  TestWithServiceType(shill::kTypeWimax, nullptr, kNetWimax);
-  TestWithServiceType(shill::kTypeBluetooth, nullptr, kNetBluetooth);
-  TestWithServiceType(shill::kTypeCellular, nullptr, kNetCellular);
+  TestWithServiceType(shill::kTypeEthernet, nullptr,
+                      NetworkConnectionType::kEthernet);
+  TestWithServiceType(shill::kTypeWifi, nullptr,
+                      NetworkConnectionType::kWifi);
+  TestWithServiceType(shill::kTypeWimax, nullptr,
+                      NetworkConnectionType::kWimax);
+  TestWithServiceType(shill::kTypeBluetooth, nullptr,
+                      NetworkConnectionType::kBluetooth);
+  TestWithServiceType(shill::kTypeCellular, nullptr,
+                      NetworkConnectionType::kCellular);
 }
 
 TEST_F(ConnectionManagerTest, PhysicalTechnologyTest) {
-  TestWithServiceType(shill::kTypeVPN, nullptr, kNetUnknown);
-  TestWithServiceType(shill::kTypeVPN, shill::kTypeVPN, kNetUnknown);
-  TestWithServiceType(shill::kTypeVPN, shill::kTypeWifi, kNetWifi);
-  TestWithServiceType(shill::kTypeVPN, shill::kTypeWimax, kNetWimax);
+  TestWithServiceType(shill::kTypeVPN, nullptr,
+                      NetworkConnectionType::kUnknown);
+  TestWithServiceType(shill::kTypeVPN, shill::kTypeVPN,
+                      NetworkConnectionType::kUnknown);
+  TestWithServiceType(shill::kTypeVPN, shill::kTypeWifi,
+                      NetworkConnectionType::kWifi);
+  TestWithServiceType(shill::kTypeVPN, shill::kTypeWimax,
+                      NetworkConnectionType::kWimax);
 }
 
 TEST_F(ConnectionManagerTest, TetheringTest) {
@@ -234,26 +188,27 @@
 }
 
 TEST_F(ConnectionManagerTest, UnknownTest) {
-  TestWithServiceType("foo", nullptr, kNetUnknown);
+  TestWithServiceType("foo", nullptr, NetworkConnectionType::kUnknown);
 }
 
 TEST_F(ConnectionManagerTest, AllowUpdatesOverEthernetTest) {
   // Updates over Ethernet are allowed even if there's no policy.
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetEthernet,
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
                                         NetworkTethering::kUnknown));
 }
 
 TEST_F(ConnectionManagerTest, AllowUpdatesOverWifiTest) {
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWifi, NetworkTethering::kUnknown));
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
+                                        NetworkTethering::kUnknown));
 }
 
 TEST_F(ConnectionManagerTest, AllowUpdatesOverWimaxTest) {
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWimax,
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWimax,
                                         NetworkTethering::kUnknown));
 }
 
 TEST_F(ConnectionManagerTest, BlockUpdatesOverBluetoothTest) {
-  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetBluetooth,
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kBluetooth,
                                          NetworkTethering::kUnknown));
 }
 
@@ -264,13 +219,14 @@
 
   // This test tests cellular (3G) being the only connection type being allowed.
   set<string> allowed_set;
-  allowed_set.insert(cmut_.StringForConnectionType(kNetCellular));
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
 
   EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_))
       .Times(1)
-      .WillOnce(DoAll(SetArgumentPointee<0>(allowed_set), Return(true)));
+      .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(true)));
 
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetCellular,
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
                                         NetworkTethering::kUnknown));
 }
 
@@ -283,43 +239,47 @@
   // 3G one among them. Only Cellular is currently enforced by the policy
   // setting, the others are ignored (see Bluetooth for example).
   set<string> allowed_set;
-  allowed_set.insert(cmut_.StringForConnectionType(kNetCellular));
-  allowed_set.insert(cmut_.StringForConnectionType(kNetBluetooth));
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kBluetooth));
 
   EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_))
       .Times(3)
-      .WillRepeatedly(DoAll(SetArgumentPointee<0>(allowed_set), Return(true)));
+      .WillRepeatedly(DoAll(SetArgPointee<0>(allowed_set), Return(true)));
 
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetEthernet,
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
                                         NetworkTethering::kUnknown));
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetEthernet,
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
                                         NetworkTethering::kNotDetected));
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetCellular,
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
                                         NetworkTethering::kUnknown));
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWifi, NetworkTethering::kUnknown));
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWimax, NetworkTethering::kUnknown));
-  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetBluetooth,
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
+                                        NetworkTethering::kUnknown));
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWimax,
+                                        NetworkTethering::kUnknown));
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kBluetooth,
                                          NetworkTethering::kUnknown));
 
   // Tethered networks are treated in the same way as Cellular networks and
   // thus allowed.
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetEthernet,
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
                                         NetworkTethering::kConfirmed));
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWifi,
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
                                         NetworkTethering::kConfirmed));
 }
 
 TEST_F(ConnectionManagerTest, BlockUpdatesOverCellularByDefaultTest) {
-  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetCellular,
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
                                          NetworkTethering::kUnknown));
 }
 
 TEST_F(ConnectionManagerTest, BlockUpdatesOverTetheredNetworkByDefaultTest) {
-  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetWifi,
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
                                          NetworkTethering::kConfirmed));
-  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetEthernet,
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kEthernet,
                                          NetworkTethering::kConfirmed));
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetWifi,
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kWifi,
                                         NetworkTethering::kSuspected));
 }
 
@@ -331,15 +291,18 @@
   // Test that updates for 3G are blocked while updates are allowed
   // over several other types.
   set<string> allowed_set;
-  allowed_set.insert(cmut_.StringForConnectionType(kNetEthernet));
-  allowed_set.insert(cmut_.StringForConnectionType(kNetWifi));
-  allowed_set.insert(cmut_.StringForConnectionType(kNetWimax));
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kEthernet));
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kWifi));
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kWimax));
 
   EXPECT_CALL(block_3g_policy, GetAllowedConnectionTypesForUpdate(_))
       .Times(1)
-      .WillOnce(DoAll(SetArgumentPointee<0>(allowed_set), Return(true)));
+      .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(true)));
 
-  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetCellular,
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
                                          NetworkTethering::kUnknown));
 }
 
@@ -349,16 +312,17 @@
   fake_system_state_.set_device_policy(&allow_3g_policy);
 
   set<string> allowed_set;
-  allowed_set.insert(cmut_.StringForConnectionType(kNetCellular));
+  allowed_set.insert(
+      cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
 
   // Return false for GetAllowedConnectionTypesForUpdate and see
   // that updates are still blocked for 3G despite the value being in
   // the string set above.
   EXPECT_CALL(allow_3g_policy, GetAllowedConnectionTypesForUpdate(_))
       .Times(1)
-      .WillOnce(DoAll(SetArgumentPointee<0>(allowed_set), Return(false)));
+      .WillOnce(DoAll(SetArgPointee<0>(allowed_set), Return(false)));
 
-  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetCellular,
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
                                          NetworkTethering::kUnknown));
 }
 
@@ -377,7 +341,7 @@
   EXPECT_CALL(*prefs, Exists(kPrefsUpdateOverCellularPermission))
       .Times(1)
       .WillOnce(Return(false));
-  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetCellular,
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
                                          NetworkTethering::kUnknown));
 
   // Allow per user pref.
@@ -386,8 +350,8 @@
       .WillOnce(Return(true));
   EXPECT_CALL(*prefs, GetBoolean(kPrefsUpdateOverCellularPermission, _))
       .Times(1)
-      .WillOnce(DoAll(SetArgumentPointee<1>(true), Return(true)));
-  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(kNetCellular,
+      .WillOnce(DoAll(SetArgPointee<1>(true), Return(true)));
+  EXPECT_TRUE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
                                         NetworkTethering::kUnknown));
 
   // Block per user pref.
@@ -396,36 +360,36 @@
       .WillOnce(Return(true));
   EXPECT_CALL(*prefs, GetBoolean(kPrefsUpdateOverCellularPermission, _))
       .Times(1)
-      .WillOnce(DoAll(SetArgumentPointee<1>(false), Return(true)));
-  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(kNetCellular,
+      .WillOnce(DoAll(SetArgPointee<1>(false), Return(true)));
+  EXPECT_FALSE(cmut_.IsUpdateAllowedOver(NetworkConnectionType::kCellular,
                                          NetworkTethering::kUnknown));
 }
 
 TEST_F(ConnectionManagerTest, StringForConnectionTypeTest) {
   EXPECT_STREQ(shill::kTypeEthernet,
-               cmut_.StringForConnectionType(kNetEthernet));
+               cmut_.StringForConnectionType(NetworkConnectionType::kEthernet));
   EXPECT_STREQ(shill::kTypeWifi,
-               cmut_.StringForConnectionType(kNetWifi));
+               cmut_.StringForConnectionType(NetworkConnectionType::kWifi));
   EXPECT_STREQ(shill::kTypeWimax,
-               cmut_.StringForConnectionType(kNetWimax));
+               cmut_.StringForConnectionType(NetworkConnectionType::kWimax));
   EXPECT_STREQ(shill::kTypeBluetooth,
-               cmut_.StringForConnectionType(kNetBluetooth));
+               cmut_.StringForConnectionType(
+                   NetworkConnectionType::kBluetooth));
   EXPECT_STREQ(shill::kTypeCellular,
-               cmut_.StringForConnectionType(kNetCellular));
+               cmut_.StringForConnectionType(NetworkConnectionType::kCellular));
   EXPECT_STREQ("Unknown",
-               cmut_.StringForConnectionType(kNetUnknown));
+               cmut_.StringForConnectionType(NetworkConnectionType::kUnknown));
   EXPECT_STREQ("Unknown",
                cmut_.StringForConnectionType(
                    static_cast<NetworkConnectionType>(999999)));
 }
 
 TEST_F(ConnectionManagerTest, MalformedServiceList) {
-  SetupMocks("/service/guest-network");
-  SetManagerReply(kServicePath_, DBUS_TYPE_G_STRING_ARRAY);
+  SetManagerReply("/service/guest-network", false);
 
   NetworkConnectionType type;
   NetworkTethering tethering;
-  EXPECT_FALSE(cmut_.GetConnectionProperties(&dbus_iface_, &type, &tethering));
+  EXPECT_FALSE(cmut_.GetConnectionProperties(&type, &tethering));
 }
 
 }  // namespace chromeos_update_engine
diff --git a/daemon.cc b/daemon.cc
new file mode 100644
index 0000000..bc221b5
--- /dev/null
+++ b/daemon.cc
@@ -0,0 +1,137 @@
+// Copyright 2015 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 "update_engine/daemon.h"
+
+#include <sysexits.h>
+
+#include <base/bind.h>
+#include <base/location.h>
+#include <base/time/time.h>
+#include <chromeos/message_loops/message_loop.h>
+
+#include "update_engine/clock.h"
+#include "update_engine/update_attempter.h"
+
+using chromeos::MessageLoop;
+
+namespace {
+const int kDBusSystemMaxWaitSeconds = 2 * 60;
+}  // namespace
+
+namespace chromeos_update_engine {
+
+namespace {
+// Wait for passed |bus| DBus to be connected by attempting to connect it up to
+// |timeout| time. Returns whether the Connect() eventually succeeded.
+bool WaitForDBusSystem(dbus::Bus* bus, base::TimeDelta timeout) {
+  Clock clock;
+  base::Time deadline = clock.GetMonotonicTime() + timeout;
+
+  while (clock.GetMonotonicTime() < deadline) {
+    if (bus->Connect())
+      return true;
+    LOG(WARNING) << "Failed to get system bus, waiting.";
+    // Wait 1 second.
+    sleep(1);
+  }
+  LOG(ERROR) << "Failed to get system bus after " << timeout.InSeconds()
+             << " seconds.";
+  return false;
+}
+}  // namespace
+
+int UpdateEngineDaemon::OnInit() {
+  // Register the |subprocess_| singleton with this Daemon as the signal
+  // handler.
+  subprocess_.Init(this);
+
+  // We use Daemon::OnInit() and not DBusDaemon::OnInit() to gracefully wait for
+  // the D-Bus connection for up two minutes to avoid re-spawning the daemon
+  // too fast causing thrashing if dbus-daemon is not running.
+  int exit_code = Daemon::OnInit();
+  if (exit_code != EX_OK)
+    return exit_code;
+
+  dbus::Bus::Options options;
+  options.bus_type = dbus::Bus::SYSTEM;
+  bus_ = new dbus::Bus(options);
+
+  // Wait for DBus to be ready and exit if it doesn't become available after
+  // the timeout.
+  if (!WaitForDBusSystem(
+          bus_.get(),
+          base::TimeDelta::FromSeconds(kDBusSystemMaxWaitSeconds))) {
+    // TODO(deymo): Make it possible to run update_engine even if dbus-daemon
+    // is not running or constantly crashing.
+    LOG(ERROR) << "Failed to initialize DBus, aborting.";
+    return 1;
+  }
+
+  CHECK(bus_->SetUpAsyncOperations());
+
+  // Initialize update engine global state but continue if something fails.
+  real_system_state_.reset(new RealSystemState(bus_));
+  LOG_IF(ERROR, !real_system_state_->Initialize())
+      << "Failed to initialize system state.";
+  UpdateAttempter* update_attempter = real_system_state_->update_attempter();
+  CHECK(update_attempter);
+
+  // Sets static members for the certificate checker.
+  CertificateChecker::set_system_state(real_system_state_.get());
+  CertificateChecker::set_openssl_wrapper(&openssl_wrapper_);
+
+  // Create the DBus service.
+  dbus_adaptor_.reset(new UpdateEngineAdaptor(real_system_state_.get(), bus_));
+  update_attempter->set_dbus_adaptor(dbus_adaptor_.get());
+
+  dbus_adaptor_->RegisterAsync(base::Bind(&UpdateEngineDaemon::OnDBusRegistered,
+                                          base::Unretained(this)));
+  LOG(INFO) << "Waiting for DBus object to be registered.";
+  return EX_OK;
+}
+
+void UpdateEngineDaemon::OnDBusRegistered(bool succeeded) {
+  if (!succeeded) {
+    LOG(ERROR) << "Registering the UpdateEngineAdaptor";
+    QuitWithExitCode(1);
+    return;
+  }
+
+  // Take ownership of the service now that everything is initialized. We need
+  // to this now and not before to avoid exposing a well known DBus service
+  // path that doesn't have the service it is supposed to implement.
+  if (!dbus_adaptor_->RequestOwnership()) {
+    LOG(ERROR) << "Unable to take ownership of the DBus service, is there "
+               << "other update_engine daemon running?";
+    QuitWithExitCode(1);
+    return;
+  }
+
+  // Initiate update checks.
+  UpdateAttempter* update_attempter = real_system_state_->update_attempter();
+  update_attempter->ScheduleUpdates();
+
+  // Update boot flags after 45 seconds.
+  MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&UpdateAttempter::UpdateBootFlags,
+                 base::Unretained(update_attempter)),
+      base::TimeDelta::FromSeconds(45));
+
+  // Broadcast the update engine status on startup to ensure consistent system
+  // state on crashes.
+  MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+      &UpdateAttempter::BroadcastStatus,
+      base::Unretained(update_attempter)));
+
+  // Run the UpdateEngineStarted() method on |update_attempter|.
+  MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
+      &UpdateAttempter::UpdateEngineStarted,
+      base::Unretained(update_attempter)));
+
+  LOG(INFO) << "Finished initialization. Now running the loop.";
+}
+
+}  // namespace chromeos_update_engine
diff --git a/daemon.h b/daemon.h
new file mode 100644
index 0000000..4a7fe18
--- /dev/null
+++ b/daemon.h
@@ -0,0 +1,47 @@
+// Copyright 2015 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 UPDATE_ENGINE_DAEMON_H_
+#define UPDATE_ENGINE_DAEMON_H_
+
+#include <memory>
+#include <string>
+
+#include <chromeos/daemons/dbus_daemon.h>
+
+#include "update_engine/certificate_checker.h"
+#include "update_engine/dbus_service.h"
+#include "update_engine/real_system_state.h"
+#include "update_engine/subprocess.h"
+
+namespace chromeos_update_engine {
+
+class UpdateEngineDaemon : public chromeos::DBusDaemon {
+ public:
+  UpdateEngineDaemon() = default;
+
+ protected:
+  int OnInit() override;
+
+ private:
+  // Run from the main loop when the |dbus_adaptor_| object is registered. At
+  // this point we can request ownership of the DBus service name and continue
+  // initialization.
+  void OnDBusRegistered(bool succeeded);
+
+  // The Subprocess singleton class requires a chromeos::MessageLoop in the
+  // current thread, so we need to initialize it from this class instead of
+  // the main() function.
+  Subprocess subprocess_;
+
+  std::unique_ptr<RealSystemState> real_system_state_;
+  OpenSSLWrapper openssl_wrapper_;
+  std::unique_ptr<UpdateEngineAdaptor> dbus_adaptor_;
+
+  DISALLOW_COPY_AND_ASSIGN(UpdateEngineDaemon);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_DAEMON_H_
diff --git a/dbus_bindings/org.chromium.LibCrosService.xml b/dbus_bindings/org.chromium.LibCrosService.xml
new file mode 100644
index 0000000..2ea8313
--- /dev/null
+++ b/dbus_bindings/org.chromium.LibCrosService.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<node name="/org/chromium/LibCrosService"
+      xmlns:tp="http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0">
+  <interface name="org.chromium.LibCrosServiceInterface">
+    <method name="ResolveNetworkProxy">
+      <arg name="source_url" type="s" direction="in" />
+      <arg name="signal_interface" type="s" direction="in" />
+      <arg name="signal_name" type="s" direction="in" />
+      <annotation name="org.chromium.DBus.Method.Kind" value="simple" />
+    </method>
+  </interface>
+  <interface name="org.chromium.UpdateEngineLibcrosProxyResolvedInterface">
+    <signal name="ProxyResolved">
+      <arg name="source_url" type="s" direction="out" />
+      <arg name="proxy_info" type="s" direction="out" />
+      <arg name="error_message" type="s" direction="out" />
+    </signal>
+  </interface>
+</node>
diff --git a/dbus_bindings/org.chromium.UpdateEngineInterface.xml b/dbus_bindings/org.chromium.UpdateEngineInterface.xml
index dda972d..a632608 100644
--- a/dbus_bindings/org.chromium.UpdateEngineInterface.xml
+++ b/dbus_bindings/org.chromium.UpdateEngineInterface.xml
@@ -1,8 +1,4 @@
 <?xml version="1.0" encoding="utf-8" ?>
-<!-- COPYRIGHT HERE
-     dbus-binding-tool -mode=glib-server -prefix=update_engine update_engine.xml
-                        &gt; glib_server.h
--->
 <node name="/org/chromium/UpdateEngine">
   <interface name="org.chromium.UpdateEngineInterface">
     <annotation name="org.freedesktop.DBus.GLib.CSymbol"
diff --git a/dbus_constants.h b/dbus_constants.h
index d51e6fd..f8494ff 100644
--- a/dbus_constants.h
+++ b/dbus_constants.h
@@ -13,6 +13,10 @@
 static const char* const kUpdateEngineServiceInterface =
     "org.chromium.UpdateEngineInterface";
 
+// Generic UpdateEngine D-Bus error.
+static const char* const kUpdateEngineServiceErrorFailed =
+    "org.chromium.UpdateEngine.Error.Failed";
+
 // Flags used in the AttemptUpdateWithFlags() D-Bus method.
 typedef enum {
   kAttemptUpdateFlagNonInteractive = (1<<0)
diff --git a/dbus_service.cc b/dbus_service.cc
index a7ec259..7c9d0c2 100644
--- a/dbus_service.cc
+++ b/dbus_service.cc
@@ -7,13 +7,16 @@
 #include <set>
 #include <string>
 
+#include <base/location.h>
 #include <base/logging.h>
 #include <base/strings/stringprintf.h>
+#include <chromeos/bind_lambda.h>
+#include <chromeos/message_loops/message_loop.h>
 #include <chromeos/strings/string_utils.h>
 #include <policy/device_policy.h>
 
 #include "update_engine/clock_interface.h"
-#include "update_engine/connection_manager.h"
+#include "update_engine/connection_manager_interface.h"
 #include "update_engine/dbus_constants.h"
 #include "update_engine/hardware_interface.h"
 #include "update_engine/omaha_request_params.h"
@@ -23,400 +26,232 @@
 #include "update_engine/utils.h"
 
 using base::StringPrintf;
+using chromeos::ErrorPtr;
 using chromeos::string_utils::ToString;
 using chromeos_update_engine::AttemptUpdateFlags;
 using chromeos_update_engine::kAttemptUpdateFlagNonInteractive;
 using std::set;
 using std::string;
 
-#define UPDATE_ENGINE_SERVICE_ERROR update_engine_service_error_quark ()
-#define UPDATE_ENGINE_SERVICE_TYPE_ERROR \
-  (update_engine_service_error_get_type())
+namespace {
+// Log and set the error on the passed ErrorPtr.
+void LogAndSetError(ErrorPtr *error,
+                    const tracked_objects::Location& location,
+                    const string& reason) {
+  chromeos::Error::AddTo(
+      error, location,
+      chromeos::errors::dbus::kDomain,
+      chromeos_update_engine::kUpdateEngineServiceErrorFailed, reason);
+  LOG(ERROR) << "Sending DBus Failure: " << location.ToString() << ": "
+             << reason;
+}
+}  // namespace
 
-enum UpdateEngineServiceError {
-  UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-  UPDATE_ENGINE_SERVICE_NUM_ERRORS
-};
+namespace chromeos_update_engine {
 
-static GQuark update_engine_service_error_quark(void) {
-  static GQuark ret = 0;
+UpdateEngineService::UpdateEngineService(SystemState* system_state)
+    : system_state_(system_state) {}
 
-  if (ret == 0)
-    ret = g_quark_from_static_string("update_engine_service_error");
+// org::chromium::UpdateEngineInterfaceInterface methods implementation.
 
-  return ret;
+bool UpdateEngineService::AttemptUpdate(ErrorPtr* error,
+                                        const string& in_app_version,
+                                        const string& in_omaha_url) {
+  return AttemptUpdateWithFlags(
+      error, in_app_version, in_omaha_url, 0 /* no flags */);
 }
 
-#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
-
-static GType update_engine_service_error_get_type(void) {
-  static GType etype = 0;
-
-  if (etype == 0) {
-    static const GEnumValue values[] = {
-      ENUM_ENTRY(UPDATE_ENGINE_SERVICE_ERROR_FAILED, "Failed"),
-      { 0, 0, 0 }
-    };
-    G_STATIC_ASSERT(UPDATE_ENGINE_SERVICE_NUM_ERRORS ==
-                    G_N_ELEMENTS(values) - 1);
-    etype = g_enum_register_static("UpdateEngineServiceError", values);
-  }
-
-  return etype;
-}
-
-G_DEFINE_TYPE(UpdateEngineService, update_engine_service, G_TYPE_OBJECT)
-
-static void update_engine_service_finalize(GObject* object) {
-  G_OBJECT_CLASS(update_engine_service_parent_class)->finalize(object);
-}
-
-static void log_and_set_response_error(GError** error,
-                                       UpdateEngineServiceError error_code,
-                                       const string& reason) {
-  LOG(ERROR) << "Sending DBus Failure: " << reason;
-  g_set_error_literal(error, UPDATE_ENGINE_SERVICE_ERROR,
-                      error_code, reason.c_str());
-}
-
-static guint status_update_signal = 0;
-
-static void update_engine_service_class_init(UpdateEngineServiceClass* klass) {
-  GObjectClass *object_class;
-  object_class = G_OBJECT_CLASS(klass);
-  object_class->finalize = update_engine_service_finalize;
-
-  status_update_signal = g_signal_new(
-      "status_update",
-      G_OBJECT_CLASS_TYPE(klass),
-      G_SIGNAL_RUN_LAST,
-      0,  // 0 == no class method associated
-      nullptr,  // Accumulator
-      nullptr,  // Accumulator data
-      nullptr,  // Marshaller
-      G_TYPE_NONE,  // Return type
-      5,  // param count:
-      G_TYPE_INT64,
-      G_TYPE_DOUBLE,
-      G_TYPE_STRING,
-      G_TYPE_STRING,
-      G_TYPE_INT64);
-}
-
-static void update_engine_service_init(UpdateEngineService* object) {
-  dbus_g_error_domain_register(UPDATE_ENGINE_SERVICE_ERROR,
-                               "org.chromium.UpdateEngine.Error",
-                               UPDATE_ENGINE_SERVICE_TYPE_ERROR);
-}
-
-UpdateEngineService* update_engine_service_new(void) {
-  return reinterpret_cast<UpdateEngineService*>(
-      g_object_new(UPDATE_ENGINE_TYPE_SERVICE, nullptr));
-}
-
-gboolean update_engine_service_attempt_update(UpdateEngineService* self,
-                                              gchar* app_version,
-                                              gchar* omaha_url,
-                                              GError **error) {
-  return update_engine_service_attempt_update_with_flags(self,
-                                                         app_version,
-                                                         omaha_url,
-                                                         0,  // No flags set.
-                                                         error);
-}
-
-gboolean update_engine_service_attempt_update_with_flags(
-    UpdateEngineService* self,
-    gchar* app_version,
-    gchar* omaha_url,
-    gint flags_as_int,
-    GError **error) {
-  string app_version_string, omaha_url_string;
-  AttemptUpdateFlags flags = static_cast<AttemptUpdateFlags>(flags_as_int);
+bool UpdateEngineService::AttemptUpdateWithFlags(ErrorPtr* /* error */,
+                                                 const string& in_app_version,
+                                                 const string& in_omaha_url,
+                                                 int32_t in_flags_as_int) {
+  AttemptUpdateFlags flags = static_cast<AttemptUpdateFlags>(in_flags_as_int);
   bool interactive = !(flags & kAttemptUpdateFlagNonInteractive);
 
-  if (app_version)
-    app_version_string = app_version;
-  if (omaha_url)
-    omaha_url_string = omaha_url;
-
-  LOG(INFO) << "Attempt update: app_version=\"" << app_version_string << "\" "
-            << "omaha_url=\"" << omaha_url_string << "\" "
+  LOG(INFO) << "Attempt update: app_version=\"" << in_app_version << "\" "
+            << "omaha_url=\"" << in_omaha_url << "\" "
             << "flags=0x" << std::hex << flags << " "
             << "interactive=" << (interactive? "yes" : "no");
-  self->system_state_->update_attempter()->CheckForUpdate(app_version_string,
-                                                          omaha_url_string,
-                                                          interactive);
-  return TRUE;
+  system_state_->update_attempter()->CheckForUpdate(
+      in_app_version, in_omaha_url, interactive);
+  return true;
 }
 
-gboolean update_engine_service_attempt_rollback(UpdateEngineService* self,
-                                                gboolean powerwash,
-                                                GError **error) {
+bool UpdateEngineService::AttemptRollback(ErrorPtr* error,
+                                          bool in_powerwash) {
   LOG(INFO) << "Attempting rollback to non-active partitions.";
 
-  if (!self->system_state_->update_attempter()->Rollback(powerwash)) {
+  if (!system_state_->update_attempter()->Rollback(in_powerwash)) {
     // TODO(dgarrett): Give a more specific error code/reason.
-    log_and_set_response_error(error,
-                               UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-                               "Rollback attempt failed.");
-    return FALSE;
+    LogAndSetError(error, FROM_HERE, "Rollback attempt failed.");
+    return false;
   }
-
-  return TRUE;
+  return true;
 }
 
-gboolean update_engine_service_can_rollback(UpdateEngineService* self,
-                                            gboolean* out_can_rollback,
-                                            GError **error) {
-  bool can_rollback = self->system_state_->update_attempter()->CanRollback();
+bool UpdateEngineService::CanRollback(ErrorPtr* /* error */,
+                                      bool* out_can_rollback) {
+  bool can_rollback = system_state_->update_attempter()->CanRollback();
   LOG(INFO) << "Checking to see if we can rollback . Result: " << can_rollback;
   *out_can_rollback = can_rollback;
-  return TRUE;
+  return true;
 }
 
-gboolean update_engine_service_get_rollback_partition(
-    UpdateEngineService* self,
-    gchar** out_rollback_partition_name,
-    GError **error) {
-  auto name = self->system_state_->update_attempter()->GetRollbackPartition();
-  LOG(INFO) << "Getting rollback partition name. Result: " << name;
-  *out_rollback_partition_name = g_strdup(name.c_str());
-  return TRUE;
-}
-
-gboolean update_engine_service_get_kernel_devices(UpdateEngineService* self,
-                                                  gchar** out_kernel_devices,
-                                                  GError **error) {
-  auto devices = self->system_state_->update_attempter()->GetKernelDevices();
-  string info;
-  for (const auto& device : devices) {
-    base::StringAppendF(&info, "%d:%s\n",
-                        device.second ? 1 : 0, device.first.c_str());
-  }
-  LOG(INFO) << "Available kernel devices: " << info;
-  *out_kernel_devices = g_strdup(info.c_str());
-  return TRUE;
-}
-
-
-gboolean update_engine_service_reset_status(UpdateEngineService* self,
-                                            GError **error) {
-  if (!self->system_state_->update_attempter()->ResetStatus()) {
+bool UpdateEngineService::ResetStatus(ErrorPtr* error) {
+  if (!system_state_->update_attempter()->ResetStatus()) {
     // TODO(dgarrett): Give a more specific error code/reason.
-    log_and_set_response_error(error,
-                               UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-                               "ResetStatus failed.");
-    return FALSE;
+    LogAndSetError(error, FROM_HERE, "ResetStatus failed.");
+    return false;
   }
-
-  return TRUE;
+  return true;
 }
 
-gboolean update_engine_service_get_status(UpdateEngineService* self,
-                                          int64_t* last_checked_time,
-                                          double* progress,
-                                          gchar** current_operation,
-                                          gchar** new_version,
-                                          int64_t* new_size,
-                                          GError **error) {
-  string current_op;
-  string new_version_str;
-
-  CHECK(self->system_state_->update_attempter()->GetStatus(last_checked_time,
-                                                           progress,
-                                                           &current_op,
-                                                           &new_version_str,
-                                                           new_size));
-  *current_operation = g_strdup(current_op.c_str());
-  *new_version = g_strdup(new_version_str.c_str());
-
-  if (!*current_operation) {
-    log_and_set_response_error(error,
-                               UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-                               "Unable to find current_operation.");
-    return FALSE;
+bool UpdateEngineService::GetStatus(ErrorPtr* error,
+                                    int64_t* out_last_checked_time,
+                                    double* out_progress,
+                                    string* out_current_operation,
+                                    string* out_new_version,
+                                    int64_t* out_new_size) {
+  if (!system_state_->update_attempter()->GetStatus(out_last_checked_time,
+                                                    out_progress,
+                                                    out_current_operation,
+                                                    out_new_version,
+                                                    out_new_size)) {
+    LogAndSetError(error, FROM_HERE, "GetStatus failed.");
+    return false;
   }
-
-  if (!*new_version) {
-    log_and_set_response_error(error,
-                               UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-                               "Unable to find vew_version.");
-    return FALSE;
-  }
-
-  return TRUE;
+  return true;
 }
 
-gboolean update_engine_service_reboot_if_needed(UpdateEngineService* self,
-                                                GError **error) {
-  if (!self->system_state_->update_attempter()->RebootIfNeeded()) {
+bool UpdateEngineService::RebootIfNeeded(ErrorPtr* error) {
+  if (!system_state_->update_attempter()->RebootIfNeeded()) {
     // TODO(dgarrett): Give a more specific error code/reason.
-    log_and_set_response_error(error,
-                               UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-                               "Reboot not needed, or attempt failed.");
-    return FALSE;
+    LogAndSetError(error, FROM_HERE, "Reboot not needed, or attempt failed.");
+    return false;
   }
-  return TRUE;
+  return true;
 }
 
-gboolean update_engine_service_set_channel(UpdateEngineService* self,
-                                           gchar* target_channel,
-                                           gboolean is_powerwash_allowed,
-                                           GError **error) {
-  if (!target_channel) {
-    log_and_set_response_error(error,
-                               UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-                               "Target channel to set not specified.");
-    return FALSE;
-  }
-
-  const policy::DevicePolicy* device_policy =
-      self->system_state_->device_policy();
+bool UpdateEngineService::SetChannel(ErrorPtr* error,
+                                     const string& in_target_channel,
+                                     bool in_is_powerwash_allowed) {
+  const policy::DevicePolicy* device_policy = system_state_->device_policy();
 
   // The device_policy is loaded in a lazy way before an update check. Load it
   // now from the libchromeos cache if it wasn't already loaded.
   if (!device_policy) {
-    chromeos_update_engine::UpdateAttempter* update_attempter =
-        self->system_state_->update_attempter();
+    UpdateAttempter* update_attempter = system_state_->update_attempter();
     if (update_attempter) {
       update_attempter->RefreshDevicePolicy();
-      device_policy = self->system_state_->device_policy();
+      device_policy = system_state_->device_policy();
     }
   }
 
   bool delegated = false;
   if (device_policy &&
       device_policy->GetReleaseChannelDelegated(&delegated) && !delegated) {
-    log_and_set_response_error(
-        error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
+    LogAndSetError(
+        error, FROM_HERE,
         "Cannot set target channel explicitly when channel "
         "policy/settings is not delegated");
-    return FALSE;
+    return false;
   }
 
-  LOG(INFO) << "Setting destination channel to: " << target_channel;
-  if (!self->system_state_->request_params()->SetTargetChannel(
-          target_channel, is_powerwash_allowed)) {
+  LOG(INFO) << "Setting destination channel to: " << in_target_channel;
+  if (!system_state_->request_params()->SetTargetChannel(
+          in_target_channel, in_is_powerwash_allowed)) {
     // TODO(dgarrett): Give a more specific error code/reason.
-    log_and_set_response_error(error,
-                               UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-                               "Setting channel failed.");
-    return FALSE;
+    LogAndSetError(error, FROM_HERE, "Setting channel failed.");
+    return false;
   }
-
-  return TRUE;
+  return true;
 }
 
-gboolean update_engine_service_get_channel(UpdateEngineService* self,
-                                           gboolean get_current_channel,
-                                           gchar** channel,
-                                           GError **error) {
-  chromeos_update_engine::OmahaRequestParams* rp =
-      self->system_state_->request_params();
-
-  string channel_str = get_current_channel ?
-      rp->current_channel() : rp->target_channel();
-
-  *channel = g_strdup(channel_str.c_str());
-  return TRUE;
+bool UpdateEngineService::GetChannel(ErrorPtr* /* error */,
+                                     bool in_get_current_channel,
+                                     string* out_channel) {
+  OmahaRequestParams* rp = system_state_->request_params();
+  *out_channel = (in_get_current_channel ?
+                  rp->current_channel() : rp->target_channel());
+  return true;
 }
 
-gboolean update_engine_service_set_p2p_update_permission(
-    UpdateEngineService* self,
-    gboolean enabled,
-    GError **error) {
-  chromeos_update_engine::PrefsInterface* prefs = self->system_state_->prefs();
+bool UpdateEngineService::SetP2PUpdatePermission(ErrorPtr* error,
+                                                 bool in_enabled) {
+  PrefsInterface* prefs = system_state_->prefs();
 
-  if (!prefs->SetBoolean(chromeos_update_engine::kPrefsP2PEnabled, enabled)) {
-    log_and_set_response_error(
-        error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
+  if (!prefs->SetBoolean(kPrefsP2PEnabled, in_enabled)) {
+    LogAndSetError(
+        error, FROM_HERE,
         StringPrintf("Error setting the update via p2p permission to %s.",
-                     ToString(enabled).c_str()));
-    return FALSE;
+                     ToString(in_enabled).c_str()));
+    return false;
   }
-
-  return TRUE;
+  return true;
 }
 
-gboolean update_engine_service_get_p2p_update_permission(
-    UpdateEngineService* self,
-    gboolean* enabled,
-    GError **error) {
-  chromeos_update_engine::PrefsInterface* prefs = self->system_state_->prefs();
+bool UpdateEngineService::GetP2PUpdatePermission(ErrorPtr* error,
+                                                 bool* out_enabled) {
+  PrefsInterface* prefs = system_state_->prefs();
 
   bool p2p_pref = false;  // Default if no setting is present.
-  if (prefs->Exists(chromeos_update_engine::kPrefsP2PEnabled) &&
-      !prefs->GetBoolean(chromeos_update_engine::kPrefsP2PEnabled, &p2p_pref)) {
-    log_and_set_response_error(error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-                               "Error getting the P2PEnabled setting.");
-    return FALSE;
+  if (prefs->Exists(kPrefsP2PEnabled) &&
+      !prefs->GetBoolean(kPrefsP2PEnabled, &p2p_pref)) {
+    LogAndSetError(error, FROM_HERE, "Error getting the P2PEnabled setting.");
+    return false;
   }
 
-  *enabled = p2p_pref;
-  return TRUE;
+  *out_enabled = p2p_pref;
+  return true;
 }
 
-gboolean update_engine_service_set_update_over_cellular_permission(
-    UpdateEngineService* self,
-    gboolean allowed,
-    GError **error) {
+bool UpdateEngineService::SetUpdateOverCellularPermission(ErrorPtr* error,
+                                                          bool in_allowed) {
   set<string> allowed_types;
-  const policy::DevicePolicy* device_policy =
-      self->system_state_->device_policy();
+  const policy::DevicePolicy* device_policy = system_state_->device_policy();
 
   // The device_policy is loaded in a lazy way before an update check. Load it
   // now from the libchromeos cache if it wasn't already loaded.
   if (!device_policy) {
-    chromeos_update_engine::UpdateAttempter* update_attempter =
-        self->system_state_->update_attempter();
+    UpdateAttempter* update_attempter = system_state_->update_attempter();
     if (update_attempter) {
       update_attempter->RefreshDevicePolicy();
-      device_policy = self->system_state_->device_policy();
+      device_policy = system_state_->device_policy();
     }
   }
 
   // Check if this setting is allowed by the device policy.
   if (device_policy &&
       device_policy->GetAllowedConnectionTypesForUpdate(&allowed_types)) {
-    log_and_set_response_error(
-        error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-        "Ignoring the update over cellular setting since there's "
-        "a device policy enforcing this setting.");
-    return FALSE;
+    LogAndSetError(error, FROM_HERE,
+                   "Ignoring the update over cellular setting since there's "
+                   "a device policy enforcing this setting.");
+    return false;
   }
 
   // If the policy wasn't loaded yet, then it is still OK to change the local
   // setting because the policy will be checked again during the update check.
 
-  chromeos_update_engine::PrefsInterface* prefs = self->system_state_->prefs();
+  PrefsInterface* prefs = system_state_->prefs();
 
-  if (!prefs->SetBoolean(
-      chromeos_update_engine::kPrefsUpdateOverCellularPermission,
-      allowed)) {
-    log_and_set_response_error(
-        error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-        string("Error setting the update over cellular to ") +
-        (allowed ? "true" : "false"));
-    return FALSE;
+  if (!prefs->SetBoolean(kPrefsUpdateOverCellularPermission, in_allowed)) {
+    LogAndSetError(error, FROM_HERE,
+                   string("Error setting the update over cellular to ") +
+                   (in_allowed ? "true" : "false"));
+    return false;
   }
-
-  return TRUE;
+  return true;
 }
 
-gboolean update_engine_service_get_update_over_cellular_permission(
-    UpdateEngineService* self,
-    gboolean* allowed,
-    GError **error) {
-  chromeos_update_engine::ConnectionManager* cm =
-      self->system_state_->connection_manager();
+bool UpdateEngineService::GetUpdateOverCellularPermission(ErrorPtr* /* error */,
+                                                          bool* out_allowed) {
+  ConnectionManagerInterface* cm = system_state_->connection_manager();
 
   // The device_policy is loaded in a lazy way before an update check and is
   // used to determine if an update is allowed over cellular. Load the device
   // policy now from the libchromeos cache if it wasn't already loaded.
-  if (!self->system_state_->device_policy()) {
-    chromeos_update_engine::UpdateAttempter* update_attempter =
-        self->system_state_->update_attempter();
+  if (!system_state_->device_policy()) {
+    UpdateAttempter* update_attempter = system_state_->update_attempter();
     if (update_attempter)
       update_attempter->RefreshDevicePolicy();
   }
@@ -424,53 +259,70 @@
   // Return the current setting based on the same logic used while checking for
   // updates. A log message could be printed as the result of this test.
   LOG(INFO) << "Checking if updates over cellular networks are allowed:";
-  *allowed = cm->IsUpdateAllowedOver(
-      chromeos_update_engine::kNetCellular,
+  *out_allowed = cm->IsUpdateAllowedOver(
+      chromeos_update_engine::NetworkConnectionType::kCellular,
       chromeos_update_engine::NetworkTethering::kUnknown);
-
-  return TRUE;
+  return true;
 }
 
-gboolean update_engine_service_get_duration_since_update(
-    UpdateEngineService* self,
-    gint64* out_usec_wallclock,
-    GError **error) {
-
+bool UpdateEngineService::GetDurationSinceUpdate(ErrorPtr* error,
+                                                 int64_t* out_usec_wallclock) {
   base::Time time;
-  if (!self->system_state_->update_attempter()->GetBootTimeAtUpdate(&time)) {
-    log_and_set_response_error(error, UPDATE_ENGINE_SERVICE_ERROR_FAILED,
-                               "No pending update.");
-    return FALSE;
+  if (!system_state_->update_attempter()->GetBootTimeAtUpdate(&time)) {
+    LogAndSetError(error, FROM_HERE, "No pending update.");
+    return false;
   }
 
-  chromeos_update_engine::ClockInterface *clock = self->system_state_->clock();
+  ClockInterface* clock = system_state_->clock();
   *out_usec_wallclock = (clock->GetBootTime() - time).InMicroseconds();
-  return TRUE;
+  return true;
 }
 
-gboolean update_engine_service_emit_status_update(
-    UpdateEngineService* self,
-    gint64 last_checked_time,
-    gdouble progress,
-    const gchar* current_operation,
-    const gchar* new_version,
-    gint64 new_size) {
-  g_signal_emit(self,
-                status_update_signal,
-                0,
-                last_checked_time,
-                progress,
-                current_operation,
-                new_version,
-                new_size);
-  return TRUE;
+bool UpdateEngineService::GetPrevVersion(ErrorPtr* /* error */,
+                                         string* out_prev_version) {
+  *out_prev_version = system_state_->update_attempter()->GetPrevVersion();
+  return true;
 }
 
-gboolean update_engine_service_get_prev_version(
-    UpdateEngineService* self,
-    gchar** prev_version,
-    GError **error) {
-  string ver = self->system_state_->update_attempter()->GetPrevVersion();
-  *prev_version = g_strdup(ver.c_str());
-  return TRUE;
+bool UpdateEngineService::GetKernelDevices(ErrorPtr* /* error */,
+                                           string* out_kernel_devices) {
+  auto devices = system_state_->update_attempter()->GetKernelDevices();
+  string info;
+  for (const auto& device : devices) {
+    base::StringAppendF(&info, "%d:%s\n",
+                        device.second ? 1 : 0, device.first.c_str());
+  }
+  LOG(INFO) << "Available kernel devices: " << info;
+  *out_kernel_devices = info;
+  return true;
 }
+
+bool UpdateEngineService::GetRollbackPartition(
+    ErrorPtr* /* error */,
+    string* out_rollback_partition_name) {
+  string name = system_state_->update_attempter()->GetRollbackPartition();
+  LOG(INFO) << "Getting rollback partition name. Result: " << name;
+  *out_rollback_partition_name = name;
+  return true;
+}
+
+
+UpdateEngineAdaptor::UpdateEngineAdaptor(SystemState* system_state,
+                                         const scoped_refptr<dbus::Bus>& bus)
+    : org::chromium::UpdateEngineInterfaceAdaptor(&dbus_service_),
+    bus_(bus),
+    dbus_service_(system_state),
+    dbus_object_(nullptr, bus, dbus::ObjectPath(kUpdateEngineServicePath)) {}
+
+void UpdateEngineAdaptor::RegisterAsync(
+    const base::Callback<void(bool)>& completion_callback) {
+  RegisterWithDBusObject(&dbus_object_);
+  dbus_object_.RegisterAsync(completion_callback);
+}
+
+bool UpdateEngineAdaptor::RequestOwnership() {
+  return bus_->RequestOwnershipAndBlock(kUpdateEngineServiceName,
+                                        dbus::Bus::REQUIRE_PRIMARY);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/dbus_service.h b/dbus_service.h
index d61e3b5..6429240 100644
--- a/dbus_service.h
+++ b/dbus_service.h
@@ -7,171 +7,153 @@
 
 #include <inttypes.h>
 
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-bindings.h>
-#include <dbus/dbus-glib-lowlevel.h>
-#include <glib-object.h>
+#include <string>
+
+#include <base/memory/ref_counted.h>
+#include <chromeos/errors/error.h>
 
 #include "update_engine/update_attempter.h"
 
-// Type macros:
-#define UPDATE_ENGINE_TYPE_SERVICE (update_engine_service_get_type())
-#define UPDATE_ENGINE_SERVICE(obj)                                      \
-  (G_TYPE_CHECK_INSTANCE_CAST((obj), UPDATE_ENGINE_TYPE_SERVICE,        \
-                              UpdateEngineService))
-#define UPDATE_ENGINE_IS_SERVICE(obj)                                   \
-  (G_TYPE_CHECK_INSTANCE_TYPE((obj), UPDATE_ENGINE_TYPE_SERVICE))
-#define UPDATE_ENGINE_SERVICE_CLASS(klass)                      \
-  (G_TYPE_CHECK_CLASS_CAST((klass), UPDATE_ENGINE_TYPE_SERVICE, \
-                           UpdateEngineService))
-#define UPDATE_ENGINE_IS_SERVICE_CLASS(klass)                           \
-  (G_TYPE_CHECK_CLASS_TYPE((klass), UPDATE_ENGINE_TYPE_SERVICE))
-#define UPDATE_ENGINE_SERVICE_GET_CLASS(obj)                    \
-  (G_TYPE_INSTANCE_GET_CLASS((obj), UPDATE_ENGINE_TYPE_SERVICE, \
-                             UpdateEngineService))
+#include "update_engine/dbus_adaptor/org.chromium.UpdateEngineInterface.h"
 
-G_BEGIN_DECLS
+namespace chromeos {
+namespace dbus {
+class Bus;
+}  // namespace dbus
+}  // namespace chromeos
 
-struct UpdateEngineService {
-  GObject parent_instance;
+namespace chromeos_update_engine {
 
-  chromeos_update_engine::SystemState* system_state_;
+class UpdateEngineService
+    : public org::chromium::UpdateEngineInterfaceInterface {
+ public:
+  explicit UpdateEngineService(SystemState* system_state);
+  virtual ~UpdateEngineService() = default;
+
+  // Implementation of org::chromium::UpdateEngineInterfaceInterface.
+  bool AttemptUpdate(chromeos::ErrorPtr* error,
+                     const std::string& in_app_version,
+                     const std::string& in_omaha_url) override;
+
+  bool AttemptUpdateWithFlags(chromeos::ErrorPtr* error,
+                              const std::string& in_app_version,
+                              const std::string& in_omaha_url,
+                              int32_t in_flags_as_int) override;
+
+  bool AttemptRollback(chromeos::ErrorPtr* error, bool in_powerwash) override;
+
+  // Checks if the system rollback is available by verifying if the secondary
+  // system partition is valid and bootable.
+  bool CanRollback(chromeos::ErrorPtr* error, bool* out_can_rollback) override;
+
+  // Resets the status of the update_engine to idle, ignoring any applied
+  // update. This is used for development only.
+  bool ResetStatus(chromeos::ErrorPtr* error) override;
+
+  // Returns the current status of the Update Engine. If an update is in
+  // progress, the number of operations, size to download and overall progress
+  // is reported.
+  bool GetStatus(chromeos::ErrorPtr* error,
+                 int64_t* out_last_checked_time,
+                 double* out_progress,
+                 std::string* out_current_operation,
+                 std::string* out_new_version,
+                 int64_t* out_new_size) override;
+
+  // Reboots the device if an update is applied and a reboot is required.
+  bool RebootIfNeeded(chromeos::ErrorPtr* error) override;
+
+  // Changes the current channel of the device to the target channel. If the
+  // target channel is a less stable channel than the current channel, then the
+  // channel change happens immediately (at the next update check).  If the
+  // target channel is a more stable channel, then if is_powerwash_allowed is
+  // set to true, then also the change happens immediately but with a powerwash
+  // if required. Otherwise, the change takes effect eventually (when the
+  // version on the target channel goes above the version number of what the
+  // device currently has).
+  bool SetChannel(chromeos::ErrorPtr* error,
+                  const std::string& in_target_channel,
+                  bool in_is_powerwash_allowed) override;
+
+  // If get_current_channel is set to true, populates |channel| with the name of
+  // the channel that the device is currently on. Otherwise, it populates it
+  // with the name of the channel the device is supposed to be (in case of a
+  // pending channel change).
+  bool GetChannel(chromeos::ErrorPtr* error,
+                  bool in_get_current_channel,
+                  std::string* out_channel) override;
+
+  // Enables or disables the sharing and consuming updates over P2P feature
+  // according to the |enabled| argument passed.
+  bool SetP2PUpdatePermission(chromeos::ErrorPtr* error,
+                              bool in_enabled) override;
+
+  // Returns the current value for the P2P enabled setting. This involves both
+  // sharing and consuming updates over P2P.
+  bool GetP2PUpdatePermission(chromeos::ErrorPtr* error,
+                              bool* out_enabled) override;
+
+  // If there's no device policy installed, sets the update over cellular
+  // networks permission to the |allowed| value. Otherwise, this method returns
+  // with an error since this setting is overridden by the applied policy.
+  bool SetUpdateOverCellularPermission(chromeos::ErrorPtr* error,
+                                       bool in_allowed) override;
+
+  // Returns the current value of the update over cellular network setting,
+  // either forced by the device policy if the device is enrolled or the current
+  // user preference otherwise.
+  bool GetUpdateOverCellularPermission(chromeos::ErrorPtr* error,
+                                       bool* out_allowed) override;
+
+  // Returns the duration since the last successful update, as the
+  // duration on the wallclock. Returns an error if the device has not
+  // updated.
+  bool GetDurationSinceUpdate(chromeos::ErrorPtr* error,
+                              int64_t* out_usec_wallclock) override;
+
+  // Returns the version string of OS that was used before the last reboot
+  // into an updated version. This is available only when rebooting into an
+  // update from previous version, otherwise an empty string is returned.
+  bool GetPrevVersion(chromeos::ErrorPtr* error,
+                      std::string* out_prev_version) override;
+
+  // Returns a list of available kernel partitions and whether each of them
+  // can be booted from or not.
+  bool GetKernelDevices(chromeos::ErrorPtr* error,
+                        std::string* out_kernel_devices) override;
+
+  // Returns the name of kernel partition that can be rolled back into.
+  bool GetRollbackPartition(chromeos::ErrorPtr* error,
+                            std::string* out_rollback_partition_name) override;
+
+ private:
+  SystemState* system_state_;
 };
 
-struct UpdateEngineServiceClass {
-  GObjectClass parent_class;
+// The UpdateEngineAdaptor class runs the UpdateEngineInterface in the fixed
+// object path, without an ObjectManager notifying the interfaces, since it is
+// all static and clients don't expect it to be implemented.
+class UpdateEngineAdaptor : public org::chromium::UpdateEngineInterfaceAdaptor {
+ public:
+  UpdateEngineAdaptor(SystemState* system_state,
+                      const scoped_refptr<dbus::Bus>& bus);
+  ~UpdateEngineAdaptor() = default;
+
+  // Register the DBus object with the update engine service asynchronously.
+  // Calls |copmletion_callback| when done passing a boolean indicating if the
+  // registration succeeded.
+  void RegisterAsync(const base::Callback<void(bool)>& completion_callback);
+
+  // Takes ownership of the well-known DBus name and returns whether it
+  // succeeded.
+  bool RequestOwnership();
+
+ private:
+  scoped_refptr<dbus::Bus> bus_;
+  UpdateEngineService dbus_service_;
+  chromeos::dbus_utils::DBusObject dbus_object_;
 };
 
-UpdateEngineService* update_engine_service_new(void);
-GType update_engine_service_get_type(void);
-
-// Methods
-
-gboolean update_engine_service_attempt_update(UpdateEngineService* self,
-                                              gchar* app_version,
-                                              gchar* omaha_url,
-                                              GError **error);
-
-gboolean update_engine_service_attempt_update_with_flags(
-    UpdateEngineService* self,
-    gchar* app_version,
-    gchar* omaha_url,
-    gint flags_as_int,
-    GError **error);
-
-gboolean update_engine_service_attempt_rollback(UpdateEngineService* self,
-                                                gboolean powerwash,
-                                                GError **error);
-
-// Checks if the system rollback is available by verifying if the secondary
-// system partition is valid and bootable.
-gboolean update_engine_service_can_rollback(
-    UpdateEngineService* self,
-    gboolean* out_can_rollback,
-    GError **error);
-
-// Returns the name of kernel partition that can be rolled back into.
-gboolean update_engine_service_get_rollback_partition(
-  UpdateEngineService* self,
-  gchar** out_rollback_partition_name,
-  GError **error);
-
-// Returns a list of available kernel partitions and whether each of them
-// can be booted from or not.
-gboolean update_engine_service_get_kernel_devices(UpdateEngineService* self,
-                                                  gchar** out_kernel_devices,
-                                                  GError **error);
-
-gboolean update_engine_service_reset_status(UpdateEngineService* self,
-                                            GError **error);
-
-gboolean update_engine_service_get_status(UpdateEngineService* self,
-                                          int64_t* last_checked_time,
-                                          double* progress,
-                                          gchar** current_operation,
-                                          gchar** new_version,
-                                          int64_t* new_size,
-                                          GError **error);
-
-gboolean update_engine_service_reboot_if_needed(UpdateEngineService* self,
-                                                GError **error);
-
-// Changes the current channel of the device to the target channel. If the
-// target channel is a less stable channel than the current channel, then the
-// channel change happens immediately (at the next update check).  If the
-// target channel is a more stable channel, then if is_powerwash_allowed is set
-// to true, then also the change happens immediately but with a powerwash if
-// required. Otherwise, the change takes effect eventually (when the version on
-// the target channel goes above the version number of what the device
-// currently has).
-gboolean update_engine_service_set_channel(UpdateEngineService* self,
-                                           gchar* target_channel,
-                                           gboolean is_powerwash_allowed,
-                                           GError **error);
-
-// If get_current_channel is set to true, populates |channel| with the name of
-// the channel that the device is currently on. Otherwise, it populates it with
-// the name of the channel the device is supposed to be (in case of a pending
-// channel change).
-gboolean update_engine_service_get_channel(UpdateEngineService* self,
-                                           gboolean get_current_channel,
-                                           gchar** channel,
-                                           GError **error);
-
-// Enables or disables the sharing and consuming updates over P2P feature
-// according to the |enabled| argument passed.
-gboolean update_engine_service_set_p2p_update_permission(
-    UpdateEngineService* self,
-    gboolean enabled,
-    GError **error);
-
-// Returns in |enabled| the current value for the P2P enabled setting. This
-// involves both sharing and consuming updates over P2P.
-gboolean update_engine_service_get_p2p_update_permission(
-    UpdateEngineService* self,
-    gboolean* enabled,
-    GError **error);
-
-// If there's no device policy installed, sets the update over cellular networks
-// permission to the |allowed| value. Otherwise, this method returns with an
-// error since this setting is overridden by the applied policy.
-gboolean update_engine_service_set_update_over_cellular_permission(
-    UpdateEngineService* self,
-    gboolean allowed,
-    GError **error);
-
-// Returns the current value of the update over cellular network setting, either
-// forced by the device policy if the device is enrolled or the current user
-// preference otherwise.
-gboolean update_engine_service_get_update_over_cellular_permission(
-    UpdateEngineService* self,
-    gboolean* allowed,
-    GError **error);
-
-// Returns the duration since the last successful update, as the
-// duration on the wallclock. Returns an error if the device has not
-// updated.
-gboolean update_engine_service_get_duration_since_update(
-    UpdateEngineService* self,
-    gint64* out_usec_wallclock,
-    GError **error);
-
-gboolean update_engine_service_emit_status_update(
-    UpdateEngineService* self,
-    gint64 last_checked_time,
-    gdouble progress,
-    const gchar* current_operation,
-    const gchar* new_version,
-    gint64 new_size);
-
-// Returns the version string of OS that was used before the last reboot
-// into an updated version. This is available only when rebooting into an
-// update from previous version, otherwise an empty string is returned.
-gboolean update_engine_service_get_prev_version(
-    UpdateEngineService* self,
-    gchar** prev_version,
-    GError **error);
-
-G_END_DECLS
+}  // namespace chromeos_update_engine
 
 #endif  // UPDATE_ENGINE_DBUS_SERVICE_H_
diff --git a/dbus_service_unittest.cc b/dbus_service_unittest.cc
new file mode 100644
index 0000000..441f89d
--- /dev/null
+++ b/dbus_service_unittest.cc
@@ -0,0 +1,130 @@
+// Copyright 2014 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 "update_engine/dbus_service.h"
+
+#include <gtest/gtest.h>
+#include <string>
+
+#include <chromeos/errors/error.h>
+#include <policy/libpolicy.h>
+#include <policy/mock_device_policy.h>
+
+#include "update_engine/dbus_constants.h"
+#include "update_engine/fake_system_state.h"
+
+using std::string;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::_;
+
+using chromeos::errors::dbus::kDomain;
+
+namespace chromeos_update_engine {
+
+class UpdateEngineServiceTest : public ::testing::Test {
+ protected:
+  UpdateEngineServiceTest()
+      : mock_update_attempter_(fake_system_state_.mock_update_attempter()),
+        dbus_service_(&fake_system_state_) {}
+
+  void SetUp() override {
+    fake_system_state_.set_device_policy(nullptr);
+  }
+
+  // Fake/mock infrastructure.
+  FakeSystemState fake_system_state_;
+  policy::MockDevicePolicy mock_device_policy_;
+
+  // Shortcut for fake_system_state_.mock_update_attempter().
+  MockUpdateAttempter* mock_update_attempter_;
+
+  chromeos::ErrorPtr error_;
+  UpdateEngineService dbus_service_;
+};
+
+TEST_F(UpdateEngineServiceTest, AttemptUpdate) {
+  // Simple test to ensure that the default is an interactive check.
+  EXPECT_CALL(*mock_update_attempter_,
+              CheckForUpdate("app_ver", "url", true /* interactive */));
+  EXPECT_TRUE(dbus_service_.AttemptUpdate(&error_, "app_ver", "url"));
+  EXPECT_EQ(nullptr, error_);
+}
+
+TEST_F(UpdateEngineServiceTest, AttemptUpdateWithFlags) {
+  EXPECT_CALL(*mock_update_attempter_, CheckForUpdate(
+      "app_ver", "url", false /* interactive */));
+  // The update is non-interactive when we pass the non-interactive flag.
+  EXPECT_TRUE(dbus_service_.AttemptUpdateWithFlags(
+      &error_, "app_ver", "url", kAttemptUpdateFlagNonInteractive));
+  EXPECT_EQ(nullptr, error_);
+}
+
+// SetChannel is allowed when there's no device policy (the device is not
+// enterprise enrolled).
+TEST_F(UpdateEngineServiceTest, SetChannelWithNoPolicy) {
+  EXPECT_CALL(*mock_update_attempter_, RefreshDevicePolicy());
+  // If SetTargetChannel is called it means the policy check passed.
+  EXPECT_CALL(*fake_system_state_.mock_request_params(),
+              SetTargetChannel("stable-channel", true))
+      .WillOnce(Return(true));
+  EXPECT_TRUE(dbus_service_.SetChannel(&error_, "stable-channel", true));
+  ASSERT_EQ(nullptr, error_);
+}
+
+// When the policy is present, the delegated value should be checked.
+TEST_F(UpdateEngineServiceTest, SetChannelWithDelegatedPolicy) {
+  policy::MockDevicePolicy mock_device_policy;
+  fake_system_state_.set_device_policy(&mock_device_policy);
+  EXPECT_CALL(mock_device_policy, GetReleaseChannelDelegated(_))
+      .WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
+  EXPECT_CALL(*fake_system_state_.mock_request_params(),
+              SetTargetChannel("beta-channel", true))
+      .WillOnce(Return(true));
+
+  EXPECT_TRUE(dbus_service_.SetChannel(&error_, "beta-channel", true));
+  ASSERT_EQ(nullptr, error_);
+}
+
+// When passing an invalid value (SetTargetChannel fails) an error should be
+// raised.
+TEST_F(UpdateEngineServiceTest, SetChannelWithInvalidChannel) {
+  EXPECT_CALL(*mock_update_attempter_, RefreshDevicePolicy());
+  EXPECT_CALL(*fake_system_state_.mock_request_params(),
+              SetTargetChannel("foo-channel", true)).WillOnce(Return(false));
+
+  EXPECT_FALSE(dbus_service_.SetChannel(&error_, "foo-channel", true));
+  ASSERT_NE(nullptr, error_);
+  EXPECT_TRUE(error_->HasError(kDomain, kUpdateEngineServiceErrorFailed));
+}
+
+TEST_F(UpdateEngineServiceTest, GetChannel) {
+  fake_system_state_.mock_request_params()->set_current_channel("current");
+  fake_system_state_.mock_request_params()->set_target_channel("target");
+  string channel;
+  EXPECT_TRUE(dbus_service_.GetChannel(
+      &error_, true /* get_current_channel */, &channel));
+  EXPECT_EQ(nullptr, error_);
+  EXPECT_EQ("current", channel);
+
+  EXPECT_TRUE(dbus_service_.GetChannel(
+      &error_, false /* get_current_channel */, &channel));
+  EXPECT_EQ(nullptr, error_);
+  EXPECT_EQ("target", channel);
+}
+
+TEST_F(UpdateEngineServiceTest, ResetStatusSucceeds) {
+  EXPECT_CALL(*mock_update_attempter_, ResetStatus()).WillOnce(Return(true));
+  EXPECT_TRUE(dbus_service_.ResetStatus(&error_));
+  EXPECT_EQ(nullptr, error_);
+}
+
+TEST_F(UpdateEngineServiceTest, ResetStatusFails) {
+  EXPECT_CALL(*mock_update_attempter_, ResetStatus()).WillOnce(Return(false));
+  EXPECT_FALSE(dbus_service_.ResetStatus(&error_));
+  ASSERT_NE(nullptr, error_);
+  EXPECT_TRUE(error_->HasError(kDomain, kUpdateEngineServiceErrorFailed));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/dbus_test_utils.h b/dbus_test_utils.h
new file mode 100644
index 0000000..99e6d5e
--- /dev/null
+++ b/dbus_test_utils.h
@@ -0,0 +1,77 @@
+// Copyright 2015 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 UPDATE_ENGINE_DBUS_TEST_UTILS_H_
+#define UPDATE_ENGINE_DBUS_TEST_UTILS_H_
+
+#include <set>
+#include <string>
+
+#include <base/bind.h>
+#include <chromeos/message_loops/message_loop.h>
+#include <gmock/gmock.h>
+
+namespace chromeos_update_engine {
+namespace dbus_test_utils {
+
+#define MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(                           \
+    mock_signal_handler, mock_proxy, signal)                                 \
+  do {                                                                       \
+    EXPECT_CALL((mock_proxy),                                                \
+                Register##signal##SignalHandler(::testing::_, ::testing::_)) \
+        .WillOnce(::chromeos_update_engine::dbus_test_utils::GrabCallbacks(  \
+            &(mock_signal_handler)));                                        \
+  } while (false)
+
+template <typename T>
+class MockSignalHandler {
+ public:
+  MockSignalHandler() = default;
+  ~MockSignalHandler() {
+    if (callback_connected_task_ != chromeos::MessageLoop::kTaskIdNull)
+      chromeos::MessageLoop::current()->CancelTask(callback_connected_task_);
+  }
+
+  // Returns whether the signal handler is registered.
+  bool IsHandlerRegistered() const { return signal_callback_ != nullptr; }
+
+  const base::Callback<T>& signal_callback() { return *signal_callback_.get(); }
+
+  void GrabCallbacks(
+      const base::Callback<T>& signal_callback,
+      dbus::ObjectProxy::OnConnectedCallback on_connected_callback) {
+    signal_callback_.reset(new base::Callback<T>(signal_callback));
+    on_connected_callback_.reset(
+        new dbus::ObjectProxy::OnConnectedCallback(on_connected_callback));
+    // Notify from the main loop that the callback was connected.
+    callback_connected_task_ = chromeos::MessageLoop::current()->PostTask(
+        FROM_HERE,
+        base::Bind(&MockSignalHandler<T>::OnCallbackConnected,
+                   base::Unretained(this)));
+  }
+
+ private:
+  void OnCallbackConnected() {
+    callback_connected_task_ = chromeos::MessageLoop::kTaskIdNull;
+    on_connected_callback_->Run("", "", true);
+  }
+
+  chromeos::MessageLoop::TaskId callback_connected_task_{
+      chromeos::MessageLoop::kTaskIdNull};
+
+  std::unique_ptr<base::Callback<T>> signal_callback_;
+  std::unique_ptr<dbus::ObjectProxy::OnConnectedCallback>
+      on_connected_callback_;
+};
+
+// Defines the action that will call MockSignalHandler<T>::GrabCallbacks for the
+// right type.
+ACTION_P(GrabCallbacks, mock_signal_handler) {
+  mock_signal_handler->GrabCallbacks(arg0, arg1);
+}
+
+}  // namespace dbus_test_utils
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_DBUS_TEST_UTILS_H_
diff --git a/dbus_wrapper_interface.h b/dbus_wrapper_interface.h
deleted file mode 100644
index 57211c5..0000000
--- a/dbus_wrapper_interface.h
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2010 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 UPDATE_ENGINE_DBUS_WRAPPER_INTERFACE_H_
-#define UPDATE_ENGINE_DBUS_WRAPPER_INTERFACE_H_
-
-// A mockable interface for DBus.
-
-#include <dbus/dbus.h>
-#include <dbus/dbus-glib.h>
-
-#ifndef DBUS_TYPE_G_OBJECT_PATH_ARRAY
-#define DBUS_TYPE_G_OBJECT_PATH_ARRAY \
-  (dbus_g_type_get_collection("GPtrArray", DBUS_TYPE_G_OBJECT_PATH))
-#endif
-
-#ifndef DBUS_TYPE_G_STRING_ARRAY
-#define DBUS_TYPE_G_STRING_ARRAY \
-  (dbus_g_type_get_collection("GPtrArray", G_TYPE_STRING))
-#endif
-
-namespace chromeos_update_engine {
-
-class DBusWrapperInterface {
- public:
-  virtual ~DBusWrapperInterface() = default;
-
-  // Wraps dbus_g_proxy_new_for_name().
-  virtual DBusGProxy* ProxyNewForName(DBusGConnection* connection,
-                                      const char* name,
-                                      const char* path,
-                                      const char* interface) = 0;
-
-  // Wraps g_object_unref().
-  virtual void ProxyUnref(DBusGProxy* proxy) = 0;
-
-  // Wraps dbus_g_bus_get().
-  virtual DBusGConnection* BusGet(DBusBusType type, GError** error) = 0;
-
-  // Wraps dbus_g_proxy_call(). Since this is a variadic function without a
-  // va_list equivalent, we have to list specific wrappers depending on the
-  // number of input and output arguments, based on the required usage. Note,
-  // however, that we do rely on automatic signature overriding to facilitate
-  // different types of input/output arguments.
-  virtual gboolean ProxyCall_0_1(DBusGProxy* proxy,
-                                 const char* method,
-                                 GError** error,
-                                 GHashTable** out1) = 0;
-
-  virtual gboolean ProxyCall_0_1(DBusGProxy* proxy,
-                                 const char* method,
-                                 GError** error,
-                                 gint* out1) = 0;
-
-  virtual gboolean ProxyCall_1_0(DBusGProxy* proxy,
-                                 const char* method,
-                                 GError** error,
-                                 gint in1) = 0;
-
-  virtual gboolean ProxyCall_3_0(DBusGProxy* proxy,
-                                 const char* method,
-                                 GError** error,
-                                 const char* in1,
-                                 const char* in2,
-                                 const char* in3) = 0;
-
-  // Wrappers for dbus_g_proxy_add_signal() (variadic).
-  virtual void ProxyAddSignal_1(DBusGProxy* proxy,
-                                const char* signal_name,
-                                GType type1) = 0;
-
-  virtual void ProxyAddSignal_2(DBusGProxy* proxy,
-                                const char* signal_name,
-                                GType type1,
-                                GType type2) = 0;
-
-  // Wrapper for dbus_g_proxy_{connect,disconnect}_signal().
-  virtual void ProxyConnectSignal(DBusGProxy* proxy,
-                                  const char* signal_name,
-                                  GCallback handler,
-                                  void* data,
-                                  GClosureNotify free_data_func) = 0;
-  virtual void ProxyDisconnectSignal(DBusGProxy* proxy,
-                                     const char* signal_name,
-                                     GCallback handler,
-                                     void* data) = 0;
-
-  // Wraps dbus_g_connection_get_connection().
-  virtual DBusConnection* ConnectionGetConnection(DBusGConnection* gbus) = 0;
-
-  // Wraps dbus_bus_add_match().
-  virtual void DBusBusAddMatch(DBusConnection* connection,
-                               const char* rule,
-                               DBusError* error) = 0;
-
-  // Wraps dbus_connection_add_filter().
-  virtual dbus_bool_t DBusConnectionAddFilter(
-      DBusConnection* connection,
-      DBusHandleMessageFunction function,
-      void* user_data,
-      DBusFreeFunction free_data_function) = 0;
-
-  // Wraps dbus_connection_remove_filter().
-  virtual void DBusConnectionRemoveFilter(DBusConnection* connection,
-                                          DBusHandleMessageFunction function,
-                                          void* user_data) = 0;
-
-  // Wraps dbus_message_is_signal().
-  virtual dbus_bool_t DBusMessageIsSignal(DBusMessage* message,
-                                          const char* interface,
-                                          const char* signal_name) = 0;
-
-  // Wraps dbus_message_get_args(). Deploys the same approach for handling
-  // variadic arguments as ProxyCall above.
-  virtual dbus_bool_t DBusMessageGetArgs_3(DBusMessage* message,
-                                           DBusError* error,
-                                           char** out1,
-                                           char** out2,
-                                           char** out3) = 0;
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_DBUS_WRAPPER_INTERFACE_H_
diff --git a/delta_performer.cc b/delta_performer.cc
index 1006f17..bf6972f 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -58,7 +58,6 @@
 
 const uint32_t kInPlaceMinorPayloadVersion = 1;
 const uint32_t kSourceMinorPayloadVersion = 2;
-const size_t kDefaultChunkSize = 1024 * 1024;
 
 namespace {
 const int kUpdateStateOperationInvalid = -1;
@@ -129,12 +128,12 @@
   string total_operations_str("?");
   string completed_percentage_str("");
   if (num_total_operations_) {
-    total_operations_str = base::StringPrintf("%zu", num_total_operations_);
+    total_operations_str = std::to_string(num_total_operations_);
     // Upcasting to 64-bit to avoid overflow, back to size_t for formatting.
     completed_percentage_str =
         base::StringPrintf(" (%" PRIu64 "%%)",
-                     IntRatio(next_operation_num_, num_total_operations_,
-                              100));
+                           IntRatio(next_operation_num_, num_total_operations_,
+                                    100));
   }
 
   // Format download total count and percentage.
@@ -142,11 +141,11 @@
   string payload_size_str("?");
   string downloaded_percentage_str("");
   if (payload_size) {
-    payload_size_str = base::StringPrintf("%zu", payload_size);
+    payload_size_str = std::to_string(payload_size);
     // Upcasting to 64-bit to avoid overflow, back to size_t for formatting.
     downloaded_percentage_str =
         base::StringPrintf(" (%" PRIu64 "%%)",
-                     IntRatio(total_bytes_received_, payload_size, 100));
+                           IntRatio(total_bytes_received_, payload_size, 100));
   }
 
   LOG(INFO) << (message_prefix ? message_prefix : "") << next_operation_num_
@@ -551,11 +550,11 @@
 
     const bool is_kernel_partition =
         (next_operation_num_ >= num_rootfs_operations_);
-    const DeltaArchiveManifest_InstallOperation &op =
+    const InstallOperation& op =
         is_kernel_partition ?
-        manifest_.kernel_install_operations(
-            next_operation_num_ - num_rootfs_operations_) :
-        manifest_.install_operations(next_operation_num_);
+            manifest_.kernel_install_operations(next_operation_num_ -
+                                                num_rootfs_operations_) :
+            manifest_.install_operations(next_operation_num_);
 
     CopyDataToBuffer(&c_bytes, &count, op.data_length());
 
@@ -592,23 +591,21 @@
         ScopedTerminatorExitUnblocker();  // Avoids a compiler unused var bug.
 
     bool op_result;
-    if (op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
-        op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ)
+    if (op.type() == InstallOperation::REPLACE ||
+        op.type() == InstallOperation::REPLACE_BZ)
       op_result = HandleOpResult(
           PerformReplaceOperation(op, is_kernel_partition), "replace", error);
-    else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE)
+    else if (op.type() == InstallOperation::MOVE)
       op_result = HandleOpResult(
           PerformMoveOperation(op, is_kernel_partition), "move", error);
-    else if (op.type() == DeltaArchiveManifest_InstallOperation_Type_BSDIFF)
+    else if (op.type() == InstallOperation::BSDIFF)
       op_result = HandleOpResult(
           PerformBsdiffOperation(op, is_kernel_partition), "bsdiff", error);
-    else if (op.type() ==
-             DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY)
+    else if (op.type() == InstallOperation::SOURCE_COPY)
       op_result =
           HandleOpResult(PerformSourceCopyOperation(op, is_kernel_partition),
                          "source_copy", error);
-    else if (op.type() ==
-             DeltaArchiveManifest_InstallOperation_Type_SOURCE_BSDIFF)
+    else if (op.type() == InstallOperation::SOURCE_BSDIFF)
       op_result =
           HandleOpResult(PerformSourceBsdiffOperation(op, is_kernel_partition),
                          "source_bsdiff", error);
@@ -630,13 +627,11 @@
 }
 
 bool DeltaPerformer::CanPerformInstallOperation(
-    const chromeos_update_engine::DeltaArchiveManifest_InstallOperation&
-    operation) {
+    const chromeos_update_engine::InstallOperation& operation) {
   // Move and source_copy operations don't require any data blob, so they can
   // always be performed.
-  if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE ||
-      operation.type() ==
-          DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY)
+  if (operation.type() == InstallOperation::MOVE ||
+      operation.type() == InstallOperation::SOURCE_COPY)
     return true;
 
   // See if we have the entire data blob in the buffer
@@ -649,13 +644,10 @@
           buffer_offset_ + buffer_.size());
 }
 
-bool DeltaPerformer::PerformReplaceOperation(
-    const DeltaArchiveManifest_InstallOperation& operation,
-    bool is_kernel_partition) {
-  CHECK(operation.type() == \
-        DeltaArchiveManifest_InstallOperation_Type_REPLACE || \
-        operation.type() == \
-        DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+bool DeltaPerformer::PerformReplaceOperation(const InstallOperation& operation,
+                                             bool is_kernel_partition) {
+  CHECK(operation.type() == InstallOperation::REPLACE ||
+        operation.type() == InstallOperation::REPLACE_BZ);
 
   // Since we delete data off the beginning of the buffer as we use it,
   // the data we need should be exactly at the beginning of the buffer.
@@ -672,10 +664,9 @@
   // Since bzip decompression is optional, we have a variable writer that will
   // point to one of the ExtentWriter objects above.
   ExtentWriter* writer = nullptr;
-  if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+  if (operation.type() == InstallOperation::REPLACE) {
     writer = &zero_pad_writer;
-  } else if (operation.type() ==
-             DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+  } else if (operation.type() == InstallOperation::REPLACE_BZ) {
     bzip_writer.reset(new BzipExtentWriter(&zero_pad_writer));
     writer = bzip_writer.get();
   } else {
@@ -699,9 +690,8 @@
   return true;
 }
 
-bool DeltaPerformer::PerformMoveOperation(
-    const DeltaArchiveManifest_InstallOperation& operation,
-    bool is_kernel_partition) {
+bool DeltaPerformer::PerformMoveOperation(const InstallOperation& operation,
+                                          bool is_kernel_partition) {
   // Calculate buffer size. Note, this function doesn't do a sliding
   // window to copy in case the source and destination blocks overlap.
   // If we wanted to do a sliding window, we could program the server
@@ -778,7 +768,7 @@
 }  // namespace
 
 bool DeltaPerformer::PerformSourceCopyOperation(
-    const DeltaArchiveManifest_InstallOperation& operation,
+    const InstallOperation& operation,
     bool is_kernel_partition) {
   if (operation.has_src_length())
     TEST_AND_RETURN_FALSE(operation.src_length() % block_size_ == 0);
@@ -854,9 +844,8 @@
   return true;
 }
 
-bool DeltaPerformer::PerformBsdiffOperation(
-    const DeltaArchiveManifest_InstallOperation& operation,
-    bool is_kernel_partition) {
+bool DeltaPerformer::PerformBsdiffOperation(const InstallOperation& operation,
+                                            bool is_kernel_partition) {
   // Since we delete data off the beginning of the buffer as we use it,
   // the data we need should be exactly at the beginning of the buffer.
   TEST_AND_RETURN_FALSE(buffer_offset_ == operation.data_offset());
@@ -895,10 +884,8 @@
 
   int return_code = 0;
   TEST_AND_RETURN_FALSE(
-      Subprocess::SynchronousExecFlags(cmd,
-                                       G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
-                                       &return_code,
-                                       nullptr));
+      Subprocess::SynchronousExecFlags(cmd, Subprocess::kSearchPath,
+                                       &return_code, nullptr));
   TEST_AND_RETURN_FALSE(return_code == 0);
 
   if (operation.dst_length() % block_size_) {
@@ -919,7 +906,7 @@
 }
 
 bool DeltaPerformer::PerformSourceBsdiffOperation(
-    const DeltaArchiveManifest_InstallOperation& operation,
+    const InstallOperation& operation,
     bool is_kernel_partition) {
   // Since we delete data off the beginning of the buffer as we use it,
   // the data we need should be exactly at the beginning of the buffer.
@@ -966,17 +953,15 @@
 
   int return_code = 0;
   TEST_AND_RETURN_FALSE(
-      Subprocess::SynchronousExecFlags(cmd,
-                                       G_SPAWN_LEAVE_DESCRIPTORS_OPEN,
-                                       &return_code,
-                                       nullptr));
+      Subprocess::SynchronousExecFlags(cmd, Subprocess::kSearchPath,
+                                       &return_code, nullptr));
   TEST_AND_RETURN_FALSE(return_code == 0);
   return true;
 }
 
 bool DeltaPerformer::ExtractSignatureMessage(
-    const DeltaArchiveManifest_InstallOperation& operation) {
-  if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
+    const InstallOperation& operation) {
+  if (operation.type() != InstallOperation::REPLACE ||
       !manifest_.has_signatures_offset() ||
       manifest_.signatures_offset() != operation.data_offset()) {
     return false;
@@ -1142,8 +1127,7 @@
 }
 
 ErrorCode DeltaPerformer::ValidateOperationHash(
-    const DeltaArchiveManifest_InstallOperation& operation) {
-
+    const InstallOperation& operation) {
   if (!operation.data_sha256_hash().size()) {
     if (!operation.data_length()) {
       // Operations that do not have any data blob won't have any operation hash
@@ -1468,11 +1452,11 @@
     if (next_operation_num_ < num_total_operations_) {
       const bool is_kernel_partition =
           next_operation_num_ >= num_rootfs_operations_;
-      const DeltaArchiveManifest_InstallOperation &op =
-          is_kernel_partition ?
-          manifest_.kernel_install_operations(
-              next_operation_num_ - num_rootfs_operations_) :
-          manifest_.install_operations(next_operation_num_);
+      const InstallOperation& op =
+          is_kernel_partition
+              ? manifest_.kernel_install_operations(next_operation_num_ -
+                                                    num_rootfs_operations_)
+              : manifest_.install_operations(next_operation_num_);
       TEST_AND_RETURN_FALSE(prefs_->SetInt64(kPrefsUpdateStateNextDataLength,
                                              op.data_length()));
     } else {
diff --git a/delta_performer.h b/delta_performer.h
index ba479af..bc48f5d 100644
--- a/delta_performer.h
+++ b/delta_performer.h
@@ -30,9 +30,6 @@
 // The minor version used by the A to B delta generator algorithm.
 extern const uint32_t kSourceMinorPayloadVersion;
 
-// Chunk size used for payloads during test.
-extern const size_t kDefaultChunkSize;
-
 class PrefsInterface;
 
 // This class performs the actions in a delta update synchronously. The delta
@@ -238,8 +235,7 @@
 
   // Returns true if enough of the delta file has been passed via Write()
   // to be able to perform a given install operation.
-  bool CanPerformInstallOperation(
-      const DeltaArchiveManifest_InstallOperation& operation);
+  bool CanPerformInstallOperation(const InstallOperation& operation);
 
   // Checks the integrity of the payload manifest. Returns true upon success,
   // false otherwise.
@@ -248,8 +244,7 @@
   // Validates that the hash of the blobs corresponding to the given |operation|
   // matches what's specified in the manifest in the payload.
   // Returns ErrorCode::kSuccess on match or a suitable error code otherwise.
-  ErrorCode ValidateOperationHash(
-      const DeltaArchiveManifest_InstallOperation& operation);
+  ErrorCode ValidateOperationHash(const InstallOperation& operation);
 
   // Interprets the given |protobuf| as a DeltaArchiveManifest protocol buffer
   // of the given protobuf_length and verifies that the signed hash of the
@@ -263,30 +258,23 @@
                                       uint64_t protobuf_length);
 
   // Returns true on success.
-  bool PerformInstallOperation(
-      const DeltaArchiveManifest_InstallOperation& operation);
+  bool PerformInstallOperation(const InstallOperation& operation);
 
   // These perform a specific type of operation and return true on success.
-  bool PerformReplaceOperation(
-      const DeltaArchiveManifest_InstallOperation& operation,
-      bool is_kernel_partition);
-  bool PerformMoveOperation(
-      const DeltaArchiveManifest_InstallOperation& operation,
-      bool is_kernel_partition);
-  bool PerformBsdiffOperation(
-      const DeltaArchiveManifest_InstallOperation& operation,
-      bool is_kernel_partition);
-  bool PerformSourceCopyOperation(
-      const DeltaArchiveManifest_InstallOperation& operation,
-      bool is_kernel_partition);
-  bool PerformSourceBsdiffOperation(
-      const DeltaArchiveManifest_InstallOperation& operation,
-      bool is_kernel_partition);
+  bool PerformReplaceOperation(const InstallOperation& operation,
+                               bool is_kernel_partition);
+  bool PerformMoveOperation(const InstallOperation& operation,
+                            bool is_kernel_partition);
+  bool PerformBsdiffOperation(const InstallOperation& operation,
+                              bool is_kernel_partition);
+  bool PerformSourceCopyOperation(const InstallOperation& operation,
+                                  bool is_kernel_partition);
+  bool PerformSourceBsdiffOperation(const InstallOperation& operation,
+                                    bool is_kernel_partition);
 
   // Returns true if the payload signature message has been extracted from
   // |operation|, false otherwise.
-  bool ExtractSignatureMessage(
-      const DeltaArchiveManifest_InstallOperation& operation);
+  bool ExtractSignatureMessage(const InstallOperation& operation);
 
   // Updates the hash calculator with the bytes in |buffer_|. Then discard the
   // content, ensuring that memory is being deallocated. If |do_advance_offset|,
diff --git a/delta_performer_unittest.cc b/delta_performer_unittest.cc
index 5737422..a0cb72f 100644
--- a/delta_performer_unittest.cc
+++ b/delta_performer_unittest.cc
@@ -62,7 +62,7 @@
   string a_img;
   string b_img;
   string result_img;
-  int image_size;
+  size_t image_size;
 
   string delta_path;
   uint64_t metadata_size;
@@ -75,6 +75,7 @@
 
   string result_kernel;
   chromeos::Blob result_kernel_data;
+  size_t kernel_size;
 
   // The in-memory copy of delta file.
   chromeos::Blob delta;
@@ -138,19 +139,27 @@
   }
 };
 
-static void CompareFilesByBlock(const string& a_file, const string& b_file) {
+static void CompareFilesByBlock(const string& a_file, const string& b_file,
+                                size_t image_size) {
+  EXPECT_EQ(0, image_size % kBlockSize);
+
   chromeos::Blob a_data, b_data;
   EXPECT_TRUE(utils::ReadFile(a_file, &a_data)) << "file failed: " << a_file;
   EXPECT_TRUE(utils::ReadFile(b_file, &b_data)) << "file failed: " << b_file;
 
-  EXPECT_EQ(a_data.size(), b_data.size());
-  EXPECT_EQ(0, a_data.size() % kBlockSize);
-  for (size_t i = 0; i < a_data.size(); i += kBlockSize) {
+  EXPECT_GE(a_data.size(), image_size);
+  EXPECT_GE(b_data.size(), image_size);
+  for (size_t i = 0; i < image_size; i += kBlockSize) {
     EXPECT_EQ(0, i % kBlockSize);
     chromeos::Blob a_sub(&a_data[i], &a_data[i + kBlockSize]);
     chromeos::Blob b_sub(&b_data[i], &b_data[i + kBlockSize]);
     EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs";
   }
+  if (::testing::Test::HasNonfatalFailure()) {
+    LOG(INFO) << "Compared filesystems with size " << image_size
+              << ", partition A " << a_file << " size: " << a_data.size()
+              << ", partition B " << b_file << " size: " << b_data.size();
+  }
 }
 
 static bool WriteSparseFile(const string& path, off_t size) {
@@ -305,7 +314,7 @@
 static void GenerateDeltaFile(bool full_kernel,
                               bool full_rootfs,
                               bool noop,
-                              off_t chunk_size,
+                              ssize_t chunk_size,
                               SignatureTest signature_test,
                               DeltaState *state,
                               uint32_t minor_version) {
@@ -318,13 +327,11 @@
       utils::MakeTempFile("result_img.XXXXXX", &state->result_img, nullptr));
   test_utils::CreateExtImageAtPath(state->a_img, nullptr);
 
-  state->image_size = static_cast<int>(utils::FileSize(state->a_img));
+  state->image_size = utils::FileSize(state->a_img);
 
   // Extend the "partitions" holding the file system a bit.
-  EXPECT_EQ(0, System(base::StringPrintf(
-      "dd if=/dev/zero of=%s seek=%d bs=1 count=1 status=none",
-      state->a_img.c_str(),
-      state->image_size + 1024 * 1024 - 1)));
+  EXPECT_EQ(0, HANDLE_EINTR(truncate(state->a_img.c_str(),
+                                     state->image_size + 1024 * 1024)));
   EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->a_img));
 
   // Create ImageInfo A & B
@@ -405,10 +412,8 @@
     }
 
     test_utils::CreateExtImageAtPath(state->b_img, nullptr);
-    EXPECT_EQ(0, System(base::StringPrintf(
-        "dd if=/dev/zero of=%s seek=%d bs=1 count=1 status=none",
-        state->b_img.c_str(),
-        state->image_size + 1024 * 1024 - 1)));
+    EXPECT_EQ(0, HANDLE_EINTR(truncate(state->b_img.c_str(),
+                                       state->image_size + 1024 * 1024)));
     EXPECT_EQ(state->image_size + 1024 * 1024, utils::FileSize(state->b_img));
 
     // Make some changes to the B image.
@@ -487,6 +492,7 @@
                                   &state->result_kernel,
                                   nullptr));
 
+  state->kernel_size = kDefaultKernelSize;
   state->old_kernel_data.resize(kDefaultKernelSize);
   state->new_kernel_data.resize(state->old_kernel_data.size());
   state->result_kernel_data.resize(state->old_kernel_data.size());
@@ -523,7 +529,7 @@
 
     PayloadGenerationConfig payload_config;
     payload_config.is_delta = !full_rootfs;
-    payload_config.chunk_size = chunk_size;
+    payload_config.hard_chunk_size = chunk_size;
     payload_config.rootfs_partition_size = kRootFSPartitionSize;
     payload_config.minor_version = minor_version;
     if (!full_rootfs) {
@@ -535,8 +541,9 @@
       EXPECT_TRUE(payload_config.source.rootfs.OpenFilesystem());
       EXPECT_TRUE(payload_config.source.kernel.OpenFilesystem());
     } else {
-      if (payload_config.chunk_size == -1)
-        payload_config.chunk_size = kDefaultChunkSize;
+      if (payload_config.hard_chunk_size == -1)
+        // Use 1 MiB chunk size for the full unittests.
+        payload_config.hard_chunk_size = 1024 * 1024;
     }
     payload_config.target.rootfs.path = state->b_img;
     payload_config.target.kernel.path = state->new_kernel;
@@ -841,13 +848,17 @@
 
   chromeos::Blob updated_kernel_partition;
   if (minor_version == kSourceMinorPayloadVersion) {
-    CompareFilesByBlock(state->result_kernel, state->new_kernel);
-    CompareFilesByBlock(state->result_img, state->b_img);
+    CompareFilesByBlock(state->result_kernel, state->new_kernel,
+                        state->kernel_size);
+    CompareFilesByBlock(state->result_img, state->b_img,
+                        state->image_size);
     EXPECT_TRUE(utils::ReadFile(state->result_kernel,
                                 &updated_kernel_partition));
   } else {
-    CompareFilesByBlock(state->old_kernel, state->new_kernel);
-    CompareFilesByBlock(state->a_img, state->b_img);
+    CompareFilesByBlock(state->old_kernel, state->new_kernel,
+                        state->kernel_size);
+    CompareFilesByBlock(state->a_img, state->b_img,
+                        state->image_size);
     EXPECT_TRUE(utils::ReadFile(state->old_kernel, &updated_kernel_partition));
   }
 
@@ -896,7 +907,7 @@
 }
 
 void DoSmallImageTest(bool full_kernel, bool full_rootfs, bool noop,
-                      off_t chunk_size,
+                      ssize_t chunk_size,
                       SignatureTest signature_test,
                       bool hash_checks_mandatory, uint32_t minor_version) {
   DeltaState state;
diff --git a/download_action_unittest.cc b/download_action_unittest.cc
index 254dbd7..a8306f3 100644
--- a/download_action_unittest.cc
+++ b/download_action_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "update_engine/download_action.h"
 
-#include <glib.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
diff --git a/fake_p2p_manager_configuration.h b/fake_p2p_manager_configuration.h
index 1dc7c6e..806206c 100644
--- a/fake_p2p_manager_configuration.h
+++ b/fake_p2p_manager_configuration.h
@@ -47,7 +47,7 @@
                                             size_t minimum_size) override {
     std::vector<std::string> formatted_command = p2p_client_cmd_format_;
     // Replace {variable} on the passed string.
-    std::string str_minimum_size = base::SizeTToString(minimum_size);
+    std::string str_minimum_size = std::to_string(minimum_size);
     for (std::string& arg : formatted_command) {
       ReplaceSubstringsAfterOffset(&arg, 0, "{file_id}", file_id);
       ReplaceSubstringsAfterOffset(&arg, 0, "{minsize}", str_minimum_size);
diff --git a/fake_shill_proxy.cc b/fake_shill_proxy.cc
new file mode 100644
index 0000000..e38784f
--- /dev/null
+++ b/fake_shill_proxy.cc
@@ -0,0 +1,37 @@
+// Copyright 2015 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 "update_engine/fake_shill_proxy.h"
+
+#include "update_engine/dbus_proxies.h"
+
+using org::chromium::flimflam::ManagerProxyMock;
+using org::chromium::flimflam::ServiceProxyInterface;
+
+namespace chromeos_update_engine {
+
+FakeShillProxy::FakeShillProxy()
+    : manager_proxy_mock_(new ManagerProxyMock()) {}
+
+ManagerProxyMock* FakeShillProxy::GetManagerProxy() {
+  return manager_proxy_mock_.get();
+}
+
+std::unique_ptr<ServiceProxyInterface> FakeShillProxy::GetServiceForPath(
+    const std::string& path) {
+  auto it = service_proxy_mocks_.find(path);
+  CHECK(it != service_proxy_mocks_.end()) << "No ServiceProxyMock set for "
+                                          << path;
+  std::unique_ptr<ServiceProxyInterface> result = std::move(it->second);
+  service_proxy_mocks_.erase(it);
+  return result;
+}
+
+void FakeShillProxy::SetServiceForPath(
+    const std::string& path,
+    std::unique_ptr<ServiceProxyInterface> service_proxy) {
+  service_proxy_mocks_[path] = std::move(service_proxy);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/fake_shill_proxy.h b/fake_shill_proxy.h
new file mode 100644
index 0000000..a60e996
--- /dev/null
+++ b/fake_shill_proxy.h
@@ -0,0 +1,54 @@
+// Copyright 2015 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 UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
+#define UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+
+#include "update_engine/dbus_mocks.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/shill_proxy_interface.h"
+
+namespace chromeos_update_engine {
+
+// This class implements the connection to shill using real DBus calls.
+class FakeShillProxy : public ShillProxyInterface {
+ public:
+  FakeShillProxy();
+  ~FakeShillProxy() override = default;
+
+  // ShillProxyInterface overrides.
+
+  // GetManagerProxy returns the subclass ManagerProxyMock so tests can easily
+  // use it. Mocks for the return value of GetServiceForPath() can be provided
+  // with SetServiceForPath().
+  org::chromium::flimflam::ManagerProxyMock* GetManagerProxy() override;
+  std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+  GetServiceForPath(const std::string& path) override;
+
+  // Sets the service_proxy that will be returned by GetServiceForPath().
+  void SetServiceForPath(
+      const std::string& path,
+      std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+          service_proxy);
+
+ private:
+  std::unique_ptr<org::chromium::flimflam::ManagerProxyMock>
+      manager_proxy_mock_;
+
+  std::map<std::string,
+           std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>>
+      service_proxy_mocks_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeShillProxy);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_FAKE_SHILL_PROXY_H_
diff --git a/fake_system_state.cc b/fake_system_state.cc
index 8747a59..6f1bd02 100644
--- a/fake_system_state.cc
+++ b/fake_system_state.cc
@@ -9,23 +9,22 @@
 // Mock the SystemStateInterface so that we could lie that
 // OOBE is completed even when there's no such marker file, etc.
 FakeSystemState::FakeSystemState()
-  : mock_connection_manager_(this),
-    mock_update_attempter_(this, &dbus_),
-    mock_request_params_(this),
-    fake_update_manager_(&fake_clock_),
-    clock_(&fake_clock_),
-    connection_manager_(&mock_connection_manager_),
-    hardware_(&fake_hardware_),
-    metrics_lib_(&mock_metrics_lib_),
-    prefs_(&mock_prefs_),
-    powerwash_safe_prefs_(&mock_powerwash_safe_prefs_),
-    payload_state_(&mock_payload_state_),
-    update_attempter_(&mock_update_attempter_),
-    request_params_(&mock_request_params_),
-    p2p_manager_(&mock_p2p_manager_),
-    update_manager_(&fake_update_manager_),
-    device_policy_(nullptr),
-    fake_system_rebooted_(false) {
+    : mock_update_attempter_(this, nullptr, nullptr),
+      mock_request_params_(this),
+      fake_update_manager_(&fake_clock_),
+      clock_(&fake_clock_),
+      connection_manager_(&mock_connection_manager_),
+      hardware_(&fake_hardware_),
+      metrics_lib_(&mock_metrics_lib_),
+      prefs_(&mock_prefs_),
+      powerwash_safe_prefs_(&mock_powerwash_safe_prefs_),
+      payload_state_(&mock_payload_state_),
+      update_attempter_(&mock_update_attempter_),
+      request_params_(&mock_request_params_),
+      p2p_manager_(&mock_p2p_manager_),
+      update_manager_(&fake_update_manager_),
+      device_policy_(nullptr),
+      fake_system_rebooted_(false) {
   mock_payload_state_.Initialize(this);
   mock_update_attempter_.Init();
 }
diff --git a/fake_system_state.h b/fake_system_state.h
index 84c37fc..47c27e4 100644
--- a/fake_system_state.h
+++ b/fake_system_state.h
@@ -10,10 +10,11 @@
 #include <policy/mock_device_policy.h>
 
 #include "metrics/metrics_library_mock.h"
+#include "update_engine/dbus_mocks.h"
+#include "update_engine/dbus_proxies.h"
 #include "update_engine/fake_clock.h"
 #include "update_engine/fake_hardware.h"
 #include "update_engine/mock_connection_manager.h"
-#include "update_engine/mock_dbus_wrapper.h"
 #include "update_engine/mock_omaha_request_params.h"
 #include "update_engine/mock_p2p_manager.h"
 #include "update_engine/mock_payload_state.h"
@@ -45,7 +46,7 @@
     return device_policy_;
   }
 
-  inline ConnectionManager* connection_manager() override {
+  inline ConnectionManagerInterface* connection_manager() override {
     return connection_manager_;
   }
 
@@ -79,6 +80,11 @@
     return update_manager_;
   }
 
+  inline org::chromium::PowerManagerProxyInterface* power_manager_proxy()
+      override {
+    return power_manager_proxy_;
+  }
+
   inline bool system_rebooted() override { return fake_system_rebooted_; }
 
   // Setters for the various members, can be used for overriding the default
@@ -89,7 +95,8 @@
     clock_ = clock ? clock : &fake_clock_;
   }
 
-  inline void set_connection_manager(ConnectionManager* connection_manager) {
+  inline void set_connection_manager(
+      ConnectionManagerInterface* connection_manager) {
     connection_manager_ = (connection_manager ? connection_manager :
                            &mock_connection_manager_);
   }
@@ -211,11 +218,12 @@
   testing::NiceMock<MockOmahaRequestParams> mock_request_params_;
   testing::NiceMock<MockP2PManager> mock_p2p_manager_;
   chromeos_update_manager::FakeUpdateManager fake_update_manager_;
+  org::chromium::PowerManagerProxyMock mock_power_manager_;
 
   // Pointers to objects that client code can override. They are initialized to
   // the default implementations above.
   ClockInterface* clock_;
-  ConnectionManager* connection_manager_;
+  ConnectionManagerInterface* connection_manager_;
   HardwareInterface* hardware_;
   MetricsLibraryInterface* metrics_lib_;
   PrefsInterface* prefs_;
@@ -225,12 +233,13 @@
   OmahaRequestParams* request_params_;
   P2PManager* p2p_manager_;
   chromeos_update_manager::UpdateManager* update_manager_;
+  org::chromium::PowerManagerProxyInterface* power_manager_proxy_{
+      &mock_power_manager_};
 
   // Other object pointers (not preinitialized).
   const policy::DevicePolicy* device_policy_;
 
   // Other data members.
-  MockDBusWrapper dbus_;
   bool fake_system_rebooted_;
 };
 
diff --git a/filesystem_verifier_action.cc b/filesystem_verifier_action.cc
index 70156d9..d3e9fe3 100644
--- a/filesystem_verifier_action.cc
+++ b/filesystem_verifier_action.cc
@@ -14,14 +14,12 @@
 #include <string>
 
 #include <base/bind.h>
-#include <base/posix/eintr_wrapper.h>
+#include <chromeos/streams/file_stream.h>
 
 #include "update_engine/hardware_interface.h"
-#include "update_engine/subprocess.h"
 #include "update_engine/system_state.h"
 #include "update_engine/utils.h"
 
-using chromeos::MessageLoop;
 using std::string;
 
 namespace chromeos_update_engine {
@@ -98,23 +96,23 @@
       break;
   }
 
-  src_fd_ = HANDLE_EINTR(open(target_path.c_str(), O_RDONLY));
-  if (src_fd_ < 0) {
-    PLOG(ERROR) << "Unable to open " << target_path << " for reading";
+  chromeos::ErrorPtr error;
+  src_stream_ = chromeos::FileStream::Open(
+      base::FilePath(target_path),
+      chromeos::Stream::AccessMode::READ,
+      chromeos::FileStream::Disposition::OPEN_EXISTING,
+      &error);
+
+  if (!src_stream_) {
+    LOG(ERROR) << "Unable to open " << target_path << " for reading";
     return;
   }
 
-  DetermineFilesystemSize(src_fd_);
+  DetermineFilesystemSize(target_path);
   buffer_.resize(kReadFileBufferSize);
 
   // Start the first read.
-  read_task_ = MessageLoop::current()->WatchFileDescriptor(
-      FROM_HERE,
-      src_fd_,
-      MessageLoop::WatchMode::kWatchRead,
-      true,  // persistent
-      base::Bind(&FilesystemVerifierAction::OnReadReadyCallback,
-                 base::Unretained(this)));
+  ScheduleRead();
 
   abort_action_completer.set_should_complete(false);
 }
@@ -125,19 +123,11 @@
 }
 
 bool FilesystemVerifierAction::IsCleanupPending() const {
-  return (src_fd_ != -1);
+  return src_stream_ != nullptr;
 }
 
 void FilesystemVerifierAction::Cleanup(ErrorCode code) {
-  MessageLoop::current()->CancelTask(read_task_);
-  read_task_ = MessageLoop::kTaskIdNull;
-
-  if (src_fd_ != -1) {
-    if (IGNORE_EINTR(close(src_fd_)) != 0) {
-      PLOG(ERROR) << "Error closing fd " << src_fd_;
-    }
-    src_fd_ = -1;
-  }
+  src_stream_.reset();
   // This memory is not used anymore.
   buffer_.clear();
 
@@ -148,74 +138,97 @@
   processor_->ActionComplete(this, code);
 }
 
-void FilesystemVerifierAction::OnReadReadyCallback() {
+void FilesystemVerifierAction::ScheduleRead() {
   size_t bytes_to_read = std::min(static_cast<int64_t>(buffer_.size()),
                                   remaining_size_);
-
-  ssize_t bytes_read = 0;
-  if (bytes_to_read) {
-    bytes_read = HANDLE_EINTR(read(src_fd_, buffer_.data(), bytes_to_read));
-  }
-  if (bytes_read < 0) {
-    PLOG(ERROR) << "Read failed";
-    failed_ = true;
-  } else if (bytes_read == 0) {
-    read_done_ = true;
-  } else {
-    remaining_size_ -= bytes_read;
-  }
-
-  if (bytes_read > 0) {
-    CHECK(!read_done_);
-    if (!hasher_.Update(buffer_.data(), bytes_read)) {
-      LOG(ERROR) << "Unable to update the hash.";
-      failed_ = true;
-    }
-  }
-
-  CheckTerminationConditions();
-}
-
-void FilesystemVerifierAction::CheckTerminationConditions() {
-  if (failed_ || cancelled_) {
-    Cleanup(ErrorCode::kError);
+  if (!bytes_to_read) {
+    OnReadDoneCallback(0);
     return;
   }
 
-  if (read_done_) {
-    // We're done!
-    ErrorCode code = ErrorCode::kSuccess;
-    if (hasher_.Finalize()) {
-      LOG(INFO) << "Hash: " << hasher_.hash();
-      switch (partition_type_) {
-        case PartitionType::kRootfs:
-          if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
-            code = ErrorCode::kNewRootfsVerificationError;
-            LOG(ERROR) << "New rootfs verification failed.";
-          }
-          break;
-        case PartitionType::kKernel:
-          if (install_plan_.kernel_hash != hasher_.raw_hash()) {
-            code = ErrorCode::kNewKernelVerificationError;
-            LOG(ERROR) << "New kernel verification failed.";
-          }
-          break;
-        case PartitionType::kSourceRootfs:
-          install_plan_.source_rootfs_hash = hasher_.raw_hash();
-          break;
-        case PartitionType::kSourceKernel:
-          install_plan_.source_kernel_hash = hasher_.raw_hash();
-          break;
-      }
-    } else {
-      LOG(ERROR) << "Unable to finalize the hash.";
-      code = ErrorCode::kError;
-    }
-    Cleanup(code);
+  bool read_async_ok = src_stream_->ReadAsync(
+    buffer_.data(),
+    bytes_to_read,
+    base::Bind(&FilesystemVerifierAction::OnReadDoneCallback,
+               base::Unretained(this)),
+    base::Bind(&FilesystemVerifierAction::OnReadErrorCallback,
+               base::Unretained(this)),
+    nullptr);
+
+  if (!read_async_ok) {
+    LOG(ERROR) << "Unable to schedule an asynchronous read from the stream.";
+    Cleanup(ErrorCode::kError);
   }
 }
 
-void FilesystemVerifierAction::DetermineFilesystemSize(int fd) {
+void FilesystemVerifierAction::OnReadDoneCallback(size_t bytes_read) {
+  if (bytes_read == 0) {
+    read_done_ = true;
+  } else {
+    remaining_size_ -= bytes_read;
+    CHECK(!read_done_);
+    if (!hasher_.Update(buffer_.data(), bytes_read)) {
+      LOG(ERROR) << "Unable to update the hash.";
+      Cleanup(ErrorCode::kError);
+      return;
+    }
+  }
+
+  // We either terminate the action or have more data to read.
+  if (!CheckTerminationConditions())
+    ScheduleRead();
+}
+
+void FilesystemVerifierAction::OnReadErrorCallback(
+      const chromeos::Error* error) {
+  // TODO(deymo): Transform the read-error into an specific ErrorCode.
+  LOG(ERROR) << "Asynchronous read failed.";
+  Cleanup(ErrorCode::kError);
+}
+
+bool FilesystemVerifierAction::CheckTerminationConditions() {
+  if (cancelled_) {
+    Cleanup(ErrorCode::kError);
+    return true;
+  }
+
+  if (!read_done_)
+    return false;
+
+  // We're done!
+  ErrorCode code = ErrorCode::kSuccess;
+  if (hasher_.Finalize()) {
+    LOG(INFO) << "Hash: " << hasher_.hash();
+    switch (partition_type_) {
+      case PartitionType::kRootfs:
+        if (install_plan_.rootfs_hash != hasher_.raw_hash()) {
+          code = ErrorCode::kNewRootfsVerificationError;
+          LOG(ERROR) << "New rootfs verification failed.";
+        }
+        break;
+      case PartitionType::kKernel:
+        if (install_plan_.kernel_hash != hasher_.raw_hash()) {
+          code = ErrorCode::kNewKernelVerificationError;
+          LOG(ERROR) << "New kernel verification failed.";
+        }
+        break;
+      case PartitionType::kSourceRootfs:
+        install_plan_.source_rootfs_hash = hasher_.raw_hash();
+        break;
+      case PartitionType::kSourceKernel:
+        install_plan_.source_kernel_hash = hasher_.raw_hash();
+        break;
+    }
+  } else {
+    LOG(ERROR) << "Unable to finalize the hash.";
+    code = ErrorCode::kError;
+  }
+  Cleanup(code);
+  return true;
+}
+
+void FilesystemVerifierAction::DetermineFilesystemSize(
+    const std::string& path) {
   switch (partition_type_) {
     case PartitionType::kRootfs:
       remaining_size_ = install_plan_.rootfs_size;
@@ -228,7 +241,7 @@
     case PartitionType::kSourceRootfs:
       {
         int block_count = 0, block_size = 0;
-        if (utils::GetFilesystemSizeFromFD(fd, &block_count, &block_size)) {
+        if (utils::GetFilesystemSize(path, &block_count, &block_size)) {
           remaining_size_ = static_cast<int64_t>(block_count) * block_size;
           LOG(INFO) << "Filesystem size: " << remaining_size_ << " bytes ("
                     << block_count << "x" << block_size << ").";
@@ -238,7 +251,6 @@
     default:
       break;
   }
-  return;
 }
 
 }  // namespace chromeos_update_engine
diff --git a/filesystem_verifier_action.h b/filesystem_verifier_action.h
index 69e4972..e6c1908 100644
--- a/filesystem_verifier_action.h
+++ b/filesystem_verifier_action.h
@@ -11,7 +11,7 @@
 #include <string>
 #include <vector>
 
-#include <chromeos/message_loops/message_loop.h>
+#include <chromeos/streams/stream.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "update_engine/action.h"
@@ -44,7 +44,7 @@
   // Used for testing. Return true if Cleanup() has not yet been called due
   // to a callback upon the completion or cancellation of the verifier action.
   // A test should wait until IsCleanupPending() returns false before
-  // terminating the glib main loop.
+  // terminating the main loop.
   bool IsCleanupPending() const;
 
   // Debugging/logging
@@ -56,13 +56,17 @@
   FRIEND_TEST(FilesystemVerifierActionTest,
               RunAsRootDetermineFilesystemSizeTest);
 
-  // Callback from the main loop when there's data to read from the file
-  // descriptor.
-  void OnReadReadyCallback();
+  // Schedules the asynchronous read of the filesystem.
+  void ScheduleRead();
 
-  // Based on the state of the read buffers, terminates read process and the
-  // action.
-  void CheckTerminationConditions();
+  // Called from the main loop when a single read from |src_stream_| succeeds or
+  // fails, calling OnReadDoneCallback() and OnReadErrorCallback() respectively.
+  void OnReadDoneCallback(size_t bytes_read);
+  void OnReadErrorCallback(const chromeos::Error* error);
+
+  // Based on the state of the read buffer, terminates read process and the
+  // action. Return whether the action was terminated.
+  bool CheckTerminationConditions();
 
   // Cleans up all the variables we use for async operations and tells the
   // ActionProcessor we're done w/ |code| as passed in. |cancelled_| should be
@@ -72,23 +76,18 @@
   // Determine, if possible, the source file system size to avoid copying the
   // whole partition. Currently this supports only the root file system assuming
   // it's ext3-compatible.
-  void DetermineFilesystemSize(int fd);
+  void DetermineFilesystemSize(const std::string& path);
 
   // The type of the partition that we are verifying.
   PartitionType partition_type_;
 
-  // If non-null, this is the GUnixInputStream object for the opened source
-  // partition.
-  int src_fd_{-1};
+  // If not null, the FileStream used to read from the device.
+  chromeos::StreamPtr src_stream_;
 
   // Buffer for storing data we read.
   chromeos::Blob buffer_;
 
-  // The task id for the the in-flight async call.
-  chromeos::MessageLoop::TaskId read_task_{chromeos::MessageLoop::kTaskIdNull};
-
   bool read_done_{false};  // true if reached EOF on the input stream.
-  bool failed_{false};  // true if the action has failed.
   bool cancelled_{false};  // true if the action has been cancelled.
 
   // The install plan we're passed in via the input pipe.
diff --git a/filesystem_verifier_action_unittest.cc b/filesystem_verifier_action_unittest.cc
index da0fd95..9ddeb29 100644
--- a/filesystem_verifier_action_unittest.cc
+++ b/filesystem_verifier_action_unittest.cc
@@ -14,7 +14,7 @@
 #include <base/posix/eintr_wrapper.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/message_loops/fake_message_loop.h>
 #include <chromeos/message_loops/message_loop_utils.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -47,7 +47,7 @@
               bool hash_fail,
               PartitionType partition_type);
 
-  chromeos::GlibMessageLoop loop_;
+  chromeos::FakeMessageLoop loop_{nullptr};
   FakeSystemState fake_system_state_;
 };
 
@@ -352,6 +352,8 @@
 TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) {
   ASSERT_EQ(0, getuid());
   EXPECT_TRUE(DoTest(true, false, PartitionType::kKernel));
+  // TerminateEarlyTest may leak some null callbacks from the Stream class.
+  while (loop_.RunOnce(false)) {}
 }
 
 TEST_F(FilesystemVerifierActionTest, RunAsRootDetermineFilesystemSizeTest) {
@@ -362,19 +364,18 @@
   // Extend the "partition" holding the file system from 10MiB to 20MiB.
   EXPECT_EQ(0, truncate(img.c_str(), 20 * 1024 * 1024));
 
-  for (int i = 0; i < 2; ++i) {
-    PartitionType fs_type =
-        i ? PartitionType::kSourceKernel : PartitionType::kSourceRootfs;
-    FilesystemVerifierAction action(&fake_system_state_, fs_type);
+  {
+    FilesystemVerifierAction action(&fake_system_state_,
+                                    PartitionType::kSourceKernel);
     EXPECT_EQ(kint64max, action.remaining_size_);
-    {
-      int fd = HANDLE_EINTR(open(img.c_str(), O_RDONLY));
-      EXPECT_GT(fd, 0);
-      ScopedFdCloser fd_closer(&fd);
-      action.DetermineFilesystemSize(fd);
-    }
-    EXPECT_EQ(i ? kint64max : 10 * 1024 * 1024,
-              action.remaining_size_);
+    action.DetermineFilesystemSize(img);
+    EXPECT_EQ(kint64max, action.remaining_size_);
+  }
+  {
+    FilesystemVerifierAction action(&fake_system_state_,
+                                    PartitionType::kSourceRootfs);
+    action.DetermineFilesystemSize(img);
+    EXPECT_EQ(10 * 1024 * 1024, action.remaining_size_);
   }
 }
 
diff --git a/generate_image.gypi b/generate_image.gypi
deleted file mode 100644
index 025e925..0000000
--- a/generate_image.gypi
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-  'variables': {
-    'out_dir': '<(SHARED_INTERMEDIATE_DIR)/<(image_out_dir)',
-    'generator': 'sample_images/generate_image.sh',
-  },
-  'rules': [
-    {
-      'rule_name': 'generate_image',
-      'extension': 'txt',
-      'inputs': [
-        '<(generator)',
-        '<(RULE_INPUT_PATH)',
-      ],
-      'outputs': [
-        '<(out_dir)/<(RULE_INPUT_ROOT).img',
-      ],
-      'action': [
-        '<(generator)',
-        '<(RULE_INPUT_PATH)',
-        '<(out_dir)',
-      ],
-      'msvs_cygwin_shell': 0,
-      'message': 'Generating image from <(RULE_INPUT_PATH)',
-      'process_outputs_as_sources': 1,
-    },
-  ],
-}
diff --git a/glib_utils.cc b/glib_utils.cc
deleted file mode 100644
index 625d8d1..0000000
--- a/glib_utils.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2014 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 "update_engine/glib_utils.h"
-
-#include <base/strings/stringprintf.h>
-
-using std::string;
-
-namespace chromeos_update_engine {
-namespace utils {
-
-string GetAndFreeGError(GError** error) {
-  if (!*error) {
-    return "Unknown GLib error.";
-  }
-  string message =
-      base::StringPrintf("GError(%d): %s",
-                         (*error)->code,
-                         (*error)->message ? (*error)->message : "(unknown)");
-  g_error_free(*error);
-  *error = nullptr;
-  return message;
-}
-
-gchar** StringVectorToGStrv(const std::vector<string> &vec_str) {
-  GPtrArray *p = g_ptr_array_new();
-  for (const string& str : vec_str) {
-    g_ptr_array_add(p, g_strdup(str.c_str()));
-  }
-  g_ptr_array_add(p, nullptr);
-  return reinterpret_cast<gchar**>(g_ptr_array_free(p, FALSE));
-}
-
-}  // namespace utils
-}  // namespace chromeos_update_engine
diff --git a/glib_utils.h b/glib_utils.h
deleted file mode 100644
index 97020cb..0000000
--- a/glib_utils.h
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2014 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 UPDATE_ENGINE_GLIB_UTILS_H_
-#define UPDATE_ENGINE_GLIB_UTILS_H_
-
-#include <string>
-#include <vector>
-
-#include <glib.h>
-
-namespace chromeos_update_engine {
-namespace utils {
-
-// Returns the error message, if any, from a GError pointer. Frees the GError
-// object and resets error to null.
-std::string GetAndFreeGError(GError** error);
-
-// Converts a vector of strings to a NUL-terminated array of
-// strings. The resulting array should be freed with g_strfreev()
-// when are you done with it.
-gchar** StringVectorToGStrv(const std::vector<std::string>& vec_str);
-
-// A base::FreeDeleter that frees memory using g_free(). Useful when
-// integrating with GLib since it can be used with std::unique_ptr to
-// automatically free memory when going out of scope.
-struct GLibFreeDeleter {
-  inline void operator()(void* ptr) const {
-    g_free(static_cast<gpointer>(ptr));
-  }
-};
-
-// A base::FreeDeleter that frees memory using g_strfreev(). Useful
-// when integrating with GLib since it can be used with std::unique_ptr to
-// automatically free memory when going out of scope.
-struct GLibStrvFreeDeleter {
-  inline void operator()(void* ptr) const {
-    g_strfreev(static_cast<gchar**>(ptr));
-  }
-};
-
-}  // namespace utils
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_GLIB_UTILS_H_
diff --git a/http_fetcher_unittest.cc b/http_fetcher_unittest.cc
index fd520e9..d822e51 100644
--- a/http_fetcher_unittest.cc
+++ b/http_fetcher_unittest.cc
@@ -14,13 +14,17 @@
 
 #include <base/location.h>
 #include <base/logging.h>
+#include <base/message_loop/message_loop.h>
+#include <base/strings/string_number_conversions.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <base/time/time.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/message_loops/base_message_loop.h>
 #include <chromeos/message_loops/message_loop.h>
 #include <chromeos/message_loops/message_loop_utils.h>
-#include <glib.h>
+#include <chromeos/process.h>
+#include <chromeos/streams/file_stream.h>
+#include <chromeos/streams/stream.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/fake_system_state.h"
@@ -29,6 +33,7 @@
 #include "update_engine/mock_http_fetcher.h"
 #include "update_engine/multi_range_http_fetcher.h"
 #include "update_engine/proxy_resolver.h"
+#include "update_engine/test_utils.h"
 #include "update_engine/utils.h"
 
 using chromeos::MessageLoop;
@@ -88,74 +93,68 @@
 
 class PythonHttpServer : public HttpServer {
  public:
-  PythonHttpServer() : pid_(-1), port_(0) {
+  PythonHttpServer() : port_(0) {
     started_ = false;
 
     // Spawn the server process.
-    gchar *argv[] = {
-      const_cast<gchar*>("./test_http_server"),
-      nullptr
-    };
-    GError *err;
-    gint server_stdout = -1;
-    if (!g_spawn_async_with_pipes(nullptr, argv, nullptr,
-                                  G_SPAWN_DO_NOT_REAP_CHILD, nullptr, nullptr,
-                                  &pid_, nullptr, &server_stdout, nullptr,
-                                  &err)) {
-      LOG(ERROR) << "failed to spawn http server process";
+    unique_ptr<chromeos::Process> http_server(new chromeos::ProcessImpl());
+    base::FilePath test_server_path =
+        test_utils::GetBuildArtifactsPath().Append("test_http_server");
+    http_server->AddArg(test_server_path.value());
+    http_server->RedirectUsingPipe(STDOUT_FILENO, false);
+
+    if (!http_server->Start()) {
+      ADD_FAILURE() << "failed to spawn http server process";
       return;
     }
-    CHECK_GT(pid_, 0);
-    CHECK_GE(server_stdout, 0);
-    LOG(INFO) << "started http server with pid " << pid_;
+    LOG(INFO) << "started http server with pid " << http_server->pid();
 
     // Wait for server to begin accepting connections, obtain its port.
-    char line[80];
-    const size_t listening_msg_prefix_len = strlen(kServerListeningMsgPrefix);
-    CHECK_GT(sizeof(line), listening_msg_prefix_len);
-    int line_len = read(server_stdout, line, sizeof(line) - 1);
-    if (line_len <= static_cast<int>(listening_msg_prefix_len)) {
-      if (line_len < 0) {
-        PLOG(ERROR) << "error reading http server stdout";
-      } else {
-        LOG(ERROR) << "server output too short";
+    chromeos::StreamPtr stdout = chromeos::FileStream::FromFileDescriptor(
+        http_server->GetPipe(STDOUT_FILENO), false /* own */, nullptr);
+    if (!stdout)
+      return;
+
+    vector<char> buf(128);
+    string line;
+    while (line.find('\n') == string::npos) {
+      size_t read;
+      if (!stdout->ReadBlocking(buf.data(), buf.size(), &read, nullptr)) {
+        ADD_FAILURE() << "error reading http server stdout";
+        return;
       }
-      Terminate(true);
+      line.append(buf.data(), read);
+      if (read == 0)
+        break;
+    }
+    // Parse the port from the output line.
+    const size_t listening_msg_prefix_len = strlen(kServerListeningMsgPrefix);
+    if (line.size() < listening_msg_prefix_len) {
+      ADD_FAILURE() << "server output too short";
       return;
     }
 
-    line[line_len] = '\0';
-    CHECK_EQ(strstr(line, kServerListeningMsgPrefix), line);
-    const char* listening_port_str = line + listening_msg_prefix_len;
-    char* end_ptr;
-    long raw_port = strtol(listening_port_str,  // NOLINT(runtime/int)
-                           &end_ptr, 10);
-    CHECK(!*end_ptr || *end_ptr == '\n');
-    port_ = static_cast<in_port_t>(raw_port);
-    CHECK_GT(port_, 0);
+    EXPECT_EQ(kServerListeningMsgPrefix,
+              line.substr(0, listening_msg_prefix_len));
+    string port_str = line.substr(listening_msg_prefix_len);
+    port_str.resize(port_str.find('\n'));
+    EXPECT_TRUE(base::StringToUint(port_str, &port_));
+
     started_ = true;
     LOG(INFO) << "server running, listening on port " << port_;
-    LOG(INFO) << "gdb attach now!";
+
+    // Any failure before this point will SIGKILL the test server if started
+    // when the |http_server| goes out of scope.
+    http_server_ = std::move(http_server);
   }
 
   ~PythonHttpServer() {
     // If there's no process, do nothing.
-    if (pid_ == -1)
+    if (!http_server_)
       return;
-
-    // If server is responsive, request that it gracefully terminate.
-    bool do_kill = false;
-    if (started_) {
-      LOG(INFO) << "running wget to exit";
-      if (system((string("wget -t 1 --output-document=/dev/null ") +
-                  LocalServerUrlForPath(port_, "/quitquitquit")).c_str())) {
-        LOG(WARNING) << "wget failed, resorting to brute force";
-        do_kill = true;
-      }
-    }
-
-    // Server not responding or wget failed, kill the process.
-    Terminate(do_kill);
+    // Wait up to 10 seconds for the process to finish. Destroying the process
+    // will kill it with a SIGKILL otherwise.
+    http_server_->Kill(SIGTERM, 10);
   }
 
   in_port_t GetPort() const override {
@@ -163,27 +162,10 @@
   }
 
  private:
-  void Terminate(bool do_kill) {
-    ASSERT_GT(pid_, 0);
-
-    if (do_kill) {
-      LOG(INFO) << "terminating (SIGKILL) server process with pid " << pid_;
-      kill(pid_, SIGKILL);
-    }
-
-    LOG(INFO) << "waiting for http server with pid " << pid_ << " to terminate";
-    int status;
-    pid_t killed_pid = waitpid(pid_, &status, 0);
-    ASSERT_EQ(killed_pid, pid_);
-    LOG(INFO) << "http server with pid " << pid_
-              << " terminated with status " << status;
-    pid_ = -1;
-  }
-
   static const char* kServerListeningMsgPrefix;
 
-  GPid pid_;
-  in_port_t port_;
+  unique_ptr<chromeos::Process> http_server_;
+  unsigned int port_;
 };
 
 const char* PythonHttpServer::kServerListeningMsgPrefix = "listening on port ";
@@ -345,10 +327,8 @@
 template <typename T>
 class HttpFetcherTest : public ::testing::Test {
  public:
-  // TODO(deymo): Replace this with a FakeMessageLoop. We can't do that yet
-  // because these tests use g_spawn_async_with_pipes() to launch the
-  // http_test_server.
-  chromeos::GlibMessageLoop loop_;
+  base::MessageLoopForIO base_loop_;
+  chromeos::BaseMessageLoop loop_{&base_loop_};
 
   T test_;
 
diff --git a/libcros_proxy.cc b/libcros_proxy.cc
new file mode 100644
index 0000000..9f76ea9
--- /dev/null
+++ b/libcros_proxy.cc
@@ -0,0 +1,47 @@
+// Copyright 2015 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 "update_engine/libcros_proxy.h"
+
+#include "update_engine/dbus_proxies.h"
+
+using org::chromium::LibCrosServiceInterfaceProxy;
+using org::chromium::LibCrosServiceInterfaceProxyInterface;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxy;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface;
+
+namespace {
+const char kLibCrosServiceName[] = "org.chromium.LibCrosService";
+}  // namespace
+
+namespace chromeos_update_engine {
+
+LibCrosProxy::LibCrosProxy(
+    std::unique_ptr<LibCrosServiceInterfaceProxyInterface>
+        service_interface_proxy,
+    std::unique_ptr<UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface>
+        ue_proxy_resolved_interface)
+    : service_interface_proxy_(std::move(service_interface_proxy)),
+      ue_proxy_resolved_interface_(std::move(ue_proxy_resolved_interface)) {
+}
+
+LibCrosProxy::LibCrosProxy(const scoped_refptr<dbus::Bus>& bus)
+    : service_interface_proxy_(
+          new LibCrosServiceInterfaceProxy(bus, kLibCrosServiceName)),
+      ue_proxy_resolved_interface_(
+          new UpdateEngineLibcrosProxyResolvedInterfaceProxy(
+              bus,
+              kLibCrosServiceName)) {
+}
+
+LibCrosServiceInterfaceProxyInterface* LibCrosProxy::service_interface_proxy() {
+  return service_interface_proxy_.get();
+}
+
+UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface*
+LibCrosProxy::ue_proxy_resolved_interface() {
+  return ue_proxy_resolved_interface_.get();
+}
+
+}  // namespace chromeos_update_engine
diff --git a/libcros_proxy.h b/libcros_proxy.h
new file mode 100644
index 0000000..4c0b8bc
--- /dev/null
+++ b/libcros_proxy.h
@@ -0,0 +1,50 @@
+// Copyright 2015 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 UPDATE_ENGINE_LIBCROS_PROXY_H_
+#define UPDATE_ENGINE_LIBCROS_PROXY_H_
+
+#include <memory>
+
+#include <base/macros.h>
+#include <dbus/bus.h>
+
+#include "update_engine/dbus_proxies.h"
+
+namespace chromeos_update_engine {
+
+// This class handles the DBus connection with chrome to resolve proxies. This
+// is a thin class to just hold the generated proxies (real or mocked ones).
+class LibCrosProxy final {
+ public:
+  explicit LibCrosProxy(const scoped_refptr<dbus::Bus>& bus);
+  LibCrosProxy(
+      std::unique_ptr<org::chromium::LibCrosServiceInterfaceProxyInterface>
+          service_interface_proxy,
+      std::unique_ptr<
+          org::chromium::
+              UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface>
+          ue_proxy_resolved_interface);
+
+  ~LibCrosProxy() = default;
+
+  // Getters for the two proxies.
+  org::chromium::LibCrosServiceInterfaceProxyInterface*
+  service_interface_proxy();
+  org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface*
+  ue_proxy_resolved_interface();
+
+ private:
+  std::unique_ptr<org::chromium::LibCrosServiceInterfaceProxyInterface>
+      service_interface_proxy_;
+  std::unique_ptr<
+      org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyInterface>
+      ue_proxy_resolved_interface_;
+
+  DISALLOW_COPY_AND_ASSIGN(LibCrosProxy);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_LIBCROS_PROXY_H_
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index 9d32293..8010b8c 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -8,6 +8,7 @@
 #include <string>
 
 #include <base/bind.h>
+#include <base/format_macros.h>
 #include <base/location.h>
 #include <base/logging.h>
 #include <base/strings/string_util.h>
@@ -132,10 +133,10 @@
     }
 
     // Create a string representation of the desired range.
-    string range_str = (end_offset ?
-                        base::StringPrintf("%jd-%zu", resume_offset_,
-                                           end_offset) :
-                        base::StringPrintf("%jd-", resume_offset_));
+    string range_str = base::StringPrintf(
+        "%" PRIu64 "-", static_cast<uint64_t>(resume_offset_));
+    if (end_offset)
+      range_str += std::to_string(end_offset);
     CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_RANGE, range_str.c_str()),
              CURLE_OK);
   }
diff --git a/libcurl_http_fetcher.h b/libcurl_http_fetcher.h
index 4f68a9a..1b8891d 100644
--- a/libcurl_http_fetcher.h
+++ b/libcurl_http_fetcher.h
@@ -129,7 +129,7 @@
 
   // Calls into curl_multi_perform to let libcurl do its work. Returns after
   // curl_multi_perform is finished, which may actually be after more than
-  // one call to curl_multi_perform. This method will set up the glib run
+  // one call to curl_multi_perform. This method will set up the message
   // loop with sources for future work that libcurl will do.
   // This method will not block.
   // Returns true if we should resume immediately after this call.
diff --git a/main.cc b/main.cc
index 51a04ec..1d82dd6 100644
--- a/main.cc
+++ b/main.cc
@@ -5,101 +5,28 @@
 #include <unistd.h>
 
 #include <string>
-#include <vector>
 
 #include <base/at_exit.h>
 #include <base/command_line.h>
 #include <base/files/file_util.h>
-#include <base/location.h>
 #include <base/logging.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <base/time/time.h>
 #include <chromeos/flag_helper.h>
-#include <chromeos/message_loops/glib_message_loop.h>
-#include <glib.h>
+#include <chromeos/message_loops/base_message_loop.h>
 #include <metrics/metrics_library.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include "update_engine/certificate_checker.h"
-#include "update_engine/dbus_constants.h"
-#include "update_engine/dbus_service.h"
-#include "update_engine/dbus_wrapper_interface.h"
-#include "update_engine/glib_utils.h"
-#include "update_engine/real_system_state.h"
-#include "update_engine/subprocess.h"
+#include "update_engine/daemon.h"
 #include "update_engine/terminator.h"
-#include "update_engine/update_attempter.h"
-extern "C" {
-#include "update_engine/org.chromium.UpdateEngineInterface.dbusserver.h"
-}
 #include "update_engine/utils.h"
 
 using std::string;
-using std::vector;
-
-namespace {
-const int kDBusSystemMaxWaitSeconds = 2 * 60;
-}  // namespace
 
 namespace chromeos_update_engine {
-
 namespace {
 
-// Wait for DBus to be ready by attempting to get the system bus up to
-// |timeout| time. Returns whether it succeeded to get the bus.
-bool WaitForDBusSystem(base::TimeDelta timeout) {
-  GError *error = nullptr;
-  DBusGConnection *bus = nullptr;
-  Clock clock;
-  base::Time deadline = clock.GetMonotonicTime() + timeout;
-
-  while (clock.GetMonotonicTime() < deadline) {
-    bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
-    if (bus)
-      return true;
-    LOG(WARNING) << "Failed to get system bus, waiting: "
-                 << utils::GetAndFreeGError(&error);
-    // Wait 1 second.
-    sleep(1);
-  }
-  LOG(ERROR) << "Failed to get system bus after " << timeout.InSeconds()
-             << " seconds.";
-  return false;
-}
-
-void SetupDBusService(UpdateEngineService* service) {
-  DBusGConnection *bus;
-  DBusGProxy *proxy;
-  GError *error = nullptr;
-
-  bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
-  LOG_IF(FATAL, !bus) << "Failed to get bus: "
-                      << utils::GetAndFreeGError(&error);
-  proxy = dbus_g_proxy_new_for_name(bus,
-                                    DBUS_SERVICE_DBUS,
-                                    DBUS_PATH_DBUS,
-                                    DBUS_INTERFACE_DBUS);
-  guint32 request_name_ret;
-  if (!org_freedesktop_DBus_request_name(proxy,
-                                         kUpdateEngineServiceName,
-                                         0,
-                                         &request_name_ret,
-                                         &error)) {
-    LOG(FATAL) << "Failed to get name: " << utils::GetAndFreeGError(&error);
-  }
-  if (request_name_ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
-    g_warning("Got result code %u from requesting name", request_name_ret);
-    LOG(FATAL) << "Got result code " << request_name_ret
-               << " from requesting name, but expected "
-               << DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER;
-  }
-  dbus_g_connection_register_g_object(bus,
-                                      "/org/chromium/UpdateEngine",
-                                      G_OBJECT(service));
-}
-
 void SetupLogSymlink(const string& symlink_path, const string& log_path) {
   // TODO(petkov): To ensure a smooth transition between non-timestamped and
   // timestamped logs, move an existing log to start the first timestamped
@@ -166,11 +93,7 @@
   DEFINE_bool(foreground, false,
               "Don't daemon()ize; run in foreground.");
 
-  ::g_type_init();
-  dbus_threads_init_default();
-  base::AtExitManager exit_manager;  // Required for base/rand_util.h.
   chromeos_update_engine::Terminator::Init();
-  chromeos_update_engine::Subprocess::Init();
   chromeos::FlagHelper::Init(argc, argv, "Chromium OS Update Engine");
   chromeos_update_engine::SetupLogging(FLAGS_logtostderr);
   if (!FLAGS_foreground)
@@ -184,70 +107,10 @@
   // Done _after_ log file creation.
   umask(S_IXUSR | S_IRWXG | S_IRWXO);
 
-  // Create the single Glib main loop. Code accessing directly the glib main
-  // loop (such as calling g_timeout_add() or similar functions) will still work
-  // since the backend for the message loop is still the Glib main loop.
-  // TODO(deymo): Replace this |loop| with one based on libevent once no other
-  // code here uses glib directly.
-  chromeos::GlibMessageLoop loop;
-  loop.SetAsCurrent();
+  chromeos_update_engine::UpdateEngineDaemon update_engine_daemon;
+  int exit_code = update_engine_daemon.Run();
 
-  // Wait up to 2 minutes for DBus to be ready.
-  LOG_IF(FATAL, !chromeos_update_engine::WaitForDBusSystem(
-      base::TimeDelta::FromSeconds(kDBusSystemMaxWaitSeconds)))
-      << "Failed to initialize DBus, aborting.";
-
-  chromeos_update_engine::RealSystemState real_system_state;
-  LOG_IF(ERROR, !real_system_state.Initialize())
-      << "Failed to initialize system state.";
-  chromeos_update_engine::UpdateAttempter *update_attempter =
-      real_system_state.update_attempter();
-  CHECK(update_attempter);
-
-  // Sets static members for the certificate checker.
-  chromeos_update_engine::CertificateChecker::set_system_state(
-      &real_system_state);
-  chromeos_update_engine::OpenSSLWrapper openssl_wrapper;
-  chromeos_update_engine::CertificateChecker::set_openssl_wrapper(
-      &openssl_wrapper);
-
-  // Create the dbus service object:
-  dbus_g_object_type_install_info(UPDATE_ENGINE_TYPE_SERVICE,
-                                  &dbus_glib_update_engine_service_object_info);
-  UpdateEngineService* service = update_engine_service_new();
-  service->system_state_ = &real_system_state;
-  update_attempter->set_dbus_service(service);
-  chromeos_update_engine::SetupDBusService(service);
-
-  // Initiate update checks.
-  update_attempter->ScheduleUpdates();
-
-  // Update boot flags after 45 seconds.
-  loop.PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&chromeos_update_engine::UpdateAttempter::UpdateBootFlags,
-                 base::Unretained(update_attempter)),
-      base::TimeDelta::FromSeconds(45));
-
-  // Broadcast the update engine status on startup to ensure consistent system
-  // state on crashes.
-  loop.PostTask(FROM_HERE, base::Bind(
-      &chromeos_update_engine::UpdateAttempter::BroadcastStatus,
-      base::Unretained(update_attempter)));
-
-  // Run the UpdateEngineStarted() method on |update_attempter|.
-  loop.PostTask(FROM_HERE, base::Bind(
-      &chromeos_update_engine::UpdateAttempter::UpdateEngineStarted,
-      base::Unretained(update_attempter)));
-
-  // Run the main loop until exit time:
-  loop.Run();
-
-  // Cleanup:
-  update_attempter->set_dbus_service(nullptr);
-  g_object_unref(G_OBJECT(service));
-
-  loop.ReleaseFromCurrent();
-  LOG(INFO) << "Chrome OS Update Engine terminating";
-  return 0;
+  LOG(INFO) << "Chrome OS Update Engine terminating with exit code "
+            << exit_code;
+  return exit_code;
 }
diff --git a/mock_connection_manager.h b/mock_connection_manager.h
index 9530865..e885e2f 100644
--- a/mock_connection_manager.h
+++ b/mock_connection_manager.h
@@ -7,31 +7,23 @@
 
 #include <gmock/gmock.h>
 
-#include "update_engine/connection_manager.h"
+#include "update_engine/connection_manager_interface.h"
 
 namespace chromeos_update_engine {
 
 // This class mocks the generic interface to the connection manager
 // (e.g FlimFlam, Shill, etc.) to consolidate all connection-related
 // logic in update_engine.
-class MockConnectionManager : public ConnectionManager {
+class MockConnectionManager : public ConnectionManagerInterface {
  public:
-  explicit MockConnectionManager(SystemState* system_state)
-      : ConnectionManager(system_state) {}
+  MockConnectionManager() = default;
 
-  MOCK_CONST_METHOD3(GetConnectionProperties,
-                     bool(DBusWrapperInterface* dbus_iface,
-                          NetworkConnectionType* out_type,
-                          NetworkTethering* out_tethering));
+  MOCK_METHOD2(GetConnectionProperties,
+               bool(NetworkConnectionType* out_type,
+                    NetworkTethering* out_tethering));
 
   MOCK_CONST_METHOD2(IsUpdateAllowedOver, bool(NetworkConnectionType type,
                                                NetworkTethering tethering));
-
-  MOCK_CONST_METHOD1(StringForConnectionType,
-      const char*(NetworkConnectionType type));
-
-  MOCK_CONST_METHOD1(StringForTethering,
-      const char*(NetworkTethering tethering));
 };
 
 }  // namespace chromeos_update_engine
diff --git a/mock_dbus_wrapper.h b/mock_dbus_wrapper.h
deleted file mode 100644
index 5d630a6..0000000
--- a/mock_dbus_wrapper.h
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright (c) 2010 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 UPDATE_ENGINE_MOCK_DBUS_WRAPPER_H_
-#define UPDATE_ENGINE_MOCK_DBUS_WRAPPER_H_
-
-#include <gmock/gmock.h>
-
-#include "update_engine/dbus_wrapper_interface.h"
-
-namespace chromeos_update_engine {
-
-class MockDBusWrapper : public DBusWrapperInterface {
- public:
-  MOCK_METHOD4(ProxyNewForName, DBusGProxy*(DBusGConnection *connection,
-                                            const char *name,
-                                            const char *path,
-                                            const char *interface));
-
-  MOCK_METHOD1(ProxyUnref, void(DBusGProxy* proxy));
-
-  MOCK_METHOD2(BusGet, DBusGConnection*(DBusBusType type, GError **error));
-
-  MOCK_METHOD4(ProxyCall_0_1, gboolean(DBusGProxy *proxy,
-                                       const char *method,
-                                       GError **error,
-                                       GHashTable** out1));
-  MOCK_METHOD4(ProxyCall_0_1, gboolean(DBusGProxy *proxy,
-                                       const char *method,
-                                       GError **error,
-                                       gint* out1));
-  MOCK_METHOD4(ProxyCall_1_0, gboolean(DBusGProxy *proxy,
-                                       const char *method,
-                                       GError **error,
-                                       gint in1));
-  MOCK_METHOD6(ProxyCall_3_0, gboolean(DBusGProxy* proxy,
-                                       const char* method,
-                                       GError** error,
-                                       const char* in1,
-                                       const char* in2,
-                                       const char* in3));
-
-  MOCK_METHOD3(ProxyAddSignal_1, void(DBusGProxy* proxy,
-                                      const char* signal_name,
-                                      GType type1));
-
-  MOCK_METHOD4(ProxyAddSignal_2, void(DBusGProxy* proxy,
-                                      const char* signal_name,
-                                      GType type1,
-                                      GType type2));
-
-  MOCK_METHOD5(ProxyConnectSignal, void(DBusGProxy* proxy,
-                                        const char* signal_name,
-                                        GCallback handler,
-                                        void* data,
-                                        GClosureNotify free_data_func));
-
-  MOCK_METHOD4(ProxyDisconnectSignal, void(DBusGProxy* proxy,
-                                           const char* signal_name,
-                                           GCallback handler,
-                                           void* data));
-
-  MOCK_METHOD1(ConnectionGetConnection, DBusConnection*(DBusGConnection* gbus));
-
-  MOCK_METHOD3(DBusBusAddMatch, void(DBusConnection* connection,
-                                     const char* rule,
-                                     DBusError* error));
-
-  MOCK_METHOD4(DBusConnectionAddFilter, dbus_bool_t(
-      DBusConnection* connection,
-      DBusHandleMessageFunction function,
-      void* user_data,
-      DBusFreeFunction free_data_function));
-
-  MOCK_METHOD3(DBusConnectionRemoveFilter, void(
-      DBusConnection* connection,
-      DBusHandleMessageFunction function,
-      void* user_data));
-
-  MOCK_METHOD3(DBusMessageIsSignal, dbus_bool_t(DBusMessage* message,
-                                                const char* interface,
-                                                const char* signal_name));
-
-  MOCK_METHOD5(DBusMessageGetArgs_3, dbus_bool_t(DBusMessage* message,
-                                                 DBusError* error,
-                                                 char** out1,
-                                                 char** out2,
-                                                 char** out3));
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_MOCK_DBUS_WRAPPER_H_
diff --git a/mock_http_fetcher.h b/mock_http_fetcher.h
index 89f5d21..fa718bc 100644
--- a/mock_http_fetcher.h
+++ b/mock_http_fetcher.h
@@ -10,7 +10,6 @@
 
 #include <base/logging.h>
 #include <chromeos/message_loops/message_loop.h>
-#include <glib.h>
 
 #include "update_engine/fake_system_state.h"
 #include "update_engine/http_fetcher.h"
@@ -40,8 +39,7 @@
         timeout_id_(chromeos::MessageLoop::kTaskIdNull),
         paused_(false),
         fail_transfer_(false),
-        never_use_(false),
-        mock_connection_manager_(&fake_system_state_) {
+        never_use_(false) {
     fake_system_state_.set_connection_manager(&mock_connection_manager_);
     data_.insert(data_.end(), data, data + size);
   }
diff --git a/multi_range_http_fetcher.cc b/multi_range_http_fetcher.cc
index 07118a3..8d1fd06 100644
--- a/multi_range_http_fetcher.cc
+++ b/multi_range_http_fetcher.cc
@@ -169,9 +169,9 @@
 std::string MultiRangeHttpFetcher::Range::ToString() const {
   std::string range_str = base::StringPrintf("%jd+", offset());
   if (HasLength())
-    base::StringAppendF(&range_str, "%zu", length());
+    range_str += std::to_string(length());
   else
-    base::StringAppendF(&range_str, "?");
+    range_str += "?";
   return range_str;
 }
 
diff --git a/omaha_hash_calculator_unittest.cc b/omaha_hash_calculator_unittest.cc
index 29a0e45..d575fb0 100644
--- a/omaha_hash_calculator_unittest.cc
+++ b/omaha_hash_calculator_unittest.cc
@@ -11,7 +11,6 @@
 #include <vector>
 
 #include <chromeos/secure_blob.h>
-#include <glib.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/libcurl_http_fetcher.h"
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index dff8ad3..3d7cccc 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -21,6 +21,7 @@
 #include <expat.h>
 
 #include "update_engine/action_pipe.h"
+#include "update_engine/connection_manager.h"
 #include "update_engine/constants.h"
 #include "update_engine/hardware_interface.h"
 #include "update_engine/omaha_hash_calculator.h"
@@ -28,7 +29,6 @@
 #include "update_engine/p2p_manager.h"
 #include "update_engine/payload_state_interface.h"
 #include "update_engine/prefs_interface.h"
-#include "update_engine/real_dbus_wrapper.h"
 #include "update_engine/utils.h"
 
 using base::Time;
@@ -1447,17 +1447,16 @@
 bool OmahaRequestAction::IsUpdateAllowedOverCurrentConnection() const {
   NetworkConnectionType type;
   NetworkTethering tethering;
-  RealDBusWrapper dbus_iface;
-  ConnectionManager* connection_manager = system_state_->connection_manager();
-  if (!connection_manager->GetConnectionProperties(&dbus_iface,
-                                                   &type, &tethering)) {
+  ConnectionManagerInterface* connection_manager =
+      system_state_->connection_manager();
+  if (!connection_manager->GetConnectionProperties(&type, &tethering)) {
     LOG(INFO) << "We could not determine our connection type. "
               << "Defaulting to allow updates.";
     return true;
   }
   bool is_allowed = connection_manager->IsUpdateAllowedOver(type, tethering);
   LOG(INFO) << "We are connected via "
-            << connection_manager->StringForConnectionType(type)
+            << ConnectionManager::StringForConnectionType(type)
             << ", Updates allowed: " << (is_allowed ? "Yes" : "No");
   return is_allowed;
 }
diff --git a/omaha_request_action_unittest.cc b/omaha_request_action_unittest.cc
index 1131c9c..2a11d7d 100644
--- a/omaha_request_action_unittest.cc
+++ b/omaha_request_action_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "update_engine/omaha_request_action.h"
 
-#include <glib.h>
 #include <stdint.h>
 
 #include <string>
@@ -445,17 +444,16 @@
   OmahaResponse response;
   // Set up a connection manager that doesn't allow a valid update over
   // the current ethernet connection.
-  MockConnectionManager mock_cm(nullptr);
+  MockConnectionManager mock_cm;
   fake_system_state_.set_connection_manager(&mock_cm);
 
-  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _, _))
-    .WillRepeatedly(DoAll(SetArgumentPointee<1>(kNetEthernet),
-                          SetArgumentPointee<2>(NetworkTethering::kUnknown),
-                          Return(true)));
-  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(kNetEthernet, _))
+  EXPECT_CALL(mock_cm, GetConnectionProperties(_, _))
+      .WillRepeatedly(
+          DoAll(SetArgumentPointee<0>(NetworkConnectionType::kEthernet),
+                SetArgumentPointee<1>(NetworkTethering::kUnknown),
+                Return(true)));
+  EXPECT_CALL(mock_cm, IsUpdateAllowedOver(NetworkConnectionType::kEthernet, _))
     .WillRepeatedly(Return(false));
-  EXPECT_CALL(mock_cm, StringForConnectionType(kNetEthernet))
-    .WillRepeatedly(Return(shill::kTypeEthernet));
 
   ASSERT_FALSE(
       TestUpdateCheck(nullptr,  // request_params
diff --git a/omaha_response_handler_action.cc b/omaha_response_handler_action.cc
index 67639cc..6c7c166 100644
--- a/omaha_response_handler_action.cc
+++ b/omaha_response_handler_action.cc
@@ -10,7 +10,7 @@
 #include <base/strings/string_util.h>
 #include <policy/device_policy.h>
 
-#include "update_engine/connection_manager.h"
+#include "update_engine/connection_manager_interface.h"
 #include "update_engine/constants.h"
 #include "update_engine/delta_performer.h"
 #include "update_engine/hardware_interface.h"
diff --git a/p2p_manager.cc b/p2p_manager.cc
index c72a023..9b6a659 100644
--- a/p2p_manager.cc
+++ b/p2p_manager.cc
@@ -30,11 +30,11 @@
 #include <base/bind.h>
 #include <base/files/file_enumerator.h>
 #include <base/files/file_path.h>
+#include <base/format_macros.h>
 #include <base/logging.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 
-#include "update_engine/glib_utils.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/update_manager/policy.h"
 #include "update_engine/update_manager/update_manager.h"
@@ -91,7 +91,7 @@
     vector<string> args;
     args.push_back("p2p-client");
     args.push_back(string("--get-url=") + file_id);
-    args.push_back(StringPrintf("--minimum-size=%zu", minimum_size));
+    args.push_back(StringPrintf("--minimum-size=%" PRIuS, minimum_size));
     return args;
   }
 
@@ -369,8 +369,8 @@
   ~LookupData() {
     if (timeout_task_ != MessageLoop::kTaskIdNull)
       MessageLoop::current()->CancelTask(timeout_task_);
-    if (child_tag_)
-      Subprocess::Get().KillExec(child_tag_);
+    if (child_pid_)
+      Subprocess::Get().KillExec(child_pid_);
   }
 
   void InitiateLookup(const vector<string>& cmd, TimeDelta timeout) {
@@ -380,11 +380,11 @@
     // guarantee is useful for testing).
 
     // We expect to run just "p2p-client" and find it in the path.
-    child_tag_ = Subprocess::Get().ExecFlags(
-        cmd, G_SPAWN_SEARCH_PATH, false /* redirect stderr */, OnLookupDone,
-        this);
+    child_pid_ = Subprocess::Get().ExecFlags(
+        cmd, Subprocess::kSearchPath,
+        Bind(&LookupData::OnLookupDone, base::Unretained(this)));
 
-    if (!child_tag_) {
+    if (!child_pid_) {
       LOG(ERROR) << "Error spawning " << utils::StringVectorToString(cmd);
       ReportErrorAndDeleteInIdle();
       return;
@@ -442,19 +442,16 @@
     reported_ = true;
   }
 
-  static void OnLookupDone(int return_code,
-                           const string& output,
-                           void *user_data) {
-    LookupData *lookup_data = reinterpret_cast<LookupData*>(user_data);
-    lookup_data->child_tag_ = 0;
+  void OnLookupDone(int return_code, const string& output) {
+    child_pid_ = 0;
     if (return_code != 0) {
       LOG(INFO) << "Child exited with non-zero exit code "
                 << return_code;
-      lookup_data->ReportError();
+      ReportError();
     } else {
-      lookup_data->ReportSuccess(output);
+      ReportSuccess(output);
     }
-    delete lookup_data;
+    delete this;
   }
 
   void OnTimeout() {
@@ -467,7 +464,7 @@
 
   // The Subprocess tag of the running process. A value of 0 means that the
   // process is not running.
-  uint32_t child_tag_{0};
+  pid_t child_pid_{0};
 
   // The timeout task_id we are waiting on, if any.
   MessageLoop::TaskId timeout_task_{MessageLoop::kTaskIdNull};
@@ -557,7 +554,7 @@
       }
     }
 
-    string decimal_size = StringPrintf("%zu", expected_size);
+    string decimal_size = std::to_string(expected_size);
     if (fsetxattr(fd, kCrosP2PFileSizeXAttrName,
                   decimal_size.c_str(), decimal_size.size(), 0) != 0) {
       PLOG(ERROR) << "Error setting xattr " << path.value();
diff --git a/p2p_manager.h b/p2p_manager.h
index fb07c2e..559bba8 100644
--- a/p2p_manager.h
+++ b/p2p_manager.h
@@ -77,7 +77,7 @@
   // Asynchronously finds a peer that serves the file identified by
   // |file_id|. If |minimum_size| is non-zero, will find a peer that
   // has at least that many bytes. When the result is ready |callback|
-  // is called from the default GLib mainloop.
+  // is called from the current message loop.
   //
   // This operation may take a very long time to complete because part
   // of the p2p protocol involves waiting for the LAN-wide sum of all
diff --git a/p2p_manager_unittest.cc b/p2p_manager_unittest.cc
index 8667578..7e3f57f 100644
--- a/p2p_manager_unittest.cc
+++ b/p2p_manager_unittest.cc
@@ -17,8 +17,10 @@
 #include <base/bind.h>
 #include <base/callback.h>
 #include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
 #include <base/strings/stringprintf.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/asynchronous_signal_handler.h>
+#include <chromeos/message_loops/base_message_loop.h>
 #include <chromeos/message_loops/message_loop.h>
 #include <chromeos/message_loops/message_loop_utils.h>
 #include <gmock/gmock.h>
@@ -57,6 +59,8 @@
   // Derived from testing::Test.
   void SetUp() override {
     loop_.SetAsCurrent();
+    async_signal_handler_.Init();
+    subprocess_.Init(&async_signal_handler_);
     test_conf_ = new FakeP2PManagerConfiguration();
 
     // Allocate and install a mock policy implementation in the fake Update
@@ -71,14 +75,10 @@
                                          TimeDelta::FromDays(5)));
   }
 
-  void TearDown() override {
-    EXPECT_EQ(0, chromeos::MessageLoopRunMaxIterations(&loop_, 1));
-  }
-
-  // TODO(deymo): Replace this with a FakeMessageLoop. P2PManager uses glib to
-  // interact with the p2p-client tool, so we need to run a GlibMessageLoop
-  // here.
-  chromeos::GlibMessageLoop loop_;
+  base::MessageLoopForIO base_loop_;
+  chromeos::BaseMessageLoop loop_{&base_loop_};
+  chromeos::AsynchronousSignalHandler async_signal_handler_;
+  Subprocess subprocess_;
 
   // The P2PManager::Configuration instance used for testing.
   FakeP2PManagerConfiguration *test_conf_;
@@ -312,7 +312,7 @@
   }
 
   if (size_xattr != 0) {
-    string decimal_size = base::StringPrintf("%zu", size_xattr);
+    string decimal_size = std::to_string(size_xattr);
     if (fsetxattr(fd, "user.cros-p2p-filesize",
                   decimal_size.c_str(), decimal_size.size(), 0) != 0) {
       PLOG(ERROR) << "Error setting xattr on " << path;
diff --git a/payload_generator/ab_generator.cc b/payload_generator/ab_generator.cc
index 67163eb..573c5b8 100644
--- a/payload_generator/ab_generator.cc
+++ b/payload_generator/ab_generator.cc
@@ -16,47 +16,28 @@
 #include "update_engine/utils.h"
 
 using std::string;
-using std::unique_ptr;
 using std::vector;
 
 namespace chromeos_update_engine {
 
-namespace {
-
-// Compare two AnnotatedOperations by the start block of the first Extent in
-// their destination extents.
-bool CompareAopsByDestination(AnnotatedOperation first_aop,
-                              AnnotatedOperation second_aop) {
-  // We want empty operations to be at the end of the payload.
-  if (!first_aop.op.dst_extents().size() || !second_aop.op.dst_extents().size())
-    return ((!first_aop.op.dst_extents().size()) <
-            (!second_aop.op.dst_extents().size()));
-  uint32_t first_dst_start = first_aop.op.dst_extents(0).start_block();
-  uint32_t second_dst_start = second_aop.op.dst_extents(0).start_block();
-  return first_dst_start < second_dst_start;
-}
-
-}  // namespace
-
 bool ABGenerator::GenerateOperations(
     const PayloadGenerationConfig& config,
-    int data_file_fd,
-    off_t* data_file_size,
+    BlobFileWriter* blob_file,
     vector<AnnotatedOperation>* rootfs_ops,
     vector<AnnotatedOperation>* kernel_ops) {
 
-  off_t chunk_blocks = (config.chunk_size == -1 ? -1 :
-                        config.chunk_size / config.block_size);
+  ssize_t hard_chunk_blocks = (config.hard_chunk_size == -1 ? -1 :
+                               config.hard_chunk_size / config.block_size);
+  size_t soft_chunk_blocks = config.soft_chunk_size / config.block_size;
 
   rootfs_ops->clear();
   TEST_AND_RETURN_FALSE(diff_utils::DeltaReadPartition(
       rootfs_ops,
       config.source.rootfs,
       config.target.rootfs,
-      chunk_blocks,
-      data_file_fd,
-      data_file_size,
-      false,  // skip_block_0
+      hard_chunk_blocks,
+      soft_chunk_blocks,
+      blob_file,
       true));  // src_ops_allowed
   LOG(INFO) << "done reading normal files";
 
@@ -65,60 +46,58 @@
       kernel_ops,
       config.source.kernel,
       config.target.kernel,
-      chunk_blocks,
-      data_file_fd,
-      data_file_size,
-      false,  // skip_block_0
+      hard_chunk_blocks,
+      soft_chunk_blocks,
+      blob_file,
       true));  // src_ops_allowed
   LOG(INFO) << "done reading kernel";
 
   TEST_AND_RETURN_FALSE(FragmentOperations(rootfs_ops,
                                            config.target.rootfs.path,
-                                           data_file_fd,
-                                           data_file_size));
+                                           blob_file));
   TEST_AND_RETURN_FALSE(FragmentOperations(kernel_ops,
                                            config.target.kernel.path,
-                                           data_file_fd,
-                                           data_file_size));
+                                           blob_file));
   SortOperationsByDestination(rootfs_ops);
   SortOperationsByDestination(kernel_ops);
-  // TODO(alliewood): Change merge operations to use config.chunk_size once
-  // specifying chunk_size on the command line works. crbug/485397.
+
+  // Use the soft_chunk_size when merging operations to prevent merging all
+  // the operations into a huge one if there's no hard limit.
+  size_t merge_chunk_blocks = soft_chunk_blocks;
+  if (hard_chunk_blocks != -1 &&
+      static_cast<size_t>(hard_chunk_blocks) < soft_chunk_blocks) {
+    merge_chunk_blocks = hard_chunk_blocks;
+  }
+
   TEST_AND_RETURN_FALSE(MergeOperations(rootfs_ops,
-                                        kDefaultChunkSize,
+                                        merge_chunk_blocks,
                                         config.target.rootfs.path,
-                                        data_file_fd,
-                                        data_file_size));
+                                        blob_file));
   TEST_AND_RETURN_FALSE(MergeOperations(kernel_ops,
-                                        kDefaultChunkSize,
+                                        merge_chunk_blocks,
                                         config.target.kernel.path,
-                                        data_file_fd,
-                                        data_file_size));
+                                        blob_file));
   return true;
 }
 
 void ABGenerator::SortOperationsByDestination(
     vector<AnnotatedOperation>* aops) {
-  sort(aops->begin(), aops->end(), CompareAopsByDestination);
+  sort(aops->begin(), aops->end(), diff_utils::CompareAopsByDestination);
 }
 
 bool ABGenerator::FragmentOperations(
     vector<AnnotatedOperation>* aops,
     const string& target_part_path,
-    int data_fd,
-    off_t* data_file_size) {
+    BlobFileWriter* blob_file) {
   vector<AnnotatedOperation> fragmented_aops;
   for (const AnnotatedOperation& aop : *aops) {
-    if (aop.op.type() ==
-        DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY) {
+    if (aop.op.type() == InstallOperation::SOURCE_COPY) {
       TEST_AND_RETURN_FALSE(SplitSourceCopy(aop, &fragmented_aops));
-    } else if ((aop.op.type() ==
-                DeltaArchiveManifest_InstallOperation_Type_REPLACE) ||
-               (aop.op.type() ==
-                DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ)) {
+    } else if ((aop.op.type() == InstallOperation::REPLACE) ||
+               (aop.op.type() == InstallOperation::REPLACE_BZ)) {
       TEST_AND_RETURN_FALSE(SplitReplaceOrReplaceBz(aop, &fragmented_aops,
-                                                    target_part_path, data_fd,
-                                                    data_file_size));
+                                                    target_part_path,
+                                                    blob_file));
     } else {
       fragmented_aops.push_back(aop);
     }
@@ -130,16 +109,15 @@
 bool ABGenerator::SplitSourceCopy(
     const AnnotatedOperation& original_aop,
     vector<AnnotatedOperation>* result_aops) {
-  DeltaArchiveManifest_InstallOperation original_op = original_aop.op;
-  TEST_AND_RETURN_FALSE(original_op.type() ==
-                        DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+  InstallOperation original_op = original_aop.op;
+  TEST_AND_RETURN_FALSE(original_op.type() == InstallOperation::SOURCE_COPY);
   // Keeps track of the index of curr_src_ext.
   int curr_src_ext_index = 0;
   Extent curr_src_ext = original_op.src_extents(curr_src_ext_index);
   for (int i = 0; i < original_op.dst_extents_size(); i++) {
     Extent dst_ext = original_op.dst_extents(i);
     // The new operation which will have only one dst extent.
-    DeltaArchiveManifest_InstallOperation new_op;
+    InstallOperation new_op;
     uint64_t blocks_left = dst_ext.num_blocks();
     while (blocks_left > 0) {
       if (curr_src_ext.num_blocks() <= blocks_left) {
@@ -164,7 +142,7 @@
       }
     }
     // Fix up our new operation and add it to the results.
-    new_op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+    new_op.set_type(InstallOperation::SOURCE_COPY);
     *(new_op.add_dst_extents()) = dst_ext;
     new_op.set_src_length(dst_ext.num_blocks() * kBlockSize);
     new_op.set_dst_length(dst_ext.num_blocks() * kBlockSize);
@@ -185,27 +163,23 @@
     const AnnotatedOperation& original_aop,
     vector<AnnotatedOperation>* result_aops,
     const string& target_part_path,
-    int data_fd,
-    off_t* data_file_size) {
-  DeltaArchiveManifest_InstallOperation original_op = original_aop.op;
-  const bool is_replace =
-      original_op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE;
-  TEST_AND_RETURN_FALSE(
-      is_replace ||
-      (original_op.type() ==
-       DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ));
+    BlobFileWriter* blob_file) {
+  InstallOperation original_op = original_aop.op;
+  const bool is_replace = original_op.type() == InstallOperation::REPLACE;
+  TEST_AND_RETURN_FALSE(is_replace ||
+                        original_op.type() == InstallOperation::REPLACE_BZ);
 
   uint32_t data_offset = original_op.data_offset();
   for (int i = 0; i < original_op.dst_extents_size(); i++) {
     Extent dst_ext = original_op.dst_extents(i);
     // Make a new operation with only one dst extent.
-    DeltaArchiveManifest_InstallOperation new_op;
+    InstallOperation new_op;
     *(new_op.add_dst_extents()) = dst_ext;
     uint32_t data_size = dst_ext.num_blocks() * kBlockSize;
     new_op.set_dst_length(data_size);
     // If this is a REPLACE, attempt to reuse portions of the existing blob.
     if (is_replace) {
-      new_op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+      new_op.set_type(InstallOperation::REPLACE);
       new_op.set_data_length(data_size);
       new_op.set_data_offset(data_offset);
       data_offset += data_size;
@@ -214,8 +188,8 @@
     AnnotatedOperation new_aop;
     new_aop.op = new_op;
     new_aop.name = base::StringPrintf("%s:%d", original_aop.name.c_str(), i);
-    TEST_AND_RETURN_FALSE(AddDataAndSetType(&new_aop, target_part_path, data_fd,
-                                            data_file_size));
+    TEST_AND_RETURN_FALSE(AddDataAndSetType(&new_aop, target_part_path,
+                                            blob_file));
 
     result_aops->push_back(new_aop);
   }
@@ -223,10 +197,9 @@
 }
 
 bool ABGenerator::MergeOperations(vector<AnnotatedOperation>* aops,
-                                  off_t chunk_size,
+                                  size_t chunk_blocks,
                                   const string& target_part_path,
-                                  int data_fd,
-                                  off_t* data_file_size) {
+                                  BlobFileWriter* blob_file) {
   vector<AnnotatedOperation> new_aops;
   for (const AnnotatedOperation& curr_aop : *aops) {
     if (new_aops.empty()) {
@@ -248,17 +221,13 @@
     uint32_t combined_block_count =
         last_aop.op.dst_extents(last_dst_idx).num_blocks() +
         curr_aop.op.dst_extents(0).num_blocks();
-    bool good_op_type =
-        curr_aop.op.type() ==
-            DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY ||
-        curr_aop.op.type() ==
-            DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
-        curr_aop.op.type() ==
-            DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ;
+    bool good_op_type = curr_aop.op.type() == InstallOperation::SOURCE_COPY ||
+                        curr_aop.op.type() == InstallOperation::REPLACE ||
+                        curr_aop.op.type() == InstallOperation::REPLACE_BZ;
     if (good_op_type &&
         last_aop.op.type() == curr_aop.op.type() &&
         last_end_block == curr_start_block &&
-        static_cast<off_t>(combined_block_count * kBlockSize) <= chunk_size) {
+        combined_block_count <= chunk_blocks) {
       // If the operations have the same type (which is a type that we can
       // merge), are contiguous, are fragmented to have one destination extent,
       // and their combined block count would be less than chunk size, merge
@@ -278,10 +247,8 @@
         last_aop.op.set_dst_length(last_aop.op.dst_length() +
                                    curr_aop.op.dst_length());
       // Set the data length to zero so we know to add the blob later.
-      if (curr_aop.op.type() ==
-          DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
-          curr_aop.op.type() ==
-          DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+      if (curr_aop.op.type() == InstallOperation::REPLACE ||
+          curr_aop.op.type() == InstallOperation::REPLACE_BZ) {
         last_aop.op.set_data_length(0);
       }
     } else {
@@ -293,12 +260,10 @@
   // Set the blobs for REPLACE/REPLACE_BZ operations that have been merged.
   for (AnnotatedOperation& curr_aop : new_aops) {
     if (curr_aop.op.data_length() == 0 &&
-        (curr_aop.op.type() ==
-            DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
-         curr_aop.op.type() ==
-            DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ)) {
+        (curr_aop.op.type() == InstallOperation::REPLACE ||
+         curr_aop.op.type() == InstallOperation::REPLACE_BZ)) {
       TEST_AND_RETURN_FALSE(AddDataAndSetType(&curr_aop, target_part_path,
-                                              data_fd, data_file_size));
+                                              blob_file));
     }
   }
 
@@ -308,11 +273,9 @@
 
 bool ABGenerator::AddDataAndSetType(AnnotatedOperation* aop,
                                     const string& target_part_path,
-                                    int data_fd,
-                                    off_t* data_file_size) {
-  TEST_AND_RETURN_FALSE(
-      aop->op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
-      aop->op.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+                                    BlobFileWriter* blob_file) {
+  TEST_AND_RETURN_FALSE(aop->op.type() == InstallOperation::REPLACE ||
+                        aop->op.type() == InstallOperation::REPLACE_BZ);
 
   chromeos::Blob data(aop->op.dst_length());
   vector<Extent> dst_extents;
@@ -328,35 +291,20 @@
   CHECK(!data_bz.empty());
 
   chromeos::Blob* data_p = nullptr;
-  DeltaArchiveManifest_InstallOperation_Type new_op_type;
+  InstallOperation_Type new_op_type;
   if (data_bz.size() < data.size()) {
-    new_op_type = DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ;
+    new_op_type = InstallOperation::REPLACE_BZ;
     data_p = &data_bz;
   } else {
-    new_op_type = DeltaArchiveManifest_InstallOperation_Type_REPLACE;
+    new_op_type = InstallOperation::REPLACE;
     data_p = &data;
   }
 
-  // If the operation already points to a data blob, check whether it's
-  // identical to the new one, in which case don't add it.
-  if (aop->op.type() == new_op_type &&
-      aop->op.data_length() == data_p->size()) {
-    chromeos::Blob current_data(data_p->size());
-    ssize_t bytes_read;
-    TEST_AND_RETURN_FALSE(utils::PReadAll(data_fd,
-                                          current_data.data(),
-                                          aop->op.data_length(),
-                                          aop->op.data_offset(),
-                                          &bytes_read));
-    TEST_AND_RETURN_FALSE(bytes_read ==
-                          static_cast<ssize_t>(aop->op.data_length()));
-    if (current_data == *data_p)
-      data_p = nullptr;
-  }
-
-  if (data_p) {
+  // If the operation doesn't point to a data blob, then we add it.
+  if (aop->op.type() != new_op_type ||
+      aop->op.data_length() != data_p->size()) {
     aop->op.set_type(new_op_type);
-    aop->SetOperationBlob(data_p, data_fd, data_file_size);
+    aop->SetOperationBlob(data_p, blob_file);
   }
 
   return true;
diff --git a/payload_generator/ab_generator.h b/payload_generator/ab_generator.h
index d027f22..1aa74af 100644
--- a/payload_generator/ab_generator.h
+++ b/payload_generator/ab_generator.h
@@ -12,6 +12,7 @@
 #include <chromeos/secure_blob.h>
 
 #include "update_engine/payload_constants.h"
+#include "update_engine/payload_generator/blob_file_writer.h"
 #include "update_engine/payload_generator/extent_utils.h"
 #include "update_engine/payload_generator/filesystem_interface.h"
 #include "update_engine/payload_generator/operations_generator.h"
@@ -34,13 +35,10 @@
   // write the new image on the target partition, also possibly in random order.
   // The rootfs operations are stored in |rootfs_ops| and should be executed in
   // that order. The kernel operations are stored in |kernel_ops|. All
-  // the offsets in the operations reference the data written to |data_file_fd|.
-  // The total amount of data written to that file is stored in
-  // |data_file_size|.
+  // the offsets in the operations reference the data written to |blob_file|.
   bool GenerateOperations(
       const PayloadGenerationConfig& config,
-      int data_file_fd,
-      off_t* data_file_size,
+      BlobFileWriter* blob_file,
       std::vector<AnnotatedOperation>* rootfs_ops,
       std::vector<AnnotatedOperation>* kernel_ops) override;
 
@@ -50,13 +48,10 @@
   // fragmented except BSDIFF and SOURCE_BSDIFF operations.
   // The |target_rootfs_part| is the filename of the new image, where the
   // destination extents refer to. The blobs of the operations in |aops| should
-  // reference the file |data_fd| whose initial size is |*data_file_size|. The
-  // file contents and the value pointed by |data_file_size| are updated if
-  // needed.
+  // reference |blob_file|. |blob_file| are updated if needed.
   static bool FragmentOperations(std::vector<AnnotatedOperation>* aops,
                                  const std::string& target_rootfs_part,
-                                 int data_fd,
-                                 off_t* data_file_size);
+                                 BlobFileWriter* blob_file);
 
   // Takes a vector of AnnotatedOperations |aops| and sorts them by the first
   // start block in their destination extents. Sets |aops| to a vector of the
@@ -84,33 +79,32 @@
       const AnnotatedOperation& original_aop,
       std::vector<AnnotatedOperation>* result_aops,
       const std::string& target_part,
-      int data_fd,
-      off_t* data_file_size);
+      BlobFileWriter* blob_file);
 
   // Takes a sorted (by first destination extent) vector of operations |aops|
   // and merges SOURCE_COPY, REPLACE, and REPLACE_BZ operations in that vector.
   // It will merge two operations if:
   //   - They are of the same type.
   //   - They are contiguous.
-  //   - Their combined blocks do not exceed |chunk_size|.
+  //   - Their combined blocks do not exceed |chunk_blocks| blocks.
+  // Note that unlike other methods, you can't pass a negative number in
+  // |chunk_blocks|.
   static bool MergeOperations(std::vector<AnnotatedOperation>* aops,
-                              off_t chunk_size,
+                              size_t chunk_blocks,
                               const std::string& target_part,
-                              int data_fd,
-                              off_t* data_file_size);
+                              BlobFileWriter* blob_file);
 
  private:
   // Adds the data payload for a REPLACE/REPLACE_BZ operation |aop| by reading
   // its output extents from |target_part_path| and appending a corresponding
   // data blob to |data_fd|. The blob will be compressed if this is smaller than
   // the uncompressed form, and the operation type will be set accordingly.
-  // |*data_file_size| will be updated as well. If the operation happens to have
-  // the right type and already points to a data blob, we check whether its
-  // content is identical to the new one, in which case nothing is written.
+  // |*blob_file| will be updated as well. If the operation happens to have
+  // the right type and already points to a data blob, nothing is written.
+  // Caller should only set type and data blob if it's valid.
   static bool AddDataAndSetType(AnnotatedOperation* aop,
                                 const std::string& target_part_path,
-                                int data_fd,
-                                off_t* data_file_size);
+                                BlobFileWriter* blob_file);
 
   DISALLOW_COPY_AND_ASSIGN(ABGenerator);
 };
diff --git a/payload_generator/ab_generator_unittest.cc b/payload_generator/ab_generator_unittest.cc
index 9241701..b06ba63 100644
--- a/payload_generator/ab_generator_unittest.cc
+++ b/payload_generator/ab_generator_unittest.cc
@@ -33,9 +33,8 @@
 }
 
 // Tests splitting of a REPLACE/REPLACE_BZ operation.
-void TestSplitReplaceOrReplaceBzOperation(
-    DeltaArchiveManifest_InstallOperation_Type orig_type,
-    bool compressible) {
+void TestSplitReplaceOrReplaceBzOperation(InstallOperation_Type orig_type,
+                                          bool compressible) {
   const size_t op_ex1_start_block = 2;
   const size_t op_ex1_num_blocks = 2;
   const size_t op_ex2_start_block = 6;
@@ -66,7 +65,7 @@
   const size_t op_ex1_size = op_ex1_num_blocks * kBlockSize;
   const size_t op_ex2_offset = op_ex2_start_block * kBlockSize;
   const size_t op_ex2_size = op_ex2_num_blocks * kBlockSize;
-  DeltaArchiveManifest_InstallOperation op;
+  InstallOperation op;
   op.set_type(orig_type);
   *(op.add_dst_extents()) = ExtentForRange(op_ex1_start_block,
                                            op_ex1_num_blocks);
@@ -82,7 +81,7 @@
                  part_data.begin() + op_ex2_offset,
                  part_data.begin() + op_ex2_offset + op_ex2_size);
   chromeos::Blob op_blob;
-  if (orig_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+  if (orig_type == InstallOperation::REPLACE) {
     op_blob = op_data;
   } else {
     ASSERT_TRUE(BzipCompress(op_data, &op_blob));
@@ -105,22 +104,21 @@
   EXPECT_TRUE(utils::WriteFile(data_path.c_str(), op_blob.data(),
                                op_blob.size()));
   off_t data_file_size = op_blob.size();
+  BlobFileWriter blob_file(data_fd, &data_file_size);
 
   // Split the operation.
   vector<AnnotatedOperation> result_ops;
   ASSERT_TRUE(ABGenerator::SplitReplaceOrReplaceBz(
-          aop, &result_ops, part_path, data_fd, &data_file_size));
+          aop, &result_ops, part_path, &blob_file));
 
   // Check the result.
-  DeltaArchiveManifest_InstallOperation_Type expected_type =
-      compressible ?
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ :
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE;
+  InstallOperation_Type expected_type =
+      compressible ? InstallOperation::REPLACE_BZ : InstallOperation::REPLACE;
 
   ASSERT_EQ(2, result_ops.size());
 
   EXPECT_EQ("SplitTestOp:0", result_ops[0].name);
-  DeltaArchiveManifest_InstallOperation first_op = result_ops[0].op;
+  InstallOperation first_op = result_ops[0].op;
   EXPECT_EQ(expected_type, first_op.type());
   EXPECT_EQ(op_ex1_size, first_op.dst_length());
   EXPECT_EQ(1, first_op.dst_extents().size());
@@ -149,7 +147,7 @@
   EXPECT_EQ(first_expected_blob, first_data_blob);
 
   EXPECT_EQ("SplitTestOp:1", result_ops[1].name);
-  DeltaArchiveManifest_InstallOperation second_op = result_ops[1].op;
+  InstallOperation second_op = result_ops[1].op;
   EXPECT_EQ(expected_type, second_op.type());
   EXPECT_EQ(op_ex2_size, second_op.dst_length());
   EXPECT_EQ(1, second_op.dst_extents().size());
@@ -181,16 +179,14 @@
             second_op.data_offset());
   EXPECT_EQ(second_op.data_offset() + second_op.data_length(), data_file_size);
   // If we split a REPLACE into multiple ones, ensure reuse of preexisting blob.
-  if (!compressible &&
-      orig_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+  if (!compressible && orig_type == InstallOperation::REPLACE) {
     EXPECT_EQ(0, first_op.data_offset());
   }
 }
 
 // Tests merging of REPLACE/REPLACE_BZ operations.
-void TestMergeReplaceOrReplaceBzOperations(
-    DeltaArchiveManifest_InstallOperation_Type orig_type,
-    bool compressible) {
+void TestMergeReplaceOrReplaceBzOperations(InstallOperation_Type orig_type,
+                                           bool compressible) {
   const size_t first_op_num_blocks = 1;
   const size_t second_op_num_blocks = 2;
   const size_t total_op_num_blocks = first_op_num_blocks + second_op_num_blocks;
@@ -220,7 +216,7 @@
   chromeos::Blob blob_data;
   const size_t total_op_size = total_op_num_blocks * kBlockSize;
 
-  DeltaArchiveManifest_InstallOperation first_op;
+  InstallOperation first_op;
   first_op.set_type(orig_type);
   const size_t first_op_size = first_op_num_blocks * kBlockSize;
   first_op.set_dst_length(first_op_size);
@@ -228,7 +224,7 @@
   chromeos::Blob first_op_data(part_data.begin(),
                                part_data.begin() + first_op_size);
   chromeos::Blob first_op_blob;
-  if (orig_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+  if (orig_type == InstallOperation::REPLACE) {
     first_op_blob = first_op_data;
   } else {
     ASSERT_TRUE(BzipCompress(first_op_data, &first_op_blob));
@@ -241,7 +237,7 @@
   first_aop.name = "first";
   aops.push_back(first_aop);
 
-  DeltaArchiveManifest_InstallOperation second_op;
+  InstallOperation second_op;
   second_op.set_type(orig_type);
   const size_t second_op_size = second_op_num_blocks * kBlockSize;
   second_op.set_dst_length(second_op_size);
@@ -250,7 +246,7 @@
   chromeos::Blob second_op_data(part_data.begin() + first_op_size,
                                 part_data.begin() + total_op_size);
   chromeos::Blob second_op_blob;
-  if (orig_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+  if (orig_type == InstallOperation::REPLACE) {
     second_op_blob = second_op_data;
   } else {
     ASSERT_TRUE(BzipCompress(second_op_data, &second_op_blob));
@@ -275,18 +271,17 @@
   EXPECT_TRUE(utils::WriteFile(data_path.c_str(), blob_data.data(),
                                blob_data.size()));
   off_t data_file_size = blob_data.size();
+  BlobFileWriter blob_file(data_fd, &data_file_size);
 
   // Merge the operations.
   EXPECT_TRUE(ABGenerator::MergeOperations(
-      &aops, 5 * kBlockSize, part_path, data_fd, &data_file_size));
+      &aops, 5, part_path, &blob_file));
 
   // Check the result.
-  DeltaArchiveManifest_InstallOperation_Type expected_op_type =
-      compressible ?
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ :
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE;
+  InstallOperation_Type expected_op_type =
+      compressible ? InstallOperation::REPLACE_BZ : InstallOperation::REPLACE;
   EXPECT_EQ(1, aops.size());
-  DeltaArchiveManifest_InstallOperation new_op = aops[0].op;
+  InstallOperation new_op = aops[0].op;
   EXPECT_EQ(expected_op_type, new_op.type());
   EXPECT_FALSE(new_op.has_src_length());
   EXPECT_EQ(total_op_num_blocks * kBlockSize, new_op.dst_length());
@@ -321,8 +316,8 @@
 class ABGeneratorTest : public ::testing::Test {};
 
 TEST_F(ABGeneratorTest, SplitSourceCopyTest) {
-  DeltaArchiveManifest_InstallOperation op;
-  op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+  InstallOperation op;
+  op.set_type(InstallOperation::SOURCE_COPY);
   *(op.add_src_extents()) = ExtentForRange(2, 3);
   *(op.add_src_extents()) = ExtentForRange(6, 1);
   *(op.add_src_extents()) = ExtentForRange(8, 4);
@@ -338,9 +333,8 @@
   EXPECT_EQ(result_ops.size(), 3);
 
   EXPECT_EQ("SplitSourceCopyTestOp:0", result_ops[0].name);
-  DeltaArchiveManifest_InstallOperation first_op = result_ops[0].op;
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY,
-            first_op.type());
+  InstallOperation first_op = result_ops[0].op;
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, first_op.type());
   EXPECT_EQ(kBlockSize * 2, first_op.src_length());
   EXPECT_EQ(1, first_op.src_extents().size());
   EXPECT_EQ(2, first_op.src_extents(0).start_block());
@@ -351,9 +345,8 @@
   EXPECT_EQ(2, first_op.dst_extents(0).num_blocks());
 
   EXPECT_EQ("SplitSourceCopyTestOp:1", result_ops[1].name);
-  DeltaArchiveManifest_InstallOperation second_op = result_ops[1].op;
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY,
-            second_op.type());
+  InstallOperation second_op = result_ops[1].op;
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, second_op.type());
   EXPECT_EQ(kBlockSize * 3, second_op.src_length());
   EXPECT_EQ(3, second_op.src_extents().size());
   EXPECT_EQ(4, second_op.src_extents(0).start_block());
@@ -368,9 +361,8 @@
   EXPECT_EQ(3, second_op.dst_extents(0).num_blocks());
 
   EXPECT_EQ("SplitSourceCopyTestOp:2", result_ops[2].name);
-  DeltaArchiveManifest_InstallOperation third_op = result_ops[2].op;
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY,
-            third_op.type());
+  InstallOperation third_op = result_ops[2].op;
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, third_op.type());
   EXPECT_EQ(kBlockSize * 3, third_op.src_length());
   EXPECT_EQ(1, third_op.src_extents().size());
   EXPECT_EQ(9, third_op.src_extents(0).start_block());
@@ -382,29 +374,25 @@
 }
 
 TEST_F(ABGeneratorTest, SplitReplaceTest) {
-  TestSplitReplaceOrReplaceBzOperation(
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE, false);
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE, false);
 }
 
 TEST_F(ABGeneratorTest, SplitReplaceIntoReplaceBzTest) {
-  TestSplitReplaceOrReplaceBzOperation(
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE, true);
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE, true);
 }
 
 TEST_F(ABGeneratorTest, SplitReplaceBzTest) {
-  TestSplitReplaceOrReplaceBzOperation(
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ, true);
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE_BZ, true);
 }
 
 TEST_F(ABGeneratorTest, SplitReplaceBzIntoReplaceTest) {
-  TestSplitReplaceOrReplaceBzOperation(
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ, false);
+  TestSplitReplaceOrReplaceBzOperation(InstallOperation::REPLACE_BZ, false);
 }
 
 TEST_F(ABGeneratorTest, SortOperationsByDestinationTest) {
   vector<AnnotatedOperation> aops;
   // One operation with multiple destination extents.
-  DeltaArchiveManifest_InstallOperation first_op;
+  InstallOperation first_op;
   *(first_op.add_dst_extents()) = ExtentForRange(6, 1);
   *(first_op.add_dst_extents()) = ExtentForRange(10, 2);
   AnnotatedOperation first_aop;
@@ -413,14 +401,14 @@
   aops.push_back(first_aop);
 
   // One with no destination extent. Should end up at the end of the vector.
-  DeltaArchiveManifest_InstallOperation second_op;
+  InstallOperation second_op;
   AnnotatedOperation second_aop;
   second_aop.op = second_op;
   second_aop.name = "second";
   aops.push_back(second_aop);
 
   // One with one destination extent.
-  DeltaArchiveManifest_InstallOperation third_op;
+  InstallOperation third_op;
   *(third_op.add_dst_extents()) = ExtentForRange(3, 2);
   AnnotatedOperation third_aop;
   third_aop.op = third_op;
@@ -436,8 +424,8 @@
 
 TEST_F(ABGeneratorTest, MergeSourceCopyOperationsTest) {
   vector<AnnotatedOperation> aops;
-  DeltaArchiveManifest_InstallOperation first_op;
-  first_op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+  InstallOperation first_op;
+  first_op.set_type(InstallOperation::SOURCE_COPY);
   first_op.set_src_length(kBlockSize);
   first_op.set_dst_length(kBlockSize);
   *(first_op.add_src_extents()) = ExtentForRange(1, 1);
@@ -447,8 +435,8 @@
   first_aop.name = "1";
   aops.push_back(first_aop);
 
-  DeltaArchiveManifest_InstallOperation second_op;
-  second_op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+  InstallOperation second_op;
+  second_op.set_type(InstallOperation::SOURCE_COPY);
   second_op.set_src_length(3 * kBlockSize);
   second_op.set_dst_length(3 * kBlockSize);
   *(second_op.add_src_extents()) = ExtentForRange(2, 2);
@@ -460,8 +448,8 @@
   second_aop.name = "2";
   aops.push_back(second_aop);
 
-  DeltaArchiveManifest_InstallOperation third_op;
-  third_op.set_type(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+  InstallOperation third_op;
+  third_op.set_type(InstallOperation::SOURCE_COPY);
   third_op.set_src_length(kBlockSize);
   third_op.set_dst_length(kBlockSize);
   *(third_op.add_src_extents()) = ExtentForRange(11, 1);
@@ -471,13 +459,12 @@
   third_aop.name = "3";
   aops.push_back(third_aop);
 
-  EXPECT_TRUE(ABGenerator::MergeOperations(&aops, 5 * kBlockSize,
-                                           "", 0, nullptr));
+  BlobFileWriter blob_file(0, nullptr);
+  EXPECT_TRUE(ABGenerator::MergeOperations(&aops, 5, "", &blob_file));
 
   EXPECT_EQ(aops.size(), 1);
-  DeltaArchiveManifest_InstallOperation first_result_op = aops[0].op;
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY,
-            first_result_op.type());
+  InstallOperation first_result_op = aops[0].op;
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, first_result_op.type());
   EXPECT_EQ(kBlockSize * 5, first_result_op.src_length());
   EXPECT_EQ(3, first_result_op.src_extents().size());
   EXPECT_TRUE(ExtentEquals(first_result_op.src_extents(0), 1, 3));
@@ -491,30 +478,26 @@
 }
 
 TEST_F(ABGeneratorTest, MergeReplaceOperationsTest) {
-  TestMergeReplaceOrReplaceBzOperations(
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE, false);
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE, false);
 }
 
 TEST_F(ABGeneratorTest, MergeReplaceOperationsToReplaceBzTest) {
-  TestMergeReplaceOrReplaceBzOperations(
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE, true);
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE, true);
 }
 
 TEST_F(ABGeneratorTest, MergeReplaceBzOperationsTest) {
-  TestMergeReplaceOrReplaceBzOperations(
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ, true);
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE_BZ, true);
 }
 
 TEST_F(ABGeneratorTest, MergeReplaceBzOperationsToReplaceTest) {
-  TestMergeReplaceOrReplaceBzOperations(
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ, false);
+  TestMergeReplaceOrReplaceBzOperations(InstallOperation::REPLACE_BZ, false);
 }
 
 TEST_F(ABGeneratorTest, NoMergeOperationsTest) {
   // Test to make sure we don't merge operations that shouldn't be merged.
   vector<AnnotatedOperation> aops;
-  DeltaArchiveManifest_InstallOperation first_op;
-  first_op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+  InstallOperation first_op;
+  first_op.set_type(InstallOperation::REPLACE_BZ);
   *(first_op.add_dst_extents()) = ExtentForRange(0, 1);
   first_op.set_data_length(kBlockSize);
   AnnotatedOperation first_aop;
@@ -522,8 +505,8 @@
   aops.push_back(first_aop);
 
   // Should merge with first, except op types don't match...
-  DeltaArchiveManifest_InstallOperation second_op;
-  second_op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+  InstallOperation second_op;
+  second_op.set_type(InstallOperation::REPLACE);
   *(second_op.add_dst_extents()) = ExtentForRange(1, 2);
   second_op.set_data_length(2 * kBlockSize);
   AnnotatedOperation second_aop;
@@ -531,8 +514,8 @@
   aops.push_back(second_aop);
 
   // Should merge with second, except it would exceed chunk size...
-  DeltaArchiveManifest_InstallOperation third_op;
-  third_op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+  InstallOperation third_op;
+  third_op.set_type(InstallOperation::REPLACE);
   *(third_op.add_dst_extents()) = ExtentForRange(3, 3);
   third_op.set_data_length(3 * kBlockSize);
   AnnotatedOperation third_aop;
@@ -540,16 +523,16 @@
   aops.push_back(third_aop);
 
   // Should merge with third, except they aren't contiguous...
-  DeltaArchiveManifest_InstallOperation fourth_op;
-  fourth_op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+  InstallOperation fourth_op;
+  fourth_op.set_type(InstallOperation::REPLACE);
   *(fourth_op.add_dst_extents()) = ExtentForRange(7, 2);
   fourth_op.set_data_length(2 * kBlockSize);
   AnnotatedOperation fourth_aop;
   fourth_aop.op = fourth_op;
   aops.push_back(fourth_aop);
 
-  EXPECT_TRUE(ABGenerator::MergeOperations(
-      &aops, 4 * kBlockSize, "", 0, nullptr));
+  BlobFileWriter blob_file(0, nullptr);
+  EXPECT_TRUE(ABGenerator::MergeOperations(&aops, 4, "", &blob_file));
 
   // No operations were merged, the number of ops is the same.
   EXPECT_EQ(aops.size(), 4);
diff --git a/payload_generator/annotated_operation.cc b/payload_generator/annotated_operation.cc
index 2aa79fc..6c7bf10 100644
--- a/payload_generator/annotated_operation.cc
+++ b/payload_generator/annotated_operation.cc
@@ -25,33 +25,34 @@
 }
 }  // namespace
 
-bool AnnotatedOperation::SetOperationBlob(chromeos::Blob* blob, int data_fd,
-                                          off_t* data_file_size) {
-  TEST_AND_RETURN_FALSE(utils::PWriteAll(data_fd,
-                                         blob->data(),
-                                         blob->size(),
-                                         *data_file_size));
+bool AnnotatedOperation::SetOperationBlob(chromeos::Blob* blob,
+                                          BlobFileWriter* blob_file) {
   op.set_data_length(blob->size());
-  op.set_data_offset(*data_file_size);
-  *data_file_size += blob->size();
+  off_t data_offset = blob_file->StoreBlob(*blob);
+  if (data_offset == -1)
+    return false;
+  op.set_data_offset(data_offset);
   return true;
 }
 
-string InstallOperationTypeName(
-    DeltaArchiveManifest_InstallOperation_Type op_type) {
+string InstallOperationTypeName(InstallOperation_Type op_type) {
   switch (op_type) {
-    case DeltaArchiveManifest_InstallOperation_Type_BSDIFF:
+    case InstallOperation::BSDIFF:
       return "BSDIFF";
-    case DeltaArchiveManifest_InstallOperation_Type_MOVE:
+    case InstallOperation::MOVE:
       return "MOVE";
-    case DeltaArchiveManifest_InstallOperation_Type_REPLACE:
+    case InstallOperation::REPLACE:
       return "REPLACE";
-    case DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ:
+    case InstallOperation::REPLACE_BZ:
       return "REPLACE_BZ";
-    case DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY:
+    case InstallOperation::SOURCE_COPY:
       return "SOURCE_COPY";
-    case DeltaArchiveManifest_InstallOperation_Type_SOURCE_BSDIFF:
+    case InstallOperation::SOURCE_BSDIFF:
       return "SOURCE_BSDIFF";
+    case InstallOperation::ZERO:
+      return "ZERO";
+    case InstallOperation::DISCARD:
+      return "DISCARD";
   }
   return "UNK";
 }
diff --git a/payload_generator/annotated_operation.h b/payload_generator/annotated_operation.h
index bacc0ab..e28c0e8 100644
--- a/payload_generator/annotated_operation.h
+++ b/payload_generator/annotated_operation.h
@@ -9,6 +9,8 @@
 #include <string>
 
 #include <chromeos/secure_blob.h>
+
+#include "update_engine/payload_generator/blob_file_writer.h"
 #include "update_engine/update_metadata.pb.h"
 
 namespace chromeos_update_engine {
@@ -19,20 +21,18 @@
   std::string name;
 
   // The InstallOperation, as defined by the protobuf.
-  DeltaArchiveManifest_InstallOperation op;
+  InstallOperation op;
 
   // Writes |blob| to the end of |data_fd|, and updates |data_file_size| to
   // match the new size of |data_fd|. It sets the data_offset and data_length
   // in AnnotatedOperation to match the offset and size of |blob| in |data_fd|.
-  bool SetOperationBlob(chromeos::Blob* blob, int data_fd,
-                        off_t* data_file_size);
+  bool SetOperationBlob(chromeos::Blob* blob, BlobFileWriter* blob_file);
 };
 
 // For logging purposes.
 std::ostream& operator<<(std::ostream& os, const AnnotatedOperation& aop);
 
-std::string InstallOperationTypeName(
-    DeltaArchiveManifest_InstallOperation_Type op_type);
+std::string InstallOperationTypeName(InstallOperation_Type op_type);
 
 }  // namespace chromeos_update_engine
 
diff --git a/payload_generator/blob_file_writer.cc b/payload_generator/blob_file_writer.cc
new file mode 100644
index 0000000..2032e14
--- /dev/null
+++ b/payload_generator/blob_file_writer.cc
@@ -0,0 +1,34 @@
+// Copyright 2015 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 "update_engine/payload_generator/blob_file_writer.h"
+
+#include "update_engine/utils.h"
+
+namespace chromeos_update_engine {
+
+off_t BlobFileWriter::StoreBlob(const chromeos::Blob& blob) {
+  base::AutoLock auto_lock(blob_mutex_);
+  if (!utils::PWriteAll(blob_fd_, blob.data(), blob.size(), *blob_file_size_))
+    return -1;
+
+  off_t result = *blob_file_size_;
+  *blob_file_size_ += blob.size();
+
+  stored_blobs_++;
+  if (total_blobs_ > 0 &&
+      (10 * (stored_blobs_ - 1) / total_blobs_) !=
+      (10 * stored_blobs_ / total_blobs_)) {
+    LOG(INFO) << (100 * stored_blobs_ / total_blobs_)
+              << "% complete " << stored_blobs_ << "/" << total_blobs_
+              << " ops (output size: " << *blob_file_size_ << ")";
+  }
+  return result;
+}
+
+void BlobFileWriter::SetTotalBlobs(size_t total_blobs) {
+  total_blobs_ = total_blobs;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/blob_file_writer.h b/payload_generator/blob_file_writer.h
new file mode 100644
index 0000000..be8fd0d
--- /dev/null
+++ b/payload_generator/blob_file_writer.h
@@ -0,0 +1,46 @@
+// Copyright 2015 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 UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOB_FILE_WRITER_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOB_FILE_WRITER_H_
+
+#include <base/macros.h>
+
+#include <base/synchronization/lock.h>
+#include <chromeos/secure_blob.h>
+
+namespace chromeos_update_engine {
+
+class BlobFileWriter {
+ public:
+  // Create the BlobFileWriter object that will manage the blobs stored to
+  // |blob_fd| in a thread safe way.
+  BlobFileWriter(int blob_fd, off_t* blob_file_size)
+    : blob_fd_(blob_fd),
+      blob_file_size_(blob_file_size) {}
+
+  // Store the passed |blob| in the blob file. Returns the offset at which it
+  // was stored, or -1 in case of failure.
+  off_t StoreBlob(const chromeos::Blob& blob);
+
+  // The number of |total_blobs| is the number of blobs that will be stored but
+  // is only used for logging purposes. If not set, logging will be skipped.
+  void SetTotalBlobs(size_t total_blobs);
+
+ private:
+  size_t total_blobs_{0};
+  size_t stored_blobs_{0};
+
+  // The file and its size are protected with the |blob_mutex_|.
+  int blob_fd_;
+  off_t* blob_file_size_;
+
+  base::Lock blob_mutex_;
+
+  DISALLOW_COPY_AND_ASSIGN(BlobFileWriter);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOB_FILE_WRITER_H_
diff --git a/payload_generator/blob_file_writer_unittest.cc b/payload_generator/blob_file_writer_unittest.cc
new file mode 100644
index 0000000..c845f97
--- /dev/null
+++ b/payload_generator/blob_file_writer_unittest.cc
@@ -0,0 +1,47 @@
+// Copyright 2015 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 "update_engine/payload_generator/blob_file_writer.h"
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/test_utils.h"
+#include "update_engine/utils.h"
+
+using chromeos_update_engine::test_utils::FillWithData;
+using std::string;
+
+namespace chromeos_update_engine {
+
+class BlobFileWriterTest : public ::testing::Test {};
+
+TEST(BlobFileWriterTest, SimpleTest) {
+  string blob_path;
+  int blob_fd;
+  EXPECT_TRUE(utils::MakeTempFile("BlobFileWriterTest.XXXXXX",
+                                  &blob_path,
+                                  &blob_fd));
+  off_t blob_file_size = 0;
+  BlobFileWriter blob_file(blob_fd, &blob_file_size);
+
+  off_t blob_size = 1024;
+  chromeos::Blob blob(blob_size);
+  FillWithData(&blob);
+  EXPECT_EQ(0, blob_file.StoreBlob(blob));
+  EXPECT_EQ(blob_size, blob_file.StoreBlob(blob));
+
+  chromeos::Blob stored_blob(blob_size);
+  ssize_t bytes_read;
+  ASSERT_TRUE(utils::PReadAll(blob_fd,
+                              stored_blob.data(),
+                              blob_size,
+                              0,
+                              &bytes_read));
+  EXPECT_EQ(bytes_read, blob_size);
+  EXPECT_EQ(blob, stored_blob);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/block_mapping.cc b/payload_generator/block_mapping.cc
new file mode 100644
index 0000000..734f44f
--- /dev/null
+++ b/payload_generator/block_mapping.cc
@@ -0,0 +1,149 @@
+// Copyright 2015 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 "update_engine/payload_generator/block_mapping.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "update_engine/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace {
+
+size_t HashValue(const chromeos::Blob& blob) {
+  std::hash<string> hash_fn;
+  return hash_fn(string(blob.begin(), blob.end()));
+}
+
+}  // namespace
+
+namespace chromeos_update_engine {
+
+BlockMapping::BlockId BlockMapping::AddBlock(const chromeos::Blob& block_data) {
+  return AddBlock(-1, 0, block_data);
+}
+
+BlockMapping::BlockId BlockMapping::AddDiskBlock(int fd, off_t byte_offset) {
+  chromeos::Blob blob(block_size_);
+  ssize_t bytes_read = 0;
+  if (!utils::PReadAll(fd, blob.data(), block_size_, byte_offset, &bytes_read))
+    return -1;
+  if (static_cast<size_t>(bytes_read) != block_size_)
+    return -1;
+  return AddBlock(fd, byte_offset, blob);
+}
+
+bool BlockMapping::AddManyDiskBlocks(int fd,
+                                     off_t initial_byte_offset,
+                                     size_t num_blocks,
+                                     std::vector<BlockId>* block_ids) {
+  bool ret = true;
+  block_ids->resize(num_blocks);
+  for (size_t block = 0; block < num_blocks; block++) {
+    (*block_ids)[block] = AddDiskBlock(
+        fd, initial_byte_offset + block * block_size_);
+    ret = ret && (*block_ids)[block] != -1;
+  }
+  return ret;
+}
+
+BlockMapping::BlockId BlockMapping::AddBlock(int fd,
+                                             off_t byte_offset,
+                                             const chromeos::Blob& block_data) {
+  if (block_data.size() != block_size_)
+    return -1;
+  size_t h = HashValue(block_data);
+
+  // We either reuse a UniqueBlock or create a new one. If we need a new
+  // UniqueBlock it could also be part of a new or existing bucket (if there is
+  // a hash collision).
+  vector<UniqueBlock> *bucket = nullptr;
+
+  auto mapping_it = mapping_.find(h);
+  if (mapping_it == mapping_.end()) {
+    bucket = &mapping_[h];
+  } else {
+    for (UniqueBlock& existing_block : mapping_it->second) {
+      bool equals = false;
+      if (!existing_block.CompareData(block_data, &equals))
+        return -1;
+      if (equals)
+        return existing_block.block_id;
+    }
+    bucket = &mapping_it->second;
+  }
+
+  // No existing block was found at this point, so we create and fill in a new
+  // one.
+  bucket->emplace_back();
+  UniqueBlock *new_ublock = &bucket->back();
+
+  new_ublock->times_read = 1;
+  new_ublock->fd = fd;
+  new_ublock->byte_offset = byte_offset;
+  new_ublock->block_id = used_block_ids++;
+  // We need to cache blocks that are not referencing any disk location.
+  if (fd == -1)
+    new_ublock->block_data = block_data;
+
+  return new_ublock->block_id;
+}
+
+bool BlockMapping::UniqueBlock::CompareData(const chromeos::Blob& other_block,
+                                            bool* equals) {
+  if (!block_data.empty()) {
+    *equals = block_data == other_block;
+    return true;
+  }
+  const size_t block_size = other_block.size();
+  chromeos::Blob blob(block_size);
+  ssize_t bytes_read = 0;
+  if (!utils::PReadAll(fd, blob.data(), block_size, byte_offset, &bytes_read))
+    return false;
+  if (static_cast<size_t>(bytes_read) != block_size)
+    return false;
+  *equals = blob == other_block;
+
+  // We increase the number of times we had to read this block from disk and
+  // we cache this block based on that. This caching method is optimized for
+  // the common use case of having two partitions that share blocks between them
+  // but have few repeated blocks inside each partition, such as the block
+  // with all zeros or duplicated files.
+  times_read++;
+  if (times_read > 3)
+    block_data = std::move(blob);
+  return true;
+}
+
+bool MapPartitionBlocks(const string& old_part,
+                        const string& new_part,
+                        size_t old_size,
+                        size_t new_size,
+                        size_t block_size,
+                        vector<BlockMapping::BlockId>* old_block_ids,
+                        vector<BlockMapping::BlockId>* new_block_ids) {
+  BlockMapping mapping(block_size);
+  if (mapping.AddBlock(chromeos::Blob(block_size, '\0')) != 0)
+    return false;
+  int old_fd = HANDLE_EINTR(open(old_part.c_str(), O_RDONLY));
+  int new_fd = HANDLE_EINTR(open(new_part.c_str(), O_RDONLY));
+  ScopedFdCloser old_fd_closer(&old_fd);
+  ScopedFdCloser new_fd_closer(&new_fd);
+
+  TEST_AND_RETURN_FALSE(mapping.AddManyDiskBlocks(
+      old_fd, 0, old_size / block_size, old_block_ids));
+  TEST_AND_RETURN_FALSE(mapping.AddManyDiskBlocks(
+      new_fd, 0, new_size / block_size, new_block_ids));
+  return true;
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/block_mapping.h b/payload_generator/block_mapping.h
new file mode 100644
index 0000000..2cfca56
--- /dev/null
+++ b/payload_generator/block_mapping.h
@@ -0,0 +1,100 @@
+// Copyright 2015 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 UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOCK_MAPPING_H_
+#define UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOCK_MAPPING_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <chromeos/secure_blob.h>
+#include <gtest/gtest_prod.h>  // for FRIEND_TEST
+
+#include "update_engine/payload_generator/payload_generation_config.h"
+
+namespace chromeos_update_engine {
+
+// BlockMapping allows to map data blocks (chromeos::Blobs of block_size size)
+// into unique integer values called "block ids". This mapping differs from a
+// hash function in that two blocks with the same data will have the same id but
+// also two blocks with the same id will have the same data. This is only valid
+// in the context of the same BlockMapping instance.
+class BlockMapping {
+ public:
+  using BlockId = int64_t;
+
+  explicit BlockMapping(size_t block_size) : block_size_(block_size) {}
+
+  // Add a single data block to the mapping. Returns its unique block id.
+  // In case of error returns -1.
+  BlockId AddBlock(const chromeos::Blob& block_data);
+
+  // Add a block from disk reading it from the file descriptor |fd| from the
+  // offset in bytes |byte_offset|. The data block may or may not be cached, so
+  // the file descriptor must be available until the BlockMapping is destroyed.
+  // Returns the unique block id of the added block or -1 in case of error.
+  BlockId AddDiskBlock(int fd, off_t byte_offset);
+
+  // This is a helper method to add |num_blocks| contiguous blocks reading them
+  // from the file descriptor |fd| starting at offset |initial_byte_offset|.
+  // Returns whether it succeeded to add all the disk blocks and stores in
+  // |block_ids| the block id for each one of the added blocks.
+  bool AddManyDiskBlocks(int fd, off_t initial_byte_offset, size_t num_blocks,
+                         std::vector<BlockId>* block_ids);
+
+ private:
+  FRIEND_TEST(BlockMappingTest, BlocksAreNotKeptInMemory);
+
+  // Add a single block passed in |block_data|. If |fd| is not -1, the block
+  // can be discarded to save RAM and retrieved later from |fd| at the position
+  // |byte_offset|.
+  BlockId AddBlock(int fd, off_t byte_offset, const chromeos::Blob& block_data);
+
+  size_t block_size_;
+
+  BlockId used_block_ids{0};
+
+  // The UniqueBlock represents the data of a block associated to a unique
+  // block id.
+  struct UniqueBlock {
+    chromeos::Blob block_data;
+
+    // The block id assigned to this unique block.
+    BlockId block_id;
+
+    // The location on this unique block on disk (if not cached in block_data).
+    int fd{-1};
+    off_t byte_offset{0};
+
+    // Number of times we have seen this data block. Used for caching.
+    uint32_t times_read{0};
+
+    // Compares the UniqueBlock data with the other_block data and stores if
+    // they are equal in |equals|. Returns whether there was an error reading
+    // the block from disk while comparing it.
+    bool CompareData(const chromeos::Blob& other_block, bool* equals);
+  };
+
+  // A mapping from hash values to possible block ids.
+  std::map<size_t, std::vector<UniqueBlock>> mapping_;
+};
+
+// Maps the blocks of the old and new partitions |old_part| and |new_part| whose
+// size in bytes are |old_size| and |new_size| into block ids where two blocks
+// with the same data will have the same block id and vice versa, regardless of
+// the partition they are on.
+// The block ids number 0 corresponds to the block with all zeros, but any
+// other block id number is assigned randomly.
+bool MapPartitionBlocks(const std::string& old_part,
+                        const std::string& new_part,
+                        size_t old_size,
+                        size_t new_size,
+                        size_t block_size,
+                        std::vector<BlockMapping::BlockId>* old_block_ids,
+                        std::vector<BlockMapping::BlockId>* new_block_ids);
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_PAYLOAD_GENERATOR_BLOCK_MAPPING_H_
diff --git a/payload_generator/block_mapping_unittest.cc b/payload_generator/block_mapping_unittest.cc
new file mode 100644
index 0000000..699ac60
--- /dev/null
+++ b/payload_generator/block_mapping_unittest.cc
@@ -0,0 +1,122 @@
+// Copyright 2015 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 "update_engine/payload_generator/block_mapping.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "update_engine/test_utils.h"
+#include "update_engine/utils.h"
+
+using std::string;
+using std::vector;
+
+namespace chromeos_update_engine {
+
+namespace {
+
+}  // namespace
+
+class BlockMappingTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    EXPECT_TRUE(utils::MakeTempFile("BlockMappingTest_old.XXXXXX",
+                                    &old_part_path_,
+                                    nullptr));
+    EXPECT_TRUE(utils::MakeTempFile("BlockMappingTest_new.XXXXXX",
+                                    &new_part_path_,
+                                    nullptr));
+
+    old_part_unlinker_.reset(new ScopedPathUnlinker(old_part_path_));
+    new_part_unlinker_.reset(new ScopedPathUnlinker(new_part_path_));
+  }
+
+  // Old new partition files used in testing.
+  string old_part_path_;
+  string new_part_path_;
+  std::unique_ptr<ScopedPathUnlinker> old_part_unlinker_;
+  std::unique_ptr<ScopedPathUnlinker> new_part_unlinker_;
+
+  size_t block_size_{1024};
+  BlockMapping bm_{block_size_};  // BlockMapping under test.
+};
+
+TEST_F(BlockMappingTest, FirstAddedBlockIsZero) {
+  chromeos::Blob blob(block_size_);
+  // The BlockMapping just assigns the block ids in order, so it doesn't matter
+  // what are the contents of the first block.
+  blob[0] = 42;
+  EXPECT_EQ(0, bm_.AddBlock(blob));
+  blob[0] = 5;
+  EXPECT_EQ(1, bm_.AddBlock(blob));
+}
+
+TEST_F(BlockMappingTest, BlocksAreNotKeptInMemory) {
+  test_utils::WriteFileString(old_part_path_, string(block_size_, 'a'));
+  int old_fd = HANDLE_EINTR(open(old_part_path_.c_str(), O_RDONLY));
+  ScopedFdCloser old_fd_closer(&old_fd);
+
+  EXPECT_EQ(0, bm_.AddDiskBlock(old_fd, 0));
+
+  // Check that the block_data is not stored on memory if we just used the block
+  // once.
+  for (const auto& it : bm_.mapping_) {
+    for (const BlockMapping::UniqueBlock& ublock : it.second) {
+      EXPECT_TRUE(ublock.block_data.empty());
+    }
+  }
+
+  chromeos::Blob block(block_size_, 'a');
+  for (int i = 0; i < 5; ++i) {
+    // Re-add the same block 5 times.
+    EXPECT_EQ(0, bm_.AddBlock(block));
+  }
+
+  for (const auto& it : bm_.mapping_) {
+    for (const BlockMapping::UniqueBlock& ublock : it.second) {
+      EXPECT_FALSE(ublock.block_data.empty());
+      // The block was loaded from disk only 4 times, and after that the counter
+      // is not updated anymore.
+      EXPECT_EQ(4, ublock.times_read);
+    }
+  }
+}
+
+TEST_F(BlockMappingTest, MapPartitionBlocks) {
+  // A string with 10 blocks where all the blocks are different.
+  string old_contents(10 * block_size_, '\0');
+  for (size_t i = 0; i < old_contents.size(); ++i)
+    old_contents[i] = 4 + i / block_size_;
+  test_utils::WriteFileString(old_part_path_, old_contents);
+
+  // A string including the block with all zeros and overlapping some of the
+  // other blocks in old_contents.
+  string new_contents(6 * block_size_, '\0');
+  for (size_t i = 0; i < new_contents.size(); ++i)
+    new_contents[i] = i / block_size_;
+  test_utils::WriteFileString(new_part_path_, new_contents);
+
+  vector<BlockMapping::BlockId> old_ids, new_ids;
+  EXPECT_TRUE(MapPartitionBlocks(old_part_path_,
+                                 new_part_path_,
+                                 old_contents.size(),
+                                 new_contents.size(),
+                                 block_size_,
+                                 &old_ids,
+                                 &new_ids));
+
+  EXPECT_EQ((vector<BlockMapping::BlockId>{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}),
+            old_ids);
+  EXPECT_EQ((vector<BlockMapping::BlockId>{0, 11, 12, 13, 1, 2}),
+            new_ids);
+}
+
+}  // namespace chromeos_update_engine
diff --git a/payload_generator/cycle_breaker.cc b/payload_generator/cycle_breaker.cc
index 906502d..9c92161 100644
--- a/payload_generator/cycle_breaker.cc
+++ b/payload_generator/cycle_breaker.cc
@@ -43,9 +43,9 @@
   skipped_ops_ = 0;
 
   for (Graph::size_type i = 0; i < subgraph_.size(); i++) {
-    DeltaArchiveManifest_InstallOperation_Type op_type = graph[i].op.type();
-    if (op_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
-        op_type == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+    InstallOperation_Type op_type = graph[i].aop.op.type();
+    if (op_type == InstallOperation::REPLACE ||
+        op_type == InstallOperation::REPLACE_BZ) {
       skipped_ops_++;
       continue;
     }
diff --git a/payload_generator/cycle_breaker_unittest.cc b/payload_generator/cycle_breaker_unittest.cc
index b58bb08..69443ed 100644
--- a/payload_generator/cycle_breaker_unittest.cc
+++ b/payload_generator/cycle_breaker_unittest.cc
@@ -25,8 +25,8 @@
 
 namespace {
 void SetOpForNodes(Graph* graph) {
-  for (Graph::iterator it = graph->begin(), e = graph->end(); it != e; ++it) {
-    it->op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+  for (Vertex& vertex : *graph) {
+    vertex.aop.op.set_type(InstallOperation::MOVE);
   }
 }
 }  // namespace
@@ -249,8 +249,8 @@
 
   Graph graph(kNodeCount);
   SetOpForNodes(&graph);
-  graph[n_a].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
-  graph[n_c].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+  graph[n_a].aop.op.set_type(InstallOperation::REPLACE_BZ);
+  graph[n_c].aop.op.set_type(InstallOperation::REPLACE);
 
   graph[n_a].out_edges.insert(EdgeWithWeight(n_b, 1));
   graph[n_c].out_edges.insert(EdgeWithWeight(n_b, 1));
diff --git a/payload_generator/delta_diff_generator.cc b/payload_generator/delta_diff_generator.cc
index dbb63a8..ca8fb38 100644
--- a/payload_generator/delta_diff_generator.cc
+++ b/payload_generator/delta_diff_generator.cc
@@ -21,6 +21,7 @@
 #include "update_engine/delta_performer.h"
 #include "update_engine/payload_constants.h"
 #include "update_engine/payload_generator/ab_generator.h"
+#include "update_engine/payload_generator/blob_file_writer.h"
 #include "update_engine/payload_generator/delta_diff_utils.h"
 #include "update_engine/payload_generator/full_update_generator.h"
 #include "update_engine/payload_generator/inplace_generator.h"
@@ -115,10 +116,10 @@
 
   {
     ScopedFdCloser data_file_fd_closer(&data_file_fd);
+    BlobFileWriter blob_file(data_file_fd, &data_file_size);
     // Generate the operations using the strategy we selected above.
     TEST_AND_RETURN_FALSE(strategy->GenerateOperations(config,
-                                                       data_file_fd,
-                                                       &data_file_size,
+                                                       &blob_file,
                                                        &rootfs_ops,
                                                        &kernel_ops));
   }
diff --git a/payload_generator/delta_diff_utils.cc b/payload_generator/delta_diff_utils.cc
index 6c15919..4b39806 100644
--- a/payload_generator/delta_diff_utils.cc
+++ b/payload_generator/delta_diff_utils.cc
@@ -13,6 +13,7 @@
 
 #include "update_engine/bzip.h"
 #include "update_engine/omaha_hash_calculator.h"
+#include "update_engine/payload_generator/block_mapping.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
 #include "update_engine/payload_generator/extent_ranges.h"
 #include "update_engine/payload_generator/extent_utils.h"
@@ -21,7 +22,6 @@
 
 using std::map;
 using std::string;
-using std::unique_ptr;
 using std::vector;
 
 namespace chromeos_update_engine {
@@ -142,23 +142,24 @@
     vector<AnnotatedOperation>* aops,
     const PartitionConfig& old_part,
     const PartitionConfig& new_part,
-    off_t chunk_blocks,
-    int data_fd,
-    off_t* data_file_size,
-    bool skip_block_0,
+    ssize_t hard_chunk_blocks,
+    size_t soft_chunk_blocks,
+    BlobFileWriter* blob_file,
     bool src_ops_allowed) {
   ExtentRanges old_visited_blocks;
   ExtentRanges new_visited_blocks;
 
-  // We can't produce a MOVE operation with a 0 block as neither source nor
-  // destination, so we avoid generating an operation for the block 0 here, and
-  // we will add an operation for it in the InplaceGenerator. Excluding both
-  // old and new blocks ensures that identical images would still produce empty
-  // deltas.
-  if (skip_block_0) {
-    old_visited_blocks.AddBlock(0);
-    new_visited_blocks.AddBlock(0);
-  }
+  TEST_AND_RETURN_FALSE(DeltaMovedAndZeroBlocks(
+      aops,
+      old_part.path,
+      new_part.path,
+      old_part.fs_interface ? old_part.fs_interface->GetBlockCount() : 0,
+      new_part.fs_interface->GetBlockCount(),
+      soft_chunk_blocks,
+      src_ops_allowed,
+      blob_file,
+      &old_visited_blocks,
+      &new_visited_blocks));
 
   map<string, vector<Extent>> old_files_map;
   if (old_part.fs_interface) {
@@ -213,9 +214,8 @@
         old_file_extents,
         new_file_extents,
         new_file.name,  // operation name
-        chunk_blocks,
-        data_fd,
-        data_file_size,
+        hard_chunk_blocks,
+        blob_file,
         src_ops_allowed));
   }
   // Process all the blocks not included in any file. We provided all the unused
@@ -233,7 +233,11 @@
   }
 
   LOG(INFO) << "Scanning " << BlocksInExtents(new_unvisited)
-            << " unwritten blocks";
+            << " unwritten blocks using chunk size of "
+            << soft_chunk_blocks << " blocks.";
+  // We use the soft_chunk_blocks limit for the <non-file-data> as we don't
+  // really know the structure of this data and we should not expect it to have
+  // redundancy between partitions.
   TEST_AND_RETURN_FALSE(DeltaReadFile(
       aops,
       old_part.path,
@@ -241,14 +245,156 @@
       old_unvisited,
       new_unvisited,
       "<non-file-data>",  // operation name
-      chunk_blocks,
-      data_fd,
-      data_file_size,
+      soft_chunk_blocks,
+      blob_file,
       src_ops_allowed));
 
   return true;
 }
 
+bool DeltaMovedAndZeroBlocks(
+    vector<AnnotatedOperation>* aops,
+    const string& old_part,
+    const string& new_part,
+    size_t old_num_blocks,
+    size_t new_num_blocks,
+    ssize_t chunk_blocks,
+    bool src_ops_allowed,
+    BlobFileWriter* blob_file,
+    ExtentRanges* old_visited_blocks,
+    ExtentRanges* new_visited_blocks) {
+  vector<BlockMapping::BlockId> old_block_ids;
+  vector<BlockMapping::BlockId> new_block_ids;
+  TEST_AND_RETURN_FALSE(MapPartitionBlocks(old_part,
+                                           new_part,
+                                           old_num_blocks * kBlockSize,
+                                           new_num_blocks * kBlockSize,
+                                           kBlockSize,
+                                           &old_block_ids,
+                                           &new_block_ids));
+
+  // For minor-version=1, we map all the blocks that didn't move, regardless of
+  // the contents since they are already copied and no operation is required.
+  if (!src_ops_allowed) {
+    uint64_t num_blocks = std::min(old_num_blocks, new_num_blocks);
+    for (uint64_t block = 0; block < num_blocks; block++) {
+      if (old_block_ids[block] == new_block_ids[block] &&
+          !old_visited_blocks->ContainsBlock(block) &&
+          !new_visited_blocks->ContainsBlock(block)) {
+        old_visited_blocks->AddBlock(block);
+        new_visited_blocks->AddBlock(block);
+      }
+    }
+  }
+
+  // A mapping from the block_id to the list of block numbers with that block id
+  // in the old partition. This is used to lookup where in the old partition
+  // is a block from the new partition.
+  map<BlockMapping::BlockId, vector<uint64_t>> old_blocks_map;
+
+  for (uint64_t block = old_num_blocks; block-- > 0; ) {
+    if (old_block_ids[block] != 0 && !old_visited_blocks->ContainsBlock(block))
+      old_blocks_map[old_block_ids[block]].push_back(block);
+  }
+
+  // The collection of blocks in the new partition with just zeros. This is a
+  // common case for free-space that's also problematic for bsdiff, so we want
+  // to optimize it using REPLACE_BZ operations. The blob for a REPLACE_BZ of
+  // just zeros is so small that it doesn't make sense to spend the I/O reading
+  // zeros from the old partition.
+  vector<Extent> new_zeros;
+
+  vector<Extent> old_identical_blocks;
+  vector<Extent> new_identical_blocks;
+
+  for (uint64_t block = 0; block < new_num_blocks; block++) {
+    // Only produce operations for blocks that were not yet visited.
+    if (new_visited_blocks->ContainsBlock(block))
+      continue;
+    if (new_block_ids[block] == 0) {
+      AppendBlockToExtents(&new_zeros, block);
+      continue;
+    }
+
+    auto old_blocks_map_it = old_blocks_map.find(new_block_ids[block]);
+    // Check if the block exists in the old partition at all.
+    if (old_blocks_map_it == old_blocks_map.end() ||
+        old_blocks_map_it->second.empty())
+      continue;
+
+    AppendBlockToExtents(&old_identical_blocks,
+                         old_blocks_map_it->second.back());
+    AppendBlockToExtents(&new_identical_blocks, block);
+    // We can't reuse source blocks in minor version 1 because the cycle
+    // breaking algorithm doesn't support that.
+    if (!src_ops_allowed)
+      old_blocks_map_it->second.pop_back();
+  }
+
+  // Produce operations for the zero blocks split per output extent.
+  size_t num_ops = aops->size();
+  new_visited_blocks->AddExtents(new_zeros);
+  for (Extent extent : new_zeros) {
+    TEST_AND_RETURN_FALSE(DeltaReadFile(
+        aops,
+        "",
+        new_part,
+        vector<Extent>(),  // old_extents
+        vector<Extent>{extent},  // new_extents
+        "<zeros>",
+        chunk_blocks,
+        blob_file,
+        src_ops_allowed));
+  }
+  LOG(INFO) << "Produced " << (aops->size() - num_ops) << " operations for "
+            << BlocksInExtents(new_zeros) << " zeroed blocks";
+
+  // Produce MOVE/SOURCE_COPY operations for the moved blocks.
+  num_ops = aops->size();
+  if (chunk_blocks == -1)
+    chunk_blocks = new_num_blocks;
+  uint64_t used_blocks = 0;
+  old_visited_blocks->AddExtents(old_identical_blocks);
+  new_visited_blocks->AddExtents(new_identical_blocks);
+  for (Extent extent : new_identical_blocks) {
+    // We split the operation at the extent boundary or when bigger than
+    // chunk_blocks.
+    for (uint64_t op_block_offset = 0; op_block_offset < extent.num_blocks();
+         op_block_offset += chunk_blocks) {
+      aops->emplace_back();
+      AnnotatedOperation* aop = &aops->back();
+      aop->name = "<identical-blocks>";
+      aop->op.set_type(src_ops_allowed ? InstallOperation::SOURCE_COPY
+                                       : InstallOperation::MOVE);
+
+      uint64_t chunk_num_blocks =
+        std::min(extent.num_blocks() - op_block_offset,
+                 static_cast<uint64_t>(chunk_blocks));
+
+      // The current operation represents the move/copy operation for the
+      // sublist starting at |used_blocks| of length |chunk_num_blocks| where
+      // the src and dst are from |old_identical_blocks| and
+      // |new_identical_blocks| respectively.
+      StoreExtents(
+          ExtentsSublist(old_identical_blocks, used_blocks, chunk_num_blocks),
+          aop->op.mutable_src_extents());
+
+      Extent* op_dst_extent = aop->op.add_dst_extents();
+      op_dst_extent->set_start_block(extent.start_block() + op_block_offset);
+      op_dst_extent->set_num_blocks(chunk_num_blocks);
+      CHECK(
+          vector<Extent>{*op_dst_extent} ==  // NOLINT(whitespace/braces)
+          ExtentsSublist(new_identical_blocks, used_blocks, chunk_num_blocks));
+
+      used_blocks += chunk_num_blocks;
+    }
+  }
+  LOG(INFO) << "Produced " << (aops->size() - num_ops) << " operations for "
+            << used_blocks << " identical blocks moved";
+
+  return true;
+}
+
 bool DeltaReadFile(
     vector<AnnotatedOperation>* aops,
     const string& old_part,
@@ -256,12 +402,11 @@
     const vector<Extent>& old_extents,
     const vector<Extent>& new_extents,
     const string& name,
-    off_t chunk_blocks,
-    int data_fd,
-    off_t* data_file_size,
+    ssize_t chunk_blocks,
+    BlobFileWriter* blob_file,
     bool src_ops_allowed) {
   chromeos::Blob data;
-  DeltaArchiveManifest_InstallOperation operation;
+  InstallOperation operation;
 
   uint64_t total_blocks = BlocksInExtents(new_extents);
   if (chunk_blocks == -1)
@@ -305,7 +450,7 @@
 
     // Check if the operation writes nothing.
     if (operation.dst_extents_size() == 0) {
-      if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
+      if (operation.type() == InstallOperation::MOVE) {
         LOG(INFO) << "Empty MOVE operation ("
                   << name << "), skipping";
         continue;
@@ -315,17 +460,6 @@
       }
     }
 
-    // Write the data
-    if (operation.type() != DeltaArchiveManifest_InstallOperation_Type_MOVE &&
-        operation.type() !=
-            DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY) {
-      operation.set_data_offset(*data_file_size);
-      operation.set_data_length(data.size());
-    }
-
-    TEST_AND_RETURN_FALSE(utils::WriteAll(data_fd, data.data(), data.size()));
-    *data_file_size += data.size();
-
     // Now, insert into the list of operations.
     AnnotatedOperation aop;
     aop.name = name;
@@ -334,6 +468,14 @@
                                     name.c_str(), block_offset / chunk_blocks);
     }
     aop.op = operation;
+
+    // Write the data
+    if (operation.type() != InstallOperation::MOVE &&
+        operation.type() != InstallOperation::SOURCE_COPY) {
+      TEST_AND_RETURN_FALSE(aop.SetOperationBlob(&data, blob_file));
+    } else {
+      TEST_AND_RETURN_FALSE(blob_file->StoreBlob(data) != -1);
+    }
     aops->emplace_back(aop);
   }
   return true;
@@ -345,9 +487,9 @@
                        const vector<Extent>& new_extents,
                        bool bsdiff_allowed,
                        chromeos::Blob* out_data,
-                       DeltaArchiveManifest_InstallOperation* out_op,
+                       InstallOperation* out_op,
                        bool src_ops_allowed) {
-  DeltaArchiveManifest_InstallOperation operation;
+  InstallOperation operation;
   // Data blob that will be written to delta file.
   const chromeos::Blob* data_blob = nullptr;
 
@@ -370,7 +512,7 @@
 
 
   // Using a REPLACE is always an option.
-  operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+  operation.set_type(InstallOperation::REPLACE);
   data_blob = &new_data;
 
   // Try compressing it with bzip2.
@@ -379,7 +521,7 @@
   CHECK(!new_data_bz.empty());
   if (new_data_bz.size() < data_blob->size()) {
     // A REPLACE_BZ is better.
-    operation.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+    operation.set_type(InstallOperation::REPLACE_BZ);
     data_blob = &new_data_bz;
   }
 
@@ -394,10 +536,9 @@
     if (old_data == new_data) {
       // No change in data.
       if (src_ops_allowed) {
-        operation.set_type(
-            DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY);
+        operation.set_type(InstallOperation::SOURCE_COPY);
       } else {
-        operation.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+        operation.set_type(InstallOperation::MOVE);
       }
       data_blob = &empty_blob;
     } else if (bsdiff_allowed) {
@@ -421,10 +562,9 @@
       CHECK_GT(bsdiff_delta.size(), static_cast<chromeos::Blob::size_type>(0));
       if (bsdiff_delta.size() < data_blob->size()) {
         if (src_ops_allowed) {
-          operation.set_type(
-              DeltaArchiveManifest_InstallOperation_Type_SOURCE_BSDIFF);
+          operation.set_type(InstallOperation::SOURCE_BSDIFF);
         } else {
-          operation.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
+          operation.set_type(InstallOperation::BSDIFF);
         }
         data_blob = &bsdiff_delta;
       }
@@ -433,7 +573,7 @@
 
   size_t removed_bytes = 0;
   // Remove identical src/dst block ranges in MOVE operations.
-  if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE) {
+  if (operation.type() == InstallOperation::MOVE) {
     removed_bytes = RemoveIdenticalBlockRanges(
         &src_extents, &dst_extents, new_data.size());
   }
@@ -446,9 +586,8 @@
   StoreExtents(dst_extents, operation.mutable_dst_extents());
 
   // Replace operations should not have source extents.
-  if (operation.type() == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
-      operation.type() ==
-          DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
+  if (operation.type() == InstallOperation::REPLACE ||
+      operation.type() == InstallOperation::REPLACE_BZ) {
     operation.clear_src_extents();
     operation.clear_src_length();
   }
@@ -487,8 +626,8 @@
 
 // Returns true if |op| is a no-op operation that doesn't do any useful work
 // (e.g., a move operation that copies blocks onto themselves).
-bool IsNoopOperation(const DeltaArchiveManifest_InstallOperation& op) {
-  return (op.type() == DeltaArchiveManifest_InstallOperation_Type_MOVE &&
+bool IsNoopOperation(const InstallOperation& op) {
+  return (op.type() == InstallOperation::MOVE &&
           ExpandExtents(op.src_extents()) == ExpandExtents(op.dst_extents()));
 }
 
@@ -512,6 +651,17 @@
   return true;
 }
 
+bool CompareAopsByDestination(AnnotatedOperation first_aop,
+                              AnnotatedOperation second_aop) {
+  // We want empty operations to be at the end of the payload.
+  if (!first_aop.op.dst_extents().size() || !second_aop.op.dst_extents().size())
+    return ((!first_aop.op.dst_extents().size()) <
+            (!second_aop.op.dst_extents().size()));
+  uint32_t first_dst_start = first_aop.op.dst_extents(0).start_block();
+  uint32_t second_dst_start = second_aop.op.dst_extents(0).start_block();
+  return first_dst_start < second_dst_start;
+}
+
 }  // namespace diff_utils
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_utils.h b/payload_generator/delta_diff_utils.h
index 0be8666..ece8115 100644
--- a/payload_generator/delta_diff_utils.h
+++ b/payload_generator/delta_diff_utils.h
@@ -11,6 +11,7 @@
 #include <chromeos/secure_blob.h>
 
 #include "update_engine/payload_generator/annotated_operation.h"
+#include "update_engine/payload_generator/extent_ranges.h"
 #include "update_engine/payload_generator/payload_generation_config.h"
 #include "update_engine/update_metadata.pb.h"
 
@@ -24,15 +25,41 @@
 // blocks in that partition (if available) to determine the best way to compress
 // the new files (REPLACE, REPLACE_BZ, COPY, BSDIFF) and writes any necessary
 // data to the end of |data_fd| updating |data_file_size| accordingly.
+// |hard_chunk_blocks| and |soft_chunk_blocks| are the hard and soft chunk
+// limits in number of blocks respectively. The soft chunk limit is used to
+// split MOVE and SOURCE_COPY operations and REPLACE_BZ of zeroed blocks, while
+// the hard limit is used to split a file when generating other operations. A
+// value of -1 in |hard_chunk_blocks| means whole files.
 bool DeltaReadPartition(std::vector<AnnotatedOperation>* aops,
                         const PartitionConfig& old_part,
                         const PartitionConfig& new_part,
-                        off_t chunk_blocks,
-                        int data_fd,
-                        off_t* data_file_size,
-                        bool skip_block_0,
+                        ssize_t hard_chunk_blocks,
+                        size_t soft_chunk_blocks,
+                        BlobFileWriter* blob_file,
                         bool src_ops_allowed);
 
+// Create operations in |aops| for identical blocks that moved around in the old
+// and new partition and also handle zeroed blocks. The old and new partition
+// are stored in the |old_part| and |new_part| files and have |old_num_blocks|
+// and |new_num_blocks| respectively. The maximum operation size is
+// |chunk_blocks| blocks, or unlimited if |chunk_blocks| is -1. The blobs of the
+// produced operations are stored in the |data_fd| file whose size is updated
+// in the value pointed by |data_file_size|.
+// The collections |old_visited_blocks| and |new_visited_blocks| state what
+// blocks already have operations reading or writing them and only operations
+// for unvisited blocks are produced by this function updating both collections
+// with the used blocks.
+bool DeltaMovedAndZeroBlocks(std::vector<AnnotatedOperation>* aops,
+                             const std::string& old_part,
+                             const std::string& new_part,
+                             size_t old_num_blocks,
+                             size_t new_num_blocks,
+                             ssize_t chunk_blocks,
+                             bool src_ops_allowed,
+                             BlobFileWriter* blob_file,
+                             ExtentRanges* old_visited_blocks,
+                             ExtentRanges* new_visited_blocks);
+
 // For a given file |name| append operations to |aops| to produce it in the
 // |new_part|. The file will be split in chunks of |chunk_blocks| blocks each
 // or treated as a single chunk if |chunk_blocks| is -1. The file data is
@@ -47,9 +74,8 @@
                    const std::vector<Extent>& old_extents,
                    const std::vector<Extent>& new_extents,
                    const std::string& name,
-                   off_t chunk_blocks,
-                   int data_fd,
-                   off_t* data_file_size,
+                   ssize_t chunk_blocks,
+                   BlobFileWriter* blob_file,
                    bool src_ops_allowed);
 
 // Reads the blocks |old_extents| from |old_part| (if it exists) and the
@@ -67,7 +93,7 @@
                        const std::vector<Extent>& new_extents,
                        bool bsdiff_allowed,
                        chromeos::Blob* out_data,
-                       DeltaArchiveManifest_InstallOperation* out_op,
+                       InstallOperation* out_op,
                        bool src_ops_allowed);
 
 // Runs the bsdiff tool on two files and returns the resulting delta in
@@ -78,7 +104,7 @@
 
 // Returns true if |op| is a no-op operation that doesn't do any useful work
 // (e.g., a move operation that copies blocks onto themselves).
-bool IsNoopOperation(const DeltaArchiveManifest_InstallOperation& op);
+bool IsNoopOperation(const InstallOperation& op);
 
 // Filters all the operations that are no-op, maintaining the relative order
 // of the rest of the operations.
@@ -87,6 +113,11 @@
 bool InitializePartitionInfo(const PartitionConfig& partition,
                              PartitionInfo* info);
 
+// Compare two AnnotatedOperations by the start block of the first Extent in
+// their destination extents.
+bool CompareAopsByDestination(AnnotatedOperation first_aop,
+                              AnnotatedOperation second_aop);
+
 }  // namespace diff_utils
 
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/delta_diff_utils_unittest.cc b/payload_generator/delta_diff_utils_unittest.cc
index 2157aa1..0425785 100644
--- a/payload_generator/delta_diff_utils_unittest.cc
+++ b/payload_generator/delta_diff_utils_unittest.cc
@@ -9,6 +9,8 @@
 #include <vector>
 
 #include <base/files/scoped_file.h>
+#include <base/format_macros.h>
+#include <base/strings/stringprintf.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/payload_generator/delta_diff_generator.h"
@@ -19,7 +21,6 @@
 #include "update_engine/utils.h"
 
 using std::string;
-using std::unique_ptr;
 using std::vector;
 
 namespace chromeos_update_engine {
@@ -51,61 +52,117 @@
   return true;
 }
 
+// Create a fake filesystem of the given |size| and initialize the partition
+// holding it in the PartitionConfig |part|.
+void CreatePartition(PartitionConfig* part, const string& pattern,
+                     uint64_t block_size, off_t size) {
+  int fd = -1;
+  ASSERT_TRUE(utils::MakeTempFile(pattern.c_str(), &part->path, &fd));
+  ASSERT_EQ(0, ftruncate(fd, size));
+  ASSERT_EQ(0, close(fd));
+  part->fs_interface.reset(new FakeFilesystem(block_size, size / block_size));
+  part->size = size;
+}
+
+// Writes to the |partition| path blocks such they are all different and they
+// include the tag passed in |tag|, making them also different to any other
+// block on a partition initialized with this function with a different tag.
+// The |block_size| should be a divisor of the partition size.
+// Returns whether the function succeeded writing the partition.
+bool InitializePartitionWithUniqueBlocks(const PartitionConfig& part,
+                                         uint64_t block_size,
+                                         uint64_t tag) {
+  TEST_AND_RETURN_FALSE(part.size % block_size == 0);
+  size_t num_blocks = part.size / block_size;
+  chromeos::Blob file_data(part.size);
+  for (size_t i = 0; i < num_blocks; ++i) {
+    string prefix = base::StringPrintf(
+        "block tag 0x%.16" PRIx64 ", block number %16" PRIuS " ", tag, i);
+    chromeos::Blob block_data(prefix.begin(), prefix.end());
+    TEST_AND_RETURN_FALSE(prefix.size() <= block_size);
+    block_data.resize(block_size, 'X');
+    std::copy(block_data.begin(), block_data.end(),
+              file_data.begin() + i * block_size);
+  }
+  return test_utils::WriteFileVector(part.path, file_data);
+}
+
 }  // namespace
 
 class DeltaDiffUtilsTest : public ::testing::Test {
  protected:
-  const uint64_t kFilesystemSize = kBlockSize * 1024;
+  const uint64_t kDefaultBlockCount = 128;
 
   void SetUp() override {
-    old_part_path_ = "DeltaDiffUtilsTest-old_part_path-XXXXXX";
-    CreateFilesystem(&old_fs_, &old_part_path_, kFilesystemSize);
-
-    new_part_path_ = "DeltaDiffUtilsTest-new_part_path-XXXXXX";
-    CreateFilesystem(&old_fs_, &new_part_path_, kFilesystemSize);
+    CreatePartition(&old_part_, "DeltaDiffUtilsTest-old_part-XXXXXX",
+                    block_size_, block_size_ * kDefaultBlockCount);
+    CreatePartition(&new_part_, "DeltaDiffUtilsTest-old_part-XXXXXX",
+                    block_size_, block_size_ * kDefaultBlockCount);
+    ASSERT_TRUE(utils::MakeTempFile("DeltaDiffUtilsTest-blob-XXXXXX",
+                                    &blob_path_,
+                                    &blob_fd_));
   }
 
   void TearDown() override {
-    unlink(old_part_path_.c_str());
-    unlink(new_part_path_.c_str());
+    unlink(old_part_.path.c_str());
+    unlink(new_part_.path.c_str());
+    if (blob_fd_ != -1)
+      close(blob_fd_);
+    unlink(blob_path_.c_str());
   }
 
-  // Create a fake filesystem of the given size and initialize the partition
-  // holding it.
-  void CreateFilesystem(unique_ptr<FakeFilesystem>* fs, string* filename,
-                        uint64_t size) {
-    string pattern = *filename;
-    ASSERT_TRUE(utils::MakeTempFile(pattern.c_str(), filename, nullptr));
-    ASSERT_EQ(0, truncate(filename->c_str(), size));
-    fs->reset(new FakeFilesystem(kBlockSize, size / kBlockSize));
+  // Helper function to call DeltaMovedAndZeroBlocks() using this class' data
+  // members. This simply avoid repeating all the arguments that never change.
+  bool RunDeltaMovedAndZeroBlocks(ssize_t chunk_blocks,
+                                  bool src_ops_allowed) {
+    BlobFileWriter blob_file(blob_fd_, &blob_size_);
+    return diff_utils::DeltaMovedAndZeroBlocks(
+        &aops_,
+        old_part_.path,
+        new_part_.path,
+        old_part_.size / block_size_,
+        new_part_.size / block_size_,
+        chunk_blocks,
+        src_ops_allowed,
+        &blob_file,
+        &old_visited_blocks_,
+        &new_visited_blocks_);
   }
 
-  // Paths to old and new temporary filesystems used in the tests.
-  string old_part_path_;
-  string new_part_path_;
+  // Old and new temporary partitions used in the tests. These are initialized
+  // with
+  PartitionConfig old_part_{PartitionName::kRootfs};
+  PartitionConfig new_part_{PartitionName::kRootfs};
 
-  // FilesystemInterface fake implementations used to mock out the file/block
-  // distribution.
-  unique_ptr<FakeFilesystem> old_fs_;
-  unique_ptr<FakeFilesystem> new_fs_;
+  // The file holding the output blob from the various diff utils functions.
+  string blob_path_;
+  int blob_fd_{-1};
+  off_t blob_size_{0};
+
+  size_t block_size_{kBlockSize};
+
+  // Default input/output arguments used when calling DeltaMovedAndZeroBlocks().
+  vector<AnnotatedOperation> aops_;
+  ExtentRanges old_visited_blocks_;
+  ExtentRanges new_visited_blocks_;
 };
 
 TEST_F(DeltaDiffUtilsTest, MoveSmallTest) {
-  chromeos::Blob data_blob(kBlockSize);
+  chromeos::Blob data_blob(block_size_);
   test_utils::FillWithData(&data_blob);
 
   // The old file is on a different block than the new one.
   vector<Extent> old_extents = { ExtentForRange(11, 1) };
   vector<Extent> new_extents = { ExtentForRange(1, 1) };
 
-  EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
-  EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
 
   chromeos::Blob data;
-  DeltaArchiveManifest_InstallOperation op;
+  InstallOperation op;
   EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
-      old_part_path_,
-      new_part_path_,
+      old_part_.path,
+      new_part_.path,
       old_extents,
       new_extents,
       true,  // bsdiff_allowed
@@ -115,7 +172,7 @@
   EXPECT_TRUE(data.empty());
 
   EXPECT_TRUE(op.has_type());
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, op.type());
+  EXPECT_EQ(InstallOperation::MOVE, op.type());
   EXPECT_FALSE(op.has_data_offset());
   EXPECT_FALSE(op.has_data_length());
   EXPECT_EQ(1, op.src_extents_size());
@@ -158,14 +215,14 @@
     file_data.resize(file_data.size() + kBlockSize, 'a' + i);
   }
 
-  EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, file_data));
-  EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, file_data));
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, file_data));
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, file_data));
 
   chromeos::Blob data;
-  DeltaArchiveManifest_InstallOperation op;
+  InstallOperation op;
   EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
-      old_part_path_,
-      new_part_path_,
+      old_part_.path,
+      new_part_.path,
       old_extents,
       new_extents,
       true,  // bsdiff_allowed
@@ -176,7 +233,7 @@
   EXPECT_TRUE(data.empty());
 
   EXPECT_TRUE(op.has_type());
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, op.type());
+  EXPECT_EQ(InstallOperation::MOVE, op.type());
   EXPECT_FALSE(op.has_data_offset());
   EXPECT_FALSE(op.has_data_length());
 
@@ -220,16 +277,16 @@
   vector<Extent> old_extents = { ExtentForRange(1, 1) };
   vector<Extent> new_extents = { ExtentForRange(2, 1) };
 
-  EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
   // Modify one byte in the new file.
   data_blob[0]++;
-  EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
 
   chromeos::Blob data;
-  DeltaArchiveManifest_InstallOperation op;
+  InstallOperation op;
   EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
-      old_part_path_,
-      new_part_path_,
+      old_part_.path,
+      new_part_.path,
       old_extents,
       new_extents,
       true,  // bsdiff_allowed
@@ -240,7 +297,7 @@
   EXPECT_FALSE(data.empty());
 
   EXPECT_TRUE(op.has_type());
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_BSDIFF, op.type());
+  EXPECT_EQ(InstallOperation::BSDIFF, op.type());
   EXPECT_FALSE(op.has_data_offset());
   EXPECT_FALSE(op.has_data_length());
   EXPECT_EQ(1, op.src_extents_size());
@@ -262,16 +319,16 @@
   vector<Extent> old_extents = { ExtentForRange(1, 1) };
   vector<Extent> new_extents = { ExtentForRange(2, 1) };
 
-  EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
   // Modify one byte in the new file.
   data_blob[0]++;
-  EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
 
   chromeos::Blob data;
-  DeltaArchiveManifest_InstallOperation op;
+  InstallOperation op;
   EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
-      old_part_path_,
-      new_part_path_,
+      old_part_.path,
+      new_part_.path,
       old_extents,
       new_extents,
       false,  // bsdiff_allowed
@@ -284,7 +341,7 @@
   // The point of this test is that we don't use BSDIFF the way the above
   // did. The rest of the details are to be caught in other tests.
   EXPECT_TRUE(op.has_type());
-  EXPECT_NE(DeltaArchiveManifest_InstallOperation_Type_BSDIFF, op.type());
+  EXPECT_NE(InstallOperation::BSDIFF, op.type());
 }
 
 TEST_F(DeltaDiffUtilsTest, BsdiffNotAllowedMoveTest) {
@@ -295,14 +352,14 @@
   vector<Extent> old_extents = { ExtentForRange(1, 1) };
   vector<Extent> new_extents = { ExtentForRange(2, 1) };
 
-  EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
-  EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
 
   chromeos::Blob data;
-  DeltaArchiveManifest_InstallOperation op;
+  InstallOperation op;
   EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
-      old_part_path_,
-      new_part_path_,
+      old_part_.path,
+      new_part_.path,
       old_extents,
       new_extents,
       false,  // bsdiff_allowed
@@ -315,7 +372,7 @@
   // The point of this test is that we can still use a MOVE for a file
   // that is blacklisted.
   EXPECT_TRUE(op.has_type());
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE, op.type());
+  EXPECT_EQ(InstallOperation::MOVE, op.type());
 }
 
 TEST_F(DeltaDiffUtilsTest, ReplaceSmallTest) {
@@ -337,14 +394,14 @@
   for (int i = 0; i < 2; i++) {
     chromeos::Blob data_to_test = i == 0 ? random_data : ones;
     // The old_extents will be initialized with 0.
-    EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize,
+    EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize,
                              data_to_test));
 
     chromeos::Blob data;
-    DeltaArchiveManifest_InstallOperation op;
+    InstallOperation op;
     EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
-        old_part_path_,
-        new_part_path_,
+        old_part_.path,
+        new_part_.path,
         old_extents,
         new_extents,
         true,  // bsdiff_allowed
@@ -354,9 +411,8 @@
     EXPECT_FALSE(data.empty());
 
     EXPECT_TRUE(op.has_type());
-    const DeltaArchiveManifest_InstallOperation_Type expected_type =
-        (i == 0 ? DeltaArchiveManifest_InstallOperation_Type_REPLACE :
-         DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+    const InstallOperation_Type expected_type =
+        (i == 0 ? InstallOperation::REPLACE : InstallOperation::REPLACE_BZ);
     EXPECT_EQ(expected_type, op.type());
     EXPECT_FALSE(op.has_data_offset());
     EXPECT_FALSE(op.has_data_length());
@@ -379,14 +435,14 @@
   vector<Extent> old_extents = { ExtentForRange(11, 1) };
   vector<Extent> new_extents = { ExtentForRange(1, 1) };
 
-  EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
-  EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
 
   chromeos::Blob data;
-  DeltaArchiveManifest_InstallOperation op;
+  InstallOperation op;
   EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
-      old_part_path_,
-      new_part_path_,
+      old_part_.path,
+      new_part_.path,
       old_extents,
       new_extents,
       true,  // bsdiff_allowed
@@ -396,7 +452,7 @@
   EXPECT_TRUE(data.empty());
 
   EXPECT_TRUE(op.has_type());
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_COPY, op.type());
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, op.type());
 }
 
 TEST_F(DeltaDiffUtilsTest, SourceBsdiffTest) {
@@ -410,16 +466,16 @@
   vector<Extent> old_extents = { ExtentForRange(1, 1) };
   vector<Extent> new_extents = { ExtentForRange(2, 1) };
 
-  EXPECT_TRUE(WriteExtents(old_part_path_, old_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_extents, kBlockSize, data_blob));
   // Modify one byte in the new file.
   data_blob[0]++;
-  EXPECT_TRUE(WriteExtents(new_part_path_, new_extents, kBlockSize, data_blob));
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_extents, kBlockSize, data_blob));
 
   chromeos::Blob data;
-  DeltaArchiveManifest_InstallOperation op;
+  InstallOperation op;
   EXPECT_TRUE(diff_utils::ReadExtentsToDiff(
-      old_part_path_,
-      new_part_path_,
+      old_part_.path,
+      new_part_.path,
       old_extents,
       new_extents,
       true,  // bsdiff_allowed
@@ -429,15 +485,14 @@
 
   EXPECT_FALSE(data.empty());
   EXPECT_TRUE(op.has_type());
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_SOURCE_BSDIFF,
-            op.type());
+  EXPECT_EQ(InstallOperation::SOURCE_BSDIFF, op.type());
 }
 
 TEST_F(DeltaDiffUtilsTest, IsNoopOperationTest) {
-  DeltaArchiveManifest_InstallOperation op;
-  op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+  InstallOperation op;
+  op.set_type(InstallOperation::REPLACE_BZ);
   EXPECT_FALSE(diff_utils::IsNoopOperation(op));
-  op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+  op.set_type(InstallOperation::MOVE);
   EXPECT_TRUE(diff_utils::IsNoopOperation(op));
   *(op.add_src_extents()) = ExtentForRange(3, 2);
   *(op.add_dst_extents()) = ExtentForRange(3, 2);
@@ -456,7 +511,7 @@
 
 TEST_F(DeltaDiffUtilsTest, FilterNoopOperations) {
   AnnotatedOperation aop1;
-  aop1.op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
+  aop1.op.set_type(InstallOperation::REPLACE_BZ);
   *(aop1.op.add_dst_extents()) = ExtentForRange(3, 2);
   aop1.name = "aop1";
 
@@ -464,7 +519,7 @@
   aop2.name = "aop2";
 
   AnnotatedOperation noop;
-  noop.op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+  noop.op.set_type(InstallOperation::MOVE);
   *(noop.op.add_src_extents()) = ExtentForRange(3, 2);
   *(noop.op.add_dst_extents()) = ExtentForRange(3, 2);
   noop.name = "noop";
@@ -476,4 +531,233 @@
   EXPECT_EQ("aop2", ops[1].name);
 }
 
+// Test the simple case where all the blocks are different and no new blocks are
+// zeroed.
+TEST_F(DeltaDiffUtilsTest, NoZeroedOrUniqueBlocksDetected) {
+  InitializePartitionWithUniqueBlocks(old_part_, block_size_, 5);
+  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
+
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
+                                         false));  // src_ops_allowed
+
+  EXPECT_EQ(0, old_visited_blocks_.blocks());
+  EXPECT_EQ(0, new_visited_blocks_.blocks());
+  EXPECT_EQ(0, blob_size_);
+  EXPECT_TRUE(aops_.empty());
+}
+
+// Test that when the partitions have identical blocks in the same positions no
+// MOVE operation is performed and all the blocks are handled.
+TEST_F(DeltaDiffUtilsTest, IdenticalPartitionsDontMove) {
+  InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
+  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
+
+  // Mark some of the blocks as already visited.
+  vector<Extent> already_visited = {ExtentForRange(5, 10),
+                                    ExtentForRange(25, 10)};
+  old_visited_blocks_.AddExtents(already_visited);
+  new_visited_blocks_.AddExtents(already_visited);
+
+  // Most of the blocks rest in the same place, but there's no need for MOVE
+  // operations on those blocks.
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
+                                         false));  // src_ops_allowed
+
+  EXPECT_EQ(kDefaultBlockCount, old_visited_blocks_.blocks());
+  EXPECT_EQ(kDefaultBlockCount, new_visited_blocks_.blocks());
+  EXPECT_EQ(0, blob_size_);
+  EXPECT_TRUE(aops_.empty());
+}
+
+// Test that when the partitions have identical blocks in the same positions
+// MOVE operation is performed and all the blocks are handled.
+TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedFromSource) {
+  // We use a smaller partition for this test.
+  old_part_.size = kBlockSize * 50;
+  new_part_.size = kBlockSize * 50;
+
+  InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
+  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 42);
+
+  // Mark some of the blocks as already visited.
+  vector<Extent> already_visited = {ExtentForRange(5, 5),
+                                    ExtentForRange(25, 7)};
+  old_visited_blocks_.AddExtents(already_visited);
+  new_visited_blocks_.AddExtents(already_visited);
+
+  // Override some of the old blocks with different data.
+  vector<Extent> different_blocks = {ExtentForRange(40, 5)};
+  EXPECT_TRUE(WriteExtents(old_part_.path, different_blocks, kBlockSize,
+                           chromeos::Blob(5 * kBlockSize, 'a')));
+
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(10,  // chunk_blocks
+                                         true));  // src_ops_allowed
+
+  ExtentRanges expected_ranges;
+  expected_ranges.AddExtent(ExtentForRange(0, 50));
+  expected_ranges.SubtractExtents(different_blocks);
+
+  EXPECT_EQ(expected_ranges.extent_set(), old_visited_blocks_.extent_set());
+  EXPECT_EQ(expected_ranges.extent_set(), new_visited_blocks_.extent_set());
+  EXPECT_EQ(0, blob_size_);
+
+  // We expect all the blocks that we didn't override with |different_blocks|
+  // and that we didn't mark as visited in |already_visited| to match and have a
+  // SOURCE_COPY operation chunked at 10 blocks.
+  vector<Extent> expected_op_extents = {
+      ExtentForRange(0, 5),
+      ExtentForRange(10, 10),
+      ExtentForRange(20, 5),
+      ExtentForRange(32, 8),
+      ExtentForRange(45, 5),
+  };
+
+  EXPECT_EQ(expected_op_extents.size(), aops_.size());
+  for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
+    SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
+    const AnnotatedOperation& aop = aops_[i];
+    EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+    EXPECT_EQ(1, aop.op.src_extents_size());
+    EXPECT_EQ(expected_op_extents[i], aop.op.src_extents(0));
+    EXPECT_EQ(1, aop.op.dst_extents_size());
+    EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
+  }
+}
+
+TEST_F(DeltaDiffUtilsTest, IdenticalBlocksAreCopiedInOder) {
+  // We use a smaller partition for this test.
+  old_part_.size = block_size_ * 50;
+  new_part_.size = block_size_ * 50;
+
+  // Create two identical partitions with 5 copies of the same unique "file".
+  chromeos::Blob file_data(block_size_ * 10, 'a');
+  for (size_t offset = 0; offset < file_data.size(); offset += block_size_)
+    file_data[offset] = 'a' + offset / block_size_;
+
+  chromeos::Blob partition_data(old_part_.size);
+  for (size_t offset = 0; offset < partition_data.size();
+       offset += file_data.size()) {
+    std::copy(file_data.begin(), file_data.end(),
+              partition_data.begin() + offset);
+  }
+  EXPECT_TRUE(test_utils::WriteFileVector(old_part_.path, partition_data));
+  EXPECT_TRUE(test_utils::WriteFileVector(new_part_.path, partition_data));
+
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
+                                         true));  // src_ops_allowed
+
+  // There should be only one SOURCE_COPY, for the whole partition and the
+  // source extents should cover only the first copy of the source file since
+  // we prefer to re-read files (maybe cached) instead of continue reading the
+  // rest of the partition.
+  EXPECT_EQ(1, aops_.size());
+  const AnnotatedOperation& aop = aops_[0];
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+  EXPECT_EQ(5, aop.op.src_extents_size());
+  for (int i = 0; i < aop.op.src_extents_size(); ++i) {
+    EXPECT_EQ(ExtentForRange(0, 10), aop.op.src_extents(i));
+  }
+
+  EXPECT_EQ(1, aop.op.dst_extents_size());
+  EXPECT_EQ(ExtentForRange(0, 50), aop.op.dst_extents(0));
+
+  EXPECT_EQ(0, blob_size_);
+}
+
+// Test that all blocks with zeros are handled separately using REPLACE_BZ
+// operations unless they are not moved.
+TEST_F(DeltaDiffUtilsTest, ZeroBlocksUseReplaceBz) {
+  InitializePartitionWithUniqueBlocks(old_part_, block_size_, 42);
+  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 5);
+
+  // We set four ranges of blocks with zeros: a single block, a range that fits
+  // in the chunk size, a range that doesn't and finally a range of zeros that
+  // was also zeros in the old image.
+  vector<Extent> new_zeros = {
+      ExtentForRange(10, 1),
+      ExtentForRange(20, 4),
+      // The last range is split since the old image has zeros in part of it.
+      ExtentForRange(30, 20),
+  };
+  chromeos::Blob zeros_data(BlocksInExtents(new_zeros) * block_size_, '\0');
+  EXPECT_TRUE(WriteExtents(new_part_.path, new_zeros, block_size_, zeros_data));
+
+  vector<Extent> old_zeros = vector<Extent>{ExtentForRange(43, 7)};
+  EXPECT_TRUE(WriteExtents(old_part_.path, old_zeros, block_size_, zeros_data));
+
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(5,  // chunk_blocks
+                                         false));  // src_ops_allowed
+
+  // Zeroed blocks from old_visited_blocks_ were copied over, so me actually
+  // use them regardless of the trivial MOVE operation not being emitted.
+  EXPECT_EQ(old_zeros,
+            old_visited_blocks_.GetExtentsForBlockCount(
+                old_visited_blocks_.blocks()));
+
+  // All the new zeroed blocks should be used, part with REPLACE_BZ and part
+  // trivial MOVE operations (not included).
+  EXPECT_EQ(new_zeros,
+            new_visited_blocks_.GetExtentsForBlockCount(
+                new_visited_blocks_.blocks()));
+
+  vector<Extent> expected_op_extents = {
+      ExtentForRange(10, 1),
+      ExtentForRange(20, 4),
+      // This range should be split.
+      ExtentForRange(30, 5),
+      ExtentForRange(35, 5),
+      ExtentForRange(40, 3),
+  };
+
+  EXPECT_EQ(expected_op_extents.size(), aops_.size());
+  for (size_t i = 0; i < aops_.size() && i < expected_op_extents.size(); ++i) {
+    SCOPED_TRACE(base::StringPrintf("Failed on operation number %" PRIuS, i));
+    const AnnotatedOperation& aop = aops_[i];
+    EXPECT_EQ(InstallOperation::REPLACE_BZ, aop.op.type());
+    EXPECT_EQ(0, aop.op.src_extents_size());
+    EXPECT_EQ(1, aop.op.dst_extents_size());
+    EXPECT_EQ(expected_op_extents[i], aop.op.dst_extents(0));
+  }
+  EXPECT_NE(0, blob_size_);
+}
+
+TEST_F(DeltaDiffUtilsTest, ShuffledBlocksAreTracked) {
+  vector<uint64_t> permutation = {0, 1, 5, 6, 7, 2, 3, 4, 9, 10, 11, 12, 8};
+  vector<Extent> perm_extents;
+  for (uint64_t x : permutation)
+    AppendBlockToExtents(&perm_extents, x);
+
+  // We use a smaller partition for this test.
+  old_part_.size = block_size_ * permutation.size();
+  new_part_.size = block_size_ * permutation.size();
+  InitializePartitionWithUniqueBlocks(new_part_, block_size_, 123);
+
+  // We initialize the old_part_ with the blocks from new_part but in the
+  // |permutation| order. Block i in the old_part_ will contain the same data
+  // as block permutation[i] in the new_part_.
+  chromeos::Blob new_contents;
+  EXPECT_TRUE(utils::ReadFile(new_part_.path, &new_contents));
+  EXPECT_TRUE(WriteExtents(old_part_.path, perm_extents, block_size_,
+                           new_contents));
+
+  EXPECT_TRUE(RunDeltaMovedAndZeroBlocks(-1,  // chunk_blocks
+                                         true));  // src_ops_allowed
+
+  EXPECT_EQ(permutation.size(), old_visited_blocks_.blocks());
+  EXPECT_EQ(permutation.size(), new_visited_blocks_.blocks());
+
+  // There should be only one SOURCE_COPY, with a complicate list of extents.
+  EXPECT_EQ(1, aops_.size());
+  const AnnotatedOperation& aop = aops_[0];
+  EXPECT_EQ(InstallOperation::SOURCE_COPY, aop.op.type());
+  vector<Extent> aop_src_extents;
+  ExtentsToVector(aop.op.src_extents(), &aop_src_extents);
+  EXPECT_EQ(perm_extents, aop_src_extents);
+
+  EXPECT_EQ(1, aop.op.dst_extents_size());
+  EXPECT_EQ(ExtentForRange(0, permutation.size()), aop.op.dst_extents(0));
+
+  EXPECT_EQ(0, blob_size_);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/extent_ranges.cc b/payload_generator/extent_ranges.cc
index d86bcfc..9151efe 100644
--- a/payload_generator/extent_ranges.cc
+++ b/payload_generator/extent_ranges.cc
@@ -180,6 +180,23 @@
   }
 }
 
+bool ExtentRanges::ContainsBlock(uint64_t block) const {
+  auto lower = extent_set_.lower_bound(ExtentForRange(block, 1));
+  // The block could be on the extent before the one in |lower|.
+  if (lower != extent_set_.begin())
+    lower--;
+  // Any extent starting at block+1 or later is not interesting, so this is the
+  // upper limit.
+  auto upper = extent_set_.lower_bound(ExtentForRange(block + 1, 0));
+  for (auto iter = lower; iter != upper; ++iter) {
+    if (iter->start_block() <= block &&
+        block < iter->start_block() + iter->num_blocks()) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void ExtentRanges::Dump() const {
   LOG(INFO) << "ExtentRanges Dump. blocks: " << blocks_;
   for (ExtentSet::const_iterator it = extent_set_.begin(),
@@ -223,7 +240,7 @@
   return out;
 }
 
-vector<Extent> FilterExtentRanges(const std::vector<Extent>& extents,
+vector<Extent> FilterExtentRanges(const vector<Extent>& extents,
                                   const ExtentRanges& ranges) {
   vector<Extent> result;
   const ExtentRanges::ExtentSet& extent_set = ranges.extent_set();
diff --git a/payload_generator/extent_ranges.h b/payload_generator/extent_ranges.h
index fc15149..f14bb61 100644
--- a/payload_generator/extent_ranges.h
+++ b/payload_generator/extent_ranges.h
@@ -48,6 +48,9 @@
   void AddRanges(const ExtentRanges& ranges);
   void SubtractRanges(const ExtentRanges& ranges);
 
+  // Returns whether the block |block| is in this ExtentRange.
+  bool ContainsBlock(uint64_t block) const;
+
   static bool ExtentsOverlapOrTouch(const Extent& a, const Extent& b);
   static bool ExtentsOverlap(const Extent& a, const Extent& b);
 
diff --git a/payload_generator/extent_ranges_unittest.cc b/payload_generator/extent_ranges_unittest.cc
index b7036a5..2cda195 100644
--- a/payload_generator/extent_ranges_unittest.cc
+++ b/payload_generator/extent_ranges_unittest.cc
@@ -256,6 +256,25 @@
   }
 }
 
+TEST(ExtentRangesTest, ContainsBlockTest) {
+  ExtentRanges ranges;
+  EXPECT_FALSE(ranges.ContainsBlock(123));
+
+  ranges.AddExtent(ExtentForRange(10, 10));
+  ranges.AddExtent(ExtentForRange(100, 1));
+
+  EXPECT_FALSE(ranges.ContainsBlock(9));
+  EXPECT_TRUE(ranges.ContainsBlock(10));
+  EXPECT_TRUE(ranges.ContainsBlock(15));
+  EXPECT_TRUE(ranges.ContainsBlock(19));
+  EXPECT_FALSE(ranges.ContainsBlock(20));
+
+  // Test for an extent with just the block we are requesting.
+  EXPECT_FALSE(ranges.ContainsBlock(99));
+  EXPECT_TRUE(ranges.ContainsBlock(100));
+  EXPECT_FALSE(ranges.ContainsBlock(101));
+}
+
 TEST(ExtentRangesTest, FilterExtentRangesEmptyRanges) {
   ExtentRanges ranges;
   EXPECT_EQ(vector<Extent>(),
diff --git a/payload_generator/extent_utils_unittest.cc b/payload_generator/extent_utils_unittest.cc
index 188164d..3f66d3e 100644
--- a/payload_generator/extent_utils_unittest.cc
+++ b/payload_generator/extent_utils_unittest.cc
@@ -63,11 +63,11 @@
 }
 
 TEST(ExtentUtilsTest, ExtendExtentsTest) {
-  DeltaArchiveManifest_InstallOperation first_op;
+  InstallOperation first_op;
   *(first_op.add_src_extents()) = ExtentForRange(1, 1);
   *(first_op.add_src_extents()) = ExtentForRange(3, 1);
 
-  DeltaArchiveManifest_InstallOperation second_op;
+  InstallOperation second_op;
   *(second_op.add_src_extents()) = ExtentForRange(4, 2);
   *(second_op.add_src_extents()) = ExtentForRange(8, 2);
 
diff --git a/payload_generator/full_update_generator.cc b/payload_generator/full_update_generator.cc
index c28f7e8..00d7300 100644
--- a/payload_generator/full_update_generator.cc
+++ b/payload_generator/full_update_generator.cc
@@ -12,14 +12,15 @@
 #include <memory>
 
 #include <base/format_macros.h>
-#include <base/strings/stringprintf.h>
 #include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <base/synchronization/lock.h>
+#include <base/threading/simple_thread.h>
+#include <chromeos/secure_blob.h>
 
 #include "update_engine/bzip.h"
 #include "update_engine/utils.h"
 
-using std::deque;
-using std::shared_ptr;
 using std::string;
 using std::vector;
 
@@ -29,82 +30,81 @@
 
 const size_t kDefaultFullChunkSize = 1024 * 1024;  // 1 MiB
 
-// This class encapsulates a full update chunk processing thread. The processor
-// reads a chunk of data from the input file descriptor and compresses it. The
-// processor needs to be started through Start() then waited on through Wait().
-class ChunkProcessor {
+// This class encapsulates a full update chunk processing thread work. The
+// processor reads a chunk of data from the input file descriptor and compresses
+// it. The processor will destroy itself when the work is done.
+class ChunkProcessor : public base::DelegateSimpleThread::Delegate {
  public:
   // Read a chunk of |size| bytes from |fd| starting at offset |offset|.
-  ChunkProcessor(int fd, off_t offset, size_t size)
-      : thread_(nullptr),
-        fd_(fd),
-        offset_(offset),
-        buffer_in_(size) {}
-  ~ChunkProcessor() { Wait(); }
+  ChunkProcessor(int fd, off_t offset, size_t size,
+                 BlobFileWriter* blob_file, AnnotatedOperation* aop)
+    : fd_(fd),
+      offset_(offset),
+      size_(size),
+      blob_file_(blob_file),
+      aop_(aop) {}
+  // We use a default move constructor since all the data members are POD types.
+  ChunkProcessor(ChunkProcessor&&) = default;
+  ~ChunkProcessor() override = default;
 
-  off_t offset() const { return offset_; }
-  const chromeos::Blob& buffer_in() const { return buffer_in_; }
-  const chromeos::Blob& buffer_compressed() const { return buffer_compressed_; }
-
-  // Starts the processor. Returns true on success, false on failure.
-  bool Start();
-
-  // Waits for the processor to complete. Returns true on success, false on
-  // failure.
-  bool Wait();
-
-  bool ShouldCompress() const {
-    return buffer_compressed_.size() < buffer_in_.size();
-  }
+  // Overrides DelegateSimpleThread::Delegate.
+  // Run() handles the read from |fd| in a thread-safe way, and stores the
+  // new operation to generate the region starting at |offset| of size |size|
+  // in the output operation |aop|. The associated blob data is stored in
+  // |blob_fd| and |blob_file_size| is updated.
+  void Run() override;
 
  private:
-  // Reads the input data into |buffer_in_| and compresses it into
-  // |buffer_compressed_|. Returns true on success, false otherwise.
-  bool ReadAndCompress();
-  static gpointer ReadAndCompressThread(gpointer data);
+  bool ProcessChunk();
 
-  GThread* thread_;
+  // Stores the operation blob in the |blob_fd_| and updates the
+  // |blob_file_size| accordingly.
+  // This method is thread-safe since it uses a mutex to access the file.
+  // Returns the data offset where the data was written to.
+  off_t StoreBlob(const chromeos::Blob& blob);
+
+  // Work parameters.
   int fd_;
   off_t offset_;
-  chromeos::Blob buffer_in_;
-  chromeos::Blob buffer_compressed_;
+  size_t size_;
+  BlobFileWriter* blob_file_;
+  AnnotatedOperation* aop_;
 
   DISALLOW_COPY_AND_ASSIGN(ChunkProcessor);
 };
 
-bool ChunkProcessor::Start() {
-  // g_thread_create is deprecated since glib 2.32. Use
-  // g_thread_new instead.
-  thread_ = g_thread_try_new("chunk_proc", ReadAndCompressThread, this,
-                             nullptr);
-  TEST_AND_RETURN_FALSE(thread_ != nullptr);
-  return true;
-}
-
-bool ChunkProcessor::Wait() {
-  if (!thread_) {
-    return false;
+void ChunkProcessor::Run() {
+  if (!ProcessChunk()) {
+    LOG(ERROR) << "Error processing region at " << offset_ << " of size "
+               << size_;
   }
-  gpointer result = g_thread_join(thread_);
-  thread_ = nullptr;
-  TEST_AND_RETURN_FALSE(result == this);
-  return true;
 }
 
-gpointer ChunkProcessor::ReadAndCompressThread(gpointer data) {
-  return reinterpret_cast<ChunkProcessor*>(data)->ReadAndCompress() ?
-      data : nullptr;
-}
-
-bool ChunkProcessor::ReadAndCompress() {
+bool ChunkProcessor::ProcessChunk() {
+  chromeos::Blob buffer_in_(size_);
+  chromeos::Blob op_blob;
   ssize_t bytes_read = -1;
   TEST_AND_RETURN_FALSE(utils::PReadAll(fd_,
                                         buffer_in_.data(),
                                         buffer_in_.size(),
                                         offset_,
                                         &bytes_read));
-  TEST_AND_RETURN_FALSE(bytes_read == static_cast<ssize_t>(buffer_in_.size()));
-  TEST_AND_RETURN_FALSE(BzipCompress(buffer_in_, &buffer_compressed_));
+  TEST_AND_RETURN_FALSE(bytes_read == static_cast<ssize_t>(size_));
+  TEST_AND_RETURN_FALSE(BzipCompress(buffer_in_, &op_blob));
+
+  InstallOperation_Type op_type = InstallOperation::REPLACE_BZ;
+
+  if (op_blob.size() >= buffer_in_.size()) {
+    // A REPLACE is cheaper than a REPLACE_BZ in this case.
+    op_blob = std::move(buffer_in_);
+    op_type = InstallOperation::REPLACE;
+  }
+
+  off_t op_offset = blob_file_->StoreBlob(op_blob);
+  TEST_AND_RETURN_FALSE(op_offset >= 0);
+  aop_->op.set_data_offset(op_offset);
+  aop_->op.set_data_length(op_blob.size());
+  aop_->op.set_type(op_type);
   return true;
 }
 
@@ -112,101 +112,106 @@
 
 bool FullUpdateGenerator::GenerateOperations(
     const PayloadGenerationConfig& config,
-    int fd,
-    off_t* data_file_size,
+    BlobFileWriter* blob_file,
     vector<AnnotatedOperation>* rootfs_ops,
     vector<AnnotatedOperation>* kernel_ops) {
   TEST_AND_RETURN_FALSE(config.Validate());
-  rootfs_ops->clear();
-  kernel_ops->clear();
 
   // FullUpdateGenerator requires a positive chunk_size, otherwise there will
   // be only one operation with the whole partition which should not be allowed.
-  size_t full_chunk_size = kDefaultFullChunkSize;
-  if (config.chunk_size >= 0) {
-    full_chunk_size = config.chunk_size;
+  // For performance reasons, we force a small default hard limit of 1 MiB. This
+  // limit can be changed in the config, and we will use the smaller of the two
+  // soft/hard limits.
+  size_t full_chunk_size;
+  if (config.hard_chunk_size >= 0) {
+    full_chunk_size = std::min(static_cast<size_t>(config.hard_chunk_size),
+                               config.soft_chunk_size);
   } else {
+    full_chunk_size = std::min(kDefaultFullChunkSize, config.soft_chunk_size);
     LOG(INFO) << "No chunk_size provided, using the default chunk_size for the "
               << "full operations: " << full_chunk_size << " bytes.";
   }
   TEST_AND_RETURN_FALSE(full_chunk_size > 0);
   TEST_AND_RETURN_FALSE(full_chunk_size % config.block_size == 0);
 
+  TEST_AND_RETURN_FALSE(GenerateOperationsForPartition(
+      config.target.rootfs,
+      config.block_size,
+      full_chunk_size / config.block_size,
+      blob_file,
+      rootfs_ops));
+  TEST_AND_RETURN_FALSE(GenerateOperationsForPartition(
+      config.target.kernel,
+      config.block_size,
+      full_chunk_size / config.block_size,
+      blob_file,
+      kernel_ops));
+  return true;
+}
+
+bool FullUpdateGenerator::GenerateOperationsForPartition(
+    const PartitionConfig& new_part,
+    size_t block_size,
+    size_t chunk_blocks,
+    BlobFileWriter* blob_file,
+    vector<AnnotatedOperation>* aops) {
   size_t max_threads = std::max(sysconf(_SC_NPROCESSORS_ONLN), 4L);
-  LOG(INFO) << "Max threads: " << max_threads;
+  LOG(INFO) << "Compressing partition " << PartitionNameString(new_part.name)
+            << " from " << new_part.path << " splitting in chunks of "
+            << chunk_blocks << " blocks (" << block_size
+            << " bytes each) using " << max_threads << " threads";
 
-  const PartitionConfig* partitions[] = {
-      &config.target.rootfs,
-      &config.target.kernel};
+  int in_fd = open(new_part.path.c_str(), O_RDONLY, 0);
+  TEST_AND_RETURN_FALSE(in_fd >= 0);
+  ScopedFdCloser in_fd_closer(&in_fd);
 
-  for (int part_id = 0; part_id < 2; ++part_id) {
-    const PartitionConfig* partition = partitions[part_id];
-    LOG(INFO) << "compressing " << partition->path;
-    int in_fd = open(partition->path.c_str(), O_RDONLY, 0);
-    TEST_AND_RETURN_FALSE(in_fd >= 0);
-    ScopedFdCloser in_fd_closer(&in_fd);
-    deque<shared_ptr<ChunkProcessor>> threads;
-    int last_progress_update = INT_MIN;
-    size_t bytes_left = partition->size, counter = 0, offset = 0;
-    while (bytes_left > 0 || !threads.empty()) {
-      // Check and start new chunk processors if possible.
-      while (threads.size() < max_threads && bytes_left > 0) {
-        size_t this_chunk_bytes = std::min(bytes_left, full_chunk_size);
-        shared_ptr<ChunkProcessor> processor(
-            new ChunkProcessor(in_fd, offset, this_chunk_bytes));
-        threads.push_back(processor);
-        TEST_AND_RETURN_FALSE(processor->Start());
-        bytes_left -= this_chunk_bytes;
-        offset += this_chunk_bytes;
-      }
+  // We potentially have all the ChunkProcessors in memory but only
+  // |max_threads| will actually hold a block in memory while we process.
+  size_t partition_blocks = new_part.size / block_size;
+  size_t num_chunks = (partition_blocks + chunk_blocks - 1) / chunk_blocks;
+  aops->resize(num_chunks);
+  vector<ChunkProcessor> chunk_processors;
+  chunk_processors.reserve(num_chunks);
+  blob_file->SetTotalBlobs(num_chunks);
 
-      // Need to wait for a chunk processor to complete and process its output
-      // before spawning new processors.
-      shared_ptr<ChunkProcessor> processor = threads.front();
-      threads.pop_front();
-      TEST_AND_RETURN_FALSE(processor->Wait());
+  const string part_name_str = PartitionNameString(new_part.name);
 
-      DeltaArchiveManifest_InstallOperation* op = nullptr;
-      if (part_id == 0) {
-        rootfs_ops->emplace_back();
-        rootfs_ops->back().name =
-            base::StringPrintf("<rootfs-operation-%" PRIuS ">", counter++);
-        op = &rootfs_ops->back().op;
-      } else {
-        kernel_ops->emplace_back();
-        kernel_ops->back().name =
-            base::StringPrintf("<kernel-operation-%" PRIuS ">", counter++);
-        op = &kernel_ops->back().op;
-      }
+  for (size_t i = 0; i < num_chunks; ++i) {
+    size_t start_block = i * chunk_blocks;
+    // The last chunk could be smaller.
+    size_t num_blocks = std::min(chunk_blocks,
+                                 partition_blocks - i * chunk_blocks);
 
-      const bool compress = processor->ShouldCompress();
-      const chromeos::Blob& use_buf =
-          compress ? processor->buffer_compressed() : processor->buffer_in();
-      op->set_type(compress ?
-                   DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ :
-                   DeltaArchiveManifest_InstallOperation_Type_REPLACE);
-      op->set_data_offset(*data_file_size);
-      TEST_AND_RETURN_FALSE(utils::WriteAll(fd, use_buf.data(),
-                                            use_buf.size()));
-      *data_file_size += use_buf.size();
-      op->set_data_length(use_buf.size());
-      Extent* dst_extent = op->add_dst_extents();
-      dst_extent->set_start_block(processor->offset() / config.block_size);
-      dst_extent->set_num_blocks(
-          processor->buffer_in().size() / config.block_size);
+    // Preset all the static information about the operations. The
+    // ChunkProcessor will set the rest.
+    AnnotatedOperation* aop = aops->data() + i;
+    aop->name = base::StringPrintf("<%s-operation-%" PRIuS ">",
+                                   part_name_str.c_str(), i);
+    Extent* dst_extent = aop->op.add_dst_extents();
+    dst_extent->set_start_block(start_block);
+    dst_extent->set_num_blocks(num_blocks);
 
-      int progress = static_cast<int>(
-          (processor->offset() + processor->buffer_in().size()) * 100.0 /
-          partition->size);
-      if (last_progress_update < progress &&
-          (last_progress_update + 10 <= progress || progress == 100)) {
-        LOG(INFO) << progress << "% complete (output size: "
-                  << *data_file_size << ")";
-        last_progress_update = progress;
-      }
-    }
+    chunk_processors.emplace_back(
+        in_fd,
+        static_cast<off_t>(start_block) * block_size,
+        num_blocks * block_size,
+        blob_file,
+        aop);
   }
 
+  // Thread pool used for worker threads.
+  base::DelegateSimpleThreadPool thread_pool("full-update-generator",
+                                             max_threads);
+  thread_pool.Start();
+  for (ChunkProcessor& processor : chunk_processors)
+    thread_pool.AddWork(&processor);
+  thread_pool.JoinAll();
+  // All the operations must have a type set at this point. Otherwise, a
+  // ChunkProcessor failed to complete.
+  for (const AnnotatedOperation& aop : *aops) {
+    if (!aop.op.has_type())
+      return false;
+  }
   return true;
 }
 
diff --git a/payload_generator/full_update_generator.h b/payload_generator/full_update_generator.h
index b4d1135..4d75b22 100644
--- a/payload_generator/full_update_generator.h
+++ b/payload_generator/full_update_generator.h
@@ -10,6 +10,7 @@
 
 #include <base/macros.h>
 
+#include "update_engine/payload_generator/blob_file_writer.h"
 #include "update_engine/payload_generator/operations_generator.h"
 #include "update_engine/payload_generator/payload_generation_config.h"
 
@@ -19,6 +20,7 @@
  public:
   FullUpdateGenerator() = default;
 
+  // OperationsGenerator override.
   // Creates a full update for the target image defined in |config|. |config|
   // must be a valid payload generation configuration for a full payload.
   // Populates |rootfs_ops| and |kernel_ops|, with data about the update
@@ -26,11 +28,24 @@
   // |data_file_size| as it does.
   bool GenerateOperations(
       const PayloadGenerationConfig& config,
-      int data_file_fd,
-      off_t* data_file_size,
+      BlobFileWriter* blob_file,
       std::vector<AnnotatedOperation>* rootfs_ops,
       std::vector<AnnotatedOperation>* kernel_ops) override;
 
+  // Generates the list of operations to update inplace from the partition
+  // |old_part| to |new_part|. The |partition_size| should be at least
+  // |new_part.size| and any extra space there could be used as scratch space.
+  // The operations generated will not write more than |chunk_blocks| blocks.
+  // The new operations will create blobs in |data_file_fd| and update
+  // the file size pointed by |data_file_size| if needed.
+  // On success, stores the new operations in |aops| and returns true.
+  static bool GenerateOperationsForPartition(
+      const PartitionConfig& new_part,
+      size_t block_size,
+      size_t chunk_blocks,
+      BlobFileWriter* blob_file,
+      std::vector<AnnotatedOperation>* aops);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(FullUpdateGenerator);
 };
diff --git a/payload_generator/full_update_generator_unittest.cc b/payload_generator/full_update_generator_unittest.cc
index 0a04e4e..8c47ef6 100644
--- a/payload_generator/full_update_generator_unittest.cc
+++ b/payload_generator/full_update_generator_unittest.cc
@@ -24,7 +24,7 @@
   void SetUp() override {
     config_.is_delta = false;
     config_.minor_version = DeltaPerformer::kFullPayloadMinorVersion;
-    config_.chunk_size = 128 * 1024;
+    config_.hard_chunk_size = 128 * 1024;
     config_.block_size = 4096;
 
     EXPECT_TRUE(utils::MakeTempFile("FullUpdateTest_rootfs.XXXXXX",
@@ -37,6 +37,7 @@
                                     &out_blobs_path_,
                                     &out_blobs_fd_));
 
+    blob_file_.reset(new BlobFileWriter(out_blobs_fd_, &out_blobs_length_));
     rootfs_part_unlinker_.reset(
         new ScopedPathUnlinker(config_.target.rootfs.path));
     kernel_part_unlinker_.reset(
@@ -49,8 +50,10 @@
   // Output file holding the payload blobs.
   string out_blobs_path_;
   int out_blobs_fd_{-1};
+  off_t out_blobs_length_{0};
   ScopedFdCloser out_blobs_fd_closer_{&out_blobs_fd_};
 
+  std::unique_ptr<BlobFileWriter> blob_file_;
   std::unique_ptr<ScopedPathUnlinker> rootfs_part_unlinker_;
   std::unique_ptr<ScopedPathUnlinker> kernel_part_unlinker_;
   std::unique_ptr<ScopedPathUnlinker> out_blobs_unlinker_;
@@ -75,29 +78,25 @@
   EXPECT_TRUE(test_utils::WriteFileVector(config_.target.kernel.path,
                                           new_kern));
 
-  off_t out_blobs_length = 0;
   vector<AnnotatedOperation> rootfs_ops;
   vector<AnnotatedOperation> kernel_ops;
 
   EXPECT_TRUE(generator_.GenerateOperations(config_,
-                                            out_blobs_fd_,
-                                            &out_blobs_length,
+                                            blob_file_.get(),
                                             &rootfs_ops,
                                             &kernel_ops));
   int64_t target_rootfs_chunks =
-      config_.target.rootfs.size / config_.chunk_size;
+      config_.target.rootfs.size / config_.hard_chunk_size;
   EXPECT_EQ(target_rootfs_chunks, rootfs_ops.size());
-  EXPECT_EQ(new_kern.size() / config_.chunk_size, kernel_ops.size());
+  EXPECT_EQ(new_kern.size() / config_.hard_chunk_size, kernel_ops.size());
   for (off_t i = 0; i < target_rootfs_chunks; ++i) {
     EXPECT_EQ(1, rootfs_ops[i].op.dst_extents_size());
-    EXPECT_EQ(i * config_.chunk_size / config_.block_size,
+    EXPECT_EQ(i * config_.hard_chunk_size / config_.block_size,
               rootfs_ops[i].op.dst_extents(0).start_block()) << "i = " << i;
-    EXPECT_EQ(config_.chunk_size / config_.block_size,
+    EXPECT_EQ(config_.hard_chunk_size / config_.block_size,
               rootfs_ops[i].op.dst_extents(0).num_blocks());
-    if (rootfs_ops[i].op.type() !=
-        DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
-      EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ,
-                rootfs_ops[i].op.type());
+    if (rootfs_ops[i].op.type() != InstallOperation::REPLACE) {
+      EXPECT_EQ(InstallOperation::REPLACE_BZ, rootfs_ops[i].op.type());
     }
   }
 }
@@ -105,7 +104,8 @@
 // Test that if the chunk size is not a divisor of the image size, it handles
 // correctly the last chunk of each partition.
 TEST_F(FullUpdateGeneratorTest, ChunkSizeTooBig) {
-  config_.chunk_size = 1024 * 1024;
+  config_.hard_chunk_size = 1024 * 1024;
+  config_.soft_chunk_size = config_.hard_chunk_size;
   chromeos::Blob new_root(1536 * 1024);  // 1.5 MiB
   chromeos::Blob new_kern(128 * 1024);
   config_.rootfs_partition_size = new_root.size();
@@ -117,20 +117,18 @@
   EXPECT_TRUE(test_utils::WriteFileVector(config_.target.kernel.path,
                                           new_kern));
 
-  off_t out_blobs_length = 0;
   vector<AnnotatedOperation> rootfs_ops;
   vector<AnnotatedOperation> kernel_ops;
 
   EXPECT_TRUE(generator_.GenerateOperations(config_,
-                                            out_blobs_fd_,
-                                            &out_blobs_length,
+                                            blob_file_.get(),
                                             &rootfs_ops,
                                             &kernel_ops));
   // rootfs has one chunk and a half.
   EXPECT_EQ(2, rootfs_ops.size());
-  EXPECT_EQ(config_.chunk_size / config_.block_size,
+  EXPECT_EQ(config_.hard_chunk_size / config_.block_size,
             BlocksInExtents(rootfs_ops[0].op.dst_extents()));
-  EXPECT_EQ((new_root.size() - config_.chunk_size) / config_.block_size,
+  EXPECT_EQ((new_root.size() - config_.hard_chunk_size) / config_.block_size,
             BlocksInExtents(rootfs_ops[1].op.dst_extents()));
 
   // kernel has less than one chunk.
diff --git a/payload_generator/generate_delta_main.cc b/payload_generator/generate_delta_main.cc
index 170a612..ecdadc7 100644
--- a/payload_generator/generate_delta_main.cc
+++ b/payload_generator/generate_delta_main.cc
@@ -16,7 +16,6 @@
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/string_split.h>
 #include <chromeos/flag_helper.h>
-#include <glib.h>
 
 #include "update_engine/delta_performer.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
@@ -25,7 +24,6 @@
 #include "update_engine/payload_generator/payload_signer.h"
 #include "update_engine/payload_verifier.h"
 #include "update_engine/prefs.h"
-#include "update_engine/subprocess.h"
 #include "update_engine/terminator.h"
 #include "update_engine/update_metadata.pb.h"
 #include "update_engine/utils.h"
@@ -217,12 +215,6 @@
 }
 
 int Main(int argc, char** argv) {
-  DEFINE_string(old_dir, "",
-                "[DEPRECATED] Directory where the old rootfs is loop mounted "
-                "read-only. Not required anymore.");
-  DEFINE_string(new_dir, "",
-                "[DEPRECATED] Directory where the new rootfs is loop mounted "
-                "read-only. Not required anymore.");
   DEFINE_string(old_image, "", "Path to the old rootfs");
   DEFINE_string(new_image, "", "Path to the new rootfs");
   DEFINE_string(old_kernel, "", "Path to the old kernel partition image");
@@ -252,7 +244,8 @@
                 "e.g. /path/to/sig:/path/to/next:/path/to/last_sig . Each "
                 "signature will be assigned a client version, starting from "
                 "kSignatureOriginalVersion.");
-  DEFINE_int32(chunk_size, -1, "Payload chunk size (-1 -- no limit/default)");
+  DEFINE_int32(chunk_size, 200 * 1024 * 1024,
+               "Payload chunk size (-1 for whole files)");
   DEFINE_uint64(rootfs_partition_size,
                chromeos_update_engine::kRootFSPartitionSize,
                "RootFS partition size for the image once installed");
@@ -302,24 +295,15 @@
       "image is provided. It also provides debugging options to apply, sign\n"
       "and verify payloads.");
   Terminator::Init();
-  Subprocess::Init();
 
   logging::LoggingSettings log_settings;
   log_settings.log_file     = "delta_generator.log";
   log_settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
-  log_settings.lock_log     = logging::DONT_LOCK_LOG_FILE;
+  log_settings.lock_log     = logging::LOCK_LOG_FILE;
   log_settings.delete_old   = logging::APPEND_TO_OLD_LOG_FILE;
 
   logging::InitLogging(log_settings);
 
-  // Check flags.
-  if (!FLAGS_old_dir.empty()) {
-    LOG(INFO) << "--old_dir flag is deprecated and ignored.";
-  }
-  if (!FLAGS_new_dir.empty()) {
-    LOG(INFO) << "--new_dir flag is deprecated and ignored.";
-  }
-
   vector<int> signature_sizes;
   ParseSignatureSizes(FLAGS_signature_size, &signature_sizes);
 
@@ -359,7 +343,8 @@
   payload_config.target.rootfs.path = FLAGS_new_image;
   payload_config.target.kernel.path = FLAGS_new_kernel;
 
-  payload_config.chunk_size = FLAGS_chunk_size;
+  // Use the default soft_chunk_size defined in the config.
+  payload_config.hard_chunk_size = FLAGS_chunk_size;
   payload_config.block_size = kBlockSize;
 
   // The kernel and rootfs size is never passed to the delta_generator, so we
diff --git a/payload_generator/graph_types.h b/payload_generator/graph_types.h
index 7b20bb5..16572b4 100644
--- a/payload_generator/graph_types.h
+++ b/payload_generator/graph_types.h
@@ -13,6 +13,7 @@
 
 #include <base/macros.h>
 
+#include "update_engine/payload_generator/annotated_operation.h"
 #include "update_engine/payload_generator/extent_utils.h"
 #include "update_engine/update_metadata.pb.h"
 
@@ -59,8 +60,7 @@
   std::vector<Vertex>::size_type lowlink;
 
   // Other Vertex properties:
-  DeltaArchiveManifest_InstallOperation op;
-  std::string file_name;
+  AnnotatedOperation aop;
 
   typedef std::vector<Vertex>::size_type Index;
   static const Vertex::Index kInvalidIndex;
diff --git a/payload_generator/graph_utils.cc b/payload_generator/graph_utils.cc
index 9166560..265177f 100644
--- a/payload_generator/graph_utils.cc
+++ b/payload_generator/graph_utils.cc
@@ -114,12 +114,12 @@
   for (Graph::size_type i = 0, e = graph.size(); i != e; ++i) {
     LOG(INFO) << i
               << (graph[i].valid ? "" : "-INV")
-              << ": " << graph[i].file_name
-              << ": " << InstallOperationTypeName(graph[i].op.type());
+              << ": " << graph[i].aop.name
+              << ": " << InstallOperationTypeName(graph[i].aop.op.type());
     LOG(INFO) << "  src_extents:";
-    DumpExtents(graph[i].op.src_extents(), 4);
+    DumpExtents(graph[i].aop.op.src_extents(), 4);
     LOG(INFO) << "  dst_extents:";
-    DumpExtents(graph[i].op.dst_extents(), 4);
+    DumpExtents(graph[i].aop.op.dst_extents(), 4);
     LOG(INFO) << "  out edges:";
     DumpOutEdges(graph[i].out_edges);
   }
diff --git a/payload_generator/inplace_generator.cc b/payload_generator/inplace_generator.cc
index 2e0dbaf..f2d8a11 100644
--- a/payload_generator/inplace_generator.cc
+++ b/payload_generator/inplace_generator.cc
@@ -27,13 +27,14 @@
 using std::pair;
 using std::set;
 using std::string;
-using std::unique_ptr;
 using std::vector;
 
 namespace chromeos_update_engine {
 
 using Block = InplaceGenerator::Block;
 
+namespace {
+
 // This class allocates non-existent temp blocks, starting from
 // kTempBlockStart. Other code is responsible for converting these
 // temp blocks into real blocks, as the client can't read or write to
@@ -62,9 +63,28 @@
   return new_extents;
 }
 
+// Helper class to compare two operations by start block of the first Extent in
+// their destination extents given the index of the operations in the graph.
+class IndexedInstallOperationsDstComparator {
+ public:
+  explicit IndexedInstallOperationsDstComparator(Graph* graph)
+    : graph_(graph) {}
+
+  // Compares the operations in the vertex a and b of graph_.
+  bool operator()(size_t a, size_t b) const {
+    return diff_utils::CompareAopsByDestination((*graph_)[a].aop,
+                                                (*graph_)[b].aop);
+  }
+
+ private:
+  const Graph* const graph_;
+};
+
+}  // namespace
+
 void InplaceGenerator::CheckGraph(const Graph& graph) {
   for (const Vertex& v : graph) {
-    CHECK(v.op.has_type());
+    CHECK(v.aop.op.has_type());
   }
 }
 
@@ -74,7 +94,7 @@
     const vector<Extent>& replace_extents) {
   // First, expand out the blocks that op reads from
   vector<uint64_t> read_blocks =
-      ExpandExtents(vertex->op.src_extents());
+      ExpandExtents(vertex->aop.op.src_extents());
   {
     // Expand remove_extents and replace_extents
     vector<uint64_t> remove_extents_expanded = ExpandExtents(remove_extents);
@@ -95,9 +115,9 @@
     }
   }
   // Convert read_blocks back to extents
-  vertex->op.clear_src_extents();
+  vertex->aop.op.clear_src_extents();
   vector<Extent> new_extents = CompressExtents(read_blocks);
-  StoreExtents(new_extents, vertex->op.mutable_src_extents());
+  StoreExtents(new_extents, vertex->aop.op.mutable_src_extents());
 }
 
 bool InplaceGenerator::CutEdges(Graph* graph,
@@ -136,14 +156,14 @@
                                                     cut_edge_properties));
 
     // Set src/dst extents and other proto variables for copy operation
-    graph->back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+    graph->back().aop.op.set_type(InstallOperation::MOVE);
     StoreExtents(cut_edge_properties.extents,
-                 graph->back().op.mutable_src_extents());
+                 graph->back().aop.op.mutable_src_extents());
     StoreExtents(cuts.back().tmp_extents,
-                 graph->back().op.mutable_dst_extents());
-    graph->back().op.set_src_length(
+                 graph->back().aop.op.mutable_dst_extents());
+    graph->back().aop.op.set_src_length(
         graph_utils::EdgeWeight(*graph, edge) * kBlockSize);
-    graph->back().op.set_dst_length(graph->back().op.src_length());
+    graph->back().aop.op.set_dst_length(graph->back().aop.op.src_length());
 
     // make the dest node read from the scratch space
     SubstituteBlocks(
@@ -235,24 +255,26 @@
   sort(cuts->begin(), cuts->end(), less);
 }
 
-void InplaceGenerator::MoveFullOpsToBack(Graph* graph,
-                                         vector<Vertex::Index>* op_indexes) {
+void InplaceGenerator::MoveAndSortFullOpsToBack(
+    Graph* graph,
+    vector<Vertex::Index>* op_indexes) {
   vector<Vertex::Index> ret;
   vector<Vertex::Index> full_ops;
   ret.reserve(op_indexes->size());
-  for (vector<Vertex::Index>::size_type i = 0, e = op_indexes->size(); i != e;
-       ++i) {
-    DeltaArchiveManifest_InstallOperation_Type type =
-        (*graph)[(*op_indexes)[i]].op.type();
-    if (type == DeltaArchiveManifest_InstallOperation_Type_REPLACE ||
-        type == DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ) {
-      full_ops.push_back((*op_indexes)[i]);
+  for (auto op_index : *op_indexes) {
+    InstallOperation_Type type = (*graph)[op_index].aop.op.type();
+    if (type == InstallOperation::REPLACE ||
+        type == InstallOperation::REPLACE_BZ) {
+      full_ops.push_back(op_index);
     } else {
-      ret.push_back((*op_indexes)[i]);
+      ret.push_back(op_index);
     }
   }
   LOG(INFO) << "Stats: " << full_ops.size() << " full ops out of "
             << (full_ops.size() + ret.size()) << " total ops.";
+  // Sort full ops according to their dst_extents.
+  sort(full_ops.begin(), full_ops.end(),
+       IndexedInstallOperationsDstComparator(graph));
   ret.insert(ret.end(), full_ops.begin(), full_ops.end());
   op_indexes->swap(ret);
 }
@@ -285,8 +307,7 @@
 bool ConvertCutsToFull(
     Graph* graph,
     const string& new_part,
-    int data_fd,
-    off_t* data_file_size,
+    BlobFileWriter* blob_file,
     vector<Vertex::Index>* op_indexes,
     vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
     const vector<CutEdgeVertexes>& cuts) {
@@ -297,8 +318,7 @@
         graph,
         cut,
         new_part,
-        data_fd,
-        data_file_size));
+        blob_file));
     deleted_nodes.insert(cut.new_vertex);
   }
   deleted_nodes.insert(cuts[0].old_dst);
@@ -325,8 +345,7 @@
 bool AssignBlockForAdjoiningCuts(
     Graph* graph,
     const string& new_part,
-    int data_fd,
-    off_t* data_file_size,
+    BlobFileWriter* blob_file,
     vector<Vertex::Index>* op_indexes,
     vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
     const vector<CutEdgeVertexes>& cuts) {
@@ -357,10 +376,10 @@
       continue;
     // See if this node has sufficient blocks
     ExtentRanges ranges;
-    ranges.AddRepeatedExtents((*graph)[test_node].op.dst_extents());
+    ranges.AddRepeatedExtents((*graph)[test_node].aop.op.dst_extents());
     ranges.SubtractExtent(ExtentForRange(
         kTempBlockStart, kSparseHole - kTempBlockStart));
-    ranges.SubtractRepeatedExtents((*graph)[test_node].op.src_extents());
+    ranges.SubtractRepeatedExtents((*graph)[test_node].aop.op.src_extents());
     // For now, for simplicity, subtract out all blocks in read-before
     // dependencies.
     for (Vertex::EdgeMap::const_iterator edge_i =
@@ -369,6 +388,14 @@
          edge_i != edge_e; ++edge_i) {
       ranges.SubtractExtents(edge_i->second.extents);
     }
+
+    // Prevent using the block 0 as scratch space due to crbug.com/480751.
+    if (ranges.ContainsBlock(0)) {
+      LOG(INFO) << "Removing block 0 from the selected scratch range in vertex "
+                << i;
+      ranges.SubtractBlock(0);
+    }
+
     if (ranges.blocks() == 0)
       continue;
 
@@ -389,8 +416,7 @@
     LOG(INFO) << "Unable to find sufficient scratch";
     TEST_AND_RETURN_FALSE(ConvertCutsToFull(graph,
                                             new_part,
-                                            data_fd,
-                                            data_file_size,
+                                            blob_file,
                                             op_indexes,
                                             reverse_op_indexes,
                                             cuts));
@@ -423,7 +449,7 @@
     // Fix the new node w/ the real blocks. Since the new node is just a
     // copy operation, we can replace all the dest extents w/ the real
     // blocks.
-    DeltaArchiveManifest_InstallOperation *op = &(*graph)[cut.new_vertex].op;
+    InstallOperation* op = &(*graph)[cut.new_vertex].aop.op;
     op->clear_dst_extents();
     StoreExtents(real_extents, op->mutable_dst_extents());
   }
@@ -435,8 +461,7 @@
 bool InplaceGenerator::AssignTempBlocks(
     Graph* graph,
     const string& new_part,
-    int data_fd,
-    off_t* data_file_size,
+    BlobFileWriter* blob_file,
     vector<Vertex::Index>* op_indexes,
     vector<vector<Vertex::Index>::size_type>* reverse_op_indexes,
     const vector<CutEdgeVertexes>& cuts) {
@@ -450,7 +475,7 @@
     LOG(INFO) << "Fixing temp blocks in cut " << i
               << ": old dst: " << cuts[i].old_dst << " new vertex: "
               << cuts[i].new_vertex << " path: "
-              << (*graph)[cuts[i].old_dst].file_name;
+              << (*graph)[cuts[i].old_dst].aop.name;
 
     if (cuts_group.empty() || (cuts_group[0].old_dst == cuts[i].old_dst)) {
       cuts_group.push_back(cuts[i]);
@@ -458,8 +483,7 @@
       CHECK(!cuts_group.empty());
       TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(graph,
                                                         new_part,
-                                                        data_fd,
-                                                        data_file_size,
+                                                        blob_file,
                                                         op_indexes,
                                                         reverse_op_indexes,
                                                         cuts_group));
@@ -475,8 +499,7 @@
   CHECK(!cuts_group.empty());
   TEST_AND_RETURN_FALSE(AssignBlockForAdjoiningCuts(graph,
                                                     new_part,
-                                                    data_fd,
-                                                    data_file_size,
+                                                    blob_file,
                                                     op_indexes,
                                                     reverse_op_indexes,
                                                     cuts_group));
@@ -489,7 +512,7 @@
        ++it, ++idx) {
     if (!it->valid)
       continue;
-    const DeltaArchiveManifest_InstallOperation& op = it->op;
+    const InstallOperation& op = it->aop.op;
     if (TempBlocksExistInExtents(op.dst_extents()) ||
         TempBlocksExistInExtents(op.src_extents())) {
       LOG(INFO) << "bad extents in node " << idx;
@@ -513,15 +536,12 @@
 bool InplaceGenerator::ConvertCutToFullOp(Graph* graph,
                                           const CutEdgeVertexes& cut,
                                           const string& new_part,
-                                          int data_fd,
-                                          off_t* data_file_size) {
+                                          BlobFileWriter* blob_file) {
   // Drop all incoming edges, keep all outgoing edges
 
   // Keep all outgoing edges
-  if ((*graph)[cut.old_dst].op.type() !=
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ &&
-      (*graph)[cut.old_dst].op.type() !=
-      DeltaArchiveManifest_InstallOperation_Type_REPLACE) {
+  if ((*graph)[cut.old_dst].aop.op.type() != InstallOperation::REPLACE_BZ &&
+      (*graph)[cut.old_dst].aop.op.type() != InstallOperation::REPLACE) {
     Vertex::EdgeMap out_edges = (*graph)[cut.old_dst].out_edges;
     graph_utils::DropWriteBeforeDeps(&out_edges);
 
@@ -529,7 +549,7 @@
     // |new_extents| list of blocks and update the graph.
     vector<AnnotatedOperation> new_aop;
     vector<Extent> new_extents;
-    ExtentsToVector((*graph)[cut.old_dst].op.dst_extents(),
+    ExtentsToVector((*graph)[cut.old_dst].aop.op.dst_extents(),
                     &new_extents);
     TEST_AND_RETURN_FALSE(diff_utils::DeltaReadFile(
         &new_aop,
@@ -537,10 +557,9 @@
         new_part,
         vector<Extent>(),  // old_extents
         new_extents,
-        (*graph)[cut.old_dst].file_name,
+        (*graph)[cut.old_dst].aop.name,
         -1,  // chunk_blocks, forces to have a single operation.
-        data_fd,
-        data_file_size,
+        blob_file,
         false));  // src_ops_allowed
     TEST_AND_RETURN_FALSE(new_aop.size() == 1);
     TEST_AND_RETURN_FALSE(AddInstallOpToGraph(
@@ -564,8 +583,7 @@
 
 bool InplaceGenerator::ConvertGraphToDag(Graph* graph,
                                          const string& new_part,
-                                         int fd,
-                                         off_t* data_file_size,
+                                         BlobFileWriter* blob_file,
                                          vector<Vertex::Index>* final_order,
                                          Vertex::Index scratch_vertex) {
   CycleBreaker cycle_breaker;
@@ -590,7 +608,7 @@
   CheckGraph(*graph);
 
   LOG(INFO) << "Moving full ops to the back";
-  MoveFullOpsToBack(graph, final_order);
+  MoveAndSortFullOpsToBack(graph, final_order);
   LOG(INFO) << "done moving full ops to back";
 
   vector<vector<Vertex::Index>::size_type> inverse_final_order;
@@ -601,8 +619,7 @@
   if (!cuts.empty())
     TEST_AND_RETURN_FALSE(AssignTempBlocks(graph,
                                            new_part,
-                                           fd,
-                                           data_file_size,
+                                           blob_file,
                                            final_order,
                                            &inverse_final_order,
                                            cuts));
@@ -625,17 +642,17 @@
 void InplaceGenerator::CreateScratchNode(uint64_t start_block,
                                          uint64_t num_blocks,
                                          Vertex* vertex) {
-  vertex->file_name = "<scratch>";
-  vertex->op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
-  vertex->op.set_data_offset(0);
-  vertex->op.set_data_length(0);
-  Extent* extent = vertex->op.add_dst_extents();
+  vertex->aop.name = "<scratch>";
+  vertex->aop.op.set_type(InstallOperation::REPLACE_BZ);
+  vertex->aop.op.set_data_offset(0);
+  vertex->aop.op.set_data_length(0);
+  Extent* extent = vertex->aop.op.add_dst_extents();
   extent->set_start_block(start_block);
   extent->set_num_blocks(num_blocks);
 }
 
 bool InplaceGenerator::AddInstallOpToBlocksVector(
-    const DeltaArchiveManifest_InstallOperation& operation,
+    const InstallOperation& operation,
     const Graph& graph,
     Vertex::Index vertex,
     vector<Block>* blocks) {
@@ -661,9 +678,9 @@
           LOG(FATAL) << "Block " << block << " is already "
                      << past_participle << " by "
                      << (*blocks)[block].*access_type << "("
-                     << graph[(*blocks)[block].*access_type].file_name
+                     << graph[(*blocks)[block].*access_type].aop.name
                      << ") and also " << vertex << "("
-                     << graph[vertex].file_name << ")";
+                     << graph[vertex].aop.name << ")";
         }
         (*blocks)[block].*access_type = vertex;
       }
@@ -672,24 +689,23 @@
   return true;
 }
 
-bool InplaceGenerator::AddInstallOpToGraph(
-    Graph* graph,
-    Vertex::Index existing_vertex,
-    vector<Block>* blocks,
-    const DeltaArchiveManifest_InstallOperation operation,
-    const string& op_name) {
+bool InplaceGenerator::AddInstallOpToGraph(Graph* graph,
+                                           Vertex::Index existing_vertex,
+                                           vector<Block>* blocks,
+                                           const InstallOperation operation,
+                                           const string& op_name) {
   Vertex::Index vertex = existing_vertex;
   if (vertex == Vertex::kInvalidIndex) {
     graph->emplace_back();
     vertex = graph->size() - 1;
   }
-  (*graph)[vertex].op = operation;
-  CHECK((*graph)[vertex].op.has_type());
-  (*graph)[vertex].file_name = op_name;
+  (*graph)[vertex].aop.op = operation;
+  CHECK((*graph)[vertex].aop.op.has_type());
+  (*graph)[vertex].aop.name = op_name;
 
   if (blocks)
     TEST_AND_RETURN_FALSE(InplaceGenerator::AddInstallOpToBlocksVector(
-        (*graph)[vertex].op,
+        (*graph)[vertex].aop.op,
         *graph,
         vertex,
         blocks));
@@ -705,62 +721,31 @@
   }
 }
 
-bool InplaceGenerator::GenerateOperations(
-    const PayloadGenerationConfig& config,
-    int data_file_fd,
-    off_t* data_file_size,
-    vector<AnnotatedOperation>* rootfs_ops,
-    vector<AnnotatedOperation>* kernel_ops) {
-  off_t chunk_blocks = (config.chunk_size == -1 ? -1 :
-                        config.chunk_size / config.block_size);
-
-  // Temporary list of operations used to construct the dependency graph.
-  vector<AnnotatedOperation> aops;
-  TEST_AND_RETURN_FALSE(
-      diff_utils::DeltaReadPartition(&aops,
-                                     config.source.rootfs,
-                                     config.target.rootfs,
-                                     chunk_blocks,
-                                     data_file_fd,
-                                     data_file_size,
-                                     true,  // skip_block_0
-                                     false));  // src_ops_allowed
-  // Convert the rootfs operations to the graph.
+bool InplaceGenerator::ResolveReadAfterWriteDependencies(
+    const PartitionConfig& new_part,
+    uint64_t partition_size,
+    size_t block_size,
+    BlobFileWriter* blob_file,
+    vector<AnnotatedOperation>* aops) {
+  // Convert the operations to the graph.
   Graph graph;
   CheckGraph(graph);
-  vector<Block> blocks(config.target.rootfs.size / config.block_size);
-  for (const auto& aop : aops) {
+  vector<Block> blocks(new_part.size / block_size);
+  for (const auto& aop : *aops) {
     AddInstallOpToGraph(
         &graph, Vertex::kInvalidIndex, &blocks, aop.op, aop.name);
   }
-  LOG(INFO) << "done reading normal files";
   CheckGraph(graph);
 
   // Final scratch block (if there's space)
   Vertex::Index scratch_vertex = Vertex::kInvalidIndex;
-  if (blocks.size() < (config.rootfs_partition_size / kBlockSize)) {
+  if (blocks.size() < (partition_size / block_size)) {
     scratch_vertex = graph.size();
     graph.emplace_back();
-    CreateScratchNode(
-        blocks.size(),
-        (config.rootfs_partition_size / kBlockSize) - blocks.size(),
-        &graph.back());
+    size_t scratch_blocks = (partition_size / block_size) - blocks.size();
+    LOG(INFO) << "Added " << scratch_blocks << " scratch space blocks.";
+    CreateScratchNode(blocks.size(), scratch_blocks, &graph.back());
   }
-
-  // Read kernel partition
-  LOG(INFO) << "Delta compressing kernel partition...";
-  // It is safe to not skip the block 0 since we will not be using the cycle
-  // breaking algorithm on this list of operations as we expect no cycles here.
-  TEST_AND_RETURN_FALSE(
-      diff_utils::DeltaReadPartition(kernel_ops,
-                                     config.source.kernel,
-                                     config.target.kernel,
-                                     chunk_blocks,
-                                     data_file_fd,
-                                     data_file_size,
-                                     false,  // skip_block_0
-                                     false));  // src_ops_allowed
-  LOG(INFO) << "done reading kernel";
   CheckGraph(graph);
 
   LOG(INFO) << "Creating edges...";
@@ -771,34 +756,81 @@
   vector<Vertex::Index> final_order;
   TEST_AND_RETURN_FALSE(ConvertGraphToDag(
       &graph,
-      config.target.rootfs.path,
-      data_file_fd,
-      data_file_size,
+      new_part.path,
+      blob_file,
       &final_order,
       scratch_vertex));
 
-  // Copy operations over to the rootfs_ops in the final_order generated by the
-  // topological sort.
-  rootfs_ops->clear();
+  // Copy operations over to the |aops| vector in the final_order generated by
+  // the topological sort.
+  aops->clear();
+  aops->reserve(final_order.size());
   for (const Vertex::Index vertex_index : final_order) {
     const Vertex& vertex = graph[vertex_index];
-    rootfs_ops->emplace_back();
-    rootfs_ops->back().op = vertex.op;
-    rootfs_ops->back().name = vertex.file_name;
+    aops->push_back(vertex.aop);
   }
+  return true;
+}
 
-  // Re-add the operation for the block 0.
-  TEST_AND_RETURN_FALSE(diff_utils::DeltaReadFile(
-      rootfs_ops,
-      config.source.rootfs.path,
-      config.target.rootfs.path,
-      vector<Extent>{ExtentForRange(0, 1)},
-      vector<Extent>{ExtentForRange(0, 1)},
-      "<block-0>",  // operation name
-      -1,  // chunk_blocks
-      data_file_fd,
-      data_file_size,
-      false));  // src_ops_allowed
+bool InplaceGenerator::GenerateOperationsForPartition(
+    const PartitionConfig& old_part,
+    const PartitionConfig& new_part,
+    uint64_t partition_size,
+    size_t block_size,
+    ssize_t hard_chunk_blocks,
+    size_t soft_chunk_blocks,
+    BlobFileWriter* blob_file,
+    vector<AnnotatedOperation>* aops) {
+  const string part_name = PartitionNameString(new_part.name);
+  LOG(INFO) << "Delta compressing " << part_name << " partition...";
+  TEST_AND_RETURN_FALSE(
+      diff_utils::DeltaReadPartition(aops,
+                                     old_part,
+                                     new_part,
+                                     hard_chunk_blocks,
+                                     soft_chunk_blocks,
+                                     blob_file,
+                                     false));  // src_ops_allowed
+  LOG(INFO) << "Done reading " << part_name;
+
+  TEST_AND_RETURN_FALSE(
+      ResolveReadAfterWriteDependencies(new_part,
+                                        partition_size,
+                                        block_size,
+                                        blob_file,
+                                        aops));
+  LOG(INFO) << "Done reordering " << part_name;
+  return true;
+}
+
+bool InplaceGenerator::GenerateOperations(
+    const PayloadGenerationConfig& config,
+    BlobFileWriter* blob_file,
+    vector<AnnotatedOperation>* rootfs_ops,
+    vector<AnnotatedOperation>* kernel_ops) {
+  ssize_t hard_chunk_blocks = (config.hard_chunk_size == -1 ? -1 :
+                               config.hard_chunk_size / config.block_size);
+  size_t soft_chunk_blocks = config.soft_chunk_size / config.block_size;
+
+  TEST_AND_RETURN_FALSE(GenerateOperationsForPartition(
+      config.source.rootfs,
+      config.target.rootfs,
+      config.rootfs_partition_size,
+      config.block_size,
+      hard_chunk_blocks,
+      soft_chunk_blocks,
+      blob_file,
+      rootfs_ops));
+
+  TEST_AND_RETURN_FALSE(GenerateOperationsForPartition(
+      config.source.kernel,
+      config.target.kernel,
+      config.target.kernel.size,  // kernel "filesystem" is the whole partition.
+      config.block_size,
+      hard_chunk_blocks,
+      soft_chunk_blocks,
+      blob_file,
+      kernel_ops));
 
   return true;
 }
diff --git a/payload_generator/inplace_generator.h b/payload_generator/inplace_generator.h
index 158e364..70e868c 100644
--- a/payload_generator/inplace_generator.h
+++ b/payload_generator/inplace_generator.h
@@ -10,6 +10,7 @@
 #include <string>
 #include <vector>
 
+#include "update_engine/payload_generator/blob_file_writer.h"
 #include "update_engine/payload_generator/delta_diff_generator.h"
 #include "update_engine/payload_generator/graph_types.h"
 #include "update_engine/payload_generator/operations_generator.h"
@@ -103,7 +104,7 @@
   // Given a topologically sorted graph |op_indexes| and |graph|, alters
   // |op_indexes| to move all the full operations to the end of the vector.
   // Full operations should not be depended on, so this is safe.
-  static void MoveFullOpsToBack(Graph* graph,
+  static void MoveAndSortFullOpsToBack(Graph* graph,
                                 std::vector<Vertex::Index>* op_indexes);
 
   // Returns true iff there are no extents in the graph that refer to temp
@@ -120,8 +121,7 @@
   static bool AssignTempBlocks(
       Graph* graph,
       const std::string& new_part,
-      int data_fd,
-      off_t* data_file_size,
+      BlobFileWriter* blob_file,
       std::vector<Vertex::Index>* op_indexes,
       std::vector<std::vector<Vertex::Index>::size_type>* reverse_op_indexes,
       const std::vector<CutEdgeVertexes>& cuts);
@@ -136,8 +136,7 @@
   static bool ConvertCutToFullOp(Graph* graph,
                                  const CutEdgeVertexes& cut,
                                  const std::string& new_part,
-                                 int data_fd,
-                                 off_t* data_file_size);
+                                 BlobFileWriter* blob_file);
 
   // Takes a graph, which is not a DAG, which represents the files just
   // read from disk, and converts it into a DAG by breaking all cycles
@@ -150,8 +149,7 @@
   // Returns true on success.
   static bool ConvertGraphToDag(Graph* graph,
                                 const std::string& new_part,
-                                int fd,
-                                off_t* data_file_size,
+                                BlobFileWriter* blob_file,
                                 std::vector<Vertex::Index>* final_order,
                                 Vertex::Index scratch_vertex);
 
@@ -170,23 +168,21 @@
   // in |blocks| and set the reader/writer field to the vertex passed.
   // |graph| is not strictly necessary, but useful for printing out
   // error messages.
-  static bool AddInstallOpToBlocksVector(
-      const DeltaArchiveManifest_InstallOperation& operation,
-      const Graph& graph,
-      Vertex::Index vertex,
-      std::vector<Block>* blocks);
+  static bool AddInstallOpToBlocksVector(const InstallOperation& operation,
+                                         const Graph& graph,
+                                         Vertex::Index vertex,
+                                         std::vector<Block>* blocks);
 
   // Add a vertex (if |existing_vertex| is kInvalidVertex) or update an
   // |existing_vertex| with the passed |operation|.
   // This method will also register the vertex as the reader or writer of the
   // blocks involved in the operation updating the |blocks| vector. The
   // |op_name| associated with the Vertex is used for logging purposes.
-  static bool AddInstallOpToGraph(
-      Graph* graph,
-      Vertex::Index existing_vertex,
-      std::vector<Block>* blocks,
-      const DeltaArchiveManifest_InstallOperation operation,
-      const std::string& op_name);
+  static bool AddInstallOpToGraph(Graph* graph,
+                                  Vertex::Index existing_vertex,
+                                  std::vector<Block>* blocks,
+                                  const InstallOperation operation,
+                                  const std::string& op_name);
 
   // Apply the transformation stored in |the_map| to the |collection| vector
   // replacing the map keys found in |collection| with its associated value in
@@ -194,6 +190,39 @@
   static void ApplyMap(std::vector<uint64_t>* collection,
                        const std::map<uint64_t, uint64_t>& the_map);
 
+  // Resolve all read-after-write dependencies in the operation list |aops|. The
+  // operations in |aops| are such that they generate the desired |new_part| if
+  // applied reading always from the original image. This function reorders the
+  // operations and generates new operations when needed to make these
+  // operations produce the same |new_part| result when applied in-place.
+  // The new operations will create blobs in |data_file_fd| and update
+  // the file size pointed by |data_file_size| if needed.
+  // On success, stores the new operations in |aops| in the right order and
+  // returns true.
+  static bool ResolveReadAfterWriteDependencies(
+      const PartitionConfig& new_part,
+      uint64_t partition_size,
+      size_t block_size,
+      BlobFileWriter* blob_file,
+      std::vector<AnnotatedOperation>* aops);
+
+  // Generates the list of operations to update inplace from the partition
+  // |old_part| to |new_part|. The |partition_size| should be at least
+  // |new_part.size| and any extra space there could be used as scratch space.
+  // The operations generated will not write more than |chunk_blocks| blocks.
+  // The new operations will create blobs in |data_file_fd| and update
+  // the file size pointed by |data_file_size| if needed.
+  // On success, stores the new operations in |aops| and returns true.
+  static bool GenerateOperationsForPartition(
+      const PartitionConfig& old_part,
+      const PartitionConfig& new_part,
+      uint64_t partition_size,
+      size_t block_size,
+      ssize_t hard_chunk_blocks,
+      size_t soft_chunk_blocks,
+      BlobFileWriter* blob_file,
+      std::vector<AnnotatedOperation>* aops);
+
   // Generate the update payload operations for the kernel and rootfs using
   // only operations that read from the target and/or write to the target,
   // hence, applying the payload "in-place" in the target partition. This method
@@ -207,8 +236,7 @@
   // |data_file_size|.
   bool GenerateOperations(
       const PayloadGenerationConfig& config,
-      int data_file_fd,
-      off_t* data_file_size,
+      BlobFileWriter* blob_file,
       std::vector<AnnotatedOperation>* rootfs_ops,
       std::vector<AnnotatedOperation>* kernel_ops) override;
 
diff --git a/payload_generator/inplace_generator_unittest.cc b/payload_generator/inplace_generator_unittest.cc
index 5de0df9..d938d0d 100644
--- a/payload_generator/inplace_generator_unittest.cc
+++ b/payload_generator/inplace_generator_unittest.cc
@@ -11,8 +11,10 @@
 #include <utility>
 #include <vector>
 
+#include <base/format_macros.h>
 #include <base/logging.h>
 #include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/payload_generator/cycle_breaker.h"
@@ -35,20 +37,15 @@
 
 namespace {
 
-#define OP_BSDIFF DeltaArchiveManifest_InstallOperation_Type_BSDIFF
-#define OP_MOVE DeltaArchiveManifest_InstallOperation_Type_MOVE
-#define OP_REPLACE DeltaArchiveManifest_InstallOperation_Type_REPLACE
-#define OP_REPLACE_BZ DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ
-
 void GenVertex(Vertex* out,
                const vector<Extent>& src_extents,
                const vector<Extent>& dst_extents,
                const string& path,
-               DeltaArchiveManifest_InstallOperation_Type type) {
-  out->op.set_type(type);
-  out->file_name = path;
-  StoreExtents(src_extents, out->op.mutable_src_extents());
-  StoreExtents(dst_extents, out->op.mutable_dst_extents());
+               InstallOperation_Type type) {
+  out->aop.op.set_type(type);
+  out->aop.name = path;
+  StoreExtents(src_extents, out->aop.op.mutable_src_extents());
+  StoreExtents(dst_extents, out->aop.op.mutable_dst_extents());
 }
 
 vector<Extent> VectOfExt(uint64_t start_block, uint64_t num_blocks) {
@@ -83,9 +80,7 @@
   vect->back().set_num_blocks(length);
 }
 
-void OpAppendExtent(DeltaArchiveManifest_InstallOperation* op,
-                    uint64_t start,
-                    uint64_t length) {
+void OpAppendExtent(InstallOperation* op, uint64_t start, uint64_t length) {
   Extent* extent = op->add_src_extents();
   extent->set_start_block(start);
   extent->set_num_blocks(length);
@@ -94,6 +89,31 @@
 }  // namespace
 
 class InplaceGeneratorTest : public ::testing::Test {
+ protected:
+  // Initialize |blob_path_|, |blob_file_size_| and |blob_file_fd_| variables
+  // with a new blob file. The file is closed and removed automatically when
+  // the test finishes.
+  void CreateBlobFile() {
+    // blob_fd_closer_ takes a pointer to blob_fd_. Make sure we destroy a
+    // previous instance before overriding blob_fd_.
+    blob_fd_closer_.reset();
+    EXPECT_TRUE(utils::MakeTempFile(
+        "InplaceGenerator_blob_file.XXXXXX", &blob_path_, &blob_fd_));
+    blob_path_unlinker_.reset(new ScopedPathUnlinker(blob_path_));
+    blob_fd_closer_.reset(new ScopedFdCloser(&blob_fd_));
+    blob_file_size_ = 0;
+    EXPECT_GE(blob_fd_, 0);
+    blob_file_.reset(new BlobFileWriter(blob_fd_, &blob_file_size_));
+  }
+
+  // Blob file name, file descriptor and file size used to store operation
+  // blobs.
+  string blob_path_;
+  int blob_fd_{-1};
+  off_t blob_file_size_{0};
+  std::unique_ptr<BlobFileWriter> blob_file_;
+  std::unique_ptr<ScopedPathUnlinker> blob_path_unlinker_;
+  std::unique_ptr<ScopedFdCloser> blob_fd_closer_;
 };
 
 TEST_F(InplaceGeneratorTest, BlockDefaultValues) {
@@ -112,7 +132,7 @@
   AppendExtent(&replace_blocks, 10, 2);
   AppendExtent(&replace_blocks, 13, 2);
   Vertex vertex;
-  DeltaArchiveManifest_InstallOperation& op = vertex.op;
+  InstallOperation& op = vertex.aop.op;
   OpAppendExtent(&op, 4, 3);
   OpAppendExtent(&op, kSparseHole, 4);  // Sparse hole in file
   OpAppendExtent(&op, 3, 1);
@@ -144,14 +164,13 @@
   // Create nodes in graph
   {
     graph.resize(graph.size() + 1);
-    graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+    graph.back().aop.op.set_type(InstallOperation::MOVE);
     // Reads from blocks 3, 5, 7
     vector<Extent> extents;
     AppendBlockToExtents(&extents, 3);
     AppendBlockToExtents(&extents, 5);
     AppendBlockToExtents(&extents, 7);
-    StoreExtents(extents,
-                                     graph.back().op.mutable_src_extents());
+    StoreExtents(extents, graph.back().aop.op.mutable_src_extents());
     blocks[3].reader = graph.size() - 1;
     blocks[5].reader = graph.size() - 1;
     blocks[7].reader = graph.size() - 1;
@@ -161,22 +180,20 @@
     AppendBlockToExtents(&extents, 1);
     AppendBlockToExtents(&extents, 2);
     AppendBlockToExtents(&extents, 4);
-    StoreExtents(extents,
-                                     graph.back().op.mutable_dst_extents());
+    StoreExtents(extents, graph.back().aop.op.mutable_dst_extents());
     blocks[1].writer = graph.size() - 1;
     blocks[2].writer = graph.size() - 1;
     blocks[4].writer = graph.size() - 1;
   }
   {
     graph.resize(graph.size() + 1);
-    graph.back().op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+    graph.back().aop.op.set_type(InstallOperation::MOVE);
     // Reads from blocks 1, 2, 4
     vector<Extent> extents;
     AppendBlockToExtents(&extents, 1);
     AppendBlockToExtents(&extents, 2);
     AppendBlockToExtents(&extents, 4);
-    StoreExtents(extents,
-                                     graph.back().op.mutable_src_extents());
+    StoreExtents(extents, graph.back().aop.op.mutable_src_extents());
     blocks[1].reader = graph.size() - 1;
     blocks[2].reader = graph.size() - 1;
     blocks[4].reader = graph.size() - 1;
@@ -186,8 +203,7 @@
     AppendBlockToExtents(&extents, 3);
     AppendBlockToExtents(&extents, 5);
     AppendBlockToExtents(&extents, 6);
-    StoreExtents(extents,
-                                     graph.back().op.mutable_dst_extents());
+    StoreExtents(extents, graph.back().aop.op.mutable_dst_extents());
     blocks[3].writer = graph.size() - 1;
     blocks[5].writer = graph.size() - 1;
     blocks[6].writer = graph.size() - 1;
@@ -211,27 +227,26 @@
   EXPECT_EQ(3, graph.size());
 
   // Check new node in graph:
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_MOVE,
-            graph.back().op.type());
-  EXPECT_EQ(2, graph.back().op.src_extents_size());
-  EXPECT_EQ(1, graph.back().op.dst_extents_size());
-  EXPECT_EQ(kTempBlockStart, graph.back().op.dst_extents(0).start_block());
-  EXPECT_EQ(2, graph.back().op.dst_extents(0).num_blocks());
+  EXPECT_EQ(InstallOperation::MOVE, graph.back().aop.op.type());
+  EXPECT_EQ(2, graph.back().aop.op.src_extents_size());
+  EXPECT_EQ(1, graph.back().aop.op.dst_extents_size());
+  EXPECT_EQ(kTempBlockStart, graph.back().aop.op.dst_extents(0).start_block());
+  EXPECT_EQ(2, graph.back().aop.op.dst_extents(0).num_blocks());
   EXPECT_TRUE(graph.back().out_edges.empty());
 
   // Check that old node reads from new blocks
-  EXPECT_EQ(2, graph[0].op.src_extents_size());
-  EXPECT_EQ(kTempBlockStart, graph[0].op.src_extents(0).start_block());
-  EXPECT_EQ(2, graph[0].op.src_extents(0).num_blocks());
-  EXPECT_EQ(7, graph[0].op.src_extents(1).start_block());
-  EXPECT_EQ(1, graph[0].op.src_extents(1).num_blocks());
+  EXPECT_EQ(2, graph[0].aop.op.src_extents_size());
+  EXPECT_EQ(kTempBlockStart, graph[0].aop.op.src_extents(0).start_block());
+  EXPECT_EQ(2, graph[0].aop.op.src_extents(0).num_blocks());
+  EXPECT_EQ(7, graph[0].aop.op.src_extents(1).start_block());
+  EXPECT_EQ(1, graph[0].aop.op.src_extents(1).num_blocks());
 
   // And that the old dst extents haven't changed
-  EXPECT_EQ(2, graph[0].op.dst_extents_size());
-  EXPECT_EQ(1, graph[0].op.dst_extents(0).start_block());
-  EXPECT_EQ(2, graph[0].op.dst_extents(0).num_blocks());
-  EXPECT_EQ(4, graph[0].op.dst_extents(1).start_block());
-  EXPECT_EQ(1, graph[0].op.dst_extents(1).num_blocks());
+  EXPECT_EQ(2, graph[0].aop.op.dst_extents_size());
+  EXPECT_EQ(1, graph[0].aop.op.dst_extents(0).start_block());
+  EXPECT_EQ(2, graph[0].aop.op.dst_extents(0).num_blocks());
+  EXPECT_EQ(4, graph[0].aop.op.dst_extents(1).start_block());
+  EXPECT_EQ(1, graph[0].aop.op.dst_extents(1).num_blocks());
 
   // Ensure it only depends on the next node and the new temp node
   EXPECT_EQ(2, graph[0].out_edges.size());
@@ -240,17 +255,17 @@
                                                                   1));
 
   // Check second node has unchanged extents
-  EXPECT_EQ(2, graph[1].op.src_extents_size());
-  EXPECT_EQ(1, graph[1].op.src_extents(0).start_block());
-  EXPECT_EQ(2, graph[1].op.src_extents(0).num_blocks());
-  EXPECT_EQ(4, graph[1].op.src_extents(1).start_block());
-  EXPECT_EQ(1, graph[1].op.src_extents(1).num_blocks());
+  EXPECT_EQ(2, graph[1].aop.op.src_extents_size());
+  EXPECT_EQ(1, graph[1].aop.op.src_extents(0).start_block());
+  EXPECT_EQ(2, graph[1].aop.op.src_extents(0).num_blocks());
+  EXPECT_EQ(4, graph[1].aop.op.src_extents(1).start_block());
+  EXPECT_EQ(1, graph[1].aop.op.src_extents(1).num_blocks());
 
-  EXPECT_EQ(2, graph[1].op.dst_extents_size());
-  EXPECT_EQ(3, graph[1].op.dst_extents(0).start_block());
-  EXPECT_EQ(1, graph[1].op.dst_extents(0).num_blocks());
-  EXPECT_EQ(5, graph[1].op.dst_extents(1).start_block());
-  EXPECT_EQ(2, graph[1].op.dst_extents(1).num_blocks());
+  EXPECT_EQ(2, graph[1].aop.op.dst_extents_size());
+  EXPECT_EQ(3, graph[1].aop.op.dst_extents(0).start_block());
+  EXPECT_EQ(1, graph[1].aop.op.dst_extents(0).num_blocks());
+  EXPECT_EQ(5, graph[1].aop.op.dst_extents(1).start_block());
+  EXPECT_EQ(2, graph[1].aop.op.dst_extents(1).num_blocks());
 
   // Ensure it only depends on the next node
   EXPECT_EQ(1, graph[1].out_edges.size());
@@ -268,9 +283,18 @@
   cuts.resize(3);
 
   // Simple broken loop:
-  GenVertex(&graph[0], VectOfExt(0, 1), VectOfExt(1, 1), "", OP_MOVE);
-  GenVertex(&graph[1], VectOfExt(tmp, 1), VectOfExt(0, 1), "", OP_MOVE);
-  GenVertex(&graph[2], VectOfExt(1, 1), VectOfExt(tmp, 1), "", OP_MOVE);
+  GenVertex(
+      &graph[0], VectOfExt(0, 1), VectOfExt(1, 1), "", InstallOperation::MOVE);
+  GenVertex(&graph[1],
+            VectOfExt(tmp, 1),
+            VectOfExt(0, 1),
+            "",
+            InstallOperation::MOVE);
+  GenVertex(&graph[2],
+            VectOfExt(1, 1),
+            VectOfExt(tmp, 1),
+            "",
+            InstallOperation::MOVE);
   // Corresponding edges:
   graph[0].out_edges[2] = EdgeWithReadDep(VectOfExt(1, 1));
   graph[1].out_edges[2] = EdgeWithWriteDep(VectOfExt(tmp, 1));
@@ -283,11 +307,25 @@
   tmp++;
 
   // Slightly more complex pair of loops:
-  GenVertex(&graph[3], VectOfExt(4, 2), VectOfExt(2, 2), "", OP_MOVE);
-  GenVertex(&graph[4], VectOfExt(6, 1), VectOfExt(7, 1), "", OP_MOVE);
-  GenVertex(&graph[5], VectOfExt(tmp, 3), VectOfExt(4, 3), kFilename, OP_MOVE);
-  GenVertex(&graph[6], VectOfExt(2, 2), VectOfExt(tmp, 2), "", OP_MOVE);
-  GenVertex(&graph[7], VectOfExt(7, 1), VectOfExt(tmp + 2, 1), "", OP_MOVE);
+  GenVertex(
+      &graph[3], VectOfExt(4, 2), VectOfExt(2, 2), "", InstallOperation::MOVE);
+  GenVertex(
+      &graph[4], VectOfExt(6, 1), VectOfExt(7, 1), "", InstallOperation::MOVE);
+  GenVertex(&graph[5],
+            VectOfExt(tmp, 3),
+            VectOfExt(4, 3),
+            kFilename,
+            InstallOperation::MOVE);
+  GenVertex(&graph[6],
+            VectOfExt(2, 2),
+            VectOfExt(tmp, 2),
+            "",
+            InstallOperation::MOVE);
+  GenVertex(&graph[7],
+            VectOfExt(7, 1),
+            VectOfExt(tmp + 2, 1),
+            "",
+            InstallOperation::MOVE);
   // Corresponding edges:
   graph[3].out_edges[6] = EdgeWithReadDep(VectOfExt(2, 2));
   graph[4].out_edges[7] = EdgeWithReadDep(VectOfExt(7, 1));
@@ -306,7 +344,7 @@
   cuts[2].tmp_extents = VectOfExt(tmp + 2, 1);
 
   // Supplier of temp block:
-  GenVertex(&graph[8], empt, VectOfExt(8, 1), "", OP_REPLACE);
+  GenVertex(&graph[8], empt, VectOfExt(8, 1), "", InstallOperation::REPLACE);
 
   // Specify the final order:
   vector<Vertex::Index> op_indexes;
@@ -324,50 +362,43 @@
   InplaceGenerator::GenerateReverseTopoOrderMap(op_indexes,
                                                 &reverse_op_indexes);
 
-  int fd;
-  EXPECT_TRUE(utils::MakeTempFile("AssignTempBlocksReuseTest.XXXXXX",
-                                  nullptr,
-                                  &fd));
-  ScopedFdCloser fd_closer(&fd);
-  off_t data_file_size = 0;
-
+  CreateBlobFile();
   EXPECT_TRUE(InplaceGenerator::AssignTempBlocks(&graph,
                                                  "/dev/zero",
-                                                 fd,
-                                                 &data_file_size,
+                                                 blob_file_.get(),
                                                  &op_indexes,
                                                  &reverse_op_indexes,
                                                  cuts));
   EXPECT_FALSE(graph[6].valid);
   EXPECT_FALSE(graph[7].valid);
-  EXPECT_EQ(1, graph[1].op.src_extents_size());
-  EXPECT_EQ(2, graph[1].op.src_extents(0).start_block());
-  EXPECT_EQ(1, graph[1].op.src_extents(0).num_blocks());
-  EXPECT_EQ(OP_REPLACE_BZ, graph[5].op.type());
+  EXPECT_EQ(1, graph[1].aop.op.src_extents_size());
+  EXPECT_EQ(2, graph[1].aop.op.src_extents(0).start_block());
+  EXPECT_EQ(1, graph[1].aop.op.src_extents(0).num_blocks());
+  EXPECT_EQ(InstallOperation::REPLACE_BZ, graph[5].aop.op.type());
 }
 
-TEST_F(InplaceGeneratorTest, MoveFullOpsToBackTest) {
+TEST_F(InplaceGeneratorTest, MoveAndSortFullOpsToBackTest) {
   Graph graph(4);
-  graph[0].file_name = "A";
-  graph[0].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
-  graph[1].file_name = "B";
-  graph[1].op.set_type(DeltaArchiveManifest_InstallOperation_Type_BSDIFF);
-  graph[2].file_name = "C";
-  graph[2].op.set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ);
-  graph[3].file_name = "D";
-  graph[3].op.set_type(DeltaArchiveManifest_InstallOperation_Type_MOVE);
+  graph[0].aop.name = "A";
+  graph[0].aop.op.set_type(InstallOperation::REPLACE);
+  graph[1].aop.name = "B";
+  graph[1].aop.op.set_type(InstallOperation::BSDIFF);
+  graph[2].aop.name = "C";
+  graph[2].aop.op.set_type(InstallOperation::REPLACE_BZ);
+  graph[3].aop.name = "D";
+  graph[3].aop.op.set_type(InstallOperation::MOVE);
 
   vector<Vertex::Index> vect(graph.size());
 
   for (vector<Vertex::Index>::size_type i = 0; i < vect.size(); ++i) {
     vect[i] = i;
   }
-  InplaceGenerator::MoveFullOpsToBack(&graph, &vect);
+  InplaceGenerator::MoveAndSortFullOpsToBack(&graph, &vect);
   EXPECT_EQ(vect.size(), graph.size());
-  EXPECT_EQ(graph[vect[0]].file_name, "B");
-  EXPECT_EQ(graph[vect[1]].file_name, "D");
-  EXPECT_EQ(graph[vect[2]].file_name, "A");
-  EXPECT_EQ(graph[vect[3]].file_name, "C");
+  EXPECT_EQ(graph[vect[0]].aop.name, "B");
+  EXPECT_EQ(graph[vect[1]].aop.name, "D");
+  EXPECT_EQ(graph[vect[2]].aop.name, "A");
+  EXPECT_EQ(graph[vect[3]].aop.name, "C");
 }
 
 TEST_F(InplaceGeneratorTest, AssignTempBlocksTest) {
@@ -376,20 +407,36 @@
   const string kFilename = "/foo";
 
   // Some scratch space:
-  GenVertex(&graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE);
-  GenVertex(&graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE);
-  GenVertex(&graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE);
+  GenVertex(&graph[0], empt, VectOfExt(200, 1), "", InstallOperation::REPLACE);
+  GenVertex(&graph[1], empt, VectOfExt(210, 10), "", InstallOperation::REPLACE);
+  GenVertex(&graph[2], empt, VectOfExt(220, 1), "", InstallOperation::REPLACE);
 
   // A cycle that requires 10 blocks to break:
-  GenVertex(&graph[3], VectOfExt(10, 11), VectOfExt(0, 9), "", OP_BSDIFF);
+  GenVertex(&graph[3],
+            VectOfExt(10, 11),
+            VectOfExt(0, 9),
+            "",
+            InstallOperation::BSDIFF);
   graph[3].out_edges[4] = EdgeWithReadDep(VectOfExt(0, 9));
-  GenVertex(&graph[4], VectOfExt(0, 9), VectOfExt(10, 11), "", OP_BSDIFF);
+  GenVertex(&graph[4],
+            VectOfExt(0, 9),
+            VectOfExt(10, 11),
+            "",
+            InstallOperation::BSDIFF);
   graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
 
   // A cycle that requires 9 blocks to break:
-  GenVertex(&graph[5], VectOfExt(40, 11), VectOfExt(30, 10), "", OP_BSDIFF);
+  GenVertex(&graph[5],
+            VectOfExt(40, 11),
+            VectOfExt(30, 10),
+            "",
+            InstallOperation::BSDIFF);
   graph[5].out_edges[6] = EdgeWithReadDep(VectOfExt(30, 10));
-  GenVertex(&graph[6], VectOfExt(30, 10), VectOfExt(40, 11), "", OP_BSDIFF);
+  GenVertex(&graph[6],
+            VectOfExt(30, 10),
+            VectOfExt(40, 11),
+            "",
+            InstallOperation::BSDIFF);
   graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
 
   // A cycle that requires 40 blocks to break (which is too many):
@@ -397,62 +444,67 @@
             VectOfExt(120, 50),
             VectOfExt(60, 40),
             "",
-            OP_BSDIFF);
+            InstallOperation::BSDIFF);
   graph[7].out_edges[8] = EdgeWithReadDep(VectOfExt(60, 40));
   GenVertex(&graph[8],
             VectOfExt(60, 40),
             VectOfExt(120, 50),
             kFilename,
-            OP_BSDIFF);
+            InstallOperation::BSDIFF);
   graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
 
   graph_utils::DumpGraph(graph);
 
   vector<Vertex::Index> final_order;
 
-  int fd;
-  EXPECT_TRUE(utils::MakeTempFile("AssignTempBlocksTestData.XXXXXX",
-                                  nullptr,
-                                  &fd));
-  ScopedFdCloser fd_closer(&fd);
-  off_t data_file_size = 0;
-
+  CreateBlobFile();
   EXPECT_TRUE(InplaceGenerator::ConvertGraphToDag(&graph,
                                                   "/dev/zero",
-                                                  fd,
-                                                  &data_file_size,
+                                                  blob_file_.get(),
                                                   &final_order,
                                                   Vertex::kInvalidIndex));
 
   Graph expected_graph(12);
-  GenVertex(&expected_graph[0], empt, VectOfExt(200, 1), "", OP_REPLACE);
-  GenVertex(&expected_graph[1], empt, VectOfExt(210, 10), "", OP_REPLACE);
-  GenVertex(&expected_graph[2], empt, VectOfExt(220, 1), "", OP_REPLACE);
+  GenVertex(&expected_graph[0],
+            empt,
+            VectOfExt(200, 1),
+            "",
+            InstallOperation::REPLACE);
+  GenVertex(&expected_graph[1],
+            empt,
+            VectOfExt(210, 10),
+            "",
+            InstallOperation::REPLACE);
+  GenVertex(&expected_graph[2],
+            empt,
+            VectOfExt(220, 1),
+            "",
+            InstallOperation::REPLACE);
   GenVertex(&expected_graph[3],
             VectOfExt(10, 11),
             VectOfExt(0, 9),
             "",
-            OP_BSDIFF);
+            InstallOperation::BSDIFF);
   expected_graph[3].out_edges[9] = EdgeWithReadDep(VectOfExt(0, 9));
   GenVertex(&expected_graph[4],
             VectOfExt(60, 9),
             VectOfExt(10, 11),
             "",
-            OP_BSDIFF);
+            InstallOperation::BSDIFF);
   expected_graph[4].out_edges[3] = EdgeWithReadDep(VectOfExt(10, 11));
   expected_graph[4].out_edges[9] = EdgeWithWriteDep(VectOfExt(60, 9));
   GenVertex(&expected_graph[5],
             VectOfExt(40, 11),
             VectOfExt(30, 10),
             "",
-            OP_BSDIFF);
+            InstallOperation::BSDIFF);
   expected_graph[5].out_edges[10] = EdgeWithReadDep(VectOfExt(30, 10));
 
   GenVertex(&expected_graph[6],
             VectOfExt(60, 10),
             VectOfExt(40, 11),
             "",
-            OP_BSDIFF);
+            InstallOperation::BSDIFF);
   expected_graph[6].out_edges[5] = EdgeWithReadDep(VectOfExt(40, 11));
   expected_graph[6].out_edges[10] = EdgeWithWriteDep(VectOfExt(60, 10));
 
@@ -460,23 +512,27 @@
             VectOfExt(120, 50),
             VectOfExt(60, 40),
             "",
-            OP_BSDIFF);
+            InstallOperation::BSDIFF);
   expected_graph[7].out_edges[6] = EdgeWithReadDep(VectOfExt(60, 10));
 
-  GenVertex(&expected_graph[8], empt, VectOfExt(0, 50), "/foo", OP_REPLACE_BZ);
+  GenVertex(&expected_graph[8],
+            empt,
+            VectOfExt(0, 50),
+            "/foo",
+            InstallOperation::REPLACE_BZ);
   expected_graph[8].out_edges[7] = EdgeWithReadDep(VectOfExt(120, 50));
 
   GenVertex(&expected_graph[9],
             VectOfExt(0, 9),
             VectOfExt(60, 9),
             "",
-            OP_MOVE);
+            InstallOperation::MOVE);
 
   GenVertex(&expected_graph[10],
             VectOfExt(30, 10),
             VectOfExt(60, 10),
             "",
-            OP_MOVE);
+            InstallOperation::MOVE);
   expected_graph[10].out_edges[4] = EdgeWithReadDep(VectOfExt(60, 9));
 
   EXPECT_EQ(12, graph.size());
@@ -494,13 +550,12 @@
 TEST_F(InplaceGeneratorTest, CreateScratchNodeTest) {
   Vertex vertex;
   InplaceGenerator::CreateScratchNode(12, 34, &vertex);
-  EXPECT_EQ(DeltaArchiveManifest_InstallOperation_Type_REPLACE_BZ,
-            vertex.op.type());
-  EXPECT_EQ(0, vertex.op.data_offset());
-  EXPECT_EQ(0, vertex.op.data_length());
-  EXPECT_EQ(1, vertex.op.dst_extents_size());
-  EXPECT_EQ(12, vertex.op.dst_extents(0).start_block());
-  EXPECT_EQ(34, vertex.op.dst_extents(0).num_blocks());
+  EXPECT_EQ(InstallOperation::REPLACE_BZ, vertex.aop.op.type());
+  EXPECT_EQ(0, vertex.aop.op.data_offset());
+  EXPECT_EQ(0, vertex.aop.op.data_length());
+  EXPECT_EQ(1, vertex.aop.op.dst_extents_size());
+  EXPECT_EQ(12, vertex.aop.op.dst_extents(0).start_block());
+  EXPECT_EQ(34, vertex.aop.op.dst_extents(0).num_blocks());
 }
 
 TEST_F(InplaceGeneratorTest, ApplyMapTest) {
@@ -515,4 +570,77 @@
   EXPECT_EQ(expected_values, collection);
 }
 
+// We can't produce MOVE operations with a source or destination in the block 0.
+// This test checks that the cycle breaker procedure doesn't produce such
+// operations.
+TEST_F(InplaceGeneratorTest, ResolveReadAfterWriteDependenciesAvoidMoveToZero) {
+  size_t block_size = 4096;
+  size_t num_blocks = 4;
+  vector<AnnotatedOperation> aops;
+
+  // Create a REPLACE_BZ for block 0, and a circular dependency among all other
+  // blocks. This situation would prefer to issue a MOVE to scratch space and
+  // the only available block is 0.
+  aops.emplace_back();
+  aops.back().name = base::StringPrintf("<bz-block-0>");
+  aops.back().op.set_type(InstallOperation::REPLACE_BZ);
+  StoreExtents({ExtentForRange(0, 1)}, aops.back().op.mutable_dst_extents());
+
+  for (size_t i = 1; i < num_blocks; i++) {
+    AnnotatedOperation aop;
+    aop.name = base::StringPrintf("<op-%" PRIuS ">", i);
+    aop.op.set_type(InstallOperation::BSDIFF);
+    StoreExtents({ExtentForRange(1 + i % (num_blocks - 1), 1)},
+                 aop.op.mutable_src_extents());
+    StoreExtents({ExtentForRange(i, 1)}, aop.op.mutable_dst_extents());
+    aops.push_back(aop);
+  }
+
+  PartitionConfig part(PartitionName::kRootfs);
+  part.path = "/dev/zero";
+  part.size = num_blocks * block_size;
+
+  CreateBlobFile();
+
+  // We ran two tests here. The first one without enough blocks for the scratch
+  // space, forcing it to create a new full operation and the second case with
+  // one extra block in the partition that can be used for the move operation.
+  for (const auto part_blocks : vector<uint64_t>{num_blocks, num_blocks + 1}) {
+    SCOPED_TRACE(base::StringPrintf("Using partition_blocs=%" PRIu64,
+                                    part_blocks));
+    vector<AnnotatedOperation> result_aops = aops;
+    EXPECT_TRUE(InplaceGenerator::ResolveReadAfterWriteDependencies(
+      part, part_blocks * block_size, block_size, blob_file_.get(),
+      &result_aops));
+
+    size_t full_ops = 0;
+    for (const auto& aop : result_aops) {
+      if (aop.op.type() == InstallOperation::REPLACE ||
+          aop.op.type() == InstallOperation::REPLACE_BZ) {
+        full_ops++;
+      }
+
+      if (aop.op.type() != InstallOperation::MOVE)
+        continue;
+      for (const Extent& extent : aop.op.src_extents()) {
+        EXPECT_NE(0, extent.start_block()) << "On src extents for aop: " << aop;
+      }
+      for (const Extent& extent : aop.op.dst_extents()) {
+        EXPECT_NE(0, extent.start_block()) << "On dst extents for aop: " << aop;
+      }
+    }
+
+    // If there's extra space in the partition, it should not use a new full
+    // operation for it.
+    EXPECT_EQ(part_blocks == num_blocks ? 2 : 1, full_ops);
+
+    if (HasNonfatalFailure()) {
+      LOG(INFO) << "Result operation list:";
+      for (const auto& aop : result_aops) {
+        LOG(INFO) << aop;
+      }
+    }
+  }
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_generator/operations_generator.h b/payload_generator/operations_generator.h
index 31d57b3..677f766 100644
--- a/payload_generator/operations_generator.h
+++ b/payload_generator/operations_generator.h
@@ -10,6 +10,7 @@
 #include <base/macros.h>
 
 #include "update_engine/payload_generator/annotated_operation.h"
+#include "update_engine/payload_generator/blob_file_writer.h"
 #include "update_engine/payload_generator/payload_generation_config.h"
 
 namespace chromeos_update_engine {
@@ -31,8 +32,7 @@
   // |data_file_size|.
   virtual bool GenerateOperations(
       const PayloadGenerationConfig& config,
-      int data_file_fd,
-      off_t* data_file_size,
+      BlobFileWriter* blob_file,
       std::vector<AnnotatedOperation>* rootfs_ops,
       std::vector<AnnotatedOperation>* kernel_ops) = 0;
 
diff --git a/payload_generator/payload_file.cc b/payload_generator/payload_file.cc
index fe453a2..92fe724 100644
--- a/payload_generator/payload_file.cc
+++ b/payload_generator/payload_file.cc
@@ -128,11 +128,10 @@
   {
     for (int i = 0; i < (manifest_.install_operations_size() +
                          manifest_.kernel_install_operations_size()); i++) {
-      DeltaArchiveManifest_InstallOperation* op =
-          i < manifest_.install_operations_size() ?
-          manifest_.mutable_install_operations(i) :
-          manifest_.mutable_kernel_install_operations(
-              i - manifest_.install_operations_size());
+      InstallOperation* op = i < manifest_.install_operations_size()
+                                 ? manifest_.mutable_install_operations(i)
+                                 : manifest_.mutable_kernel_install_operations(
+                                       i - manifest_.install_operations_size());
       if (op->has_data_offset()) {
         if (op->data_offset() != next_blob_offset) {
           LOG(FATAL) << "bad blob offset! " << op->data_offset() << " != "
@@ -249,9 +248,8 @@
   return true;
 }
 
-bool PayloadFile::AddOperationHash(
-    DeltaArchiveManifest_InstallOperation* op,
-    const chromeos::Blob& buf) {
+bool PayloadFile::AddOperationHash(InstallOperation* op,
+                                   const chromeos::Blob& buf) {
   OmahaHashCalculator hasher;
   TEST_AND_RETURN_FALSE(hasher.Update(buf.data(), buf.size()));
   TEST_AND_RETURN_FALSE(hasher.Finalize());
@@ -302,9 +300,8 @@
   manifest->set_signatures_offset(signature_blob_offset);
   LOG(INFO) << "set? " << manifest->has_signatures_offset();
   // Add a dummy op at the end to appease older clients
-  DeltaArchiveManifest_InstallOperation* dummy_op =
-      manifest->add_kernel_install_operations();
-  dummy_op->set_type(DeltaArchiveManifest_InstallOperation_Type_REPLACE);
+  InstallOperation* dummy_op = manifest->add_kernel_install_operations();
+  dummy_op->set_type(InstallOperation::REPLACE);
   dummy_op->set_data_offset(signature_blob_offset);
   manifest->set_signatures_offset(signature_blob_offset);
   dummy_op->set_data_length(signature_blob_length);
diff --git a/payload_generator/payload_file.h b/payload_generator/payload_file.h
index 150b0b0..a8f815b 100644
--- a/payload_generator/payload_file.h
+++ b/payload_generator/payload_file.h
@@ -50,8 +50,7 @@
   // dummy operation for signature blob because the contents of the signature
   // blob will not be available at payload creation time. So, update_engine will
   // gracefully ignore the dummy signature operation.
-  static bool AddOperationHash(DeltaArchiveManifest_InstallOperation* op,
-                               const chromeos::Blob& buf);
+  static bool AddOperationHash(InstallOperation* op, const chromeos::Blob& buf);
 
   // Install operations in the manifest may reference data blobs, which
   // are in data_blobs_path. This function creates a new data blobs file
diff --git a/payload_generator/payload_generation_config.cc b/payload_generator/payload_generation_config.cc
index d58c9a1..cfe1367 100644
--- a/payload_generator/payload_generation_config.cc
+++ b/payload_generator/payload_generation_config.cc
@@ -15,6 +15,16 @@
 
 namespace chromeos_update_engine {
 
+std::string PartitionNameString(PartitionName name) {
+  switch (name) {
+    case PartitionName::kKernel:
+      return "kernel";
+    case PartitionName::kRootfs:
+      return "rootfs";
+  }
+  return "other";
+}
+
 bool PartitionConfig::ValidateExists() const {
   TEST_AND_RETURN_FALSE(!path.empty());
   TEST_AND_RETURN_FALSE(utils::FileExists(path.c_str()));
@@ -36,17 +46,8 @@
   if (!fs_interface) {
     // Fall back to a RAW filesystem.
     TEST_AND_RETURN_FALSE(size % kBlockSize == 0);
-    std::string str_name = "other";
-    switch (name) {
-      case PartitionName::kKernel:
-        str_name = "kernel";
-        break;
-      case PartitionName::kRootfs:
-        str_name = "rootfs";
-        break;
-    }
     fs_interface = RawFilesystem::Create(
-      "<" + str_name + "-partition>",
+      "<" + PartitionNameString(name) + "-partition>",
       kBlockSize,
       size / kBlockSize);
   }
@@ -141,7 +142,9 @@
   TEST_AND_RETURN_FALSE(target.rootfs.size % block_size == 0);
   TEST_AND_RETURN_FALSE(target.kernel.size % block_size == 0);
 
-  TEST_AND_RETURN_FALSE(chunk_size == -1 || chunk_size % block_size == 0);
+  TEST_AND_RETURN_FALSE(hard_chunk_size == -1 ||
+                        hard_chunk_size % block_size == 0);
+  TEST_AND_RETURN_FALSE(soft_chunk_size % block_size == 0);
 
   TEST_AND_RETURN_FALSE(rootfs_partition_size % block_size == 0);
   TEST_AND_RETURN_FALSE(rootfs_partition_size >= target.rootfs.size);
diff --git a/payload_generator/payload_generation_config.h b/payload_generator/payload_generation_config.h
index f18055f..1846e5b 100644
--- a/payload_generator/payload_generation_config.h
+++ b/payload_generator/payload_generation_config.h
@@ -22,6 +22,9 @@
   kRootfs,
 };
 
+// Return a string name for the PartitionName.
+std::string PartitionNameString(PartitionName name);
+
 struct PartitionConfig {
   explicit PartitionConfig(PartitionName name) : name(name) {}
 
@@ -110,10 +113,20 @@
   // after the partition used to store the verity hashes and or the bootcache.
   uint64_t rootfs_partition_size = 0;
 
-  // The chunk size is the maximum size that a single operation should write in
-  // the destination. Operations bigger than chunk_size should be split. A value
-  // of -1 means no chunk_size limit.
-  off_t chunk_size = -1;
+  // The |hard_chunk_size| is the maximum size that a single operation should
+  // write in the destination. Operations bigger than chunk_size should be
+  // split. A value of -1 means no hard chunk size limit. A very low limit
+  // means more operations, and less of a chance to reuse the data.
+  ssize_t hard_chunk_size = -1;
+
+  // The |soft_chunk_size| is the preferred chunk size to use when there's no
+  // significant impact to the operations. For example, REPLACE, MOVE and
+  // SOURCE_COPY operations are not significantly impacted by the chunk size,
+  // except for a few bytes overhead in the manifest to describe extra
+  // operations. On the other hand, splitting BSDIFF operations impacts the
+  // payload size since it is not possible to use the redundancy *between*
+  // chunks.
+  size_t soft_chunk_size = 2 * 1024 * 1024;
 
   // TODO(deymo): Remove the block_size member and maybe replace it with a
   // minimum alignment size for blocks (if needed). Algorithms should be able to
diff --git a/payload_state.cc b/payload_state.cc
index ed7b775..aa4c73c 100644
--- a/payload_state.cc
+++ b/payload_state.cc
@@ -10,7 +10,6 @@
 #include <base/logging.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-#include <base/format_macros.h>
 #include <policy/device_policy.h>
 
 #include "update_engine/clock.h"
@@ -19,7 +18,6 @@
 #include "update_engine/install_plan.h"
 #include "update_engine/omaha_request_params.h"
 #include "update_engine/prefs.h"
-#include "update_engine/real_dbus_wrapper.h"
 #include "update_engine/system_state.h"
 #include "update_engine/utils.h"
 
@@ -175,10 +173,9 @@
   metrics::ConnectionType type;
   NetworkConnectionType network_connection_type;
   NetworkTethering tethering;
-  RealDBusWrapper dbus_iface;
-  ConnectionManager* connection_manager = system_state_->connection_manager();
-  if (!connection_manager->GetConnectionProperties(&dbus_iface,
-                                                   &network_connection_type,
+  ConnectionManagerInterface* connection_manager =
+      system_state_->connection_manager();
+  if (!connection_manager->GetConnectionProperties(&network_connection_type,
                                                    &tethering)) {
     LOG(ERROR) << "Failed to determine connection type.";
     type = metrics::ConnectionType::kUnknown;
diff --git a/payload_state_unittest.cc b/payload_state_unittest.cc
index 08ed398..c4e66ac 100644
--- a/payload_state_unittest.cc
+++ b/payload_state_unittest.cc
@@ -4,8 +4,6 @@
 
 #include "update_engine/payload_state.h"
 
-#include <glib.h>
-
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/strings/stringprintf.h>
diff --git a/postinstall_runner_action.cc b/postinstall_runner_action.cc
index f27aace..0760bd5 100644
--- a/postinstall_runner_action.cc
+++ b/postinstall_runner_action.cc
@@ -8,6 +8,8 @@
 #include <sys/mount.h>
 #include <vector>
 
+#include <base/bind.h>
+
 #include "update_engine/action_processor.h"
 #include "update_engine/subprocess.h"
 #include "update_engine/utils.h"
@@ -83,12 +85,16 @@
     command.push_back(kPostinstallScript);
   }
   command.push_back(install_device);
-  if (!Subprocess::Get().Exec(command, StaticCompletePostinstall, this)) {
-    CompletePostinstall(1);
+  if (!Subprocess::Get().Exec(command,
+                              base::Bind(
+                                  &PostinstallRunnerAction::CompletePostinstall,
+                                  base::Unretained(this)))) {
+    CompletePostinstall(1, "Postinstall didn't launch");
   }
 }
 
-void PostinstallRunnerAction::CompletePostinstall(int return_code) {
+void PostinstallRunnerAction::CompletePostinstall(int return_code,
+                                                  const string& output) {
   ScopedActionCompleter completer(processor_, this);
   ScopedTempUnmounter temp_unmounter(temp_rootfs_dir_);
   if (return_code != 0) {
@@ -123,11 +129,4 @@
   completer.set_code(ErrorCode::kSuccess);
 }
 
-void PostinstallRunnerAction::StaticCompletePostinstall(int return_code,
-                                                        const string& output,
-                                                        void* p) {
-  reinterpret_cast<PostinstallRunnerAction*>(p)->CompletePostinstall(
-      return_code);
-}
-
 }  // namespace chromeos_update_engine
diff --git a/postinstall_runner_action.h b/postinstall_runner_action.h
index e7ac1c5..18a3691 100644
--- a/postinstall_runner_action.h
+++ b/postinstall_runner_action.h
@@ -32,10 +32,8 @@
 
  private:
   // Subprocess::Exec callback.
-  void CompletePostinstall(int return_code);
-  static void StaticCompletePostinstall(int return_code,
-                                        const std::string& output,
-                                        void* p);
+  void CompletePostinstall(int return_code,
+                           const std::string& output);
 
   InstallPlan install_plan_;
   std::string temp_rootfs_dir_;
diff --git a/postinstall_runner_action_unittest.cc b/postinstall_runner_action_unittest.cc
index 4549272..056ae21 100644
--- a/postinstall_runner_action_unittest.cc
+++ b/postinstall_runner_action_unittest.cc
@@ -13,10 +13,11 @@
 #include <vector>
 
 #include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <chromeos/bind_lambda.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/message_loops/base_message_loop.h>
 #include <chromeos/message_loops/message_loop_utils.h>
 #include <gtest/gtest.h>
 
@@ -37,10 +38,8 @@
  protected:
   void SetUp() override {
     loop_.SetAsCurrent();
-  }
-
-  void TearDown() override {
-    EXPECT_EQ(0, chromeos::MessageLoopRunMaxIterations(&loop_, 1));
+    async_signal_handler_.Init();
+    subprocess_.Init(&async_signal_handler_);
   }
 
   // DoTest with various combinations of do_losetup, err_code and
@@ -50,7 +49,10 @@
  private:
   static const char* kImageMountPointTemplate;
 
-  chromeos::GlibMessageLoop loop_;
+  base::MessageLoopForIO base_loop_;
+  chromeos::BaseMessageLoop loop_{&base_loop_};
+  chromeos::AsynchronousSignalHandler async_signal_handler_;
+  Subprocess subprocess_;
 };
 
 class PostinstActionProcessorDelegate : public ActionProcessorDelegate {
diff --git a/real_dbus_wrapper.h b/real_dbus_wrapper.h
deleted file mode 100644
index 0c6847b..0000000
--- a/real_dbus_wrapper.h
+++ /dev/null
@@ -1,158 +0,0 @@
-// Copyright (c) 2010 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 UPDATE_ENGINE_REAL_DBUS_WRAPPER_H_
-#define UPDATE_ENGINE_REAL_DBUS_WRAPPER_H_
-
-// A mockable interface for DBus.
-
-#include <base/macros.h>
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-lowlevel.h>
-
-#include "update_engine/dbus_wrapper_interface.h"
-
-namespace chromeos_update_engine {
-
-class RealDBusWrapper : public DBusWrapperInterface {
-  DBusGProxy* ProxyNewForName(DBusGConnection* connection,
-                              const char* name,
-                              const char* path,
-                              const char* interface) override {
-    return dbus_g_proxy_new_for_name(connection,
-                                     name,
-                                     path,
-                                     interface);
-  }
-
-  void ProxyUnref(DBusGProxy* proxy) override {
-    g_object_unref(proxy);
-  }
-
-  DBusGConnection* BusGet(DBusBusType type, GError** error) override {
-    return dbus_g_bus_get(type, error);
-  }
-
-  gboolean ProxyCall_0_1(DBusGProxy* proxy,
-                         const char* method,
-                         GError** error,
-                         GHashTable** out1) override {
-    return dbus_g_proxy_call(proxy, method, error, G_TYPE_INVALID,
-                             dbus_g_type_get_map("GHashTable",
-                                                 G_TYPE_STRING,
-                                                 G_TYPE_VALUE),
-                             out1, G_TYPE_INVALID);
-  }
-
-  gboolean ProxyCall_0_1(DBusGProxy* proxy,
-                         const char* method,
-                         GError** error,
-                         gint* out1) override {
-    return dbus_g_proxy_call(proxy, method, error, G_TYPE_INVALID,
-                             G_TYPE_INT, out1, G_TYPE_INVALID);
-  }
-
-  gboolean ProxyCall_1_0(DBusGProxy* proxy,
-                         const char* method,
-                         GError** error,
-                         gint in1) override {
-    return dbus_g_proxy_call(proxy, method, error,
-                             G_TYPE_INT, in1,
-                             G_TYPE_INVALID, G_TYPE_INVALID);
-  }
-
-  gboolean ProxyCall_3_0(DBusGProxy* proxy,
-                         const char* method,
-                         GError** error,
-                         const char* in1,
-                         const char* in2,
-                         const char* in3) override {
-    return dbus_g_proxy_call(
-        proxy, method, error,
-        G_TYPE_STRING, in1, G_TYPE_STRING, in2, G_TYPE_STRING, in3,
-        G_TYPE_INVALID, G_TYPE_INVALID);
-  }
-
-  void ProxyAddSignal_1(DBusGProxy* proxy,
-                        const char* signal_name,
-                        GType type1) override {
-    dbus_g_object_register_marshaller(
-        g_cclosure_marshal_generic, G_TYPE_NONE, type1, G_TYPE_INVALID);
-    dbus_g_proxy_add_signal(proxy, signal_name, type1, G_TYPE_INVALID);
-  }
-
-  void ProxyAddSignal_2(DBusGProxy* proxy,
-                        const char* signal_name,
-                        GType type1,
-                        GType type2) override {
-    dbus_g_object_register_marshaller(
-        g_cclosure_marshal_generic, G_TYPE_NONE, type1, type2, G_TYPE_INVALID);
-    dbus_g_proxy_add_signal(proxy, signal_name, type1, type2, G_TYPE_INVALID);
-  }
-
-  void ProxyConnectSignal(DBusGProxy* proxy,
-                          const char* signal_name,
-                          GCallback handler,
-                          void* data,
-                          GClosureNotify free_data_func) override {
-    dbus_g_proxy_connect_signal(proxy, signal_name, handler, data,
-                                free_data_func);
-  }
-
-  void ProxyDisconnectSignal(DBusGProxy* proxy,
-                             const char* signal_name,
-                             GCallback handler,
-                             void* data) override {
-    dbus_g_proxy_disconnect_signal(proxy, signal_name, handler, data);
-  }
-
-  DBusConnection* ConnectionGetConnection(DBusGConnection* gbus) override {
-    return dbus_g_connection_get_connection(gbus);
-  }
-
-  void DBusBusAddMatch(DBusConnection* connection,
-                       const char* rule,
-                       DBusError* error) override {
-    dbus_bus_add_match(connection, rule, error);
-  }
-
-  dbus_bool_t DBusConnectionAddFilter(
-      DBusConnection* connection,
-      DBusHandleMessageFunction function,
-      void* user_data,
-      DBusFreeFunction free_data_function) override {
-    return dbus_connection_add_filter(connection,
-                                      function,
-                                      user_data,
-                                      free_data_function);
-  }
-
-  void DBusConnectionRemoveFilter(DBusConnection* connection,
-                                  DBusHandleMessageFunction function,
-                                  void* user_data) override {
-    dbus_connection_remove_filter(connection, function, user_data);
-  }
-
-  dbus_bool_t DBusMessageIsSignal(DBusMessage* message,
-                                  const char* interface,
-                                  const char* signal_name) override {
-    return dbus_message_is_signal(message, interface, signal_name);
-  }
-
-  dbus_bool_t DBusMessageGetArgs_3(DBusMessage* message,
-                                   DBusError* error,
-                                   char** out1,
-                                   char** out2,
-                                   char** out3) override {
-    return dbus_message_get_args(message, error,
-                                 DBUS_TYPE_STRING, out1,
-                                 DBUS_TYPE_STRING, out2,
-                                 DBUS_TYPE_STRING, out3,
-                                 G_TYPE_INVALID);
-  }
-};
-
-}  // namespace chromeos_update_engine
-
-#endif  // UPDATE_ENGINE_REAL_DBUS_WRAPPER_H_
diff --git a/real_system_state.cc b/real_system_state.cc
index f16f2f8..861a702 100644
--- a/real_system_state.cc
+++ b/real_system_state.cc
@@ -6,6 +6,7 @@
 
 #include <base/files/file_util.h>
 #include <base/time/time.h>
+#include <chromeos/dbus/service_constants.h>
 
 #include "update_engine/constants.h"
 #include "update_engine/update_manager/state_factory.h"
@@ -13,16 +14,22 @@
 
 namespace chromeos_update_engine {
 
-RealSystemState::RealSystemState()
-    : device_policy_(nullptr),
-      connection_manager_(this),
-      update_attempter_(this, &dbus_),
-      request_params_(this),
-      system_rebooted_(false) {}
+RealSystemState::RealSystemState(const scoped_refptr<dbus::Bus>& bus)
+    : debugd_proxy_(bus, debugd::kDebugdServiceName),
+      power_manager_proxy_(bus, power_manager::kPowerManagerServiceName),
+      session_manager_proxy_(bus, login_manager::kSessionManagerServiceName),
+      shill_proxy_(bus),
+      libcros_proxy_(bus) {
+}
 
 bool RealSystemState::Initialize() {
   metrics_lib_.Init();
 
+  if (!shill_proxy_.Init()) {
+    LOG(ERROR) << "Failed to initialize shill proxy.";
+    return false;
+  }
+
   if (!prefs_.Init(base::FilePath(kPrefsDirectory))) {
     LOG(ERROR) << "Failed to initialize preferences.";
     return false;
@@ -44,7 +51,7 @@
   // Initialize the Update Manager using the default state factory.
   chromeos_update_manager::State* um_state =
       chromeos_update_manager::DefaultStateFactory(
-          &policy_provider_, &dbus_, this);
+          &policy_provider_, &shill_proxy_, &session_manager_proxy_, this);
   if (!um_state) {
     LOG(ERROR) << "Failed to initialize the Update Manager.";
     return false;
diff --git a/real_system_state.h b/real_system_state.h
index 7e0c16d..be7778e 100644
--- a/real_system_state.h
+++ b/real_system_state.h
@@ -14,11 +14,12 @@
 
 #include "update_engine/clock.h"
 #include "update_engine/connection_manager.h"
+#include "update_engine/dbus_proxies.h"
 #include "update_engine/hardware.h"
 #include "update_engine/p2p_manager.h"
 #include "update_engine/payload_state.h"
 #include "update_engine/prefs.h"
-#include "update_engine/real_dbus_wrapper.h"
+#include "update_engine/shill_proxy.h"
 #include "update_engine/update_attempter.h"
 #include "update_engine/update_manager/update_manager.h"
 
@@ -30,7 +31,7 @@
  public:
   // Constructs all system objects that do not require separate initialization;
   // see Initialize() below for the remaining ones.
-  RealSystemState();
+  explicit RealSystemState(const scoped_refptr<dbus::Bus>& bus);
 
   // Initializes and sets systems objects that require an initialization
   // separately from construction. Returns |true| on success.
@@ -47,7 +48,7 @@
 
   inline ClockInterface* clock() override { return &clock_; }
 
-  inline ConnectionManager* connection_manager() override {
+  inline ConnectionManagerInterface* connection_manager() override {
     return &connection_manager_;
   }
 
@@ -81,18 +82,30 @@
     return update_manager_.get();
   }
 
+  inline org::chromium::PowerManagerProxyInterface* power_manager_proxy()
+      override {
+    return &power_manager_proxy_;
+  }
+
   inline bool system_rebooted() override { return system_rebooted_; }
 
  private:
+  // Real DBus proxies using the DBus connection.
+  org::chromium::debugdProxy debugd_proxy_;
+  org::chromium::PowerManagerProxy power_manager_proxy_;
+  org::chromium::SessionManagerInterfaceProxy session_manager_proxy_;
+  ShillProxy shill_proxy_;
+  LibCrosProxy libcros_proxy_;
+
   // Interface for the clock.
   Clock clock_;
 
   // The latest device policy object from the policy provider.
-  const policy::DevicePolicy* device_policy_;
+  const policy::DevicePolicy* device_policy_{nullptr};
 
-  // The connection manager object that makes download
-  // decisions depending on the current type of connection.
-  ConnectionManager connection_manager_;
+  // The connection manager object that makes download decisions depending on
+  // the current type of connection.
+  ConnectionManager connection_manager_{&shill_proxy_, this};
 
   // Interface for the hardware functions.
   Hardware hardware_;
@@ -106,18 +119,15 @@
   // Interface for persisted store that persists across powerwashes.
   Prefs powerwash_safe_prefs_;
 
-  // All state pertaining to payload state such as
-  // response, URL, backoff states.
+  // All state pertaining to payload state such as response, URL, backoff
+  // states.
   PayloadState payload_state_;
 
-  // The dbus object used to initialize the update attempter.
-  RealDBusWrapper dbus_;
-
   // Pointer to the update attempter object.
-  UpdateAttempter update_attempter_;
+  UpdateAttempter update_attempter_{this, &libcros_proxy_, &debugd_proxy_};
 
   // Common parameters for all Omaha requests.
-  OmahaRequestParams request_params_;
+  OmahaRequestParams request_params_{this};
 
   std::unique_ptr<P2PManager> p2p_manager_;
 
@@ -128,7 +138,7 @@
   // If true, this is the first instance of the update engine since the system
   // rebooted. Important for tracking whether you are running instance of the
   // update engine on first boot or due to a crash/restart.
-  bool system_rebooted_;
+  bool system_rebooted_{false};
 };
 
 }  // namespace chromeos_update_engine
diff --git a/sample_images/disk_ext2_1k.txt b/sample_images/disk_ext2_1k.txt
deleted file mode 100644
index 1494535..0000000
--- a/sample_images/disk_ext2_1k.txt
+++ /dev/null
@@ -1 +0,0 @@
-default 16777216 1024
diff --git a/sample_images/disk_ext2_4k.txt b/sample_images/disk_ext2_4k.txt
deleted file mode 100644
index cac718f..0000000
--- a/sample_images/disk_ext2_4k.txt
+++ /dev/null
@@ -1 +0,0 @@
-default 16777216 4096
diff --git a/sample_images/disk_ext2_ue_settings.txt b/sample_images/disk_ext2_ue_settings.txt
deleted file mode 100644
index 554f7da..0000000
--- a/sample_images/disk_ext2_ue_settings.txt
+++ /dev/null
@@ -1 +0,0 @@
-ue_settings 16777216 4096
diff --git a/sample_images/generate_image.sh b/sample_images/generate_images.sh
similarity index 83%
rename from sample_images/generate_image.sh
rename to sample_images/generate_images.sh
index c4f132a..d8d92f5 100755
--- a/sample_images/generate_image.sh
+++ b/sample_images/generate_images.sh
@@ -4,6 +4,11 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+# This script generates some sample images used in unittests and packages them
+# in the sample_images.tar.bz2 file. The list of generated images and their
+# options are described in the main() function. You need to manually run this
+# script to update the generated images whenever you modify this script.
+
 set -e
 
 # cleanup <path>
@@ -145,14 +150,28 @@
   trap - INT TERM EXIT
 }
 
-image_desc="${1:-}"
-output_dir="${2:-}"
+OUTPUT_DIR=$(dirname "$0")
+IMAGES=()
 
-if [[ ! -e "${image_desc}" || ! -d "${output_dir}" ]]; then
-  echo "Use: $0 <image_description.txt> <output_dir>" >&2
-  exit 1
-fi
+# generate_image <image_name> [<image args> ...]
+generate_image() {
+  echo "Generating image $1.img"
+  IMAGES+=( "$1.img" )
+  generate_fs "${OUTPUT_DIR}/$1.img" "${@:2}"
+}
 
-args=( $(cat ${image_desc}) )
-dest_image="${output_dir}/$(basename ${image_desc} .txt).img"
-generate_fs "${dest_image}" "${args[@]}"
+main() {
+  # Add more sample images here.
+  generate_image disk_ext2_1k default 16777216 1024
+  generate_image disk_ext2_4k default 16777216 4096
+  generate_image disk_ext2_ue_settings ue_settings 16777216 4096
+
+  # Generate the tarball and delete temporary images.
+  echo "Packing tar file sample_images.tar.bz2"
+  tar -jcf "${OUTPUT_DIR}/sample_images.tar.bz2" -C "${OUTPUT_DIR}" \
+    "${IMAGES[@]}"
+  cd "${OUTPUT_DIR}"
+  rm "${IMAGES[@]}"
+}
+
+main
diff --git a/sample_images/sample_images.tar.bz2 b/sample_images/sample_images.tar.bz2
new file mode 100644
index 0000000..0982271
--- /dev/null
+++ b/sample_images/sample_images.tar.bz2
Binary files differ
diff --git a/shill_proxy.cc b/shill_proxy.cc
new file mode 100644
index 0000000..f72ee8b
--- /dev/null
+++ b/shill_proxy.cc
@@ -0,0 +1,39 @@
+// Copyright 2015 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 "update_engine/shill_proxy.h"
+
+#include <chromeos/dbus/service_constants.h>
+
+#include "update_engine/dbus_proxies.h"
+
+using org::chromium::flimflam::ManagerProxy;
+using org::chromium::flimflam::ManagerProxyInterface;
+using org::chromium::flimflam::ServiceProxy;
+using org::chromium::flimflam::ServiceProxyInterface;
+
+namespace chromeos_update_engine {
+
+ShillProxy::ShillProxy(const scoped_refptr<dbus::Bus>& bus) : bus_(bus) {}
+
+bool ShillProxy::Init() {
+  manager_proxy_.reset(
+      new ManagerProxy(bus_,
+                       shill::kFlimflamServiceName,
+                       dbus::ObjectPath(shill::kFlimflamServicePath)));
+  return true;
+}
+
+ManagerProxyInterface* ShillProxy::GetManagerProxy() {
+  return manager_proxy_.get();
+}
+
+std::unique_ptr<ServiceProxyInterface> ShillProxy::GetServiceForPath(
+    const std::string& path) {
+  DCHECK(bus_.get());
+  return std::unique_ptr<ServiceProxyInterface>(new ServiceProxy(
+      bus_, shill::kFlimflamServiceName, dbus::ObjectPath(path)));
+}
+
+}  // namespace chromeos_update_engine
diff --git a/shill_proxy.h b/shill_proxy.h
new file mode 100644
index 0000000..3d1b376
--- /dev/null
+++ b/shill_proxy.h
@@ -0,0 +1,45 @@
+// Copyright 2015 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 UPDATE_ENGINE_SHILL_PROXY_H_
+#define UPDATE_ENGINE_SHILL_PROXY_H_
+
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+#include <dbus/bus.h>
+
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/shill_proxy_interface.h"
+
+namespace chromeos_update_engine {
+
+// This class implements the connection to shill using real DBus calls.
+class ShillProxy : public ShillProxyInterface {
+ public:
+  explicit ShillProxy(const scoped_refptr<dbus::Bus>& bus);
+  ~ShillProxy() override = default;
+
+  // Initializes the ShillProxy instance creating the manager proxy from the
+  // |bus_|.
+  bool Init();
+
+  // ShillProxyInterface overrides.
+  org::chromium::flimflam::ManagerProxyInterface* GetManagerProxy() override;
+  std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+  GetServiceForPath(const std::string& path) override;
+
+ private:
+  // A reference to the main bus for creating new ServiceProxy instances.
+  scoped_refptr<dbus::Bus> bus_;
+  std::unique_ptr<org::chromium::flimflam::ManagerProxyInterface>
+      manager_proxy_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShillProxy);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_SHILL_PROXY_H_
diff --git a/shill_proxy_interface.h b/shill_proxy_interface.h
new file mode 100644
index 0000000..a4d3aa3
--- /dev/null
+++ b/shill_proxy_interface.h
@@ -0,0 +1,44 @@
+// Copyright 2015 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 UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
+#define UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
+
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+
+#include "update_engine/dbus_proxies.h"
+
+namespace chromeos_update_engine {
+
+// This class handles the DBus connection with shill daemon. The DBus interface
+// with shill requires to monitor or request the current service by interacting
+// with the org::chromium::flimflam::ManagerProxy and then request or monitor
+// properties on the selected org::chromium::flimflam::ServiceProxy. This class
+// provides a mockable way to access that.
+class ShillProxyInterface {
+ public:
+  virtual ~ShillProxyInterface() = default;
+
+  // Return the ManagerProxy instance of the shill daemon. The instance is owned
+  // by this ShillProxyInterface instance.
+  virtual org::chromium::flimflam::ManagerProxyInterface* GetManagerProxy() = 0;
+
+  // Return a ServiceProxy for the given path. The ownership of the returned
+  // instance is transferred to the caller.
+  virtual std::unique_ptr<org::chromium::flimflam::ServiceProxyInterface>
+  GetServiceForPath(const std::string& path) = 0;
+
+ protected:
+  ShillProxyInterface() = default;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ShillProxyInterface);
+};
+
+}  // namespace chromeos_update_engine
+
+#endif  // UPDATE_ENGINE_SHILL_PROXY_INTERFACE_H_
diff --git a/subprocess.cc b/subprocess.cc
index 6a34641..fa7e4cb 100644
--- a/subprocess.cc
+++ b/subprocess.cc
@@ -18,47 +18,81 @@
 #include <base/posix/eintr_wrapper.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
-
-#include "update_engine/glib_utils.h"
+#include <chromeos/process.h>
+#include <chromeos/secure_blob.h>
 
 using chromeos::MessageLoop;
-using std::shared_ptr;
 using std::string;
 using std::unique_ptr;
 using std::vector;
 
 namespace chromeos_update_engine {
 
-void Subprocess::GChildExitedCallback(GPid pid, gint status, gpointer data) {
-  SubprocessRecord* record = reinterpret_cast<SubprocessRecord*>(data);
+namespace {
 
-  // Make sure we read any remaining process output and then close the pipe.
-  OnStdoutReady(record);
+bool SetupChild(const std::map<string, string>& env, uint32_t flags) {
+  // Setup the environment variables.
+  clearenv();
+  for (const auto& key_value : env) {
+    setenv(key_value.first.c_str(), key_value.second.c_str(), 0);
+  }
 
-  MessageLoop::current()->CancelTask(record->task_id);
-  record->task_id = MessageLoop::kTaskIdNull;
-  if (IGNORE_EINTR(close(record->stdout_fd)) != 0) {
-    PLOG(ERROR) << "Error closing fd " << record->stdout_fd;
+  if ((flags & Subprocess::kRedirectStderrToStdout) != 0) {
+    if (HANDLE_EINTR(dup2(STDOUT_FILENO, STDERR_FILENO)) != STDERR_FILENO)
+      return false;
   }
-  g_spawn_close_pid(pid);
-  gint use_status = status;
-  if (WIFEXITED(status))
-    use_status = WEXITSTATUS(status);
 
-  if (status) {
-    LOG(INFO) << "Subprocess status: " << use_status;
-  }
-  if (!record->stdout.empty()) {
-    LOG(INFO) << "Subprocess output:\n" << record->stdout;
-  }
-  if (record->callback) {
-    record->callback(use_status, record->stdout, record->callback_data);
-  }
-  Get().subprocess_records_.erase(record->tag);
+  int fd = HANDLE_EINTR(open("/dev/null", O_RDONLY));
+  if (fd < 0)
+    return false;
+  if (HANDLE_EINTR(dup2(fd, STDIN_FILENO)) != STDIN_FILENO)
+    return false;
+  IGNORE_EINTR(close(fd));
+
+  return true;
 }
 
-void Subprocess::GRedirectStderrToStdout(gpointer user_data) {
-  dup2(1, 2);
+// Helper function to launch a process with the given Subprocess::Flags.
+// This function only sets up and starts the process according to the |flags|.
+// The caller is responsible for watching the termination of the subprocess.
+// Return whether the process was successfully launched and fills in the |proc|
+// Process.
+bool LaunchProcess(const vector<string>& cmd,
+                   uint32_t flags,
+                   chromeos::Process* proc) {
+  for (const string& arg : cmd)
+    proc->AddArg(arg);
+  proc->SetSearchPath((flags & Subprocess::kSearchPath) != 0);
+
+  // Create an environment for the child process with just the required PATHs.
+  std::map<string, string> env;
+  for (const char* key : {"LD_LIBRARY_PATH", "PATH"}) {
+    const char* value = getenv(key);
+    if (value)
+      env.emplace(key, value);
+  }
+
+  proc->RedirectUsingPipe(STDOUT_FILENO, false);
+  proc->SetPreExecCallback(base::Bind(&SetupChild, env, flags));
+
+  return proc->Start();
+}
+
+}  // namespace
+
+void Subprocess::Init(
+      chromeos::AsynchronousSignalHandlerInterface* async_signal_handler) {
+  if (subprocess_singleton_ == this)
+    return;
+  CHECK(subprocess_singleton_ == nullptr);
+  subprocess_singleton_ = this;
+
+  process_reaper_.Register(async_signal_handler);
+}
+
+Subprocess::~Subprocess() {
+  if (subprocess_singleton_ == this)
+    subprocess_singleton_ = nullptr;
 }
 
 void Subprocess::OnStdoutReady(SubprocessRecord* record) {
@@ -71,200 +105,150 @@
       // input as we are in non-blocking mode.
       if (errno != EWOULDBLOCK && errno != EAGAIN) {
         PLOG(ERROR) << "Error reading fd " << record->stdout_fd;
+        MessageLoop::current()->CancelTask(record->stdout_task_id);
+        record->stdout_task_id = MessageLoop::kTaskIdNull;
       }
+    } else if (rc == 0) {
+      // A value of 0 means that the child closed its end of the pipe and there
+      // is nothing else to read from stdout.
+      MessageLoop::current()->CancelTask(record->stdout_task_id);
+      record->stdout_task_id = MessageLoop::kTaskIdNull;
     } else {
       record->stdout.append(buf, rc);
     }
   } while (rc > 0);
 }
 
-namespace {
-void FreeArgv(char** argv) {
-  for (int i = 0; argv[i]; i++) {
-    free(argv[i]);
-    argv[i] = nullptr;
+void Subprocess::ChildExitedCallback(const siginfo_t& info) {
+  auto pid_record = subprocess_records_.find(info.si_pid);
+  if (pid_record == subprocess_records_.end())
+    return;
+  SubprocessRecord* record = pid_record->second.get();
+
+  // Make sure we read any remaining process output and then close the pipe.
+  OnStdoutReady(record);
+
+  MessageLoop::current()->CancelTask(record->stdout_task_id);
+  record->stdout_task_id = MessageLoop::kTaskIdNull;
+
+  // Release and close all the pipes now.
+  record->proc.Release();
+  record->proc.Reset(0);
+
+  // Don't print any log if the subprocess exited with exit code 0.
+  if (info.si_code != CLD_EXITED) {
+    LOG(INFO) << "Subprocess terminated with si_code " << info.si_code;
+  } else if (info.si_status != 0) {
+    LOG(INFO) << "Subprocess exited with si_status: " << info.si_status;
   }
-}
 
-void FreeArgvInError(char** argv) {
-  FreeArgv(argv);
-  LOG(ERROR) << "Ran out of memory copying args.";
-}
-
-// Note: Caller responsible for free()ing the returned value!
-// Will return null on failure and free any allocated memory.
-char** ArgPointer() {
-  const char* keys[] = {"LD_LIBRARY_PATH", "PATH"};
-  char** ret = new char*[arraysize(keys) + 1];
-  int pointer = 0;
-  for (size_t i = 0; i < arraysize(keys); i++) {
-    if (getenv(keys[i])) {
-      ret[pointer] = strdup(base::StringPrintf("%s=%s", keys[i],
-                                               getenv(keys[i])).c_str());
-      if (!ret[pointer]) {
-        FreeArgv(ret);
-        delete [] ret;
-        return nullptr;
-      }
-      ++pointer;
-    }
+  if (!record->stdout.empty()) {
+    LOG(INFO) << "Subprocess output:\n" << record->stdout;
   }
-  ret[pointer] = nullptr;
-  return ret;
-}
-
-class ScopedFreeArgPointer {
- public:
-  explicit ScopedFreeArgPointer(char** arr) : arr_(arr) {}
-  ~ScopedFreeArgPointer() {
-    if (!arr_)
-      return;
-    for (int i = 0; arr_[i]; i++)
-      free(arr_[i]);
-    delete[] arr_;
+  if (!record->callback.is_null()) {
+    record->callback.Run(info.si_status, record->stdout);
   }
- private:
-  char** arr_;
-  DISALLOW_COPY_AND_ASSIGN(ScopedFreeArgPointer);
-};
-}  // namespace
-
-uint32_t Subprocess::Exec(const vector<string>& cmd,
-                          ExecCallback callback,
-                          void* p) {
-  return ExecFlags(cmd, static_cast<GSpawnFlags>(0), true, callback, p);
+  subprocess_records_.erase(pid_record);
 }
 
-uint32_t Subprocess::ExecFlags(const vector<string>& cmd,
-                               GSpawnFlags flags,
-                               bool redirect_stderr_to_stdout,
-                               ExecCallback callback,
-                               void* p) {
-  unique_ptr<gchar*, utils::GLibStrvFreeDeleter> argv(
-       utils::StringVectorToGStrv(cmd));
+pid_t Subprocess::Exec(const vector<string>& cmd,
+                       const ExecCallback& callback) {
+  return ExecFlags(cmd, kRedirectStderrToStdout, callback);
+}
 
-  char** argp = ArgPointer();
-  if (!argp) {
-    FreeArgvInError(argv.get());  // null in argv[i] terminates argv.
+pid_t Subprocess::ExecFlags(const vector<string>& cmd,
+                            uint32_t flags,
+                            const ExecCallback& callback) {
+  unique_ptr<SubprocessRecord> record(new SubprocessRecord(callback));
+
+  if (!LaunchProcess(cmd, flags, &record->proc)) {
+    LOG(ERROR) << "Failed to launch subprocess";
     return 0;
   }
-  ScopedFreeArgPointer argp_free(argp);
 
-  shared_ptr<SubprocessRecord> record(new SubprocessRecord);
-  record->callback = callback;
-  record->callback_data = p;
-  gint stdout_fd = -1;
-  GError* error = nullptr;
-  bool success = g_spawn_async_with_pipes(
-      nullptr,  // working directory
-      argv.get(),
-      argp,
-      static_cast<GSpawnFlags>(flags | G_SPAWN_DO_NOT_REAP_CHILD),  // flags
-      // child setup function:
-      redirect_stderr_to_stdout ? GRedirectStderrToStdout : nullptr,
-      nullptr,  // child setup data pointer
-      &record->pid,
-      nullptr,
-      &stdout_fd,
-      nullptr,
-      &error);
-  if (!success) {
-    LOG(ERROR) << "g_spawn_async failed: " << utils::GetAndFreeGError(&error);
-    return 0;
-  }
-  record->tag =
-      g_child_watch_add(record->pid, GChildExitedCallback, record.get());
-  record->stdout_fd = stdout_fd;
-  subprocess_records_[record->tag] = record;
+  pid_t pid = record->proc.pid();
+  CHECK(process_reaper_.WatchForChild(FROM_HERE, pid, base::Bind(
+      &Subprocess::ChildExitedCallback,
+      base::Unretained(this))));
 
+  record->stdout_fd = record->proc.GetPipe(STDOUT_FILENO);
   // Capture the subprocess output. Make our end of the pipe non-blocking.
-  int fd_flags = fcntl(stdout_fd, F_GETFL, 0) | O_NONBLOCK;
+  int fd_flags = fcntl(record->stdout_fd, F_GETFL, 0) | O_NONBLOCK;
   if (HANDLE_EINTR(fcntl(record->stdout_fd, F_SETFL, fd_flags)) < 0) {
     LOG(ERROR) << "Unable to set non-blocking I/O mode on fd "
                << record->stdout_fd << ".";
   }
 
-  record->task_id = MessageLoop::current()->WatchFileDescriptor(
+  record->stdout_task_id = MessageLoop::current()->WatchFileDescriptor(
       FROM_HERE,
       record->stdout_fd,
       MessageLoop::WatchMode::kWatchRead,
       true,
       base::Bind(&Subprocess::OnStdoutReady, record.get()));
 
-  return record->tag;
+  subprocess_records_[pid].reset(record.release());
+  return pid;
 }
 
-void Subprocess::KillExec(uint32_t tag) {
-  const auto& record = subprocess_records_.find(tag);
-  if (record == subprocess_records_.end())
+void Subprocess::KillExec(pid_t pid) {
+  auto pid_record = subprocess_records_.find(pid);
+  if (pid_record == subprocess_records_.end())
     return;
-  record->second->callback = nullptr;
-  kill(record->second->pid, SIGTERM);
-}
-
-bool Subprocess::SynchronousExecFlags(const vector<string>& cmd,
-                                      GSpawnFlags flags,
-                                      int* return_code,
-                                      string* stdout) {
-  if (stdout) {
-    *stdout = "";
-  }
-  GError* err = nullptr;
-  unique_ptr<char*[]> argv(new char*[cmd.size() + 1]);
-  for (unsigned int i = 0; i < cmd.size(); i++) {
-    argv[i] = strdup(cmd[i].c_str());
-    if (!argv[i]) {
-      FreeArgvInError(argv.get());  // null in argv[i] terminates argv.
-      return false;
-    }
-  }
-  argv[cmd.size()] = nullptr;
-
-  char** argp = ArgPointer();
-  if (!argp) {
-    FreeArgvInError(argv.get());  // null in argv[i] terminates argv.
-    return false;
-  }
-  ScopedFreeArgPointer argp_free(argp);
-
-  char* child_stdout;
-  bool success = g_spawn_sync(
-      nullptr,  // working directory
-      argv.get(),
-      argp,
-      static_cast<GSpawnFlags>(G_SPAWN_STDERR_TO_DEV_NULL |
-                               G_SPAWN_SEARCH_PATH | flags),  // flags
-      GRedirectStderrToStdout,  // child setup function
-      nullptr,  // data for child setup function
-      &child_stdout,
-      nullptr,
-      return_code,
-      &err);
-  FreeArgv(argv.get());
-  LOG_IF(INFO, err) << utils::GetAndFreeGError(&err);
-  if (child_stdout) {
-    if (stdout) {
-      *stdout = child_stdout;
-    } else if (*child_stdout) {
-      LOG(INFO) << "Subprocess output:\n" << child_stdout;
-    }
-    g_free(child_stdout);
-  }
-  return success;
+  pid_record->second->callback.Reset();
+  kill(pid, SIGTERM);
 }
 
 bool Subprocess::SynchronousExec(const vector<string>& cmd,
                                  int* return_code,
                                  string* stdout) {
-  return SynchronousExecFlags(cmd,
-                              static_cast<GSpawnFlags>(0),
-                              return_code,
-                              stdout);
+  // The default for SynchronousExec is to use kSearchPath since the code relies
+  // on that.
+  return SynchronousExecFlags(
+      cmd,
+      kRedirectStderrToStdout | kSearchPath,
+      return_code,
+      stdout);
+}
+
+bool Subprocess::SynchronousExecFlags(const vector<string>& cmd,
+                                      uint32_t flags,
+                                      int* return_code,
+                                      string* stdout) {
+  chromeos::ProcessImpl proc;
+  if (!LaunchProcess(cmd, flags, &proc)) {
+    LOG(ERROR) << "Failed to launch subprocess";
+    return false;
+  }
+
+  if (stdout) {
+    stdout->clear();
+  }
+
+  int fd = proc.GetPipe(STDOUT_FILENO);
+  vector<char> buffer(32 * 1024);
+  while (true) {
+    int rc = HANDLE_EINTR(read(fd, buffer.data(), buffer.size()));
+    if (rc < 0) {
+      PLOG(ERROR) << "Reading from child's output";
+      break;
+    } else if (rc == 0) {
+      break;
+    } else {
+      if (stdout)
+        stdout->append(buffer.data(), rc);
+    }
+  }
+  // At this point, the subprocess already closed the output, so we only need to
+  // wait for it to finish.
+  int proc_return_code = proc.Wait();
+  if (return_code)
+    *return_code = proc_return_code;
+  return proc_return_code != chromeos::Process::kErrorExitStatus;
 }
 
 bool Subprocess::SubprocessInFlight() {
-  for (const auto& tag_record_pair : subprocess_records_) {
-    if (tag_record_pair.second->callback)
+  for (const auto& pid_record : subprocess_records_) {
+    if (!pid_record.second->callback.is_null())
       return true;
   }
   return false;
diff --git a/subprocess.h b/subprocess.h
index 13f7eb7..775b95f 100644
--- a/subprocess.h
+++ b/subprocess.h
@@ -5,16 +5,20 @@
 #ifndef UPDATE_ENGINE_SUBPROCESS_H_
 #define UPDATE_ENGINE_SUBPROCESS_H_
 
-#include <glib.h>
+#include <unistd.h>
 
 #include <map>
 #include <memory>
 #include <string>
 #include <vector>
 
+#include <base/callback.h>
 #include <base/logging.h>
 #include <base/macros.h>
+#include <chromeos/asynchronous_signal_handler_interface.h>
 #include <chromeos/message_loops/message_loop.h>
+#include <chromeos/process.h>
+#include <chromeos/process_reaper.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 // The Subprocess class is a singleton. It's used to spawn off a subprocess
@@ -23,42 +27,51 @@
 // you know you won't call KillExec(), you may safely lose the return value
 // from Exec().
 
+// To create the Subprocess singleton just instantiate it with and call Init().
+// You can't have two Subprocess instances initialized at the same time.
+
 namespace chromeos_update_engine {
 
 class Subprocess {
  public:
-  typedef void(*ExecCallback)(int return_code,
-                              const std::string& output,
-                              void *p);
+  enum Flags {
+    kSearchPath = 1 << 0,
+    kRedirectStderrToStdout = 1 << 1,
+  };
 
-  static void Init() {
-    CHECK(!subprocess_singleton_);
-    subprocess_singleton_ = new Subprocess;
-  }
+  // Callback type used when an async process terminates. It receives the exit
+  // code and the stdout output (and stderr if redirected).
+  using ExecCallback = base::Callback<void(int, const std::string&)>;
 
-  // Returns a tag > 0 on success.
-  uint32_t Exec(const std::vector<std::string>& cmd,
-                ExecCallback callback,
-                void* p);
-  uint32_t ExecFlags(const std::vector<std::string>& cmd,
-                     GSpawnFlags flags,
-                     bool redirect_stderr_to_stdout,
-                     ExecCallback callback,
-                     void* p);
+  Subprocess() = default;
+
+  // Destroy and unregister the Subprocess singleton.
+  ~Subprocess();
+
+  // Initialize and register the Subprocess singleton.
+  void Init(chromeos::AsynchronousSignalHandlerInterface* async_signal_handler);
+
+  // Launches a process in the background and calls the passed |callback| when
+  // the process exits.
+  // Returns the process id of the new launched process or 0 in case of failure.
+  pid_t Exec(const std::vector<std::string>& cmd, const ExecCallback& callback);
+  pid_t ExecFlags(const std::vector<std::string>& cmd,
+                  uint32_t flags,
+                  const ExecCallback& callback);
 
   // Kills the running process with SIGTERM and ignores the callback.
-  void KillExec(uint32_t tag);
+  void KillExec(pid_t tag);
 
   // Executes a command synchronously. Returns true on success. If |stdout| is
   // non-null, the process output is stored in it, otherwise the output is
   // logged. Note that stderr is redirected to stdout.
-  static bool SynchronousExecFlags(const std::vector<std::string>& cmd,
-                                   GSpawnFlags flags,
-                                   int* return_code,
-                                   std::string* stdout);
   static bool SynchronousExec(const std::vector<std::string>& cmd,
                               int* return_code,
                               std::string* stdout);
+  static bool SynchronousExecFlags(const std::vector<std::string>& cmd,
+                                   uint32_t flags,
+                                   int* return_code,
+                                   std::string* stdout);
 
   // Gets the one instance.
   static Subprocess& Get() {
@@ -72,40 +85,41 @@
   FRIEND_TEST(SubprocessTest, CancelTest);
 
   struct SubprocessRecord {
-    SubprocessRecord() = default;
+    explicit SubprocessRecord(const ExecCallback& callback)
+      : callback(callback) {}
 
-    uint32_t tag{0};
-    chromeos::MessageLoop::TaskId task_id{chromeos::MessageLoop::kTaskIdNull};
+    // The callback supplied by the caller.
+    ExecCallback callback;
 
-    ExecCallback callback{nullptr};
-    void* callback_data{nullptr};
+    // The ProcessImpl instance managing the child process. Destroying this
+    // will close our end of the pipes we have open.
+    chromeos::ProcessImpl proc;
 
-    GPid pid;
-
+    // These are used to monitor the stdout of the running process, including
+    // the stderr if it was redirected.
+    chromeos::MessageLoop::TaskId stdout_task_id{
+        chromeos::MessageLoop::kTaskIdNull};
     int stdout_fd{-1};
     std::string stdout;
   };
 
-  Subprocess() {}
-
-  // Callback for when any subprocess terminates. This calls the user
-  // requested callback.
-  static void GChildExitedCallback(GPid pid, gint status, gpointer data);
-
-  // Callback which runs in the child before exec to redirect stderr onto
-  // stdout.
-  static void GRedirectStderrToStdout(gpointer user_data);
-
   // Callback which runs whenever there is input available on the subprocess
   // stdout pipe.
   static void OnStdoutReady(SubprocessRecord* record);
 
+  // Callback for when any subprocess terminates. This calls the user
+  // requested callback.
+  void ChildExitedCallback(const siginfo_t& info);
+
   // The global instance.
   static Subprocess* subprocess_singleton_;
 
   // A map from the asynchronous subprocess tag (see Exec) to the subprocess
   // record structure for all active asynchronous subprocesses.
-  std::map<uint32_t, std::shared_ptr<SubprocessRecord>> subprocess_records_;
+  std::map<pid_t, std::unique_ptr<SubprocessRecord>> subprocess_records_;
+
+  // Used to watch for child processes.
+  chromeos::ProcessReaper process_reaper_;
 
   DISALLOW_COPY_AND_ASSIGN(Subprocess);
 };
diff --git a/subprocess_unittest.cc b/subprocess_unittest.cc
index dba9e6c..d9b212b 100644
--- a/subprocess_unittest.cc
+++ b/subprocess_unittest.cc
@@ -13,19 +13,21 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <set>
 #include <string>
 #include <vector>
 
 #include <base/bind.h>
 #include <base/location.h>
+#include <base/message_loop/message_loop.h>
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <base/time/time.h>
 #include <chromeos/bind_lambda.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/message_loops/base_message_loop.h>
 #include <chromeos/message_loops/message_loop.h>
 #include <chromeos/message_loops/message_loop_utils.h>
-#include <glib.h>
+#include <chromeos/strings/string_utils.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/test_utils.h"
@@ -42,69 +44,84 @@
  protected:
   void SetUp() override {
     loop_.SetAsCurrent();
+    async_signal_handler_.Init();
+    subprocess_.Init(&async_signal_handler_);
   }
 
-  void TearDown() override {
-    EXPECT_EQ(0, chromeos::MessageLoopRunMaxIterations(&loop_, 1));
-  }
-
-  // TODO(deymo): Replace this with a FakeMessageLoop. Subprocess uses glib to
-  // asynchronously spawn a process, so we need to run a GlibMessageLoop here.
-  chromeos::GlibMessageLoop loop_;
+  base::MessageLoopForIO base_loop_;
+  chromeos::BaseMessageLoop loop_{&base_loop_};
+  chromeos::AsynchronousSignalHandler async_signal_handler_;
+  Subprocess subprocess_;
 };
 
 namespace {
+
 int local_server_port = 0;
 
-void Callback(int return_code, const string& output, void* /* unused */) {
-  EXPECT_EQ(1, return_code);
+void ExpectedResults(int expected_return_code, const string& expected_output,
+                     int return_code, const string& output) {
+  EXPECT_EQ(expected_return_code, return_code);
+  EXPECT_EQ(expected_output, output);
   MessageLoop::current()->BreakLoop();
 }
 
-void CallbackEcho(int return_code, const string& output, void* /* unused */) {
+void ExpectedEnvVars(int return_code, const string& output) {
   EXPECT_EQ(0, return_code);
-  EXPECT_NE(string::npos, output.find("this is stdout"));
-  EXPECT_NE(string::npos, output.find("this is stderr"));
-  MessageLoop::current()->BreakLoop();
-}
-
-void CallbackStdoutOnlyEcho(int return_code,
-                            const string& output,
-                            void* /* unused */) {
-  EXPECT_EQ(0, return_code);
-  EXPECT_NE(string::npos, output.find("on stdout"));
-  EXPECT_EQ(string::npos, output.find("on stderr"));
+  const std::set<string> allowed_envs = {"LD_LIBRARY_PATH", "PATH"};
+  for (string key_value : chromeos::string_utils::Split(output, "\n")) {
+    auto key_value_pair = chromeos::string_utils::SplitAtFirst(
+        key_value, "=", true);
+    EXPECT_NE(allowed_envs.end(), allowed_envs.find(key_value_pair.first));
+  }
   MessageLoop::current()->BreakLoop();
 }
 
 }  // namespace
 
+TEST_F(SubprocessTest, IsASingleton) {
+  EXPECT_EQ(&subprocess_, &Subprocess::Get());
+}
+
+TEST_F(SubprocessTest, InactiveInstancesDontChangeTheSingleton) {
+  std::unique_ptr<Subprocess> another_subprocess(new Subprocess());
+  EXPECT_EQ(&subprocess_, &Subprocess::Get());
+  another_subprocess.reset();
+  EXPECT_EQ(&subprocess_, &Subprocess::Get());
+}
+
 TEST_F(SubprocessTest, SimpleTest) {
-  Subprocess::Get().Exec(vector<string>{"/bin/false"}, Callback, nullptr);
+  EXPECT_TRUE(subprocess_.Exec({"/bin/false"},
+                               base::Bind(&ExpectedResults, 1, "")));
   loop_.Run();
 }
 
 TEST_F(SubprocessTest, EchoTest) {
-  Subprocess::Get().Exec(
-      vector<string>{
-          "/bin/sh",
-          "-c",
-          "echo this is stdout; echo this is stderr > /dev/stderr"},
-      CallbackEcho,
-      nullptr);
+  EXPECT_TRUE(subprocess_.Exec(
+      {"/bin/sh", "-c", "echo this is stdout; echo this is stderr >&2"},
+      base::Bind(&ExpectedResults, 0, "this is stdout\nthis is stderr\n")));
   loop_.Run();
 }
 
 TEST_F(SubprocessTest, StderrNotIncludedInOutputTest) {
-  Subprocess::Get().ExecFlags(
-      vector<string>{"/bin/sh", "-c", "echo on stdout; echo on stderr >&2"},
-      static_cast<GSpawnFlags>(0),
-      false,  // don't redirect stderr
-      CallbackStdoutOnlyEcho,
-      nullptr);
+  EXPECT_TRUE(subprocess_.ExecFlags(
+      {"/bin/sh", "-c", "echo on stdout; echo on stderr >&2"},
+      0,
+      base::Bind(&ExpectedResults, 0, "on stdout\n")));
   loop_.Run();
 }
 
+TEST_F(SubprocessTest, EnvVarsAreFiltered) {
+  EXPECT_TRUE(subprocess_.Exec({"/usr/bin/env"}, base::Bind(&ExpectedEnvVars)));
+  loop_.Run();
+}
+
+TEST_F(SubprocessTest, SynchronousTrueSearchsOnPath) {
+  int rc = -1;
+  EXPECT_TRUE(Subprocess::SynchronousExecFlags(
+      {"true"}, Subprocess::kSearchPath, &rc, nullptr));
+  EXPECT_EQ(0, rc);
+}
+
 TEST_F(SubprocessTest, SynchronousEchoTest) {
   vector<string> cmd = {
     "/bin/sh",
@@ -118,15 +135,16 @@
 }
 
 TEST_F(SubprocessTest, SynchronousEchoNoOutputTest) {
-  vector<string> cmd = {"/bin/sh", "-c", "echo test"};
   int rc = -1;
-  ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, nullptr));
+  ASSERT_TRUE(Subprocess::SynchronousExec(
+      {"/bin/sh", "-c", "echo test"},
+      &rc, nullptr));
   EXPECT_EQ(0, rc);
 }
 
 namespace {
-void CallbackBad(int return_code, const string& output, void* p) {
-  CHECK(false) << "should never be called.";
+void CallbackBad(int return_code, const string& output) {
+  ADD_FAILURE() << "should never be called.";
 }
 
 // TODO(garnold) this test method uses test_http_server as a representative for
@@ -148,7 +166,7 @@
   vector<string> cmd;
   cmd.push_back("./test_http_server");
   cmd.push_back(temp_file_name);
-  uint32_t tag = Subprocess::Get().Exec(cmd, CallbackBad, nullptr);
+  uint32_t tag = Subprocess::Get().Exec(cmd, base::Bind(&CallbackBad));
   EXPECT_NE(0, tag);
   *spawned = true;
   printf("test http server spawned\n");
@@ -177,7 +195,7 @@
                 << strerror(errno);
       break;
     }
-    g_usleep(kSleepTime.InMicroseconds());
+    usleep(kSleepTime.InMicroseconds());
     total_wait_time += kSleepTime;
   }
   close(temp_fd);
diff --git a/system_state.h b/system_state.h
index 21aeabd..e32aa0e 100644
--- a/system_state.h
+++ b/system_state.h
@@ -5,6 +5,8 @@
 #ifndef UPDATE_ENGINE_SYSTEM_STATE_H_
 #define UPDATE_ENGINE_SYSTEM_STATE_H_
 
+#include "update_engine/dbus_proxies.h"
+
 class MetricsLibraryInterface;
 
 namespace chromeos_update_manager {
@@ -25,8 +27,7 @@
 // any circular references in header file inclusion. Hence forward-declaring
 // the required classes.
 class ClockInterface;
-class ConnectionManager;
-class GpioHandler;
+class ConnectionManagerInterface;
 class HardwareInterface;
 class OmahaRequestParams;
 class P2PManager;
@@ -54,7 +55,7 @@
   virtual ClockInterface* clock() = 0;
 
   // Gets the connection manager object.
-  virtual ConnectionManager* connection_manager() = 0;
+  virtual ConnectionManagerInterface* connection_manager() = 0;
 
   // Gets the hardware interface object.
   virtual HardwareInterface* hardware() = 0;
@@ -87,6 +88,9 @@
   // Returns a pointer to the UpdateManager singleton.
   virtual chromeos_update_manager::UpdateManager* update_manager() = 0;
 
+  // DBus proxies. Mocked during test.
+  virtual org::chromium::PowerManagerProxyInterface* power_manager_proxy() = 0;
+
   // If true, this is the first instance of the update engine since the system
   // restarted. Important for tracking whether you are running instance of the
   // update engine on first boot or due to a crash/restart.
diff --git a/tar_bunzip2.gypi b/tar_bunzip2.gypi
new file mode 100644
index 0000000..4ba7528
--- /dev/null
+++ b/tar_bunzip2.gypi
@@ -0,0 +1,27 @@
+{
+  'variables': {
+    'out_dir': '<(SHARED_INTERMEDIATE_DIR)/<(image_out_dir)',
+  },
+  'rules': [
+    {
+      'rule_name': 'tar-bunzip2',
+      'extension': 'bz2',
+      'inputs': [
+        '<(RULE_INPUT_PATH)',
+      ],
+      'outputs': [
+        # The .flag file is used to mark the timestamp of the file extraction
+        # and re-run this action if a new .bz2 file is generated.
+        '<(out_dir)/<(RULE_INPUT_ROOT).flag',
+      ],
+      'action': [
+        'sh',
+        '-c',
+        'tar -xvf "<(RULE_INPUT_PATH)" -C "<(out_dir)" && touch <(out_dir)/<(RULE_INPUT_ROOT).flag',
+      ],
+      'msvs_cygwin_shell': 0,
+      'process_outputs_as_sources': 1,
+      'message': 'Unpacking file <(RULE_INPUT_PATH)',
+    },
+  ],
+}
diff --git a/test_utils.cc b/test_utils.cc
index 0c02279..7965f85 100644
--- a/test_utils.cc
+++ b/test_utils.cc
@@ -18,9 +18,10 @@
 #include <vector>
 
 #include <base/files/file_util.h>
+#include <base/format_macros.h>
 #include <base/logging.h>
-#include <base/strings/stringprintf.h>
 #include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
 
 #include "update_engine/file_writer.h"
 #include "update_engine/utils.h"
@@ -168,7 +169,7 @@
                                size_t size,
                                int block_size) {
   EXPECT_EQ(0, System(StringPrintf("dd if=/dev/zero of=%s"
-                                   " seek=%zu bs=1 count=1 status=none",
+                                   " seek=%" PRIuS " bs=1 count=1 status=none",
                                    path.c_str(), size)));
   EXPECT_EQ(0, System(StringPrintf("mkfs.ext3 -q -b %d -F %s",
                                    block_size, path.c_str())));
@@ -302,19 +303,6 @@
   return true;
 }
 
-GValue* GValueNewString(const char* str) {
-  GValue* gval = g_new0(GValue, 1);
-  g_value_init(gval, G_TYPE_STRING);
-  g_value_set_string(gval, str);
-  return gval;
-}
-
-void GValueFree(gpointer arg) {
-  auto gval = reinterpret_cast<GValue*>(arg);
-  g_value_unset(gval);
-  g_free(gval);
-}
-
 base::FilePath GetBuildArtifactsPath() {
   base::FilePath exe_path;
   base::ReadSymbolicLink(base::FilePath("/proc/self/exe"), &exe_path);
diff --git a/test_utils.h b/test_utils.h
index 13bbd41..84aeae1 100644
--- a/test_utils.h
+++ b/test_utils.h
@@ -18,7 +18,6 @@
 
 #include <base/callback.h>
 #include <base/files/file_path.h>
-#include <glib-object.h>
 #include <gtest/gtest.h>
 
 #include "update_engine/action.h"
@@ -183,12 +182,6 @@
 // This WILL cross filesystem boundaries.
 bool RecursiveUnlinkDir(const std::string& path);
 
-// Allocates, initializes and returns a string GValue object.
-GValue* GValueNewString(const char* str);
-
-// Frees a GValue object and its allocated resources.
-void GValueFree(gpointer arg);
-
 // Returns the path where the build artifacts are stored. This is the directory
 // where the unittest executable is being run from.
 base::FilePath GetBuildArtifactsPath();
diff --git a/testrunner.cc b/testrunner.cc
index 4786c74..845f16a 100644
--- a/testrunner.cc
+++ b/testrunner.cc
@@ -7,20 +7,12 @@
 #include <base/at_exit.h>
 #include <base/command_line.h>
 #include <chromeos/test_helpers.h>
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-bindings.h>
-#include <dbus/dbus-glib-lowlevel.h>
-#include <glib.h>
-#include <glib-object.h>
 #include <gtest/gtest.h>
 
-#include "update_engine/subprocess.h"
 #include "update_engine/terminator.h"
 
 int main(int argc, char **argv) {
   LOG(INFO) << "started";
-  ::g_type_init();
-  dbus_threads_init_default();
   base::AtExitManager exit_manager;
   // TODO(garnold) temporarily cause the unittest binary to exit with status
   // code 2 upon catching a SIGTERM. This will help diagnose why the unittest
@@ -28,7 +20,6 @@
   // the default exit status of 1.  Corresponding reverts are necessary in
   // terminator_unittest.cc.
   chromeos_update_engine::Terminator::Init(2);
-  chromeos_update_engine::Subprocess::Init();
   LOG(INFO) << "parsing command line arguments";
   base::CommandLine::Init(argc, argv);
   LOG(INFO) << "initializing gtest";
diff --git a/update_attempter.cc b/update_attempter.cc
index a1b2f11..0e9a5c2 100644
--- a/update_attempter.cc
+++ b/update_attempter.cc
@@ -23,7 +23,6 @@
 #include <chromeos/dbus/service_constants.h>
 #include <chromeos/message_loops/message_loop.h>
 
-#include <glib.h>
 #include <metrics/metrics_library.h>
 #include <policy/device_policy.h>
 #include <policy/libpolicy.h>
@@ -32,10 +31,8 @@
 #include "update_engine/clock_interface.h"
 #include "update_engine/constants.h"
 #include "update_engine/dbus_service.h"
-#include "update_engine/dbus_wrapper_interface.h"
 #include "update_engine/download_action.h"
 #include "update_engine/filesystem_verifier_action.h"
-#include "update_engine/glib_utils.h"
 #include "update_engine/hardware_interface.h"
 #include "update_engine/libcurl_http_fetcher.h"
 #include "update_engine/metrics.h"
@@ -134,18 +131,23 @@
   return code;
 }
 
-UpdateAttempter::UpdateAttempter(SystemState* system_state,
-                                 DBusWrapperInterface* dbus_iface)
-    : UpdateAttempter(system_state, dbus_iface, kUpdateCompletedMarker) {}
+UpdateAttempter::UpdateAttempter(
+    SystemState* system_state,
+    LibCrosProxy* libcros_proxy,
+    org::chromium::debugdProxyInterface* debugd_proxy)
+    : UpdateAttempter(system_state, libcros_proxy, debugd_proxy,
+                      kUpdateCompletedMarker) {}
 
-UpdateAttempter::UpdateAttempter(SystemState* system_state,
-                                 DBusWrapperInterface* dbus_iface,
-                                 const string& update_completed_marker)
+UpdateAttempter::UpdateAttempter(
+    SystemState* system_state,
+    LibCrosProxy* libcros_proxy,
+    org::chromium::debugdProxyInterface* debugd_proxy,
+    const string& update_completed_marker)
     : processor_(new ActionProcessor()),
       system_state_(system_state),
-      dbus_iface_(dbus_iface),
-      chrome_proxy_resolver_(dbus_iface),
-      update_completed_marker_(update_completed_marker) {
+      chrome_proxy_resolver_(libcros_proxy),
+      update_completed_marker_(update_completed_marker),
+      debugd_proxy_(debugd_proxy) {
   if (!update_completed_marker_.empty() &&
       utils::FileExists(update_completed_marker_.c_str())) {
     status_ = UPDATE_STATUS_UPDATED_NEED_REBOOT;
@@ -879,35 +881,17 @@
 }
 
 bool UpdateAttempter::RequestPowerManagerReboot() {
-  GError* error = nullptr;
-  DBusGConnection* bus = dbus_iface_->BusGet(DBUS_BUS_SYSTEM, &error);
-  if (!bus) {
-    LOG(ERROR) << "Failed to get system bus: "
-               << utils::GetAndFreeGError(&error);
+  org::chromium::PowerManagerProxyInterface* power_manager_proxy =
+      system_state_->power_manager_proxy();
+  if (!power_manager_proxy) {
+    LOG(WARNING) << "No PowerManager proxy defined, skipping reboot.";
     return false;
   }
-
   LOG(INFO) << "Calling " << power_manager::kPowerManagerInterface << "."
             << power_manager::kRequestRestartMethod;
-  DBusGProxy* proxy = dbus_iface_->ProxyNewForName(
-      bus,
-      power_manager::kPowerManagerServiceName,
-      power_manager::kPowerManagerServicePath,
-      power_manager::kPowerManagerInterface);
-  const gboolean success = dbus_iface_->ProxyCall_1_0(
-      proxy,
-      power_manager::kRequestRestartMethod,
-      &error,
-      power_manager::REQUEST_RESTART_FOR_UPDATE);
-  dbus_iface_->ProxyUnref(proxy);
-
-  if (!success) {
-    LOG(ERROR) << "Failed to call " << power_manager::kRequestRestartMethod
-               << ": " << utils::GetAndFreeGError(&error);
-    return false;
-  }
-
-  return true;
+  chromeos::ErrorPtr error;
+  return power_manager_proxy->RequestRestart(
+      power_manager::REQUEST_RESTART_FOR_UPDATE, &error);
 }
 
 bool UpdateAttempter::RebootDirectly() {
@@ -1220,13 +1204,21 @@
   // the script runtime.
   update_boot_flags_running_ = true;
   LOG(INFO) << "Updating boot flags...";
-  vector<string> cmd{set_good_kernel_cmd_};
-  if (!Subprocess::Get().Exec(cmd, StaticCompleteUpdateBootFlags, this)) {
-    CompleteUpdateBootFlags(1);
+  vector<string> cmd{"/usr/sbin/chromeos-setgoodkernel"};
+  if (skip_set_good_kernel_) {
+    CompleteUpdateBootFlags(1, "Skipping the call to set");
+  } else {
+    if (!Subprocess::Get().Exec(cmd,
+                                Bind(&UpdateAttempter::CompleteUpdateBootFlags,
+                                     base::Unretained(this)))) {
+      CompleteUpdateBootFlags(
+          1, "Failed to launch process to mark kernel as good");
+    }
   }
 }
 
-void UpdateAttempter::CompleteUpdateBootFlags(int return_code) {
+void UpdateAttempter::CompleteUpdateBootFlags(int return_code,
+                                              const string& output) {
   update_boot_flags_running_ = false;
   updated_boot_flags_ = true;
   if (start_action_processor_) {
@@ -1234,20 +1226,11 @@
   }
 }
 
-void UpdateAttempter::StaticCompleteUpdateBootFlags(
-    int return_code,
-    const string& output,
-    void* p) {
-  reinterpret_cast<UpdateAttempter*>(p)->CompleteUpdateBootFlags(return_code);
-}
-
 void UpdateAttempter::BroadcastStatus() {
-  if (!dbus_service_) {
+  if (!dbus_adaptor_)
     return;
-  }
   last_notify_time_ = TimeTicks::Now();
-  update_engine_service_emit_status_update(
-      dbus_service_,
+  dbus_adaptor_->SendStatusUpdateSignal(
       last_checked_time_,
       download_progress_,
       UpdateStatusToString(status_),
@@ -1650,26 +1633,11 @@
 
   // Official images in devmode are allowed a custom update source iff the
   // debugd dev tools are enabled.
-  GError* error = nullptr;
-  DBusGConnection* bus = dbus_iface_->BusGet(DBUS_BUS_SYSTEM, &error);
-  if (!bus) {
-    LOG(ERROR) << "Failed to get system bus: "
-               << utils::GetAndFreeGError(&error);
+  if (!debugd_proxy_)
     return false;
-  }
-
-  gint dev_features = debugd::DEV_FEATURES_DISABLED;
-  DBusGProxy* proxy = dbus_iface_->ProxyNewForName(
-      bus,
-      debugd::kDebugdServiceName,
-      debugd::kDebugdServicePath,
-      debugd::kDebugdInterface);
-  const gboolean success = dbus_iface_->ProxyCall_0_1(
-      proxy,
-      debugd::kQueryDevFeatures,
-      &error,
-      &dev_features);
-  dbus_iface_->ProxyUnref(proxy);
+  int32_t dev_features = debugd::DEV_FEATURES_DISABLED;
+  chromeos::ErrorPtr error;
+  bool success = debugd_proxy_->QueryDevFeatures(&dev_features, &error);
 
   // Some boards may not include debugd so it's expected that this may fail,
   // in which case we default to disallowing custom update sources.
diff --git a/update_attempter.h b/update_attempter.h
index 1326365..85b06f5 100644
--- a/update_attempter.h
+++ b/update_attempter.h
@@ -14,12 +14,12 @@
 
 #include <base/bind.h>
 #include <base/time/time.h>
-#include <glib.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "update_engine/action_processor.h"
 #include "update_engine/chrome_browser_proxy_resolver.h"
 #include "update_engine/download_action.h"
+#include "update_engine/libcros_proxy.h"
 #include "update_engine/omaha_request_params.h"
 #include "update_engine/omaha_response_handler_action.h"
 #include "update_engine/proxy_resolver.h"
@@ -28,7 +28,6 @@
 #include "update_engine/update_manager/update_manager.h"
 
 class MetricsLibraryInterface;
-struct UpdateEngineService;
 
 namespace policy {
 class PolicyProvider;
@@ -36,7 +35,7 @@
 
 namespace chromeos_update_engine {
 
-class DBusWrapperInterface;
+class UpdateEngineAdaptor;
 
 enum UpdateStatus {
   UPDATE_STATUS_IDLE = 0,
@@ -59,7 +58,8 @@
   static const int kMaxDeltaUpdateFailures;
 
   UpdateAttempter(SystemState* system_state,
-                  DBusWrapperInterface* dbus_iface);
+                  LibCrosProxy* libcros_proxy,
+                  org::chromium::debugdProxyInterface* debugd_proxy);
   ~UpdateAttempter() override;
 
   // Further initialization to be done post construction.
@@ -112,18 +112,15 @@
   void UpdateBootFlags();
 
   // Subprocess::Exec callback.
-  void CompleteUpdateBootFlags(int return_code);
-  static void StaticCompleteUpdateBootFlags(int return_code,
-                                            const std::string& output,
-                                            void* p);
+  void CompleteUpdateBootFlags(int return_code, const std::string& output);
 
   UpdateStatus status() const { return status_; }
 
   int http_response_code() const { return http_response_code_; }
   void set_http_response_code(int code) { http_response_code_ = code; }
 
-  void set_dbus_service(struct UpdateEngineService* dbus_service) {
-    dbus_service_ = dbus_service;
+  void set_dbus_adaptor(UpdateEngineAdaptor* dbus_adaptor) {
+    dbus_adaptor_ = dbus_adaptor;
   }
 
   // This is the internal entry point for going through an
@@ -225,7 +222,8 @@
 
   // Special ctor + friend declarations for testing purposes.
   UpdateAttempter(SystemState* system_state,
-                  DBusWrapperInterface* dbus_iface,
+                  LibCrosProxy* libcros_proxy,
+                  org::chromium::debugdProxyInterface* debugd_proxy,
                   const std::string& update_completed_marker);
 
   friend class UpdateAttempterUnderTest;
@@ -398,12 +396,9 @@
   // carved out separately to mock out easily in unit tests.
   SystemState* system_state_;
 
-  // Interface for getting D-Bus connections.
-  DBusWrapperInterface* dbus_iface_ = nullptr;
-
   // If non-null, this UpdateAttempter will send status updates over this
   // dbus service.
-  UpdateEngineService* dbus_service_ = nullptr;
+  UpdateEngineAdaptor* dbus_adaptor_ = nullptr;
 
   // Pointer to the OmahaResponseHandlerAction in the actions_ vector.
   std::shared_ptr<OmahaResponseHandlerAction> response_handler_action_;
@@ -469,8 +464,9 @@
   // True if UpdateBootFlags is running.
   bool update_boot_flags_running_ = false;
 
-  // The command to run to set the current kernel as good.
-  std::string set_good_kernel_cmd_ = "/usr/sbin/chromeos-setgoodkernel";
+  // Whether we should skip the async call to "setgoodkernel" command. Used in
+  // unittests.
+  bool skip_set_good_kernel_ = false;
 
   // True if the action processor needs to be started by the boot flag updater.
   bool start_action_processor_ = false;
@@ -509,6 +505,8 @@
   std::string forced_app_version_;
   std::string forced_omaha_url_;
 
+  org::chromium::debugdProxyInterface* debugd_proxy_;
+
   DISALLOW_COPY_AND_ASSIGN(UpdateAttempter);
 };
 
diff --git a/update_attempter_unittest.cc b/update_attempter_unittest.cc
index 41a8232..19f2081 100644
--- a/update_attempter_unittest.cc
+++ b/update_attempter_unittest.cc
@@ -9,9 +9,11 @@
 #include <memory>
 
 #include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
 #include <chromeos/bind_lambda.h>
 #include <chromeos/dbus/service_constants.h>
-#include <chromeos/message_loops/glib_message_loop.h>
+#include <chromeos/make_unique_ptr.h>
+#include <chromeos/message_loops/base_message_loop.h>
 #include <chromeos/message_loops/message_loop.h>
 #include <chromeos/message_loops/message_loop_utils.h>
 #include <gtest/gtest.h>
@@ -25,7 +27,6 @@
 #include "update_engine/install_plan.h"
 #include "update_engine/mock_action.h"
 #include "update_engine/mock_action_processor.h"
-#include "update_engine/mock_dbus_wrapper.h"
 #include "update_engine/mock_http_fetcher.h"
 #include "update_engine/mock_p2p_manager.h"
 #include "update_engine/mock_payload_state.h"
@@ -38,9 +39,10 @@
 using base::Time;
 using base::TimeDelta;
 using chromeos::MessageLoop;
+using org::chromium::LibCrosServiceInterfaceProxyMock;
+using org::chromium::UpdateEngineLibcrosProxyResolvedInterfaceProxyMock;
 using std::string;
 using std::unique_ptr;
-using testing::A;
 using testing::DoAll;
 using testing::InSequence;
 using testing::Ne;
@@ -50,7 +52,6 @@
 using testing::ReturnPointee;
 using testing::SaveArg;
 using testing::SetArgumentPointee;
-using testing::StrEq;
 using testing::_;
 
 namespace chromeos_update_engine {
@@ -60,17 +61,12 @@
 // methods.
 class UpdateAttempterUnderTest : public UpdateAttempter {
  public:
-  // We always feed an explicit update completed marker name; however, unless
-  // explicitly specified, we feed an empty string, which causes the
-  // UpdateAttempter class to ignore / not write the marker file.
   UpdateAttempterUnderTest(SystemState* system_state,
-                           DBusWrapperInterface* dbus_iface)
-      : UpdateAttempterUnderTest(system_state, dbus_iface, "") {}
-
-  UpdateAttempterUnderTest(SystemState* system_state,
-                           DBusWrapperInterface* dbus_iface,
+                           LibCrosProxy* libcros_proxy,
+                           org::chromium::debugdProxyInterface* debugd_proxy,
                            const string& update_completed_marker)
-      : UpdateAttempter(system_state, dbus_iface, update_completed_marker) {}
+      : UpdateAttempter(system_state, libcros_proxy, debugd_proxy,
+                        update_completed_marker) {}
 
   // Wrap the update scheduling method, allowing us to opt out of scheduled
   // updates for testing purposes.
@@ -99,10 +95,12 @@
 class UpdateAttempterTest : public ::testing::Test {
  protected:
   UpdateAttempterTest()
-      : attempter_(&fake_system_state_, &dbus_),
-        mock_connection_manager(&fake_system_state_),
-        fake_dbus_system_bus_(reinterpret_cast<DBusGConnection*>(1)),
-        fake_dbus_debugd_proxy_(reinterpret_cast<DBusGProxy*>(2)) {
+      : service_interface_mock_(new LibCrosServiceInterfaceProxyMock()),
+        ue_proxy_resolved_interface_mock_(
+            new NiceMock<UpdateEngineLibcrosProxyResolvedInterfaceProxyMock>()),
+        libcros_proxy_(
+            chromeos::make_unique_ptr(service_interface_mock_),
+            chromeos::make_unique_ptr(ue_proxy_resolved_interface_mock_)) {
     // Override system state members.
     fake_system_state_.set_connection_manager(&mock_connection_manager);
     fake_system_state_.set_update_attempter(&attempter_);
@@ -110,18 +108,14 @@
 
     // Finish initializing the attempter.
     attempter_.Init();
-
-    // We set the set_good_kernel command to a non-existent path so it fails to
-    // run it. This avoids the async call to the command and continues the
-    // update process right away. Tests testing that behavior can override the
-    // default set_good_kernel command if needed.
-    attempter_.set_good_kernel_cmd_ = "/path/to/non-existent/command";
+    // Don't run setgoodkernel command.
+    attempter_.skip_set_good_kernel_ = true;
   }
 
   void SetUp() override {
     CHECK(utils::MakeTempDirectory("UpdateAttempterTest-XXXXXX", &test_dir_));
 
-    EXPECT_EQ(nullptr, attempter_.dbus_service_);
+    EXPECT_EQ(nullptr, attempter_.dbus_adaptor_);
     EXPECT_NE(nullptr, attempter_.system_state_);
     EXPECT_EQ(0, attempter_.http_response_code_);
     EXPECT_EQ(utils::kCpuSharesNormal, attempter_.shares_);
@@ -151,21 +145,10 @@
     EXPECT_CALL(*fake_system_state_.mock_payload_state(),
                 GetUsingP2PForDownloading())
         .WillRepeatedly(ReturnPointee(&actual_using_p2p_for_sharing_));
-
-    // Set up mock debugd access over the system D-Bus. ProxyCall_0_1() also
-    // needs to be mocked in any test using debugd to provide the desired value.
-    ON_CALL(dbus_, BusGet(DBUS_BUS_SYSTEM, _))
-        .WillByDefault(Return(fake_dbus_system_bus_));
-    ON_CALL(dbus_, ProxyNewForName(fake_dbus_system_bus_,
-                                   StrEq(debugd::kDebugdServiceName),
-                                   StrEq(debugd::kDebugdServicePath),
-                                   StrEq(debugd::kDebugdInterface)))
-        .WillByDefault(Return(fake_dbus_debugd_proxy_));
   }
 
   void TearDown() override {
     test_utils::RecursiveUnlinkDir(test_dir_);
-    EXPECT_EQ(0, MessageLoopRunMaxIterations(&loop_, 1));
   }
 
  public:
@@ -193,20 +176,23 @@
     return actual_using_p2p_for_sharing_;
   }
 
-  // TODO(deymo): Replace this with a FakeMessageLoop. Some of these tests use a
-  // real LibcurlHttpFetcher, which still requires a GlibMessageLoop.
-  chromeos::GlibMessageLoop loop_;
+  base::MessageLoopForIO base_loop_;
+  chromeos::BaseMessageLoop loop_{&base_loop_};
 
   FakeSystemState fake_system_state_;
-  NiceMock<MockDBusWrapper> dbus_;
-  UpdateAttempterUnderTest attempter_;
+  org::chromium::debugdProxyMock debugd_proxy_mock_;
+  LibCrosServiceInterfaceProxyMock* service_interface_mock_;
+  UpdateEngineLibcrosProxyResolvedInterfaceProxyMock*
+      ue_proxy_resolved_interface_mock_;
+  LibCrosProxy libcros_proxy_;
+  UpdateAttempterUnderTest attempter_{&fake_system_state_,
+                                      &libcros_proxy_,
+                                      &debugd_proxy_mock_,
+                                      ""};
+
   NiceMock<MockActionProcessor>* processor_;
   NiceMock<MockPrefs>* prefs_;  // Shortcut to fake_system_state_->mock_prefs().
   NiceMock<MockConnectionManager> mock_connection_manager;
-  // fake_dbus_xxx pointers will be non-null for comparison purposes, but won't
-  // be valid objects so don't try to use them.
-  DBusGConnection* fake_dbus_system_bus_;
-  DBusGProxy* fake_dbus_debugd_proxy_;
 
   string test_dir_;
 
@@ -257,15 +243,18 @@
   ASSERT_TRUE(attempter_.error_event_.get() == nullptr);
 }
 
-TEST_F(UpdateAttempterTest, RunAsRootConstructWithUpdatedMarkerTest) {
+TEST_F(UpdateAttempterTest, ConstructWithUpdatedMarkerTest) {
   string test_update_completed_marker;
   CHECK(utils::MakeTempFile(
-          "update_attempter_unittest-update_completed_marker-XXXXXX",
-          &test_update_completed_marker, nullptr));
+      "update_attempter_unittest-update_completed_marker-XXXXXX",
+      &test_update_completed_marker,
+      nullptr));
   ScopedPathUnlinker completed_marker_unlinker(test_update_completed_marker);
   const base::FilePath marker(test_update_completed_marker);
   EXPECT_EQ(0, base::WriteFile(marker, "", 0));
-  UpdateAttempterUnderTest attempter(&fake_system_state_, &dbus_,
+  UpdateAttempterUnderTest attempter(&fake_system_state_,
+                                     nullptr,
+                                     &debugd_proxy_mock_,
                                      test_update_completed_marker);
   EXPECT_EQ(UPDATE_STATUS_UPDATED_NEED_REBOOT, attempter.status());
 }
@@ -514,9 +503,7 @@
 }
 
 TEST_F(UpdateAttempterTest, UpdateTest) {
-  loop_.PostTask(FROM_HERE,
-                 base::Bind(&UpdateAttempterTest::UpdateTestStart,
-                            base::Unretained(this)));
+  UpdateTestStart();
   loop_.Run();
 }
 
@@ -938,8 +925,10 @@
 
 TEST_F(UpdateAttempterTest, BootTimeInUpdateMarkerFile) {
   const string update_completed_marker = test_dir_ + "/update-completed-marker";
-  UpdateAttempterUnderTest attempter(&fake_system_state_, &dbus_,
-                                     update_completed_marker);
+  UpdateAttempterUnderTest attempter{&fake_system_state_,
+                                     nullptr,  // libcros_proxy
+                                     &debugd_proxy_mock_,
+                                     update_completed_marker};
 
   FakeClock fake_clock;
   fake_clock.SetBootTime(Time::FromTimeT(42));
@@ -962,11 +951,8 @@
 TEST_F(UpdateAttempterTest, AnyUpdateSourceAllowedOfficialDevmode) {
   fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
   fake_system_state_.fake_hardware()->SetIsNormalBootMode(false);
-  EXPECT_CALL(dbus_, ProxyCall_0_1(fake_dbus_debugd_proxy_,
-                                   StrEq(debugd::kQueryDevFeatures),
-                                   _, A<gint*>()))
-      .WillRepeatedly(DoAll(SetArgumentPointee<3>(0),
-                            Return(true)));
+  EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _))
+      .WillRepeatedly(DoAll(SetArgumentPointee<0>(0), Return(true)));
   EXPECT_TRUE(attempter_.IsAnyUpdateSourceAllowed());
 }
 
@@ -974,10 +960,7 @@
   fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
   fake_system_state_.fake_hardware()->SetIsNormalBootMode(true);
   // debugd should not be queried in this case.
-  EXPECT_CALL(dbus_, ProxyCall_0_1(fake_dbus_debugd_proxy_,
-                                   StrEq(debugd::kQueryDevFeatures),
-                                   _, A<gint*>()))
-      .Times(0);
+  EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _)).Times(0);
   EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
 }
 
@@ -985,20 +968,16 @@
   using debugd::DEV_FEATURES_DISABLED;
   fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
   fake_system_state_.fake_hardware()->SetIsNormalBootMode(false);
-  EXPECT_CALL(dbus_, ProxyCall_0_1(fake_dbus_debugd_proxy_,
-                                   StrEq(debugd::kQueryDevFeatures),
-                                   _, A<gint*>()))
-      .WillRepeatedly(DoAll(SetArgumentPointee<3>(DEV_FEATURES_DISABLED),
-                            Return(true)));
+  EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _))
+      .WillRepeatedly(
+          DoAll(SetArgumentPointee<0>(DEV_FEATURES_DISABLED), Return(true)));
   EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
 }
 
 TEST_F(UpdateAttempterTest, AnyUpdateSourceDisallowedDebugdFailure) {
   fake_system_state_.fake_hardware()->SetIsOfficialBuild(true);
   fake_system_state_.fake_hardware()->SetIsNormalBootMode(false);
-  EXPECT_CALL(dbus_, ProxyCall_0_1(fake_dbus_debugd_proxy_,
-                                   StrEq(debugd::kQueryDevFeatures),
-                                   _, A<gint*>()))
+  EXPECT_CALL(debugd_proxy_mock_, QueryDevFeatures(_, _, _))
       .WillRepeatedly(Return(false));
   EXPECT_FALSE(attempter_.IsAnyUpdateSourceAllowed());
 }
diff --git a/update_engine.gyp b/update_engine.gyp
index d3232a3..9bead7b 100644
--- a/update_engine.gyp
+++ b/update_engine.gyp
@@ -58,19 +58,40 @@
       ],
       'includes': ['../common-mk/protoc.gypi'],
     },
-    # D-Bus glib bindings.
+    # Chrome D-Bus bindings.
     {
-      'target_name': 'update_engine-dbus-server',
+      'target_name': 'update_engine-dbus-adaptor',
       'type': 'none',
       'variables': {
-        'dbus_glib_type': 'server',
-        'dbus_glib_out_dir': 'include/update_engine',
-        'dbus_glib_prefix': 'update_engine_service',
+        'dbus_adaptors_out_dir': 'include/update_engine/dbus_adaptor',
       },
       'sources': [
         'dbus_bindings/org.chromium.UpdateEngineInterface.xml',
       ],
-      'includes': ['../common-mk/dbus_glib.gypi'],
+      'includes': ['../common-mk/generate-dbus-adaptors.gypi'],
+    },
+    {
+      'target_name': 'update_engine-dbus-proxies',
+      'type': 'none',
+      'actions': [
+        {
+          'action_name': 'update_engine-dbus-proxies-action',
+          'variables': {
+            'dbus_service_config': '',
+            'mock_output_file': 'include/update_engine/dbus_mocks.h',
+            'proxy_output_file': 'include/update_engine/dbus_proxies.h'
+          },
+          'sources': [
+            '../debugd/share/org.chromium.debugd.xml',
+            '../login_manager/org.chromium.SessionManagerInterface.xml',
+            '../power_manager/dbus_bindings/org.chromium.PowerManager.xml',
+            '../shill/dbus_bindings/org.chromium.flimflam.Manager.xml',
+            '../shill/dbus_bindings/org.chromium.flimflam.Service.xml',
+            'dbus_bindings/org.chromium.LibCrosService.xml',
+          ],
+          'includes': ['../common-mk/generate-dbus-proxies.gypi'],
+        },
+      ],
     },
     # The main static_library with all the code.
     {
@@ -78,13 +99,12 @@
       'type': 'static_library',
       'dependencies': [
         'update_metadata-protos',
+        'update_engine-dbus-adaptor',
+        'update_engine-dbus-proxies',
       ],
       'variables': {
         'exported_deps': [
           'dbus-1',
-          'dbus-glib-1',
-          'glib-2.0',
-          'gthread-2.0',
           'libchrome-<(libbase_ver)',
           'libchromeos-<(libbase_ver)',
           'libcrypto',
@@ -125,6 +145,7 @@
         'clock.cc',
         'connection_manager.cc',
         'constants.cc',
+        'daemon.cc',
         'dbus_service.cc',
         'delta_performer.cc',
         'download_action.cc',
@@ -132,12 +153,12 @@
         'file_descriptor.cc',
         'file_writer.cc',
         'filesystem_verifier_action.cc',
-        'glib_utils.cc',
         'hardware.cc',
         'http_common.cc',
         'http_fetcher.cc',
         'hwid_override.cc',
         'install_plan.cc',
+        'libcros_proxy.cc',
         'libcurl_http_fetcher.cc',
         'metrics.cc',
         'multi_range_http_fetcher.cc',
@@ -153,6 +174,7 @@
         'prefs.cc',
         'proxy_resolver.cc',
         'real_system_state.cc',
+        'shill_proxy.cc',
         'subprocess.cc',
         'terminator.cc',
         'update_attempter.cc',
@@ -191,7 +213,6 @@
       'type': 'executable',
       'dependencies': [
         'libupdate_engine',
-        'update_engine-dbus-server',
       ],
       'sources': [
         'main.cc',
@@ -220,10 +241,10 @@
       ],
       'actions': [
         {
-          'action_name': 'update_engine-dbus-proxies',
+          'action_name': 'update_engine_client-dbus-proxies',
           'variables': {
             'dbus_service_config': 'dbus_bindings/dbus-service-config.json',
-            'proxy_output_file': 'include/update_engine/dbus_proxies.h'
+            'proxy_output_file': 'include/update_engine/client_dbus_proxies.h'
           },
           'sources': [
             'dbus_bindings/org.chromium.UpdateEngineInterface.xml',
@@ -266,6 +287,8 @@
       'sources': [
         'payload_generator/ab_generator.cc',
         'payload_generator/annotated_operation.cc',
+        'payload_generator/blob_file_writer.cc',
+        'payload_generator/block_mapping.cc',
         'payload_generator/cycle_breaker.cc',
         'payload_generator/delta_diff_generator.cc',
         'payload_generator/delta_diff_utils.cc',
@@ -320,7 +343,7 @@
           ],
           'includes': ['../common-mk/openssl_pem.gypi'],
         },
-        # Sample images used for testing.
+        # Unpacks sample images used for testing.
         {
           'target_name': 'update_engine-test_images',
           'type': 'none',
@@ -328,11 +351,9 @@
             'image_out_dir': '.',
           },
           'sources': [
-            'sample_images/disk_ext2_1k.txt',
-            'sample_images/disk_ext2_4k.txt',
-            'sample_images/disk_ext2_ue_settings.txt',
+            'sample_images/sample_images.tar.bz2',
           ],
-          'includes': ['generate_image.gypi'],
+          'includes': ['tar_bunzip2.gypi'],
         },
         # Test HTTP Server.
         {
@@ -367,10 +388,12 @@
             'certificate_checker_unittest.cc',
             'chrome_browser_proxy_resolver_unittest.cc',
             'connection_manager_unittest.cc',
+            'dbus_service_unittest.cc',
             'delta_performer_unittest.cc',
             'download_action_unittest.cc',
             'extent_writer_unittest.cc',
             'fake_prefs.cc',
+            'fake_shill_proxy.cc',
             'fake_system_state.cc',
             'file_writer_unittest.cc',
             'filesystem_verifier_action_unittest.cc',
@@ -383,6 +406,8 @@
             'omaha_response_handler_action_unittest.cc',
             'p2p_manager_unittest.cc',
             'payload_generator/ab_generator_unittest.cc',
+            'payload_generator/blob_file_writer_unittest.cc',
+            'payload_generator/block_mapping_unittest.cc',
             'payload_generator/cycle_breaker_unittest.cc',
             'payload_generator/delta_diff_utils_unittest.cc',
             'payload_generator/ext2_filesystem_unittest.cc',
diff --git a/update_engine_client.cc b/update_engine_client.cc
index d0fee7a..a2470f3 100644
--- a/update_engine_client.cc
+++ b/update_engine_client.cc
@@ -17,8 +17,8 @@
 #include <chromeos/flag_helper.h>
 #include <dbus/bus.h>
 
+#include "update_engine/client_dbus_proxies.h"
 #include "update_engine/dbus_constants.h"
-#include "update_engine/dbus_proxies.h"
 
 using chromeos_update_engine::kAttemptUpdateFlagNonInteractive;
 using chromeos_update_engine::kUpdateEngineServiceName;
diff --git a/update_manager/real_device_policy_provider.cc b/update_manager/real_device_policy_provider.cc
index 6f484f7..3586cc8 100644
--- a/update_manager/real_device_policy_provider.cc
+++ b/update_manager/real_device_policy_provider.cc
@@ -12,7 +12,6 @@
 #include <chromeos/dbus/service_constants.h>
 #include <policy/device_policy.h>
 
-#include "update_engine/glib_utils.h"
 #include "update_engine/update_manager/generic_variables.h"
 #include "update_engine/update_manager/real_shill_provider.h"
 #include "update_engine/utils.h"
@@ -33,12 +32,6 @@
 
 RealDevicePolicyProvider::~RealDevicePolicyProvider() {
   MessageLoop::current()->CancelTask(scheduled_refresh_);
-  // Detach signal handler, free manager proxy.
-  dbus_->ProxyDisconnectSignal(manager_proxy_,
-                               login_manager::kPropertyChangeCompleteSignal,
-                               G_CALLBACK(HandlePropertyChangedCompletedStatic),
-                               this);
-  dbus_->ProxyUnref(manager_proxy_);
 }
 
 bool RealDevicePolicyProvider::Init() {
@@ -49,38 +42,39 @@
 
   // We also listen for signals from the session manager to force a device
   // policy refresh.
-  GError* error = nullptr;
-  DBusGConnection* connection = dbus_->BusGet(DBUS_BUS_SYSTEM, &error);
-  if (!connection) {
-    LOG(ERROR) << "Failed to initialize DBus connection: "
-               << chromeos_update_engine::utils::GetAndFreeGError(&error);
-    return false;
-  }
-  manager_proxy_ = dbus_->ProxyNewForName(
-      connection,
-      login_manager::kSessionManagerServiceName,
-      login_manager::kSessionManagerServicePath,
-      login_manager::kSessionManagerInterface);
-
-  // Subscribe to the session manager's PropertyChangeComplete signal.
-  dbus_->ProxyAddSignal_1(manager_proxy_,
-                          login_manager::kPropertyChangeCompleteSignal,
-                          G_TYPE_STRING);
-  dbus_->ProxyConnectSignal(manager_proxy_,
-                            login_manager::kPropertyChangeCompleteSignal,
-                            G_CALLBACK(HandlePropertyChangedCompletedStatic),
-                            this, nullptr);
+  session_manager_proxy_->RegisterPropertyChangeCompleteSignalHandler(
+      base::Bind(&RealDevicePolicyProvider::OnPropertyChangedCompletedSignal,
+                 base::Unretained(this)),
+      base::Bind(&RealDevicePolicyProvider::OnSignalConnected,
+                 base::Unretained(this)));
   return true;
 }
 
-// static
-void RealDevicePolicyProvider::HandlePropertyChangedCompletedStatic(
-    DBusGProxy* proxy, const char* /* payload */, void* data) {
+void RealDevicePolicyProvider::OnPropertyChangedCompletedSignal(
+    const string& success) {
+  if (success != "success") {
+    LOG(WARNING) << "Received device policy updated signal with a failure.";
+  }
   // We refresh the policy file even if the payload string is kSignalFailure.
-  RealDevicePolicyProvider* policy_provider =
-      reinterpret_cast<RealDevicePolicyProvider*>(data);
-  LOG(INFO) << "Reloading device policy due to signal received.";
-  policy_provider->RefreshDevicePolicy();
+  LOG(INFO) << "Reloading and re-scheduling device policy due to signal "
+               "received.";
+  MessageLoop::current()->CancelTask(scheduled_refresh_);
+  scheduled_refresh_ = MessageLoop::kTaskIdNull;
+  RefreshDevicePolicyAndReschedule();
+}
+
+void RealDevicePolicyProvider::OnSignalConnected(const string& interface_name,
+                                                 const string& signal_name,
+                                                 bool successful) {
+  if (!successful) {
+    LOG(WARNING) << "We couldn't connect to SessionManager signal for updates "
+                    "on the device policy blob. We will reload the policy file "
+                    "periodically.";
+  }
+  // We do a one-time refresh of the DevicePolicy just in case we missed a
+  // signal between the first refresh and the time the signal handler was
+  // actually connected.
+  RefreshDevicePolicy();
 }
 
 void RealDevicePolicyProvider::RefreshDevicePolicyAndReschedule() {
diff --git a/update_manager/real_device_policy_provider.h b/update_manager/real_device_policy_provider.h
index b052d59..41b67c2 100644
--- a/update_manager/real_device_policy_provider.h
+++ b/update_manager/real_device_policy_provider.h
@@ -12,7 +12,7 @@
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 #include <policy/libpolicy.h>
 
-#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/dbus_proxies.h"
 #include "update_engine/update_manager/device_policy_provider.h"
 #include "update_engine/update_manager/generic_variables.h"
 
@@ -21,11 +21,11 @@
 // DevicePolicyProvider concrete implementation.
 class RealDevicePolicyProvider : public DevicePolicyProvider {
  public:
-  RealDevicePolicyProvider(
-      chromeos_update_engine::DBusWrapperInterface* const dbus,
-      policy::PolicyProvider* policy_provider)
+  RealDevicePolicyProvider(org::chromium::SessionManagerInterfaceProxyInterface*
+                               session_manager_proxy,
+                           policy::PolicyProvider* policy_provider)
       : policy_provider_(policy_provider),
-        dbus_(dbus) {}
+        session_manager_proxy_(session_manager_proxy) {}
   ~RealDevicePolicyProvider();
 
   // Initializes the provider and returns whether it succeeded.
@@ -79,9 +79,13 @@
 
   // A static handler for the PropertyChangedCompleted signal from the session
   // manager used as a callback.
-  static void HandlePropertyChangedCompletedStatic(DBusGProxy* proxy,
-                                                   const char* payload,
-                                                   void* data);
+  void OnPropertyChangedCompletedSignal(const std::string& success);
+
+  // Called when the signal in UpdateEngineLibcrosProxyResolvedInterface is
+  // connected.
+  void OnSignalConnected(const std::string& interface_name,
+                         const std::string& signal_name,
+                         bool successful);
 
   // Schedules a call to periodically refresh the device policy.
   void RefreshDevicePolicyAndReschedule();
@@ -116,12 +120,12 @@
   policy::PolicyProvider* policy_provider_;
 
   // Used to schedule refreshes of the device policy.
-  chromeos::MessageLoop::TaskId scheduled_refresh_ =
-      chromeos::MessageLoop::kTaskIdNull;
+  chromeos::MessageLoop::TaskId scheduled_refresh_{
+      chromeos::MessageLoop::kTaskIdNull};
 
-  // The DBus interface (mockable) and a session manager proxy.
-  chromeos_update_engine::DBusWrapperInterface* const dbus_;
-  DBusGProxy* manager_proxy_ = nullptr;
+  // The DBus (mockable) session manager proxy, owned by the caller.
+  org::chromium::SessionManagerInterfaceProxyInterface* session_manager_proxy_{
+      nullptr};
 
   // Variable exposing whether the policy is loaded.
   AsyncCopyVariable<bool> var_device_policy_is_loaded_{
diff --git a/update_manager/real_device_policy_provider_unittest.cc b/update_manager/real_device_policy_provider_unittest.cc
index 75d7c48..e909b9c 100644
--- a/update_manager/real_device_policy_provider_unittest.cc
+++ b/update_manager/real_device_policy_provider_unittest.cc
@@ -10,17 +10,19 @@
 #include <chromeos/message_loops/fake_message_loop.h>
 #include <chromeos/message_loops/message_loop.h>
 #include <chromeos/message_loops/message_loop_utils.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <policy/mock_device_policy.h>
 #include <policy/mock_libpolicy.h>
 
-#include "update_engine/mock_dbus_wrapper.h"
+#include "update_engine/dbus_mocks.h"
+#include "update_engine/dbus_test_utils.h"
 #include "update_engine/test_utils.h"
 #include "update_engine/update_manager/umtest_utils.h"
 
 using base::TimeDelta;
 using chromeos::MessageLoop;
-using chromeos::MessageLoopRunMaxIterations;
+using chromeos_update_engine::dbus_test_utils::MockSignalHandler;
 using std::set;
 using std::string;
 using std::unique_ptr;
@@ -28,50 +30,31 @@
 using testing::Mock;
 using testing::Return;
 using testing::ReturnRef;
-using testing::SaveArg;
-using testing::SetArgumentPointee;
-using testing::StrEq;
+using testing::SetArgPointee;
 using testing::_;
 
-namespace {
-
-// Fake dbus-glib objects. These should be different values, to ease diagnosis
-// of errors.
-DBusGConnection* const kFakeConnection = reinterpret_cast<DBusGConnection*>(1);
-DBusGProxy* const kFakeManagerProxy = reinterpret_cast<DBusGProxy*>(2);
-
-}  // namespace
-
 namespace chromeos_update_manager {
 
 class UmRealDevicePolicyProviderTest : public ::testing::Test {
  protected:
   void SetUp() override {
     loop_.SetAsCurrent();
-    provider_.reset(new RealDevicePolicyProvider(&mock_dbus_,
+    provider_.reset(new RealDevicePolicyProvider(&session_manager_proxy_mock_,
                                                  &mock_policy_provider_));
     // By default, we have a device policy loaded. Tests can call
     // SetUpNonExistentDevicePolicy() to override this.
     SetUpExistentDevicePolicy();
 
-    SetUpDBusSignalExpectations();
+    // Setup the session manager_proxy such that it will accept the signal
+    // handler and store it in the |property_change_complete_| once registered.
+    MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(property_change_complete_,
+                                              session_manager_proxy_mock_,
+                                              PropertyChangeComplete);
   }
 
   void TearDown() override {
-    // Check for leaked callbacks on the main loop.
-    EXPECT_EQ(0, MessageLoopRunMaxIterations(MessageLoop::current(), 100));
-
-    // We need to set these expectation before the object is destroyed but
-    // after it finished running the test so the values of signal_callback_ and
-    // signal_callback_data_ are correct.
-    EXPECT_CALL(mock_dbus_, ProxyDisconnectSignal(
-            kFakeManagerProxy,
-            StrEq(login_manager::kPropertyChangeCompleteSignal),
-            signal_callback_,
-            signal_callback_data_));
-    EXPECT_CALL(mock_dbus_, ProxyUnref(kFakeManagerProxy));
-
     provider_.reset();
+    // Check for leaked callbacks on the main loop.
     EXPECT_FALSE(loop_.PendingTasks());
   }
 
@@ -93,58 +76,40 @@
         .WillByDefault(ReturnRef(mock_device_policy_));
   }
 
-  void SetUpDBusSignalExpectations() {
-    // Setup the DBus connection with default actions that should be performed
-    // once.
-    EXPECT_CALL(mock_dbus_, BusGet(_, _)).WillOnce(
-        Return(kFakeConnection));
-    EXPECT_CALL(mock_dbus_, ProxyNewForName(
-            kFakeConnection, StrEq(login_manager::kSessionManagerServiceName),
-            StrEq(login_manager::kSessionManagerServicePath),
-            StrEq(login_manager::kSessionManagerInterface)))
-        .WillOnce(Return(kFakeManagerProxy));
-
-    // Expect the signal to be added, registered and released.
-    EXPECT_CALL(mock_dbus_, ProxyAddSignal_1(
-            kFakeManagerProxy,
-            StrEq(login_manager::kPropertyChangeCompleteSignal),
-            G_TYPE_STRING));
-    EXPECT_CALL(mock_dbus_, ProxyConnectSignal(
-            kFakeManagerProxy,
-            StrEq(login_manager::kPropertyChangeCompleteSignal),
-            _ /* callback */, _ /* data */, _ /* free function */))
-        .WillOnce(DoAll(SaveArg<2>(&signal_callback_),
-                        SaveArg<3>(&signal_callback_data_)));
-  }
-
   chromeos::FakeMessageLoop loop_{nullptr};
-  chromeos_update_engine::MockDBusWrapper mock_dbus_;
+  org::chromium::SessionManagerInterfaceProxyMock session_manager_proxy_mock_;
   testing::NiceMock<policy::MockDevicePolicy> mock_device_policy_;
   testing::NiceMock<policy::MockPolicyProvider> mock_policy_provider_;
   unique_ptr<RealDevicePolicyProvider> provider_;
 
   // The registered signal handler for the signal.
-  GCallback signal_callback_ = nullptr;
-  void* signal_callback_data_ = nullptr;
+  MockSignalHandler<void(const string&)> property_change_complete_;
 };
 
 TEST_F(UmRealDevicePolicyProviderTest, RefreshScheduledTest) {
-  // Check that the RefreshPolicy gets scheduled by checking the EventId.
+  // Check that the RefreshPolicy gets scheduled by checking the TaskId.
   EXPECT_TRUE(provider_->Init());
   EXPECT_NE(MessageLoop::kTaskIdNull, provider_->scheduled_refresh_);
+  loop_.RunOnce(false);
 }
 
 TEST_F(UmRealDevicePolicyProviderTest, FirstReload) {
-  // Checks that the policy is reloaded and the DevicePolicy is consulted.
+  // Checks that the policy is reloaded and the DevicePolicy is consulted twice:
+  // once on Init() and once again when the signal is connected.
   EXPECT_CALL(mock_policy_provider_, Reload());
   EXPECT_TRUE(provider_->Init());
+  Mock::VerifyAndClearExpectations(&mock_policy_provider_);
+
+  EXPECT_CALL(mock_policy_provider_, Reload());
+  loop_.RunOnce(false);
 }
 
 TEST_F(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyReloaded) {
   // Checks that the policy is reloaded by RefreshDevicePolicy().
   SetUpNonExistentDevicePolicy();
-  EXPECT_CALL(mock_policy_provider_, Reload()).Times(2);
+  EXPECT_CALL(mock_policy_provider_, Reload()).Times(3);
   EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
   // Force the policy refresh.
   provider_->RefreshDevicePolicy();
 }
@@ -154,22 +119,19 @@
   SetUpNonExistentDevicePolicy();
   EXPECT_CALL(mock_policy_provider_, Reload()).Times(2);
   EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
+  Mock::VerifyAndClearExpectations(&mock_policy_provider_);
 
-  ASSERT_NE(nullptr, signal_callback_);
-  // Convert the GCallback to a function pointer and call it. GCallback is just
-  // a void function pointer to ensure that the type of the passed callback is a
-  // pointer. We need to cast it back to the right function type before calling
-  // it.
-  typedef void (*StaticSignalHandler)(DBusGProxy*, const char*, void*);
-  StaticSignalHandler signal_handler = reinterpret_cast<StaticSignalHandler>(
-      signal_callback_);
-  (*signal_handler)(kFakeManagerProxy, "success", signal_callback_data_);
+  EXPECT_CALL(mock_policy_provider_, Reload());
+  ASSERT_TRUE(property_change_complete_.IsHandlerRegistered());
+  property_change_complete_.signal_callback().Run("success");
 }
 
 TEST_F(UmRealDevicePolicyProviderTest, NonExistentDevicePolicyEmptyVariables) {
   SetUpNonExistentDevicePolicy();
   EXPECT_CALL(mock_policy_provider_, GetDevicePolicy()).Times(0);
   EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
 
   UmTestUtils::ExpectVariableHasValue(false,
                                       provider_->var_device_policy_is_loaded());
@@ -189,14 +151,14 @@
 TEST_F(UmRealDevicePolicyProviderTest, ValuesUpdated) {
   SetUpNonExistentDevicePolicy();
   EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
   Mock::VerifyAndClearExpectations(&mock_policy_provider_);
 
   // Reload the policy with a good one and set some values as present. The
   // remaining values are false.
   SetUpExistentDevicePolicy();
   EXPECT_CALL(mock_device_policy_, GetReleaseChannel(_))
-      .WillOnce(DoAll(SetArgumentPointee<0>(string("mychannel")),
-                      Return(true)));
+      .WillOnce(DoAll(SetArgPointee<0>(string("mychannel")), Return(true)));
   EXPECT_CALL(mock_device_policy_, GetAllowedConnectionTypesForUpdate(_))
       .WillOnce(Return(false));
 
@@ -215,8 +177,10 @@
 TEST_F(UmRealDevicePolicyProviderTest, ScatterFactorConverted) {
   SetUpExistentDevicePolicy();
   EXPECT_CALL(mock_device_policy_, GetScatterFactorInSeconds(_))
-      .WillOnce(DoAll(SetArgumentPointee<0>(1234), Return(true)));
+      .Times(2)
+      .WillRepeatedly(DoAll(SetArgPointee<0>(1234), Return(true)));
   EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
 
   UmTestUtils::ExpectVariableHasValue(TimeDelta::FromSeconds(1234),
                                       provider_->var_scatter_factor());
@@ -225,8 +189,10 @@
 TEST_F(UmRealDevicePolicyProviderTest, NegativeScatterFactorIgnored) {
   SetUpExistentDevicePolicy();
   EXPECT_CALL(mock_device_policy_, GetScatterFactorInSeconds(_))
-      .WillOnce(DoAll(SetArgumentPointee<0>(-1), Return(true)));
+      .Times(2)
+      .WillRepeatedly(DoAll(SetArgPointee<0>(-1), Return(true)));
   EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
 
   UmTestUtils::ExpectVariableNotSet(provider_->var_scatter_factor());
 }
@@ -234,10 +200,12 @@
 TEST_F(UmRealDevicePolicyProviderTest, AllowedTypesConverted) {
   SetUpExistentDevicePolicy();
   EXPECT_CALL(mock_device_policy_, GetAllowedConnectionTypesForUpdate(_))
-      .WillOnce(DoAll(SetArgumentPointee<0>(
-                          set<string>{"bluetooth", "wifi", "not-a-type"}),
-                      Return(true)));
+      .Times(2)
+      .WillRepeatedly(DoAll(
+          SetArgPointee<0>(set<string>{"bluetooth", "wifi", "not-a-type"}),
+          Return(true)));
   EXPECT_TRUE(provider_->Init());
+  loop_.RunOnce(false);
 
   UmTestUtils::ExpectVariableHasValue(
       set<ConnectionType>{ConnectionType::kWifi, ConnectionType::kBluetooth},
diff --git a/update_manager/real_shill_provider.cc b/update_manager/real_shill_provider.cc
index 609fd01..dcf2eb2 100644
--- a/update_manager/real_shill_provider.cc
+++ b/update_manager/real_shill_provider.cc
@@ -10,182 +10,158 @@
 #include <base/strings/stringprintf.h>
 #include <chromeos/dbus/service_constants.h>
 
-#include "update_engine/glib_utils.h"
-
+using org::chromium::flimflam::ManagerProxyInterface;
+using org::chromium::flimflam::ServiceProxyInterface;
 using std::string;
 
-namespace {
-
-// Looks up a |key| in a GLib |hash_table| and returns the unboxed string from
-// the corresponding GValue, if found.
-const char* GetStrProperty(GHashTable* hash_table, const char* key) {
-  auto gval = reinterpret_cast<GValue*>(g_hash_table_lookup(hash_table, key));
-  return (gval ? g_value_get_string(gval) : nullptr);
-}
-
-};  // namespace
-
-
 namespace chromeos_update_manager {
 
-RealShillProvider::~RealShillProvider() {
-  // Detach signal handler, free manager proxy.
-  dbus_->ProxyDisconnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
-                               G_CALLBACK(HandlePropertyChangedStatic),
-                               this);
-  dbus_->ProxyUnref(manager_proxy_);
-}
-
-ConnectionType RealShillProvider::ParseConnectionType(const char* type_str) {
-  if (!strcmp(type_str, shill::kTypeEthernet))
+ConnectionType RealShillProvider::ParseConnectionType(const string& type_str) {
+  if (type_str == shill::kTypeEthernet) {
     return ConnectionType::kEthernet;
-  if (!strcmp(type_str, shill::kTypeWifi))
+  } else if (type_str == shill::kTypeWifi) {
     return ConnectionType::kWifi;
-  if (!strcmp(type_str, shill::kTypeWimax))
+  } else if (type_str == shill::kTypeWimax) {
     return ConnectionType::kWimax;
-  if (!strcmp(type_str, shill::kTypeBluetooth))
+  } else if (type_str == shill::kTypeBluetooth) {
     return ConnectionType::kBluetooth;
-  if (!strcmp(type_str, shill::kTypeCellular))
+  } else if (type_str == shill::kTypeCellular) {
     return ConnectionType::kCellular;
-
+  }
   return ConnectionType::kUnknown;
 }
 
 ConnectionTethering RealShillProvider::ParseConnectionTethering(
-    const char* tethering_str) {
-  if (!strcmp(tethering_str, shill::kTetheringNotDetectedState))
+    const string& tethering_str) {
+  if (tethering_str == shill::kTetheringNotDetectedState) {
     return ConnectionTethering::kNotDetected;
-  if (!strcmp(tethering_str, shill::kTetheringSuspectedState))
+  } else if (tethering_str == shill::kTetheringSuspectedState) {
     return ConnectionTethering::kSuspected;
-  if (!strcmp(tethering_str, shill::kTetheringConfirmedState))
+  } else if (tethering_str == shill::kTetheringConfirmedState) {
     return ConnectionTethering::kConfirmed;
-
+  }
   return ConnectionTethering::kUnknown;
 }
 
 bool RealShillProvider::Init() {
-  // Obtain a DBus connection.
-  GError* error = nullptr;
-  connection_ = dbus_->BusGet(DBUS_BUS_SYSTEM, &error);
-  if (!connection_) {
-    LOG(ERROR) << "Failed to initialize DBus connection: "
-               << chromeos_update_engine::utils::GetAndFreeGError(&error);
+  ManagerProxyInterface* manager_proxy = shill_proxy_->GetManagerProxy();
+  if (!manager_proxy)
     return false;
-  }
-
-  // Allocate a shill manager proxy.
-  manager_proxy_ = GetProxy(shill::kFlimflamServicePath,
-                            shill::kFlimflamManagerInterface);
 
   // Subscribe to the manager's PropertyChanged signal.
-  dbus_->ProxyAddSignal_2(manager_proxy_, shill::kMonitorPropertyChanged,
-                          G_TYPE_STRING, G_TYPE_VALUE);
-  dbus_->ProxyConnectSignal(manager_proxy_, shill::kMonitorPropertyChanged,
-                            G_CALLBACK(HandlePropertyChangedStatic),
-                            this, nullptr);
+  manager_proxy->RegisterPropertyChangedSignalHandler(
+      base::Bind(&RealShillProvider::OnManagerPropertyChanged,
+                 base::Unretained(this)),
+      base::Bind(&RealShillProvider::OnSignalConnected,
+                 base::Unretained(this)));
 
   // Attempt to read initial connection status. Even if this fails because shill
   // is not responding (e.g. it is down) we'll be notified via "PropertyChanged"
   // signal as soon as it comes up, so this is not a critical step.
-  GHashTable* hash_table = nullptr;
-  if (GetProperties(manager_proxy_, &hash_table)) {
-    GValue* value = reinterpret_cast<GValue*>(
-        g_hash_table_lookup(hash_table, shill::kDefaultServiceProperty));
-    ProcessDefaultService(value);
-    g_hash_table_unref(hash_table);
+  chromeos::VariantDictionary properties;
+  chromeos::ErrorPtr error;
+  if (!manager_proxy->GetProperties(&properties, &error))
+    return true;
+
+  const auto& prop_default_service =
+      properties.find(shill::kDefaultServiceProperty);
+  if (prop_default_service != properties.end()) {
+    OnManagerPropertyChanged(prop_default_service->first,
+                             prop_default_service->second);
   }
 
   return true;
 }
 
-DBusGProxy* RealShillProvider::GetProxy(const char* path,
-                                        const char* interface) {
-  return dbus_->ProxyNewForName(connection_, shill::kFlimflamServiceName,
-                                path, interface);
+void RealShillProvider::OnManagerPropertyChanged(const string& name,
+                                                 const chromeos::Any& value) {
+  if (name == shill::kDefaultServiceProperty)
+    ProcessDefaultService(value.TryGet<dbus::ObjectPath>().value());
 }
 
-bool RealShillProvider::GetProperties(DBusGProxy* proxy,
-                                      GHashTable** result_p) {
-  GError* error = nullptr;
-  if (!dbus_->ProxyCall_0_1(proxy, shill::kGetPropertiesFunction, &error,
-                            result_p)) {
-    LOG(ERROR) << "Calling shill via DBus proxy failed: "
-               << chromeos_update_engine::utils::GetAndFreeGError(&error);
-    return false;
+void RealShillProvider::OnSignalConnected(const string& interface_name,
+                                          const string& signal_name,
+                                          bool successful) {
+  if (!successful) {
+    LOG(ERROR) << "Couldn't connect to the signal " << interface_name << "."
+               << signal_name;
   }
-  return true;
 }
 
-bool RealShillProvider::ProcessDefaultService(GValue* value) {
-  // Decode the string from the boxed value.
-  const char* default_service_path_str = nullptr;
-  if (!(value && (default_service_path_str = g_value_get_string(value))))
-    return false;
-
-  // Anything changed?
-  if (default_service_path_ == default_service_path_str)
+bool RealShillProvider::ProcessDefaultService(
+    const string& default_service_path) {
+  // We assume that if the service path didn't change, then the connection
+  // type and the tethering status of it also didn't change.
+  if (default_service_path_ == default_service_path)
     return true;
 
   // Update the connection status.
-  default_service_path_ = default_service_path_str;
+  default_service_path_ = default_service_path;
   bool is_connected = (default_service_path_ != "/");
   var_is_connected_.SetValue(is_connected);
   var_conn_last_changed_.SetValue(clock_->GetWallclockTime());
 
-  // Update the connection attributes.
-  if (is_connected) {
-    DBusGProxy* service_proxy = GetProxy(default_service_path_.c_str(),
-                                         shill::kFlimflamServiceInterface);
-    GHashTable* hash_table = nullptr;
-    if (GetProperties(service_proxy, &hash_table)) {
-      // Get the connection type.
-      const char* type_str = GetStrProperty(hash_table, shill::kTypeProperty);
-      if (type_str && !strcmp(type_str, shill::kTypeVPN)) {
-        type_str = GetStrProperty(hash_table,
-                                  shill::kPhysicalTechnologyProperty);
-      }
-      if (type_str) {
-        var_conn_type_.SetValue(ParseConnectionType(type_str));
-      } else {
-        var_conn_type_.UnsetValue();
-        LOG(ERROR) << "Could not find connection type ("
-                   << default_service_path_ << ")";
-      }
-
-      // Get the connection tethering mode.
-      const char* tethering_str = GetStrProperty(hash_table,
-                                                 shill::kTetheringProperty);
-      if (tethering_str) {
-        var_conn_tethering_.SetValue(ParseConnectionTethering(tethering_str));
-      } else {
-        var_conn_tethering_.UnsetValue();
-        LOG(ERROR) << "Could not find connection tethering mode ("
-                   << default_service_path_ << ")";
-      }
-
-      g_hash_table_unref(hash_table);
-    }
-    dbus_->ProxyUnref(service_proxy);
-  } else {
+  if (!is_connected) {
     var_conn_type_.UnsetValue();
     var_conn_tethering_.UnsetValue();
+    return true;
+  }
+
+  // We create and dispose the ServiceProxyInterface on every request.
+  std::unique_ptr<ServiceProxyInterface> service =
+      shill_proxy_->GetServiceForPath(default_service_path_);
+
+  // Get the connection properties synchronously.
+  chromeos::VariantDictionary properties;
+  chromeos::ErrorPtr error;
+  if (!service->GetProperties(&properties, &error)) {
+    var_conn_type_.UnsetValue();
+    var_conn_tethering_.UnsetValue();
+    return false;
+  }
+
+  // Get the connection tethering mode.
+  const auto& prop_tethering = properties.find(shill::kTetheringProperty);
+  if (prop_tethering == properties.end()) {
+    // Remove the value if not present on the service. This most likely means an
+    // error in shill and the policy will handle it, but we will print a log
+    // message as well for accessing an unused variable.
+    var_conn_tethering_.UnsetValue();
+    LOG(ERROR) << "Could not find connection type (" << default_service_path_
+               << ")";
+  } else {
+    // If the property doesn't contain a string value, the empty string will
+    // become kUnknown.
+    var_conn_tethering_.SetValue(
+        ParseConnectionTethering(prop_tethering->second.TryGet<string>()));
+  }
+
+  // Get the connection type.
+  const auto& prop_type = properties.find(shill::kTypeProperty);
+  if (prop_type == properties.end()) {
+    var_conn_type_.UnsetValue();
+    LOG(ERROR) << "Could not find connection tethering mode ("
+               << default_service_path_ << ")";
+  } else {
+    string type_str = prop_type->second.TryGet<string>();
+    if (type_str == shill::kTypeVPN) {
+      const auto& prop_physical =
+          properties.find(shill::kPhysicalTechnologyProperty);
+      if (prop_physical == properties.end()) {
+        LOG(ERROR) << "No PhysicalTechnology property found for a VPN"
+                   << " connection (service: " << default_service_path
+                   << "). Using default kUnknown value.";
+        var_conn_type_.SetValue(ConnectionType::kUnknown);
+      } else {
+        var_conn_type_.SetValue(
+            ParseConnectionType(prop_physical->second.TryGet<string>()));
+      }
+    } else {
+      var_conn_type_.SetValue(ParseConnectionType(type_str));
+    }
   }
 
   return true;
 }
 
-void RealShillProvider::HandlePropertyChanged(DBusGProxy* proxy,
-                                              const char* name, GValue* value) {
-  if (!strcmp(name, shill::kDefaultServiceProperty))
-    ProcessDefaultService(value);
-}
-
-void RealShillProvider::HandlePropertyChangedStatic(DBusGProxy* proxy,
-                                                    const char* name,
-                                                    GValue* value,
-                                                    void* data) {
-  auto obj = reinterpret_cast<RealShillProvider*>(data);
-  obj->HandlePropertyChanged(proxy, name, value);
-}
-
 }  // namespace chromeos_update_manager
diff --git a/update_manager/real_shill_provider.h b/update_manager/real_shill_provider.h
index 5b50bf4..5864319 100644
--- a/update_manager/real_shill_provider.h
+++ b/update_manager/real_shill_provider.h
@@ -14,22 +14,21 @@
 #include <base/time/time.h>
 
 #include "update_engine/clock_interface.h"
-#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/shill_proxy_interface.h"
 #include "update_engine/update_manager/generic_variables.h"
 #include "update_engine/update_manager/shill_provider.h"
 
-using chromeos_update_engine::ClockInterface;
-using chromeos_update_engine::DBusWrapperInterface;
-
 namespace chromeos_update_manager {
 
 // ShillProvider concrete implementation.
 class RealShillProvider : public ShillProvider {
  public:
-  RealShillProvider(DBusWrapperInterface* dbus, ClockInterface* clock)
-      : dbus_(dbus), clock_(clock) {}
+  RealShillProvider(chromeos_update_engine::ShillProxyInterface* shill_proxy,
+                    chromeos_update_engine::ClockInterface* clock)
+      : shill_proxy_(shill_proxy), clock_(clock) {}
 
-  ~RealShillProvider() override;
+  ~RealShillProvider() override = default;
 
   // Initializes the provider and returns whether it succeeded.
   bool Init();
@@ -51,37 +50,33 @@
   }
 
   // Helper methods for converting shill strings into symbolic values.
-  static ConnectionType ParseConnectionType(const char* type_str);
+  static ConnectionType ParseConnectionType(const std::string& type_str);
   static ConnectionTethering ParseConnectionTethering(
-      const char* tethering_str);
+      const std::string& tethering_str);
 
  private:
-  // Return a DBus proxy for a given |path| and |interface| within shill.
-  DBusGProxy* GetProxy(const char* path, const char* interface);
+  // A handler for ManagerProxy.PropertyChanged signal.
+  void OnManagerPropertyChanged(const std::string& name,
+                                const chromeos::Any& value);
 
-  // Issues a GetProperties call through a given |proxy|, storing the result to
-  // |*result_p|. Returns true on success.
-  bool GetProperties(DBusGProxy* proxy, GHashTable** result_p);
+  // Called when the signal in ManagerProxy.PropertyChanged is connected.
+  void OnSignalConnected(const std::string& interface_name,
+                         const std::string& signal_name,
+                         bool successful);
 
-  // Process a default connection value, update last change time as needed.
-  bool ProcessDefaultService(GValue* value);
-
-  // A handler for manager PropertyChanged signal, and a static version.
-  void HandlePropertyChanged(DBusGProxy* proxy, const char* name,
-                             GValue* value);
-  static void HandlePropertyChangedStatic(DBusGProxy* proxy, const char* name,
-                                          GValue* value, void* data);
+  // Get the connection and populate the type and tethering status of the given
+  // default connection.
+  bool ProcessDefaultService(const std::string& default_service_path);
 
   // The current default service path, if connected.
   std::string default_service_path_;
 
-  // The DBus interface (mockable), connection, and a shill manager proxy.
-  DBusWrapperInterface* const dbus_;
-  DBusGConnection* connection_ = nullptr;
-  DBusGProxy* manager_proxy_ = nullptr;
+  // The mockable interface to access the shill DBus proxies, owned by the
+  // caller.
+  chromeos_update_engine::ShillProxyInterface* shill_proxy_;
 
   // A clock abstraction (mockable).
-  ClockInterface* const clock_;
+  chromeos_update_engine::ClockInterface* const clock_;
 
   // The provider's variables.
   AsyncCopyVariable<bool> var_is_connected_{"is_connected"};
diff --git a/update_manager/real_shill_provider_unittest.cc b/update_manager/real_shill_provider_unittest.cc
index 78bdd93..5b81e91 100644
--- a/update_manager/real_shill_provider_unittest.cc
+++ b/update_manager/real_shill_provider_unittest.cc
@@ -1,7 +1,6 @@
 // Copyright (c) 2014 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 "update_engine/update_manager/real_shill_provider.h"
 
 #include <memory>
@@ -9,46 +8,32 @@
 
 #include <base/time/time.h>
 #include <chromeos/dbus/service_constants.h>
-#include <glib.h>
+#include <chromeos/make_unique_ptr.h>
+#include <chromeos/message_loops/fake_message_loop.h>
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "update_engine/dbus_mocks.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/dbus_test_utils.h"
 #include "update_engine/fake_clock.h"
-#include "update_engine/mock_dbus_wrapper.h"
+#include "update_engine/fake_shill_proxy.h"
 #include "update_engine/test_utils.h"
 #include "update_engine/update_manager/umtest_utils.h"
 
 using base::Time;
 using base::TimeDelta;
 using chromeos_update_engine::FakeClock;
-using chromeos_update_engine::MockDBusWrapper;
-using chromeos_update_engine::test_utils::GValueFree;
-using chromeos_update_engine::test_utils::GValueNewString;
-using std::pair;
+using org::chromium::flimflam::ManagerProxyMock;
+using org::chromium::flimflam::ServiceProxyMock;
 using std::unique_ptr;
-using testing::A;
-using testing::Eq;
 using testing::Mock;
 using testing::Return;
-using testing::SaveArg;
 using testing::SetArgPointee;
-using testing::StrEq;
-using testing::StrictMock;
 using testing::_;
 
 namespace {
 
-// Fake dbus-glib objects. These should be different values, to ease diagnosis
-// of errors.
-DBusGConnection* const kFakeConnection = reinterpret_cast<DBusGConnection*>(1);
-DBusGProxy* const kFakeManagerProxy = reinterpret_cast<DBusGProxy*>(2);
-DBusGProxy* const kFakeEthernetServiceProxy = reinterpret_cast<DBusGProxy*>(3);
-DBusGProxy* const kFakeWifiServiceProxy = reinterpret_cast<DBusGProxy*>(4);
-DBusGProxy* const kFakeWimaxServiceProxy = reinterpret_cast<DBusGProxy*>(5);
-DBusGProxy* const kFakeBluetoothServiceProxy = reinterpret_cast<DBusGProxy*>(6);
-DBusGProxy* const kFakeCellularServiceProxy = reinterpret_cast<DBusGProxy*>(7);
-DBusGProxy* const kFakeVpnServiceProxy = reinterpret_cast<DBusGProxy*>(8);
-DBusGProxy* const kFakeUnknownServiceProxy = reinterpret_cast<DBusGProxy*>(9);
-
 // Fake service paths.
 const char* const kFakeEthernetServicePath = "/fake-ethernet-service";
 const char* const kFakeWifiServicePath = "/fake-wifi-service";
@@ -64,89 +49,23 @@
 
 class UmRealShillProviderTest : public ::testing::Test {
  protected:
+  // Initialize the RealShillProvider under test.
   void SetUp() override {
-    // By default, initialize the provider so that it gets an initial connection
-    // status from shill. This simulates the common case where shill is
-    // available and responding during RealShillProvider initialization.
-    Init(true);
+    fake_clock_.SetWallclockTime(InitTime());
+    loop_.SetAsCurrent();
+    provider_.reset(new RealShillProvider(&fake_shill_proxy_, &fake_clock_));
+
+    ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_.GetManagerProxy();
+
+    // The PropertyChanged signal should be subscribed to.
+    MOCK_SIGNAL_HANDLER_EXPECT_SIGNAL_HANDLER(
+        manager_property_changed_, *manager_proxy_mock, PropertyChanged);
   }
 
   void TearDown() override {
-    Shutdown();
-  }
-
-  // Initialize the RealShillProvider under test. If |do_init_conn_status| is
-  // true, configure mocks to respond to the initial connection status check
-  // with shill. Otherwise, the initial check will fail.
-  void Init(bool do_init_conn_status) {
-    // Properly shutdown a previously initialized provider.
-    if (provider_.get())
-      Shutdown();
-
-    provider_.reset(new RealShillProvider(&mock_dbus_, &fake_clock_));
-    ASSERT_NE(nullptr, provider_.get());
-    fake_clock_.SetWallclockTime(InitTime());
-
-    // A DBus connection should only be obtained once.
-    EXPECT_CALL(mock_dbus_, BusGet(_, _)).WillOnce(
-        Return(kFakeConnection));
-
-    // A manager proxy should only be obtained once.
-    EXPECT_CALL(mock_dbus_, ProxyNewForName(
-            kFakeConnection, StrEq(shill::kFlimflamServiceName),
-            StrEq(shill::kFlimflamServicePath),
-            StrEq(shill::kFlimflamManagerInterface)))
-        .WillOnce(Return(kFakeManagerProxy));
-
-    // The PropertyChanged signal should be subscribed to.
-    EXPECT_CALL(mock_dbus_, ProxyAddSignal_2(
-            kFakeManagerProxy, StrEq(shill::kMonitorPropertyChanged),
-            G_TYPE_STRING, G_TYPE_VALUE))
-        .WillOnce(Return());
-    EXPECT_CALL(mock_dbus_, ProxyConnectSignal(
-            kFakeManagerProxy, StrEq(shill::kMonitorPropertyChanged),
-            _, _, _))
-        .WillOnce(
-            DoAll(SaveArg<2>(reinterpret_cast<void (**)()>(&signal_handler_)),
-                  SaveArg<3>(&signal_data_),
-                  Return()));
-
-    // Mock a response to an initial connection check (optional).
-    GHashTable* manager_properties = nullptr;
-    if (do_init_conn_status) {
-      pair<const char*, const char*> manager_pairs[] = {
-        {shill::kDefaultServiceProperty, "/"},
-      };
-      manager_properties = SetupGetPropertiesOkay(
-          kFakeManagerProxy, arraysize(manager_pairs), manager_pairs);
-    } else {
-      SetupGetPropertiesFail(kFakeManagerProxy);
-    }
-
-    // Check that provider initializes correctly.
-    ASSERT_TRUE(provider_->Init());
-
-    // All mocked calls should have been exercised by now.
-    Mock::VerifyAndClear(&mock_dbus_);
-
-    // Release properties hash table (if provided).
-    if (manager_properties)
-      g_hash_table_unref(manager_properties);
-  }
-
-  // Deletes the RealShillProvider under test.
-  void Shutdown() {
-    // Make sure that DBus resources get freed.
-    EXPECT_CALL(mock_dbus_, ProxyDisconnectSignal(
-            kFakeManagerProxy, StrEq(shill::kMonitorPropertyChanged),
-            Eq(reinterpret_cast<void (*)()>(signal_handler_)),
-            Eq(signal_data_)))
-        .WillOnce(Return());
-    EXPECT_CALL(mock_dbus_, ProxyUnref(kFakeManagerProxy)).WillOnce(Return());
     provider_.reset();
-
-    // All mocked calls should have been exercised by now.
-    Mock::VerifyAndClear(&mock_dbus_);
+    // Check for leaked callbacks on the main loop.
+    EXPECT_FALSE(loop_.PendingTasks());
   }
 
   // These methods generate fixed timestamps for use in faking the current time.
@@ -167,53 +86,41 @@
     return InitTime() + TimeDelta::FromSeconds(10);
   }
 
-  // Sets up a successful mock "GetProperties" call on |proxy|, writing a hash
-  // table containing |num_entries| entries formed by key/value pairs from
-  // |key_val_pairs| and returning true. Keys and values are plain C strings
-  // (const char*). The proxy call is expected to be made exactly once. Returns
-  // a pointer to a newly allocated hash table, which should be unreffed with
-  // g_hash_table_unref() when done.
-  GHashTable* SetupGetPropertiesOkay(
-      DBusGProxy* proxy, size_t num_entries,
-      pair<const char*, const char*>* key_val_pairs) {
-    // Allocate and populate the hash table.
-    GHashTable* properties = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                                   free, GValueFree);
-    for (size_t i = 0; i < num_entries; i++) {
-      g_hash_table_insert(properties, strdup(key_val_pairs[i].first),
-                          GValueNewString(key_val_pairs[i].second));
-    }
+  // Sets the default_service object path in the response from the
+  // ManagerProxyMock instance.
+  void SetManagerReply(const char* default_service, bool reply_succeeds);
 
-    // Set mock expectations.
-    EXPECT_CALL(mock_dbus_,
-                ProxyCall_0_1(proxy, StrEq(shill::kGetPropertiesFunction),
-                              _, A<GHashTable**>()))
-        .WillOnce(DoAll(SetArgPointee<3>(g_hash_table_ref(properties)),
-                        Return(true)));
+  // Sets the |service_type|, |physical_technology| and |service_tethering|
+  // properties in the mocked service |service_path|. If any of the three
+  // const char* is a nullptr, the corresponding property will not be included
+  // in the response.
+  // Returns the mock object pointer, owned by the |fake_shill_proxy_|.
+  ServiceProxyMock* SetServiceReply(const std::string& service_path,
+                                    const char* service_type,
+                                    const char* physical_technology,
+                                    const char* service_tethering);
 
-    return properties;
-  }
-
-  // Sets up a failing mock "GetProperties" call on |proxy|, returning false.
-  // The proxy call is expected to be made exactly once.
-  void SetupGetPropertiesFail(DBusGProxy* proxy) {
-    EXPECT_CALL(mock_dbus_,
-                ProxyCall_0_1(proxy, StrEq(shill::kGetPropertiesFunction),
-                              _, A<GHashTable**>()))
-      .WillOnce(Return(false));
+  void InitWithDefaultService(const char* default_service) {
+    SetManagerReply(default_service, true);
+    // Check that provider initializes correctly.
+    EXPECT_TRUE(provider_->Init());
+    // RunOnce to notify the signal handler was connected properly.
+    EXPECT_TRUE(loop_.RunOnce(false));
   }
 
   // Sends a signal informing the provider about a default connection
-  // |service_path|. Returns the fake connection change time.
-  Time SendDefaultServiceSignal(const char* service_path) {
-    auto default_service_gval = GValueNewString(service_path);
+  // |service_path|. Sets the fake connection change time in
+  // |conn_change_time_p| if provided.
+  void SendDefaultServiceSignal(const std::string& service_path,
+                                Time* conn_change_time_p) {
     const Time conn_change_time = ConnChangedTime();
     fake_clock_.SetWallclockTime(conn_change_time);
-    signal_handler_(kFakeManagerProxy, shill::kDefaultServiceProperty,
-                    default_service_gval, signal_data_);
+    ASSERT_TRUE(manager_property_changed_.IsHandlerRegistered());
+    manager_property_changed_.signal_callback().Run(
+        shill::kDefaultServiceProperty, dbus::ObjectPath(service_path));
     fake_clock_.SetWallclockTime(conn_change_time + TimeDelta::FromSeconds(5));
-    GValueFree(default_service_gval);
-    return conn_change_time;
+    if (conn_change_time_p)
+      *conn_change_time_p = conn_change_time;
   }
 
   // Sets up expectations for detection of a connection |service_path| with type
@@ -221,31 +128,18 @@
   // new connection status and change time are properly detected by the
   // provider. Writes the fake connection change time to |conn_change_time_p|,
   // if provided.
-  void SetupConnectionAndAttrs(const char* service_path,
-                               DBusGProxy* service_proxy,
-                               const char* shill_type_str,
-                               const char* shill_tethering_str,
+  void SetupConnectionAndAttrs(const std::string& service_path,
+                               const char* shill_type,
+                               const char* shill_tethering,
                                Time* conn_change_time_p) {
-    // Mock logic for querying the default service attributes.
-    EXPECT_CALL(mock_dbus_,
-                ProxyNewForName(
-                    kFakeConnection, StrEq(shill::kFlimflamServiceName),
-                    StrEq(service_path),
-                    StrEq(shill::kFlimflamServiceInterface)))
-        .WillOnce(Return(service_proxy));
-    EXPECT_CALL(mock_dbus_, ProxyUnref(service_proxy)).WillOnce(Return());
-    pair<const char*, const char*> service_pairs[] = {
-      {shill::kTypeProperty, shill_type_str},
-      {shill::kTetheringProperty, shill_tethering_str},
-    };
-    auto service_properties = SetupGetPropertiesOkay(
-        service_proxy, arraysize(service_pairs), service_pairs);
+    SetServiceReply(service_path, shill_type, nullptr, shill_tethering);
+    // Note: We don't setup this |service_path| as the default service path but
+    // we instead send a signal notifying the change since the code won't call
+    // GetProperties on the Manager object at this point.
 
     // Send a signal about a new default service.
-    auto conn_change_time = SendDefaultServiceSignal(service_path);
-
-    // Release the service properties hash tables.
-    g_hash_table_unref(service_properties);
+    Time conn_change_time;
+    SendDefaultServiceSignal(service_path, &conn_change_time);
 
     // Query the connection status, ensure last change time reported correctly.
     UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
@@ -260,12 +154,12 @@
   // Sets up a connection and tests that its type is being properly detected by
   // the provider.
   void SetupConnectionAndTestType(const char* service_path,
-                                  DBusGProxy* service_proxy,
-                                  const char* shill_type_str,
+                                  const char* shill_type,
                                   ConnectionType expected_conn_type) {
     // Set up and test the connection, record the change time.
     Time conn_change_time;
-    SetupConnectionAndAttrs(service_path, service_proxy, shill_type_str,
+    SetupConnectionAndAttrs(service_path,
+                            shill_type,
                             shill::kTetheringNotDetectedState,
                             &conn_change_time);
 
@@ -279,13 +173,13 @@
   // Sets up a connection and tests that its tethering mode is being properly
   // detected by the provider.
   void SetupConnectionAndTestTethering(
-      const char* service_path, DBusGProxy* service_proxy,
-      const char* shill_tethering_str,
+      const char* service_path,
+      const char* shill_tethering,
       ConnectionTethering expected_conn_tethering) {
     // Set up and test the connection, record the change time.
     Time conn_change_time;
-    SetupConnectionAndAttrs(service_path, service_proxy, shill::kTypeEthernet,
-                            shill_tethering_str, &conn_change_time);
+    SetupConnectionAndAttrs(
+        service_path, shill::kTypeEthernet, shill_tethering, &conn_change_time);
 
     // Query the connection tethering, ensure last change time did not change.
     UmTestUtils::ExpectVariableHasValue(expected_conn_tethering,
@@ -294,16 +188,74 @@
                                         provider_->var_conn_last_changed());
   }
 
-  StrictMock<MockDBusWrapper> mock_dbus_;
+  chromeos::FakeMessageLoop loop_{nullptr};
   FakeClock fake_clock_;
+  chromeos_update_engine::FakeShillProxy fake_shill_proxy_;
+
+  // The registered signal handler for the signal Manager.PropertyChanged.
+  chromeos_update_engine::dbus_test_utils::MockSignalHandler<
+      void(const std::string&, const chromeos::Any&)> manager_property_changed_;
+
   unique_ptr<RealShillProvider> provider_;
-  void (*signal_handler_)(DBusGProxy*, const char*, GValue*, void*);
-  void* signal_data_;
 };
 
+void UmRealShillProviderTest::SetManagerReply(const char* default_service,
+                                              bool reply_succeeds) {
+  ManagerProxyMock* manager_proxy_mock = fake_shill_proxy_.GetManagerProxy();
+  if (!reply_succeeds) {
+    EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+        .WillOnce(Return(false));
+    return;
+  }
+
+  // Create a dictionary of properties and optionally include the default
+  // service.
+  chromeos::VariantDictionary reply_dict;
+  reply_dict["SomeOtherProperty"] = 0xC0FFEE;
+
+  if (default_service) {
+    reply_dict[shill::kDefaultServiceProperty] =
+        dbus::ObjectPath(default_service);
+  }
+  EXPECT_CALL(*manager_proxy_mock, GetProperties(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
+}
+
+ServiceProxyMock* UmRealShillProviderTest::SetServiceReply(
+    const std::string& service_path,
+    const char* service_type,
+    const char* physical_technology,
+    const char* service_tethering) {
+  chromeos::VariantDictionary reply_dict;
+  reply_dict["SomeOtherProperty"] = 0xC0FFEE;
+
+  if (service_type)
+    reply_dict[shill::kTypeProperty] = std::string(service_type);
+
+  if (physical_technology) {
+    reply_dict[shill::kPhysicalTechnologyProperty] =
+        std::string(physical_technology);
+  }
+
+  if (service_tethering)
+    reply_dict[shill::kTetheringProperty] = std::string(service_tethering);
+
+  ServiceProxyMock* service_proxy_mock = new ServiceProxyMock();
+
+  // Plumb return value into mock object.
+  EXPECT_CALL(*service_proxy_mock, GetProperties(_, _, _))
+      .WillOnce(DoAll(SetArgPointee<0>(reply_dict), Return(true)));
+
+  fake_shill_proxy_.SetServiceForPath(
+      service_path, chromeos::make_unique_ptr(service_proxy_mock));
+  return service_proxy_mock;
+}
+
+
 // Query the connection status, type and time last changed, as they were set
 // during initialization (no signals).
 TEST_F(UmRealShillProviderTest, ReadBaseValues) {
+  InitWithDefaultService("/");
   // Query the provider variables.
   UmTestUtils::ExpectVariableHasValue(false, provider_->var_is_connected());
   UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
@@ -313,86 +265,77 @@
 
 // Test that Ethernet connection is identified correctly.
 TEST_F(UmRealShillProviderTest, ReadConnTypeEthernet) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestType(kFakeEthernetServicePath,
-                             kFakeEthernetServiceProxy,
                              shill::kTypeEthernet,
                              ConnectionType::kEthernet);
 }
 
 // Test that Wifi connection is identified correctly.
 TEST_F(UmRealShillProviderTest, ReadConnTypeWifi) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestType(kFakeWifiServicePath,
-                             kFakeWifiServiceProxy,
                              shill::kTypeWifi,
                              ConnectionType::kWifi);
 }
 
 // Test that Wimax connection is identified correctly.
 TEST_F(UmRealShillProviderTest, ReadConnTypeWimax) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestType(kFakeWimaxServicePath,
-                             kFakeWimaxServiceProxy,
                              shill::kTypeWimax,
                              ConnectionType::kWimax);
 }
 
 // Test that Bluetooth connection is identified correctly.
 TEST_F(UmRealShillProviderTest, ReadConnTypeBluetooth) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestType(kFakeBluetoothServicePath,
-                             kFakeBluetoothServiceProxy,
                              shill::kTypeBluetooth,
                              ConnectionType::kBluetooth);
 }
 
 // Test that Cellular connection is identified correctly.
 TEST_F(UmRealShillProviderTest, ReadConnTypeCellular) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestType(kFakeCellularServicePath,
-                             kFakeCellularServiceProxy,
                              shill::kTypeCellular,
                              ConnectionType::kCellular);
 }
 
 // Test that an unknown connection is identified as such.
 TEST_F(UmRealShillProviderTest, ReadConnTypeUnknown) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestType(kFakeUnknownServicePath,
-                             kFakeUnknownServiceProxy,
                              "FooConnectionType",
                              ConnectionType::kUnknown);
 }
 
 // Tests that VPN connection is identified correctly.
 TEST_F(UmRealShillProviderTest, ReadConnTypeVpn) {
+  InitWithDefaultService("/");
   // Mock logic for returning a default service path and its type.
-  EXPECT_CALL(mock_dbus_, ProxyNewForName(
-          kFakeConnection, StrEq(shill::kFlimflamServiceName),
-          StrEq(kFakeVpnServicePath), StrEq(shill::kFlimflamServiceInterface)))
-      .WillOnce(Return(kFakeVpnServiceProxy));
-  EXPECT_CALL(mock_dbus_, ProxyUnref(kFakeVpnServiceProxy)).WillOnce(Return());
-  pair<const char*, const char*> service_pairs[] = {
-    {shill::kTypeProperty, shill::kTypeVPN},
-    {shill::kPhysicalTechnologyProperty, shill::kTypeWifi},
-  };
-  auto service_properties = SetupGetPropertiesOkay(kFakeVpnServiceProxy,
-                                                   arraysize(service_pairs),
-                                                   service_pairs);
+  SetServiceReply(kFakeVpnServicePath,
+                  shill::kTypeVPN,
+                  shill::kTypeWifi,
+                  shill::kTetheringNotDetectedState);
 
   // Send a signal about a new default service.
-  Time conn_change_time = SendDefaultServiceSignal(kFakeVpnServicePath);
+  Time conn_change_time;
+  SendDefaultServiceSignal(kFakeVpnServicePath, &conn_change_time);
 
   // Query the connection type, ensure last change time reported correctly.
   UmTestUtils::ExpectVariableHasValue(ConnectionType::kWifi,
                                       provider_->var_conn_type());
   UmTestUtils::ExpectVariableHasValue(conn_change_time,
                                       provider_->var_conn_last_changed());
-
-  // Release properties hash tables.
-  g_hash_table_unref(service_properties);
 }
 
 // Ensure that the connection type is properly cached in the provider through
 // subsequent variable readings.
 TEST_F(UmRealShillProviderTest, ConnTypeCacheUsed) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestType(kFakeEthernetServicePath,
-                             kFakeEthernetServiceProxy,
                              shill::kTypeEthernet,
                              ConnectionType::kEthernet);
 
@@ -403,12 +346,12 @@
 // Ensure that the cached connection type remains valid even when a default
 // connection signal occurs but the connection is not changed.
 TEST_F(UmRealShillProviderTest, ConnTypeCacheRemainsValid) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestType(kFakeEthernetServicePath,
-                             kFakeEthernetServiceProxy,
                              shill::kTypeEthernet,
                              ConnectionType::kEthernet);
 
-  SendDefaultServiceSignal(kFakeEthernetServicePath);
+  SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
 
   UmTestUtils::ExpectVariableHasValue(ConnectionType::kEthernet,
                                       provider_->var_conn_type());
@@ -417,53 +360,52 @@
 // Ensure that the cached connection type is invalidated and re-read when the
 // default connection changes.
 TEST_F(UmRealShillProviderTest, ConnTypeCacheInvalidated) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestType(kFakeEthernetServicePath,
-                             kFakeEthernetServiceProxy,
                              shill::kTypeEthernet,
                              ConnectionType::kEthernet);
 
   SetupConnectionAndTestType(kFakeWifiServicePath,
-                             kFakeWifiServiceProxy,
                              shill::kTypeWifi,
                              ConnectionType::kWifi);
 }
 
 // Test that a non-tethering mode is identified correctly.
 TEST_F(UmRealShillProviderTest, ReadConnTetheringNotDetected) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestTethering(kFakeWifiServicePath,
-                                  kFakeWifiServiceProxy,
                                   shill::kTetheringNotDetectedState,
                                   ConnectionTethering::kNotDetected);
 }
 
 // Test that a suspected tethering mode is identified correctly.
 TEST_F(UmRealShillProviderTest, ReadConnTetheringSuspected) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestTethering(kFakeWifiServicePath,
-                                  kFakeWifiServiceProxy,
                                   shill::kTetheringSuspectedState,
                                   ConnectionTethering::kSuspected);
 }
 
 // Test that a confirmed tethering mode is identified correctly.
 TEST_F(UmRealShillProviderTest, ReadConnTetheringConfirmed) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestTethering(kFakeWifiServicePath,
-                                  kFakeWifiServiceProxy,
                                   shill::kTetheringConfirmedState,
                                   ConnectionTethering::kConfirmed);
 }
 
 // Test that an unknown tethering mode is identified as such.
 TEST_F(UmRealShillProviderTest, ReadConnTetheringUnknown) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestTethering(kFakeWifiServicePath,
-                                  kFakeWifiServiceProxy,
                                   "FooConnTethering",
                                   ConnectionTethering::kUnknown);
 }
 
 // Ensure that the connection tethering mode is properly cached in the provider.
 TEST_F(UmRealShillProviderTest, ConnTetheringCacheUsed) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestTethering(kFakeEthernetServicePath,
-                                  kFakeEthernetServiceProxy,
                                   shill::kTetheringNotDetectedState,
                                   ConnectionTethering::kNotDetected);
 
@@ -474,12 +416,12 @@
 // Ensure that the cached connection tethering mode remains valid even when a
 // default connection signal occurs but the connection is not changed.
 TEST_F(UmRealShillProviderTest, ConnTetheringCacheRemainsValid) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestTethering(kFakeEthernetServicePath,
-                                  kFakeEthernetServiceProxy,
                                   shill::kTetheringNotDetectedState,
                                   ConnectionTethering::kNotDetected);
 
-  SendDefaultServiceSignal(kFakeEthernetServicePath);
+  SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
 
   UmTestUtils::ExpectVariableHasValue(ConnectionTethering::kNotDetected,
                                       provider_->var_conn_tethering());
@@ -488,13 +430,12 @@
 // Ensure that the cached connection tethering mode is invalidated and re-read
 // when the default connection changes.
 TEST_F(UmRealShillProviderTest, ConnTetheringCacheInvalidated) {
+  InitWithDefaultService("/");
   SetupConnectionAndTestTethering(kFakeEthernetServicePath,
-                                  kFakeEthernetServiceProxy,
                                   shill::kTetheringNotDetectedState,
                                   ConnectionTethering::kNotDetected);
 
   SetupConnectionAndTestTethering(kFakeWifiServicePath,
-                                  kFakeWifiServiceProxy,
                                   shill::kTetheringConfirmedState,
                                   ConnectionTethering::kConfirmed);
 }
@@ -504,14 +445,19 @@
 // changed, making sure that it is the time when the first signal was sent (and
 // not the second).
 TEST_F(UmRealShillProviderTest, ReadLastChangedTimeTwoSignals) {
+  InitWithDefaultService("/");
   // Send a default service signal twice, advancing the clock in between.
   Time conn_change_time;
-  SetupConnectionAndAttrs(kFakeEthernetServicePath, kFakeEthernetServiceProxy,
+  SetupConnectionAndAttrs(kFakeEthernetServicePath,
                           shill::kTypeEthernet,
-                          shill::kTetheringNotDetectedState, &conn_change_time);
-  SendDefaultServiceSignal(kFakeEthernetServicePath);
+                          shill::kTetheringNotDetectedState,
+                          &conn_change_time);
+  // This will set the service path to the same value, so it should not call
+  // GetProperties() again.
+  SendDefaultServiceSignal(kFakeEthernetServicePath, nullptr);
 
-  // Query the connection status, ensure last change time reported correctly.
+  // Query the connection status, ensure last change time reported as the first
+  // time the signal was sent.
   UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
   UmTestUtils::ExpectVariableHasValue(conn_change_time,
                                       provider_->var_conn_last_changed());
@@ -521,8 +467,10 @@
 // responding, that variables can be obtained, and that they all return a null
 // value (indicating that the underlying values were not set).
 TEST_F(UmRealShillProviderTest, NoInitConnStatusReadBaseValues) {
-  // Re-initialize the provider, no initial connection status response.
-  Init(false);
+  // Initialize the provider, no initial connection status response.
+  SetManagerReply(nullptr, false);
+  EXPECT_TRUE(provider_->Init());
+  EXPECT_TRUE(loop_.RunOnce(false));
   UmTestUtils::ExpectVariableNotSet(provider_->var_is_connected());
   UmTestUtils::ExpectVariableNotSet(provider_->var_conn_type());
   UmTestUtils::ExpectVariableNotSet(provider_->var_conn_last_changed());
@@ -531,12 +479,16 @@
 // Test that, once a signal is received, the connection status and other info
 // can be read correctly.
 TEST_F(UmRealShillProviderTest, NoInitConnStatusReadConnTypeEthernet) {
-  // Re-initialize the provider, no initial connection status response.
-  Init(false);
-  SetupConnectionAndTestType(kFakeEthernetServicePath,
-                             kFakeEthernetServiceProxy,
-                             shill::kTypeEthernet,
-                             ConnectionType::kEthernet);
+  // Initialize the provider with no initial connection status response.
+  SetManagerReply(nullptr, false);
+  EXPECT_TRUE(provider_->Init());
+  EXPECT_TRUE(loop_.RunOnce(false));
+
+  SetupConnectionAndAttrs(kFakeEthernetServicePath,
+                          shill::kTypeEthernet,
+                          shill::kTetheringNotDetectedState,
+                          nullptr);
+  UmTestUtils::ExpectVariableHasValue(true, provider_->var_is_connected());
 }
 
 }  // namespace chromeos_update_manager
diff --git a/update_manager/state_factory.cc b/update_manager/state_factory.cc
index 54c0205..acefff4 100644
--- a/update_manager/state_factory.cc
+++ b/update_manager/state_factory.cc
@@ -22,17 +22,19 @@
 
 namespace chromeos_update_manager {
 
-State* DefaultStateFactory(policy::PolicyProvider* policy_provider,
-                           chromeos_update_engine::DBusWrapperInterface* dbus,
-                           chromeos_update_engine::SystemState* system_state) {
+State* DefaultStateFactory(
+    policy::PolicyProvider* policy_provider,
+    chromeos_update_engine::ShillProxy* shill_proxy,
+    org::chromium::SessionManagerInterfaceProxyInterface* session_manager_proxy,
+    chromeos_update_engine::SystemState* system_state) {
   chromeos_update_engine::ClockInterface* const clock = system_state->clock();
   unique_ptr<RealConfigProvider> config_provider(
       new RealConfigProvider(system_state->hardware()));
   unique_ptr<RealDevicePolicyProvider> device_policy_provider(
-      new RealDevicePolicyProvider(dbus, policy_provider));
+      new RealDevicePolicyProvider(session_manager_proxy, policy_provider));
   unique_ptr<RealRandomProvider> random_provider(new RealRandomProvider());
   unique_ptr<RealShillProvider> shill_provider(
-      new RealShillProvider(dbus, clock));
+      new RealShillProvider(shill_proxy, clock));
   unique_ptr<RealSystemProvider> system_provider(
       new RealSystemProvider(system_state->hardware()));
   unique_ptr<RealTimeProvider> time_provider(new RealTimeProvider(clock));
diff --git a/update_manager/state_factory.h b/update_manager/state_factory.h
index fa1164c..fd37ea7 100644
--- a/update_manager/state_factory.h
+++ b/update_manager/state_factory.h
@@ -5,7 +5,8 @@
 #ifndef UPDATE_ENGINE_UPDATE_MANAGER_STATE_FACTORY_H_
 #define UPDATE_ENGINE_UPDATE_MANAGER_STATE_FACTORY_H_
 
-#include "update_engine/dbus_wrapper_interface.h"
+#include "update_engine/dbus_proxies.h"
+#include "update_engine/shill_proxy.h"
 #include "update_engine/system_state.h"
 #include "update_engine/update_manager/state.h"
 
@@ -18,7 +19,8 @@
 // to initialize.
 State* DefaultStateFactory(
     policy::PolicyProvider* policy_provider,
-    chromeos_update_engine::DBusWrapperInterface* dbus,
+    chromeos_update_engine::ShillProxy* shill_proxy,
+    org::chromium::SessionManagerInterfaceProxyInterface* session_manager_proxy,
     chromeos_update_engine::SystemState* system_state);
 
 }  // namespace chromeos_update_manager
diff --git a/update_metadata.proto b/update_metadata.proto
index bf0cb45..39373d5 100644
--- a/update_metadata.proto
+++ b/update_metadata.proto
@@ -110,44 +110,87 @@
   optional string build_version = 6;
 }
 
-message DeltaArchiveManifest {
-  message InstallOperation {
-    enum Type {
-      REPLACE = 0;  // Replace destination extents w/ attached data
-      REPLACE_BZ = 1;  // Replace destination extents w/ attached bzipped data
-      MOVE = 2;  // Move source extents to destination extents
-      BSDIFF = 3;  // The data is a bsdiff binary diff
-      // SOURCE_COPY and SOURCE_BSDIFF are only supported on minor version 2.
-      SOURCE_COPY = 4; // Copy from source to target partition
-      SOURCE_BSDIFF = 5; // Like BSDIFF, but read from source partition
-    }
-    required Type type = 1;
-    // The offset into the delta file (after the protobuf)
-    // where the data (if any) is stored
-    optional uint32 data_offset = 2;
-    // The length of the data in the delta file
-    optional uint32 data_length = 3;
+message InstallOperation {
+  enum Type {
+    REPLACE = 0;  // Replace destination extents w/ attached data
+    REPLACE_BZ = 1;  // Replace destination extents w/ attached bzipped data
+    MOVE = 2;  // Move source extents to destination extents
+    BSDIFF = 3;  // The data is a bsdiff binary diff
 
-    // Ordered list of extents that are read from (if any) and written to.
-    repeated Extent src_extents = 4;
-    // Byte length of src, equal to the number of blocks in src_extents *
-    // block_size. It is used for BSDIFF, because we need to pass that
-    // external program the number of bytes to read from the blocks we pass it.
-    // This is not used in any other operation.
-    optional uint64 src_length = 5;
+    // SOURCE_COPY and SOURCE_BSDIFF are only supported on minor version 2 or
+    // higher.
+    SOURCE_COPY = 4; // Copy from source to target partition
+    SOURCE_BSDIFF = 5; // Like BSDIFF, but read from source partition
 
-    repeated Extent dst_extents = 6;
-    // Byte length of dst, equal to the number of blocks in dst_extents *
-    // block_size. Used for BSDIFF, but not in any other operation.
-    optional uint64 dst_length = 7;
-
-    // Optional SHA 256 hash of the blob associated with this operation.
-    // This is used as a primary validation for http-based downloads and
-    // as a defense-in-depth validation for https-based downloads. If
-    // the operation doesn't refer to any blob, this field will have
-    // zero bytes.
-    optional bytes data_sha256_hash = 8;
+    // ZERO and DISCARD are only supported on minor version 3.
+    ZERO = 6;  // Write zeros in the destination.
+    DISCARD = 7;  // Discard the destination blocks, reading as undefined.
   }
+  required Type type = 1;
+  // The offset into the delta file (after the protobuf)
+  // where the data (if any) is stored
+  optional uint32 data_offset = 2;
+  // The length of the data in the delta file
+  optional uint32 data_length = 3;
+
+  // Ordered list of extents that are read from (if any) and written to.
+  repeated Extent src_extents = 4;
+  // Byte length of src, equal to the number of blocks in src_extents *
+  // block_size. It is used for BSDIFF, because we need to pass that
+  // external program the number of bytes to read from the blocks we pass it.
+  // This is not used in any other operation.
+  optional uint64 src_length = 5;
+
+  repeated Extent dst_extents = 6;
+  // Byte length of dst, equal to the number of blocks in dst_extents *
+  // block_size. Used for BSDIFF, but not in any other operation.
+  optional uint64 dst_length = 7;
+
+  // Optional SHA 256 hash of the blob associated with this operation.
+  // This is used as a primary validation for http-based downloads and
+  // as a defense-in-depth validation for https-based downloads. If
+  // the operation doesn't refer to any blob, this field will have
+  // zero bytes.
+  optional bytes data_sha256_hash = 8;
+
+  // Indicates the SHA 256 hash of the source data referenced in src_extents at
+  // the time of applying the operation. If present, the update_engine daemon
+  // MUST read and verify the source data before applying the operation.
+  optional bytes src_sha256_hash = 9;
+}
+
+// Describes the update to apply to a single partition.
+message PartitionUpdate {
+  // A platform-specific name to identify the partition set being updated. For
+  // example, in Chrome OS this could be "ROOT" or "KERNEL".
+  required string partition_name = 1;
+
+  // Whether this partition carries a filesystem with a "/postinstall" script
+  // that must be run to finalize the update process.
+  optional bool run_postinstall = 2;
+
+  // If present, a list of signatures of the new_partition_info.hash signed with
+  // different keys. If the update_engine daemon requires vendor-signed images
+  // and has its public key installed, one of the signatures should be valid
+  // for /postinstall to run.
+  repeated Signatures.Signature new_partition_signature = 3;
+
+  optional PartitionInfo old_partition_info = 4;
+  optional PartitionInfo new_partition_info = 5;
+
+  // The list of operations to be performed to apply this PartitionUpdate. The
+  // associated operation blobs (in operations[i].data_offset, data_length)
+  // should be stored contiguously and in the same order.
+  repeated InstallOperation operations = 6;
+}
+
+message DeltaArchiveManifest {
+  // DEPRECATED. List of install operations for the kernel and rootfs
+  // partitions. This information is now present in the |partitions| field. If
+  // you set these fields instead of the ones in the PartitionUpdate, they will
+  // be migrated to the corresponding entries in the PartitionUpdate to support
+  // full payloads on legacy daemons. Delta payloads starting with minor_version
+  // 3 MAY NOT include these fields.
   repeated InstallOperation install_operations = 1;
   repeated InstallOperation kernel_install_operations = 2;
 
@@ -162,7 +205,12 @@
   optional uint64 signatures_offset = 4;
   optional uint64 signatures_size = 5;
 
-  // Partition data that can be used to validate the update.
+  // DEPRECATED. Partition metadata used to validate the update.
+  // This information is now present in the |partitions| field. If you set
+  // these fields instead of the ones in the PartitionUpdate, they will be
+  // migrated to the corresponding entries in the PartitionUpdate to support
+  // full payloads on legacy daemons. Delta payloads starting with minor_version
+  // 3 MAY NOT include these fields.
   optional PartitionInfo old_kernel_info = 6;
   optional PartitionInfo new_kernel_info = 7;
   optional PartitionInfo old_rootfs_info = 8;
@@ -174,4 +222,7 @@
   optional ImageInfo new_image_info = 11;
 
   optional uint32 minor_version = 12 [default = 0];
+
+  // List of partitions that will be updated, in the order they will be updated.
+  repeated PartitionUpdate partitions = 13;
 }
diff --git a/utils.cc b/utils.cc
index 97ce712..1366a39 100644
--- a/utils.cc
+++ b/utils.cc
@@ -29,6 +29,7 @@
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
 #include <base/files/scoped_file.h>
+#include <base/format_macros.h>
 #include <base/location.h>
 #include <base/logging.h>
 #include <base/posix/eintr_wrapper.h>
@@ -1240,32 +1241,33 @@
     NetworkConnectionType type,
     NetworkTethering tethering) {
   switch (type) {
-    case kNetUnknown:
+    case NetworkConnectionType::kUnknown:
       return metrics::ConnectionType::kUnknown;
 
-    case kNetEthernet:
+    case NetworkConnectionType::kEthernet:
       if (tethering == NetworkTethering::kConfirmed)
         return metrics::ConnectionType::kTetheredEthernet;
       else
         return metrics::ConnectionType::kEthernet;
 
-    case kNetWifi:
+    case NetworkConnectionType::kWifi:
       if (tethering == NetworkTethering::kConfirmed)
         return metrics::ConnectionType::kTetheredWifi;
       else
         return metrics::ConnectionType::kWifi;
 
-    case kNetWimax:
+    case NetworkConnectionType::kWimax:
       return metrics::ConnectionType::kWimax;
 
-    case kNetBluetooth:
+    case NetworkConnectionType::kBluetooth:
       return metrics::ConnectionType::kBluetooth;
 
-    case kNetCellular:
+    case NetworkConnectionType::kCellular:
       return metrics::ConnectionType::kCellular;
   }
 
-  LOG(ERROR) << "Unexpected network connection type: type=" << type
+  LOG(ERROR) << "Unexpected network connection type: type="
+             << static_cast<int>(type)
              << ", tethering=" << static_cast<int>(tethering);
 
   return metrics::ConnectionType::kUnknown;
@@ -1517,7 +1519,7 @@
 
 string CalculateP2PFileId(const string& payload_hash, size_t payload_size) {
   string encoded_hash = chromeos::data_encoding::Base64Encode(payload_hash);
-  return base::StringPrintf("cros_update_size_%zu_hash_%s",
+  return base::StringPrintf("cros_update_size_%" PRIuS "_hash_%s",
                             payload_size,
                             encoded_hash.c_str());
 }
diff --git a/utils.h b/utils.h
index b661b43..efea204 100644
--- a/utils.h
+++ b/utils.h
@@ -18,13 +18,13 @@
 #include <base/files/file_path.h>
 #include <base/posix/eintr_wrapper.h>
 #include <base/time/time.h>
-#include <chromeos/secure_blob.h>
 #include <chromeos/key_value_store.h>
+#include <chromeos/secure_blob.h>
 #include "metrics/metrics_library.h"
 
 #include "update_engine/action.h"
 #include "update_engine/action_processor.h"
-#include "update_engine/connection_manager.h"
+#include "update_engine/connection_manager_interface.h"
 #include "update_engine/constants.h"
 #include "update_engine/file_descriptor.h"
 #include "update_engine/metrics.h"
@@ -397,9 +397,7 @@
 // (NOTE: This function does not currently take daylight savings time
 // into account so the result may up to one hour off. This is because
 // the glibc date and timezone routines depend on the TZ environment
-// variable and changing environment variables is not thread-safe. An
-// alternative, thread-safe API to use would be GDateTime/GTimeZone
-// available in GLib 2.26 or later.)
+// variable and changing environment variables is not thread-safe.
 bool ConvertToOmahaInstallDate(base::Time time, int *out_num_days);
 
 // This function returns the duration on the wallclock since the last
diff --git a/utils_unittest.cc b/utils_unittest.cc
index 4787e84..83ef485 100644
--- a/utils_unittest.cc
+++ b/utils_unittest.cc
@@ -682,50 +682,50 @@
 TEST(UtilsTest, GetConnectionType) {
   // Check that expected combinations map to the right value.
   EXPECT_EQ(metrics::ConnectionType::kUnknown,
-            utils::GetConnectionType(kNetUnknown,
+            utils::GetConnectionType(NetworkConnectionType::kUnknown,
                                      NetworkTethering::kUnknown));
   EXPECT_EQ(metrics::ConnectionType::kEthernet,
-            utils::GetConnectionType(kNetEthernet,
+            utils::GetConnectionType(NetworkConnectionType::kEthernet,
                                      NetworkTethering::kUnknown));
   EXPECT_EQ(metrics::ConnectionType::kWifi,
-            utils::GetConnectionType(kNetWifi,
+            utils::GetConnectionType(NetworkConnectionType::kWifi,
                                      NetworkTethering::kUnknown));
   EXPECT_EQ(metrics::ConnectionType::kWimax,
-            utils::GetConnectionType(kNetWimax,
+            utils::GetConnectionType(NetworkConnectionType::kWimax,
                                      NetworkTethering::kUnknown));
   EXPECT_EQ(metrics::ConnectionType::kBluetooth,
-            utils::GetConnectionType(kNetBluetooth,
+            utils::GetConnectionType(NetworkConnectionType::kBluetooth,
                                      NetworkTethering::kUnknown));
   EXPECT_EQ(metrics::ConnectionType::kCellular,
-            utils::GetConnectionType(kNetCellular,
+            utils::GetConnectionType(NetworkConnectionType::kCellular,
                                      NetworkTethering::kUnknown));
   EXPECT_EQ(metrics::ConnectionType::kTetheredEthernet,
-            utils::GetConnectionType(kNetEthernet,
+            utils::GetConnectionType(NetworkConnectionType::kEthernet,
                                      NetworkTethering::kConfirmed));
   EXPECT_EQ(metrics::ConnectionType::kTetheredWifi,
-            utils::GetConnectionType(kNetWifi,
+            utils::GetConnectionType(NetworkConnectionType::kWifi,
                                      NetworkTethering::kConfirmed));
 
   // Ensure that we don't report tethered ethernet unless it's confirmed.
   EXPECT_EQ(metrics::ConnectionType::kEthernet,
-            utils::GetConnectionType(kNetEthernet,
+            utils::GetConnectionType(NetworkConnectionType::kEthernet,
                                      NetworkTethering::kNotDetected));
   EXPECT_EQ(metrics::ConnectionType::kEthernet,
-            utils::GetConnectionType(kNetEthernet,
+            utils::GetConnectionType(NetworkConnectionType::kEthernet,
                                      NetworkTethering::kSuspected));
   EXPECT_EQ(metrics::ConnectionType::kEthernet,
-            utils::GetConnectionType(kNetEthernet,
+            utils::GetConnectionType(NetworkConnectionType::kEthernet,
                                      NetworkTethering::kUnknown));
 
   // Ditto for tethered wifi.
   EXPECT_EQ(metrics::ConnectionType::kWifi,
-            utils::GetConnectionType(kNetWifi,
+            utils::GetConnectionType(NetworkConnectionType::kWifi,
                                      NetworkTethering::kNotDetected));
   EXPECT_EQ(metrics::ConnectionType::kWifi,
-            utils::GetConnectionType(kNetWifi,
+            utils::GetConnectionType(NetworkConnectionType::kWifi,
                                      NetworkTethering::kSuspected));
   EXPECT_EQ(metrics::ConnectionType::kWifi,
-            utils::GetConnectionType(kNetWifi,
+            utils::GetConnectionType(NetworkConnectionType::kWifi,
                                      NetworkTethering::kUnknown));
 }