git merge --no-ff 35fb87b830270adaf3b24b18cb43db8debb62bd8
diff --git a/libweave/AUTHORS b/libweave/AUTHORS
new file mode 100644
index 0000000..e016204
--- /dev/null
+++ b/libweave/AUTHORS
@@ -0,0 +1,7 @@
+# This is the official list of The Weave authors for copyright purposes.
+# This file is distinct from the CONTRIBUTORS files.
+# See the latter for an explanation.
+# Names should be added to this file as:
+# Name or Organization <email address>
+# The email address is not required for organizations.
+Google Inc.
diff --git a/libweave/Android.mk b/libweave/Android.mk
deleted file mode 100644
index 36732d3..0000000
--- a/libweave/Android.mk
+++ /dev/null
@@ -1,230 +0,0 @@
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-LOCAL_PATH := $(call my-dir)
-
-# Common variables
-# ========================================================
-
-libweaveCommonCppExtension := .cc
-libweaveCommonCFlags := -Wall -Werror \
-	-Wno-char-subscripts -Wno-missing-field-initializers \
-	-Wno-unused-function -Wno-unused-parameter
-
-libweaveCommonCppFlags := \
-	-Wno-deprecated-register \
-	-Wno-sign-compare \
-	-Wno-sign-promo \
-	-Wno-non-virtual-dtor \
-
-libweaveCommonCIncludes := \
-	$(LOCAL_PATH)/.. \
-	$(LOCAL_PATH)/include \
-	$(LOCAL_PATH)/third_party/modp_b64/modp_b64 \
-	external/gtest/include \
-
-libweaveSharedLibraries := \
-	libchrome \
-	libexpat \
-	libcrypto \
-
-# libweave-external
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libweave-external
-LOCAL_CPP_EXTENSION := $(libweaveCommonCppExtension)
-LOCAL_CFLAGS := $(libweaveCommonCFlags)
-LOCAL_CPPFLAGS := $(libweaveCommonCppFlags)
-LOCAL_C_INCLUDES := $(libweaveCommonCIncludes)
-LOCAL_SHARED_LIBRARIES := $(libweaveSharedLibraries)
-LOCAL_STATIC_LIBRARIES :=
-LOCAL_RTTI_FLAG := -frtti
-LOCAL_CLANG := true
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/external
-
-LOCAL_SRC_FILES := \
-	external/crypto/p224.cc \
-	external/crypto/p224_spake.cc \
-	external/crypto/sha2.cc \
-	third_party/modp_b64/modp_b64.cc \
-
-include $(BUILD_STATIC_LIBRARY)
-
-# libweave-common
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libweave-common
-LOCAL_CPP_EXTENSION := $(libweaveCommonCppExtension)
-LOCAL_CFLAGS := $(libweaveCommonCFlags)
-LOCAL_CPPFLAGS := $(libweaveCommonCppFlags)
-LOCAL_C_INCLUDES := $(libweaveCommonCIncludes)
-LOCAL_SHARED_LIBRARIES := $(libweaveSharedLibraries)
-LOCAL_STATIC_LIBRARIES := libweave-external
-LOCAL_RTTI_FLAG := -frtti
-LOCAL_CLANG := true
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-
-LOCAL_SRC_FILES := \
-	src/backoff_entry.cc \
-	src/base_api_handler.cc \
-	src/commands/cloud_command_proxy.cc \
-	src/commands/command_definition.cc \
-	src/commands/command_dictionary.cc \
-	src/commands/command_instance.cc \
-	src/commands/command_manager.cc \
-	src/commands/command_queue.cc \
-	src/commands/object_schema.cc \
-	src/commands/prop_constraints.cc \
-	src/commands/prop_types.cc \
-	src/commands/prop_values.cc \
-	src/commands/schema_constants.cc \
-	src/commands/schema_utils.cc \
-	src/config.cc \
-	src/data_encoding.cc \
-	src/device_manager.cc \
-	src/device_registration_info.cc \
-	src/error.cc \
-	src/http_constants.cc \
-	src/json_error_codes.cc \
-	src/notification/notification_parser.cc \
-	src/notification/pull_channel.cc \
-	src/notification/xml_node.cc \
-	src/notification/xmpp_channel.cc \
-	src/notification/xmpp_iq_stanza_handler.cc \
-	src/notification/xmpp_stream_parser.cc \
-	src/privet/cloud_delegate.cc \
-	src/privet/constants.cc \
-	src/privet/device_delegate.cc \
-	src/privet/openssl_utils.cc \
-	src/privet/privet_handler.cc \
-	src/privet/privet_manager.cc \
-	src/privet/privet_types.cc \
-	src/privet/publisher.cc \
-	src/privet/security_manager.cc \
-	src/privet/wifi_bootstrap_manager.cc \
-	src/privet/wifi_ssid_generator.cc \
-	src/registration_status.cc \
-	src/states/error_codes.cc \
-	src/states/state_change_queue.cc \
-	src/states/state_manager.cc \
-	src/states/state_package.cc \
-	src/string_utils.cc \
-	src/utils.cc \
-
-include $(BUILD_STATIC_LIBRARY)
-
-# libweave-test
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libweave-test
-LOCAL_CPP_EXTENSION := $(libweaveCommonCppExtension)
-LOCAL_CFLAGS := $(libweaveCommonCFlags)
-LOCAL_CPPFLAGS := $(libweaveCommonCppFlags)
-LOCAL_C_INCLUDES := \
-	$(libweaveCommonCIncludes) \
-	external/gmock/include \
-
-LOCAL_SHARED_LIBRARIES := $(libweaveSharedLibraries)
-LOCAL_STATIC_LIBRARIES := libgtest libgmock
-LOCAL_CLANG := true
-LOCAL_RTTI_FLAG := -frtti
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-LOCAL_SRC_FILES := \
-	src/test/fake_stream.cc \
-	src/test/fake_task_runner.cc \
-	src/test/mock_command.cc \
-	src/test/mock_http_client.cc \
-	src/test/unittest_utils.cc \
-
-include $(BUILD_STATIC_LIBRARY)
-
-# libweave
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libweave
-LOCAL_CPP_EXTENSION := $(libweaveCommonCppExtension)
-LOCAL_CFLAGS := $(libweaveCommonCFlags)
-LOCAL_CPPFLAGS := $(libweaveCommonCppFlags)
-LOCAL_C_INCLUDES := $(libweaveCommonCIncludes)
-LOCAL_SHARED_LIBRARIES := $(libweaveSharedLibraries)
-LOCAL_WHOLE_STATIC_LIBRARIES := libweave-external libweave-common
-LOCAL_CLANG := true
-LOCAL_RTTI_FLAG := -frtti
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-
-LOCAL_SRC_FILES := \
-	src/empty.cc
-
-include $(BUILD_SHARED_LIBRARY)
-
-# libweave_testrunner
-# ========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libweave_testrunner
-LOCAL_CPP_EXTENSION := $(libweaveCommonCppExtension)
-LOCAL_CFLAGS := $(libweaveCommonCFlags)
-LOCAL_CPPFLAGS := $(libweaveCommonCppFlags)
-LOCAL_C_INCLUDES := \
-	$(libweaveCommonCIncludes) \
-	external/gmock/include \
-
-LOCAL_SHARED_LIBRARIES := \
-	$(libweaveSharedLibraries) \
-
-LOCAL_STATIC_LIBRARIES := \
-	libweave-external \
-	libweave-common \
-	libweave-test \
-	libchromeos-test-helpers \
-	libgtest libgmock \
-	libchrome_test_helpers \
-
-LOCAL_CLANG := true
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
-
-LOCAL_SRC_FILES := \
-	external/crypto/p224_spake_unittest.cc \
-	external/crypto/p224_unittest.cc \
-	external/crypto/sha2_unittest.cc \
-	src/backoff_entry_unittest.cc \
-	src/base_api_handler_unittest.cc \
-	src/commands/cloud_command_proxy_unittest.cc \
-	src/commands/command_definition_unittest.cc \
-	src/commands/command_dictionary_unittest.cc \
-	src/commands/command_instance_unittest.cc \
-	src/commands/command_manager_unittest.cc \
-	src/commands/command_queue_unittest.cc \
-	src/commands/object_schema_unittest.cc \
-	src/commands/schema_utils_unittest.cc \
-	src/config_unittest.cc \
-	src/data_encoding_unittest.cc \
-	src/device_registration_info_unittest.cc \
-	src/error_unittest.cc \
-	src/notification/notification_parser_unittest.cc \
-	src/notification/xml_node_unittest.cc \
-	src/notification/xmpp_channel_unittest.cc \
-	src/notification/xmpp_iq_stanza_handler_unittest.cc \
-	src/notification/xmpp_stream_parser_unittest.cc \
-	src/privet/privet_handler_unittest.cc \
-	src/privet/security_manager_unittest.cc \
-	src/privet/wifi_ssid_generator_unittest.cc \
-	src/states/state_change_queue_unittest.cc \
-	src/states/state_manager_unittest.cc \
-	src/states/state_package_unittest.cc \
-	src/string_utils_unittest.cc \
-	src/test/weave_testrunner.cc \
-	src/weave_unittest.cc \
-
-include $(BUILD_NATIVE_TEST)
diff --git a/libweave/CONTRIBUTORS b/libweave/CONTRIBUTORS
new file mode 100644
index 0000000..b45bd87
--- /dev/null
+++ b/libweave/CONTRIBUTORS
@@ -0,0 +1,28 @@
+# People who have agreed to one of the CLAs and can contribute patches.
+# The AUTHORS file lists the copyright holders; this file
+# lists people.  For example, Google employees are listed here
+# but not in AUTHORS, because Google holds the copyright.
+#
+# https://developers.google.com/open-source/cla/individual
+# https://developers.google.com/open-source/cla/corporate
+#
+# Names should be added to this file as:
+#     Name <email address>
+
+Alex Deymo <deymo@google.com>
+Alex Vakulenko <avakulenko@google.com>
+Anton Muhin <antonm@google.com>
+Bertrand Simonnet <bsimonnet@google.com>
+Chris Sosa <sosa@google.com>
+Christopher Wiley <wiley@google.com>
+Daniel Erat <derat@google.com>
+David Zeuthen <zeuthen@google.com>
+Gene Gutnik <gene@google.com>
+Gerry Fan <gfan@google.com>
+Johan Euphrosine <proppy@google.com>
+Mike Frysinger <vapier@google.com>
+Nathan Bullock <nathanbullock@google.com>
+Robert Ginda <rginda@google.com>
+Stefan Sauer <ensonic@google.com>
+Vitaly Buka <vitalybuka@google.com>
+Yunlian Jiang <yunlian@google.com>
diff --git a/libweave/LICENSE b/libweave/LICENSE
index cc8dd21..474e76e 100644
--- a/libweave/LICENSE
+++ b/libweave/LICENSE
@@ -1,28 +1,26 @@
-Copyright 2015, Google Inc. All Rights Reserved.
+Copyright (c) 2015, The Weave Authors.
+All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
+modification, are permitted provided that the following conditions are met:
 
-   * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
-   * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
+     * Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+     * Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+     * Neither the name of The Weave Authors nor the
+       names of its contributors may be used to endorse or promote products
+       derived from this software without specific prior written permission.
 
-   * Neither the name of Google Inc. nor the names of its
-contributors may be used to endorse or promote products derived from
-this software without specific prior written permission.
 
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/libweave/README b/libweave/README
index def7148..7e519b9 100644
--- a/libweave/README
+++ b/libweave/README
@@ -27,23 +27,13 @@
 Example of device code:
   examples/
 
-Optional dependencies:
-  external/
+Dependencies:
   third_party/
 
 Build files:
   libweave_standalone.gyp
   libweave_common.gypi
 
-ChromiumOS specific build files:
-  libweave-test.pc.in
-  libweave.pc.in
-  platform2.gyp
-  platform2_preinstall.sh
-
-AOSP specific build files:
-  Android.mk
-
 Quick start on Ubuntu
 ---------------------
 
@@ -91,7 +81,7 @@
 
 Generate ninja build files:
 
-  gyp -I libweave_common.gypi --toplevel-dir=. \
+  gyp -I libweave_common.gypi --toplevel-dir=. --depth=. \
       -f ninja libweave_standalone.gyp
 
 Build library with tests:
diff --git a/libweave/examples/ubuntu/avahi_client.cc b/libweave/examples/ubuntu/avahi_client.cc
index 7d822fb..3bc1823 100644
--- a/libweave/examples/ubuntu/avahi_client.cc
+++ b/libweave/examples/ubuntu/avahi_client.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/examples/ubuntu/avahi_client.h b/libweave/examples/ubuntu/avahi_client.h
index ba4a02b..64e0eb1 100644
--- a/libweave/examples/ubuntu/avahi_client.h
+++ b/libweave/examples/ubuntu/avahi_client.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/examples/ubuntu/bluez_client.cc b/libweave/examples/ubuntu/bluez_client.cc
index a1f13d3..35defde 100644
--- a/libweave/examples/ubuntu/bluez_client.cc
+++ b/libweave/examples/ubuntu/bluez_client.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/examples/ubuntu/bluez_client.h b/libweave/examples/ubuntu/bluez_client.h
index 23e21ad..e0bc2f2 100644
--- a/libweave/examples/ubuntu/bluez_client.h
+++ b/libweave/examples/ubuntu/bluez_client.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/examples/ubuntu/build.sh b/libweave/examples/ubuntu/build.sh
index ea7499f..cc585cb 100755
--- a/libweave/examples/ubuntu/build.sh
+++ b/libweave/examples/ubuntu/build.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright 2015 The Chromium OS Authors. All rights reserved.
+# Copyright 2015 The Weave Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -23,6 +23,6 @@
 ninja -j $CORES -C out/${BUILD_CONFIG} $BUILD_TARGET || exit 1
 
 if [[ $BUILD_TARGET == *"libweave_testrunner"* ]]; then
-  out/${BUILD_CONFIG}/libweave_testrunner || exit 1
-  out/${BUILD_CONFIG}/libweave_exports_testrunner || exit 1
+  out/${BUILD_CONFIG}/libweave_testrunner --gtest_break_on_failure || exit 1
+  out/${BUILD_CONFIG}/libweave_exports_testrunner --gtest_break_on_failure || exit 1
 fi
diff --git a/libweave/examples/ubuntu/curl_http_client.cc b/libweave/examples/ubuntu/curl_http_client.cc
index 3e996e3..0ddf57a 100644
--- a/libweave/examples/ubuntu/curl_http_client.cc
+++ b/libweave/examples/ubuntu/curl_http_client.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #include <base/bind.h>
 #include <curl/curl.h>
 #include <weave/provider/task_runner.h>
+#include <weave/enum_to_string.h>
 
 namespace weave {
 namespace examples {
@@ -14,11 +15,11 @@
 namespace {
 
 struct ResponseImpl : public provider::HttpClient::Response {
-  int GetStatusCode() const { return status; }
-  std::string GetContentType() const { return content_type; }
-  const std::string& GetData() const { return data; }
+  int GetStatusCode() const override { return status; }
+  std::string GetContentType() const override { return content_type; }
+  std::string GetData() const override { return data; }
 
-  int status;
+  long status{0};
   std::string content_type;
   std::string data;
 };
@@ -34,23 +35,27 @@
 CurlHttpClient::CurlHttpClient(provider::TaskRunner* task_runner)
     : task_runner_{task_runner} {}
 
-std::unique_ptr<provider::HttpClient::Response>
-CurlHttpClient::SendRequestAndBlock(const std::string& method,
-                                    const std::string& url,
-                                    const Headers& headers,
-                                    const std::string& data,
-                                    ErrorPtr* error) {
+void CurlHttpClient::SendRequest(Method method,
+                                 const std::string& url,
+                                 const Headers& headers,
+                                 const std::string& data,
+                                 const SendRequestCallback& callback) {
   std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> curl{curl_easy_init(),
                                                            &curl_easy_cleanup};
   CHECK(curl);
 
-  if (method == "GET") {
+  switch (method) {
+    case Method::kGet:
     CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 1L));
-  } else if (method == "POST") {
+    break;
+    case Method::kPost:
     CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPPOST, 1L));
-  } else {
-    CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST,
-                                        method.c_str()));
+    break;
+    case Method::kPatch:
+    case Method::kPut:
+      CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST,
+                                          weave::EnumToString(method).c_str()));
+      break;
   }
 
   CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str()));
@@ -61,7 +66,7 @@
 
   CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, chunk));
 
-  if (!data.empty() || method == "POST") {
+  if (!data.empty() || method == Method::kPost) {
     CHECK_EQ(CURLE_OK,
              curl_easy_setopt(curl.get(), CURLOPT_POSTFIELDS, data.c_str()));
   }
@@ -80,18 +85,21 @@
   if (chunk)
     curl_slist_free_all(chunk);
 
+  ErrorPtr error;
   if (res != CURLE_OK) {
-    Error::AddTo(error, FROM_HERE, "curl", "curl_easy_perform_error",
+    Error::AddTo(&error, FROM_HERE, "curl", "curl_easy_perform_error",
                  curl_easy_strerror(res));
-    return nullptr;
+    return task_runner_->PostDelayedTask(
+        FROM_HERE, base::Bind(callback, nullptr, base::Passed(&error)), {});
   }
 
   const std::string kContentType = "\r\nContent-Type:";
   auto pos = response->content_type.find(kContentType);
   if (pos == std::string::npos) {
-    Error::AddTo(error, FROM_HERE, "curl", "no_content_header",
+    Error::AddTo(&error, FROM_HERE, "curl", "no_content_header",
                  "Content-Type header is missing");
-    return nullptr;
+    return task_runner_->PostDelayedTask(
+        FROM_HERE, base::Bind(callback, nullptr, base::Passed(&error)), {});
   }
   pos += kContentType.size();
   auto pos_end = response->content_type.find("\r\n", pos);
@@ -103,44 +111,9 @@
 
   CHECK_EQ(CURLE_OK, curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE,
                                        &response->status));
-  return std::move(response);
-}
 
-int CurlHttpClient::SendRequest(const std::string& method,
-                                const std::string& url,
-                                const Headers& headers,
-                                const std::string& data,
-                                const SuccessCallback& success_callback,
-                                const ErrorCallback& error_callback) {
-  ++request_id_;
-  ErrorPtr error;
-  auto response = SendRequestAndBlock(method, url, headers, data, &error);
-  if (response) {
-    task_runner_->PostDelayedTask(
-        FROM_HERE, base::Bind(&CurlHttpClient::RunSuccessCallback,
-                              weak_ptr_factory_.GetWeakPtr(), success_callback,
-                              request_id_, base::Passed(&response)),
-        {});
-  } else {
-    task_runner_->PostDelayedTask(
-        FROM_HERE, base::Bind(&CurlHttpClient::RunErrorCallback,
-                              weak_ptr_factory_.GetWeakPtr(), error_callback,
-                              request_id_, base::Passed(&error)),
-        {});
-  }
-  return request_id_;
-}
-
-void CurlHttpClient::RunSuccessCallback(const SuccessCallback& success_callback,
-                                        int id,
-                                        std::unique_ptr<Response> response) {
-  success_callback.Run(id, *response);
-}
-
-void CurlHttpClient::RunErrorCallback(const ErrorCallback& error_callback,
-                                      int id,
-                                      ErrorPtr error) {
-  error_callback.Run(id, error.get());
+  task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(callback, base::Passed(&response), nullptr), {});
 }
 
 }  // namespace examples
diff --git a/libweave/examples/ubuntu/curl_http_client.h b/libweave/examples/ubuntu/curl_http_client.h
index 853b797..daf9b0c 100644
--- a/libweave/examples/ubuntu/curl_http_client.h
+++ b/libweave/examples/ubuntu/curl_http_client.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,28 +24,14 @@
  public:
   explicit CurlHttpClient(provider::TaskRunner* task_runner);
 
-  std::unique_ptr<Response> SendRequestAndBlock(const std::string& method,
-                                                const std::string& url,
-                                                const Headers& headers,
-                                                const std::string& data,
-                                                ErrorPtr* error) override;
-  int SendRequest(const std::string& method,
-                  const std::string& url,
-                  const Headers& headers,
-                  const std::string& data,
-                  const SuccessCallback& success_callback,
-                  const ErrorCallback& error_callback) override;
+  void SendRequest(Method method,
+                   const std::string& url,
+                   const Headers& headers,
+                   const std::string& data,
+                   const SendRequestCallback& callback) override;
 
  private:
-  void RunSuccessCallback(const SuccessCallback& success_callback,
-                          int id,
-                          std::unique_ptr<Response> response);
-  void RunErrorCallback(const ErrorCallback& error_callback,
-                        int id,
-                        ErrorPtr error);
-
   provider::TaskRunner* task_runner_{nullptr};
-  int request_id_ = 0;
 
   base::WeakPtrFactory<CurlHttpClient> weak_ptr_factory_{this};
 };
diff --git a/libweave/examples/ubuntu/event_http_client.cc b/libweave/examples/ubuntu/event_http_client.cc
new file mode 100644
index 0000000..22b68f2
--- /dev/null
+++ b/libweave/examples/ubuntu/event_http_client.cc
@@ -0,0 +1,156 @@
+// Copyright 2015 The Weave 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 "examples/ubuntu/event_http_client.h"
+#include "examples/ubuntu/event_task_runner.h"
+
+#include <weave/enum_to_string.h>
+
+#include <string>
+
+#include <base/bind.h>
+
+#include <event2/bufferevent.h>
+#include <event2/buffer.h>
+#include <event2/http.h>
+
+// EventHttpClient based on libevent2 http-client sample
+// TODO(proppy): https
+// TODO(proppy): hostname validation
+namespace weave {
+
+namespace {
+const weave::EnumToStringMap<evhttp_cmd_type>::Map kMapMethod[] = {
+    {EVHTTP_REQ_GET, "GET"},        {EVHTTP_REQ_POST, "POST"},
+    {EVHTTP_REQ_HEAD, "HEAD"},      {EVHTTP_REQ_PUT, "PUT"},
+    {EVHTTP_REQ_PATCH, "PATCH"},    {EVHTTP_REQ_DELETE, "DELETE"},
+    {EVHTTP_REQ_OPTIONS, "OPTIONS"}};
+}  // namespace
+
+template <>
+EnumToStringMap<evhttp_cmd_type>::EnumToStringMap()
+    : EnumToStringMap(kMapMethod) {}
+
+using namespace provider;
+
+namespace examples {
+
+namespace {
+
+class EventDeleter {
+ public:
+  void operator()(evhttp_uri* http_uri) { evhttp_uri_free(http_uri); }
+  void operator()(evhttp_connection* conn) { evhttp_connection_free(conn); }
+  void operator()(evhttp_request* req) { evhttp_request_free(req); }
+};
+
+class EventHttpResponse : public weave::provider::HttpClient::Response {
+ public:
+  int GetStatusCode() const override { return status; }
+  std::string GetContentType() const override { return content_type; }
+  std::string GetData() const { return data; }
+
+  int status;
+  std::string content_type;
+  std::string data;
+};
+
+struct EventRequestState {
+  TaskRunner* task_runner_;
+  std::unique_ptr<evhttp_uri, EventDeleter> http_uri_;
+  std::unique_ptr<evhttp_connection, EventDeleter> evcon_;
+  HttpClient::SendRequestCallback callback_;
+};
+
+void RequestDoneCallback(evhttp_request* req, void* ctx) {
+  std::unique_ptr<EventRequestState> state{
+      static_cast<EventRequestState*>(ctx)};
+  if (!req) {
+    ErrorPtr error;
+    auto err = EVUTIL_SOCKET_ERROR();
+    Error::AddToPrintf(&error, FROM_HERE, "http_client", "request_failed",
+                       "request failed: %s",
+                       evutil_socket_error_to_string(err));
+    state->task_runner_->PostDelayedTask(
+        FROM_HERE, base::Bind(state->callback_, nullptr, base::Passed(&error)),
+        {});
+    return;
+  }
+  std::unique_ptr<EventHttpResponse> response{new EventHttpResponse()};
+  response->status = evhttp_request_get_response_code(req);
+  auto buffer = evhttp_request_get_input_buffer(req);
+  auto length = evbuffer_get_length(buffer);
+  response->data.resize(length);
+  auto n = evbuffer_remove(buffer, &response->data[0], length);
+  CHECK_EQ(n, int(length));
+  state->task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(state->callback_, base::Passed(&response), nullptr),
+      {});
+}
+
+}  // namespace
+
+EventHttpClient::EventHttpClient(EventTaskRunner* task_runner)
+    : task_runner_{task_runner} {}
+
+void EventHttpClient::SendRequest(Method method,
+                                  const std::string& url,
+                                  const Headers& headers,
+                                  const std::string& data,
+                                  const SendRequestCallback& callback) {
+  evhttp_cmd_type method_id;
+  CHECK(weave::StringToEnum(weave::EnumToString(method), &method_id));
+  std::unique_ptr<evhttp_uri, EventDeleter> http_uri{
+      evhttp_uri_parse(url.c_str())};
+  CHECK(http_uri);
+  auto host = evhttp_uri_get_host(http_uri.get());
+  CHECK(host);
+  auto port = evhttp_uri_get_port(http_uri.get());
+  if (port == -1)
+    port = 80;
+  std::string path{evhttp_uri_get_path(http_uri.get())};
+  if (path.length() == 0) {
+    path = "/";
+  }
+  std::string uri{path};
+  auto query = evhttp_uri_get_query(http_uri.get());
+  if (query) {
+    uri = path + "?" + query;
+  }
+  auto bev = bufferevent_socket_new(task_runner_->GetEventBase(), -1,
+                                    BEV_OPT_CLOSE_ON_FREE);
+  CHECK(bev);
+  std::unique_ptr<evhttp_connection, EventDeleter> conn{
+      evhttp_connection_base_bufferevent_new(task_runner_->GetEventBase(), NULL,
+                                             bev, host, port)};
+  CHECK(conn);
+  std::unique_ptr<evhttp_request, EventDeleter> req{evhttp_request_new(
+      &RequestDoneCallback,
+      new EventRequestState{task_runner_, std::move(http_uri), std::move(conn),
+                            callback})};
+  CHECK(req);
+  auto output_headers = evhttp_request_get_output_headers(req.get());
+  evhttp_add_header(output_headers, "Host", host);
+  for (auto& kv : headers)
+    evhttp_add_header(output_headers, kv.first.c_str(), kv.second.c_str());
+  if (!data.empty()) {
+    auto output_buffer = evhttp_request_get_output_buffer(req.get());
+    evbuffer_add(output_buffer, data.c_str(), data.length());
+    evhttp_add_header(output_headers, "Content-Length",
+                      std::to_string(data.length()).c_str());
+  }
+  auto res =
+      evhttp_make_request(conn.get(), req.release(), method_id, uri.c_str());
+  if (res >= 0)
+    return;
+  ErrorPtr error;
+  Error::AddToPrintf(&error, FROM_HERE, "http_client", "request_failed",
+                     "request failed: %s %s", EnumToString(method).c_str(),
+                     url.c_str());
+  task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(callback, nullptr, base::Passed(&error)), {});
+}
+
+}  // namespace examples
+}  // namespace weave
diff --git a/libweave/examples/ubuntu/event_http_client.h b/libweave/examples/ubuntu/event_http_client.h
new file mode 100644
index 0000000..6ec2db6
--- /dev/null
+++ b/libweave/examples/ubuntu/event_http_client.h
@@ -0,0 +1,38 @@
+// Copyright 2015 The Weave 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 LIBWEAVE_EXAMPLES_UBUNTU_EVENT_HTTP_CLIENT_H_
+#define LIBWEAVE_EXAMPLES_UBUNTU_EVENT_HTTP_CLIENT_H_
+
+#include <string>
+
+#include <base/memory/weak_ptr.h>
+#include <weave/provider/http_client.h>
+
+namespace weave {
+namespace examples {
+
+class EventTaskRunner;
+
+// Basic implementation of weave::HttpClient using libevent.
+class EventHttpClient : public provider::HttpClient {
+ public:
+  explicit EventHttpClient(EventTaskRunner* task_runner);
+
+  void SendRequest(Method method,
+                   const std::string& url,
+                   const Headers& headers,
+                   const std::string& data,
+                   const SendRequestCallback& callback) override;
+
+ private:
+  EventTaskRunner* task_runner_{nullptr};
+
+  base::WeakPtrFactory<EventHttpClient> weak_ptr_factory_{this};
+};
+
+}  // namespace examples
+}  // namespace weave
+
+#endif  // LIBWEAVE_EXAMPLES_UBUNTU_EVENT_HTTP_CLIENT_H_
diff --git a/libweave/examples/ubuntu/event_http_server.cc b/libweave/examples/ubuntu/event_http_server.cc
index 9c0dc78..95fea21 100644
--- a/libweave/examples/ubuntu/event_http_server.cc
+++ b/libweave/examples/ubuntu/event_http_server.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -30,75 +30,49 @@
       base, -1, SSL_new(ctx), BUFFEREVENT_SSL_ACCEPTING, BEV_OPT_CLOSE_ON_FREE);
 }
 
-class MemoryReadStream : public Stream {
- public:
-  MemoryReadStream(const std::vector<uint8_t>& data,
-                   provider::TaskRunner* task_runner)
-      : data_{data}, task_runner_{task_runner} {}
-
-  void Read(void* buffer,
-            size_t size_to_read,
-            const ReadSuccessCallback& success_callback,
-            const ErrorCallback& error_callback) override {
-    CHECK_LE(read_position_, data_.size());
-    size_t size_read = std::min(size_to_read, data_.size() - read_position_);
-    if (size_read > 0)
-      memcpy(buffer, data_.data() + read_position_, size_read);
-    read_position_ += size_read;
-    success_callback.Run(size_read);
-  }
-
-  void Write(const void* buffer,
-             size_t size_to_write,
-             const SuccessCallback& success_callback,
-             const ErrorCallback& error_callback) override {
-    LOG(FATAL) << "Unsupported";
-  }
-
-  void CancelPendingOperations() override {}
-
- private:
-  std::vector<uint8_t> data_;
-  provider::TaskRunner* task_runner_;
-  size_t read_position_{0};
-};
-
 }  // namespace
 
 class HttpServerImpl::RequestImpl : public Request {
  public:
-  explicit RequestImpl(evhttp_request* req, provider::TaskRunner* task_runner)
-      : path_{evhttp_request_uri(req)}, task_runner_{task_runner} {
-    path_ = path_.substr(0, path_.find("?"));
-    path_ = path_.substr(0, path_.find("#"));
+  RequestImpl(evhttp_request* req, provider::TaskRunner* task_runner)
+      : task_runner_{task_runner} {
     req_.reset(req);
+    uri_ = evhttp_request_get_evhttp_uri(req_.get());
 
     data_.resize(evbuffer_get_length(req_->input_buffer));
-    evbuffer_remove(req_->input_buffer, data_.data(), data_.size());
+    evbuffer_remove(req_->input_buffer, &data_[0], data_.size());
   }
 
   ~RequestImpl() {}
 
-  const std::string& GetPath() const override { return path_; }
+  std::string GetPath() const override {
+    const char* path = evhttp_uri_get_path(uri_);
+    return path ? path : "";
+  }
   std::string GetFirstHeader(const std::string& name) const override {
     const char* header = evhttp_find_header(req_->input_headers, name.c_str());
     if (!header)
       return {};
     return header;
   }
-  const std::vector<uint8_t>& GetData() const override { return data_; }
-  std::unique_ptr<Stream> GetDataStream() const override {
-    return std::unique_ptr<Stream>{new MemoryReadStream{data_, task_runner_}};
+  std::string GetData() { return data_; }
+
+  void SendReply(int status_code,
+                 const std::string& data,
+                 const std::string& mime_type) override {
+    std::unique_ptr<evbuffer, decltype(&evbuffer_free)> buf{evbuffer_new(),
+                                                            &evbuffer_free};
+    evbuffer_add(buf.get(), data.data(), data.size());
+    evhttp_add_header(req_->output_headers, "Content-Type", mime_type.c_str());
+    evhttp_send_reply(req_.release(), status_code, "None", buf.get());
   }
 
-  evhttp_request* ReleaseHandler() { return req_.release(); }
-
  private:
-  std::vector<uint8_t> data_;
   std::unique_ptr<evhttp_request, decltype(&evhttp_cancel_request)> req_{
       nullptr, &evhttp_cancel_request};
-  std::string path_;
-  provider::TaskRunner* task_runner_;
+  provider::TaskRunner* task_runner_{nullptr};
+  std::string data_;
+  const evhttp_uri* uri_{nullptr};
 };
 
 HttpServerImpl::HttpServerImpl(EventTaskRunner* task_runner)
@@ -127,8 +101,6 @@
   CHECK(httpsd_);
 
   evhttp_set_bevcb(httpsd_.get(), BuffetEventCallback, ctx_.get());
-  evhttp_set_gencb(httpd_.get(), ProcessRequestCallback, this);
-  evhttp_set_gencb(httpsd_.get(), ProcessRequestCallback, this);
 
   CHECK_EQ(0, evhttp_bind_socket(httpd_.get(), "0.0.0.0", GetHttpPort()));
   CHECK_EQ(0, evhttp_bind_socket(httpsd_.get(), "0.0.0.0", GetHttpsPort()));
@@ -175,38 +147,34 @@
 }
 
 void HttpServerImpl::ProcessRequest(evhttp_request* req) {
-  std::string path = evhttp_request_uri(req);
-  for (auto i = handlers_.rbegin(); i != handlers_.rend(); ++i) {
-    if (path.compare(0, i->first.size(), i->first) == 0) {
-      auto request = std::make_shared<RequestImpl>(req, task_runner_);
-      i->second.Run(*request,
-                    base::Bind(&HttpServerImpl::ProcessReply,
-                               weak_ptr_factory_.GetWeakPtr(), request));
-      return;
-    }
+  std::unique_ptr<RequestImpl> request{new RequestImpl{req, task_runner_}};
+  std::string path = request->GetPath();
+  auto it = handlers_.find(path);
+  if (it != handlers_.end()) {
+    return it->second.Run(std::move(request));
   }
   NotFound(req);
 }
+
 void HttpServerImpl::ProcessReply(std::shared_ptr<RequestImpl> request,
                                   int status_code,
                                   const std::string& data,
                                   const std::string& mime_type) {
-  std::unique_ptr<evbuffer, decltype(&evbuffer_free)> buf{evbuffer_new(),
-                                                          &evbuffer_free};
-  evbuffer_add(buf.get(), data.data(), data.size());
-  evhttp_request* req = request->ReleaseHandler();
-  evhttp_add_header(req->output_headers, "Content-Type", mime_type.c_str());
-  evhttp_send_reply(req, status_code, "None", buf.get());
+
 }
 
-void HttpServerImpl::AddOnStateChangedCallback(
-    const OnStateChangedCallback& callback) {
-  callback.Run(*this);
+void HttpServerImpl::AddHttpRequestHandler(
+    const std::string& path,
+    const RequestHandlerCallback& callback) {
+  handlers_.emplace(path, callback);
+  evhttp_set_cb(httpd_.get(), path.c_str(), &ProcessRequestCallback, this);
 }
 
-void HttpServerImpl::AddRequestHandler(const std::string& path_prefix,
-                                       const OnRequestCallback& callback) {
-  handlers_.emplace(path_prefix, callback);
+void HttpServerImpl::AddHttpsRequestHandler(
+    const std::string& path,
+    const RequestHandlerCallback& callback) {
+  handlers_.emplace(path, callback);
+  evhttp_set_cb(httpsd_.get(), path.c_str(), &ProcessRequestCallback, this);
 }
 
 uint16_t HttpServerImpl::GetHttpPort() const {
@@ -217,8 +185,7 @@
   return 7781;
 }
 
-const std::vector<uint8_t>& HttpServerImpl::GetHttpsCertificateFingerprint()
-    const {
+std::vector<uint8_t> HttpServerImpl::GetHttpsCertificateFingerprint() const {
   return cert_fingerprint_;
 }
 
diff --git a/libweave/examples/ubuntu/event_http_server.h b/libweave/examples/ubuntu/event_http_server.h
index 1005edc..4828b72 100644
--- a/libweave/examples/ubuntu/event_http_server.h
+++ b/libweave/examples/ubuntu/event_http_server.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -28,13 +28,13 @@
 
   explicit HttpServerImpl(EventTaskRunner* task_runner);
 
-  void AddOnStateChangedCallback(
-      const OnStateChangedCallback& callback) override;
-  void AddRequestHandler(const std::string& path_prefix,
-                         const OnRequestCallback& callback) override;
+  void AddHttpRequestHandler(const std::string& path_prefix,
+                             const RequestHandlerCallback& callback) override;
+  void AddHttpsRequestHandler(const std::string& path_prefix,
+                              const RequestHandlerCallback& callback) override;
   uint16_t GetHttpPort() const override;
   uint16_t GetHttpsPort() const override;
-  const std::vector<uint8_t>& GetHttpsCertificateFingerprint() const override;
+  std::vector<uint8_t> GetHttpsCertificateFingerprint() const override;
 
  private:
   void GenerateX509();
@@ -46,7 +46,7 @@
                     const std::string& mime_type);
   void NotFound(evhttp_request* req);
 
-  std::map<std::string, OnRequestCallback> handlers_;
+  std::map<std::string, RequestHandlerCallback> handlers_;
 
   std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)> ec_key_{nullptr,
                                                           &EC_KEY_free};
diff --git a/libweave/examples/ubuntu/event_network.cc b/libweave/examples/ubuntu/event_network.cc
new file mode 100644
index 0000000..b411f80
--- /dev/null
+++ b/libweave/examples/ubuntu/event_network.cc
@@ -0,0 +1,115 @@
+// 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 "examples/ubuntu/event_network.h"
+
+#include <weave/enum_to_string.h>
+
+#include <base/bind.h>
+#include <event2/dns.h>
+#include <event2/bufferevent.h>
+
+#include "examples/ubuntu/event_task_runner.h"
+#include "examples/ubuntu/ssl_stream.h"
+
+namespace weave {
+namespace examples {
+
+namespace {
+const char kNetworkProbeHostname[] = "talk.google.com";
+const int kNetworkProbePort = 5223;
+}  // namespace
+
+void EventNetworkImpl::Deleter::operator()(evdns_base* dns_base) {
+  evdns_base_free(dns_base, 0);
+}
+
+void EventNetworkImpl::Deleter::operator()(bufferevent* bev) {
+  bufferevent_free(bev);
+}
+
+EventNetworkImpl::EventNetworkImpl(EventTaskRunner* task_runner)
+    : task_runner_(task_runner) {
+  UpdateNetworkState();
+}
+
+void EventNetworkImpl::AddConnectionChangedCallback(
+    const ConnectionChangedCallback& callback) {
+  callbacks_.push_back(callback);
+}
+
+void EventNetworkImpl::UpdateNetworkState() {
+  std::unique_ptr<bufferevent, Deleter> bev{
+      bufferevent_socket_new(task_runner_->GetEventBase(), -1,
+                             BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS)};
+  bufferevent_setcb(
+      bev.get(), nullptr, nullptr,
+      [](struct bufferevent* buf, short events, void* ctx) {
+        EventNetworkImpl* network = static_cast<EventNetworkImpl*>(ctx);
+        std::unique_ptr<bufferevent, Deleter> bev{buf};
+        if (events & BEV_EVENT_CONNECTED) {
+          network->UpdateNetworkStateCallback(State::kOnline);
+          return;
+        }
+        if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF | BEV_EVENT_TIMEOUT)) {
+          int err = bufferevent_socket_get_dns_error(bev.get());
+          if (err) {
+            LOG(ERROR) << "network connect dns error: "
+                       << evutil_gai_strerror(err);
+          }
+          network->UpdateNetworkStateCallback(State::kOffline);
+          return;
+        }
+      },
+      this);
+  int err = bufferevent_socket_connect_hostname(bev.get(), dns_base_.get(),
+                                                AF_INET, kNetworkProbeHostname,
+                                                kNetworkProbePort);
+  if (err) {
+    LOG(ERROR) << " network connect socket error: " << evutil_gai_strerror(err);
+    UpdateNetworkStateCallback(State::kOffline);
+    return;
+  }
+  // release the bufferevent, so that the eventcallback can free it.
+  bev.release();
+}
+
+void EventNetworkImpl::UpdateNetworkStateCallback(
+    provider::Network::State state) {
+  network_state_ = state;
+  LOG(INFO) << "network state updated: " << weave::EnumToString(state);
+  for (const auto& cb : callbacks_)
+    cb.Run();
+  // TODO(proppy): use netlink interface event instead of polling
+  task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(&EventNetworkImpl::UpdateNetworkState,
+                            weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromSeconds(10));
+}
+
+weave::provider::Network::State EventNetworkImpl::GetConnectionState() const {
+  return network_state_;
+}
+
+void EventNetworkImpl::OpenSslSocket(const std::string& host,
+                                     uint16_t port,
+                                     const OpenSslSocketCallback& callback) {
+  // Connect to SSL port instead of upgrading to TLS.
+  std::unique_ptr<SSLStream> tls_stream{new SSLStream{task_runner_}};
+
+  if (tls_stream->Init(host, port)) {
+    task_runner_->PostDelayedTask(
+        FROM_HERE, base::Bind(callback, base::Passed(&tls_stream), nullptr),
+        {});
+  } else {
+    ErrorPtr error;
+    Error::AddTo(&error, FROM_HERE, "tls", "tls_init_failed",
+                 "Failed to initialize TLS stream.");
+    task_runner_->PostDelayedTask(
+        FROM_HERE, base::Bind(callback, nullptr, base::Passed(&error)), {});
+  }
+}
+
+}  // namespace examples
+}  // namespace weave
diff --git a/libweave/examples/ubuntu/event_network.h b/libweave/examples/ubuntu/event_network.h
new file mode 100644
index 0000000..79c458c
--- /dev/null
+++ b/libweave/examples/ubuntu/event_network.h
@@ -0,0 +1,49 @@
+// Copyright 2015 The Weave 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 LIBWEAVE_EXAMPLES_UBUNTU_EVENT_NETWORK_H_
+#define LIBWEAVE_EXAMPLES_UBUNTU_EVENT_NETWORK_H_
+
+#include <weave/provider/network.h>
+
+#include <base/memory/weak_ptr.h>
+
+struct evdns_base;
+struct bufferevent;
+
+namespace weave {
+namespace examples {
+
+class EventTaskRunner;
+
+class EventNetworkImpl : public weave::provider::Network {
+  class Deleter {
+   public:
+    void operator()(evdns_base* dns_base);
+    void operator()(bufferevent* bev);
+  };
+
+ public:
+  explicit EventNetworkImpl(EventTaskRunner* task_runner_);
+  void AddConnectionChangedCallback(
+      const ConnectionChangedCallback& callback) override;
+  State GetConnectionState() const override;
+  void OpenSslSocket(const std::string& host,
+                     uint16_t port,
+                     const OpenSslSocketCallback& callback) override;
+
+ private:
+  void UpdateNetworkState();
+  void UpdateNetworkStateCallback(provider::Network::State state);
+  EventTaskRunner* task_runner_{nullptr};
+  std::unique_ptr<evdns_base, Deleter> dns_base_;
+  std::vector<ConnectionChangedCallback> callbacks_;
+  provider::Network::State network_state_{provider::Network::State::kOffline};
+  base::WeakPtrFactory<EventNetworkImpl> weak_ptr_factory_{this};
+};
+
+}  // namespace examples
+}  // namespace weave
+
+#endif  // LIBWEAVE_EXAMPLES_UBUNTU_EVENT_NETWORK_H_
diff --git a/libweave/examples/ubuntu/event_task_runner.cc b/libweave/examples/ubuntu/event_task_runner.cc
index 7365fb3..b781ff4 100644
--- a/libweave/examples/ubuntu/event_task_runner.cc
+++ b/libweave/examples/ubuntu/event_task_runner.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/examples/ubuntu/event_task_runner.h b/libweave/examples/ubuntu/event_task_runner.h
index 3f04995..77d7e72 100644
--- a/libweave/examples/ubuntu/event_task_runner.h
+++ b/libweave/examples/ubuntu/event_task_runner.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/examples/ubuntu/file_config_store.cc b/libweave/examples/ubuntu/file_config_store.cc
index c59de56..d3f10ef 100644
--- a/libweave/examples/ubuntu/file_config_store.cc
+++ b/libweave/examples/ubuntu/file_config_store.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -60,48 +60,5 @@
   str << settings;
 }
 
-std::vector<std::string> FileConfigStore::LoadCommandDefs() {
-  return {R"({
-    "base": {
-      "updateBaseConfiguration": {},
-      "identify": {},
-      "updateDeviceInfo": {}
-    },
-    "_greeter": {
-      "_greet": {
-        "minimalRole": "user",
-        "parameters": { "_name": "string"},
-        "results": { "_greeting": "string" }
-      }
-    },
-    "_ledflasher": {
-       "_set":{
-         "parameters": {
-           "_led": {"minimum": 1, "maximum": 3},
-           "_on": "boolean"
-         }
-       },
-       "_toggle":{
-         "parameters": {
-           "_led": {"minimum": 1, "maximum": 3}
-         }
-      }
-    }
-  })"};
-}
-std::vector<std::string> FileConfigStore::LoadStateDefs() {
-  return {R"({
-    "_greeter": {"_greetings_counter":"integer"},
-    "_ledflasher": {"_leds": {"items": "boolean"}}
-  })"};
-}
-
-std::vector<std::string> FileConfigStore::LoadStateDefaults() {
-  return {R"({
-    "_greeter": {"_greetings_counter": 0},
-    "_ledflasher":{"_leds": [false, false, false]}
-  })"};
-}
-
 }  // namespace examples
 }  // namespace weave
diff --git a/libweave/examples/ubuntu/file_config_store.h b/libweave/examples/ubuntu/file_config_store.h
index 926576f..1f3d181 100644
--- a/libweave/examples/ubuntu/file_config_store.h
+++ b/libweave/examples/ubuntu/file_config_store.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,9 +21,6 @@
   bool LoadDefaults(Settings* settings) override;
   std::string LoadSettings() override;
   void SaveSettings(const std::string& settings) override;
-  std::vector<std::string> LoadCommandDefs() override;
-  std::vector<std::string> LoadStateDefs() override;
-  std::vector<std::string> LoadStateDefaults() override;
 
  private:
   bool disable_security_{false};
diff --git a/libweave/examples/ubuntu/main.cc b/libweave/examples/ubuntu/main.cc
index 5c84d9c..cd6dc06 100644
--- a/libweave/examples/ubuntu/main.cc
+++ b/libweave/examples/ubuntu/main.cc
@@ -1,8 +1,9 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave 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 <bitset>
+
 #include <base/bind.h>
 #include <base/values.h>
 #include <weave/device.h>
@@ -25,6 +26,7 @@
   LOG(ERROR) << "\nUsage: " << name << " <option(s)>"
              << "\nOptions:\n"
              << "\t-h,--help                    Show this help message\n"
+             << "\t--v=LEVEL                    Logging level\n"
              << "\t-b,--bootstrapping           Force WiFi bootstrapping\n"
              << "\t-d,--disable_security        Disable privet security\n"
              << "\t--registration_ticket=TICKET Register device with the "
@@ -33,67 +35,170 @@
 
 class CommandHandler {
  public:
-  explicit CommandHandler(weave::Device* device) : device_{device} {
-    device->AddCommandAddedCallback(base::Bind(&CommandHandler::OnNewCommand,
-                                               weak_ptr_factory_.GetWeakPtr()));
+  CommandHandler(weave::Device* device,
+                 weave::provider::TaskRunner* task_runner)
+      : device_{device}, task_runner_(task_runner) {
+    device->AddStateDefinitionsFromJson(R"({
+      "_greeter": {"_greetings_counter":"integer"},
+      "_ledflasher": {"_leds": {"items": "boolean"}}
+    })");
+
+    device->SetStatePropertiesFromJson(R"({
+      "_greeter": {"_greetings_counter": 0},
+      "_ledflasher":{"_leds": [false, false, false]}
+    })",
+                                       nullptr);
+
+    device->AddCommandDefinitionsFromJson(R"({
+      "_greeter": {
+        "_greet": {
+          "minimalRole": "user",
+          "parameters": {
+            "_name": "string",
+            "_count": {"minimum": 1, "maximum": 100}
+          },
+          "progress": { "_todo": "integer"},
+          "results": { "_greeting": "string" }
+        }
+      },
+      "_ledflasher": {
+         "_set":{
+           "parameters": {
+             "_led": {"minimum": 1, "maximum": 3},
+             "_on": "boolean"
+           }
+         },
+         "_toggle":{
+           "parameters": {
+             "_led": {"minimum": 1, "maximum": 3}
+           }
+        }
+      }
+    })");
+    device->AddCommandHandler(
+        "_ledflasher._toggle",
+        base::Bind(&CommandHandler::OnFlasherToggleCommand,
+                   weak_ptr_factory_.GetWeakPtr()));
+    device->AddCommandHandler("_greeter._greet",
+                              base::Bind(&CommandHandler::OnGreetCommand,
+                                         weak_ptr_factory_.GetWeakPtr()));
+    device->AddCommandHandler("_ledflasher._set",
+                              base::Bind(&CommandHandler::OnFlasherSetCommand,
+                                         weak_ptr_factory_.GetWeakPtr()));
+    device->AddCommandHandler("",
+                              base::Bind(&CommandHandler::OnUnhandledCommand,
+                                         weak_ptr_factory_.GetWeakPtr()));
   }
 
  private:
-  void OnNewCommand(weave::Command* cmd) {
-    LOG(INFO) << "received command: " << cmd->GetName();
-    if (cmd->GetName() == "_greeter._greet") {
-      std::string name;
-      if (!cmd->GetParameters()->GetString("_name", &name))
-        name = "anonymous";
+  void DoGreet(const std::weak_ptr<weave::Command>& command, int todo) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
 
-      LOG(INFO) << cmd->GetName() << " command in progress";
-      cmd->SetProgress(base::DictionaryValue{}, nullptr);
+    std::string name;
+    if (!cmd->GetParameters()->GetString("_name", &name)) {
+      weave::ErrorPtr error;
+      weave::Error::AddTo(&error, FROM_HERE, "example",
+                          "invalid_parameter_value", "Name is missing");
+      cmd->Abort(error.get(), nullptr);
+      return;
+    }
 
-      base::DictionaryValue result;
-      result.SetString("_greeting", "Hello " + name);
-      cmd->SetResults(result, nullptr);
-      LOG(INFO) << cmd->GetName() << " command finished: " << result;
+    if (todo-- > 0) {
+      LOG(INFO) << "Hello " << name;
+
+      base::DictionaryValue progress;
+      progress.SetInteger("_todo", todo);
+      cmd->SetProgress(progress, nullptr);
 
       base::DictionaryValue state;
-      state.SetIntegerWithoutPathExpansion("_greeter._greetings_counter",
-                                           ++counter_);
+      state.SetInteger("_greeter._greetings_counter", ++counter_);
       device_->SetStateProperties(state, nullptr);
+    }
 
-      LOG(INFO) << "New state: " << *device_->GetState();
+    if (todo > 0) {
+      task_runner_->PostDelayedTask(
+          FROM_HERE, base::Bind(&CommandHandler::DoGreet,
+                                weak_ptr_factory_.GetWeakPtr(), command, todo),
+          base::TimeDelta::FromSeconds(1));
+      return;
+    }
 
-      cmd->Done();
-    } else if (cmd->GetName() == "_ledflasher._set") {
-      int32_t led_index;
-      bool cmd_value;
-      if (cmd->GetParameters()->GetInteger("_led", &led_index) &&
-          cmd->GetParameters()->GetBoolean("_on", &cmd_value)) {
-        // Display this command in terminal
-        LOG(INFO) << cmd->GetName() << " _led: " << led_index
-                  << ", _on: " << (cmd_value ? "true" : "false");
+    base::DictionaryValue result;
+    result.SetString("_greeting", "Hello " + name);
+    cmd->Complete(result, nullptr);
+    LOG(INFO) << cmd->GetName() << " command finished: " << result;
+    LOG(INFO) << "New state: " << *device_->GetState();
+  }
 
-        led_index--;
-        int new_state = cmd_value ? 1 : 0;
-        int cur_state = led_status_[led_index];
-        led_status_[led_index] = new_state;
+  void OnGreetCommand(const std::weak_ptr<weave::Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
 
-        if (cmd_value != cur_state) {
-          UpdateLedState();
-        }
-      }
-      cmd->Done();
-    } else if (cmd->GetName() == "_ledflasher._toggle") {
-      int32_t led_index;
-      if (cmd->GetParameters()->GetInteger("_led", &led_index)) {
-        LOG(INFO) << cmd->GetName() << " _led: " << led_index;
-        led_index--;
-        led_status_[led_index] = ~led_status_[led_index];
+    int todo = 1;
+    cmd->GetParameters()->GetInteger("_count", &todo);
+    DoGreet(command, todo);
+  }
 
+  void OnFlasherSetCommand(const std::weak_ptr<weave::Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
+    int32_t led_index = 0;
+    bool cmd_value = false;
+    if (cmd->GetParameters()->GetInteger("_led", &led_index) &&
+        cmd->GetParameters()->GetBoolean("_on", &cmd_value)) {
+      // Display this command in terminal
+      LOG(INFO) << cmd->GetName() << " _led: " << led_index
+                << ", _on: " << (cmd_value ? "true" : "false");
+
+      led_index--;
+      int new_state = cmd_value ? 1 : 0;
+      int cur_state = led_status_[led_index];
+      led_status_[led_index] = new_state;
+
+      if (cmd_value != cur_state) {
         UpdateLedState();
       }
-      cmd->Done();
-    } else {
-      LOG(INFO) << cmd->GetName() << " unimplemented command: ignored";
+      cmd->Complete({}, nullptr);
+      return;
     }
+    weave::ErrorPtr error;
+    weave::Error::AddTo(&error, FROM_HERE, "example", "invalid_parameter_value",
+                        "Invalid parameters");
+    cmd->Abort(error.get(), nullptr);
+  }
+
+  void OnFlasherToggleCommand(const std::weak_ptr<weave::Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
+    int32_t led_index = 0;
+    if (cmd->GetParameters()->GetInteger("_led", &led_index)) {
+      LOG(INFO) << cmd->GetName() << " _led: " << led_index;
+      led_index--;
+      led_status_[led_index] = ~led_status_[led_index];
+
+      UpdateLedState();
+      cmd->Complete({}, nullptr);
+      return;
+    }
+    weave::ErrorPtr error;
+    weave::Error::AddTo(&error, FROM_HERE, "example", "invalid_parameter_value",
+                        "Invalid parameters");
+    cmd->Abort(error.get(), nullptr);
+  }
+
+  void OnUnhandledCommand(const std::weak_ptr<weave::Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << cmd->GetName() << " unimplemented command: ignored";
   }
 
   void UpdateLedState(void) {
@@ -105,6 +210,8 @@
   }
 
   weave::Device* device_{nullptr};
+  weave::provider::TaskRunner* task_runner_{nullptr};
+
   int counter_{0};
 
   // Simulate LED status on this device so client app could explore
@@ -114,6 +221,13 @@
   base::WeakPtrFactory<CommandHandler> weak_ptr_factory_{this};
 };
 
+void OnRegisterDeviceDone(weave::Device* device, weave::ErrorPtr error) {
+  if (error)
+    LOG(ERROR) << "Fail to register device: " << error->GetMessage();
+  else
+    LOG(INFO) << "Device registered: " << device->GetSettings().cloud_id;
+}
+
 }  // namespace
 
 int main(int argc, char** argv) {
@@ -136,6 +250,13 @@
         return 1;
       }
       registration_ticket = arg.substr(pos + 1);
+    } else if (arg.find("--v") != std::string::npos) {
+      auto pos = arg.find("=");
+      if (pos == std::string::npos) {
+        ShowUsage(argv[0]);
+        return 1;
+      }
+      logging::SetMinLogLevel(-std::stoi(arg.substr(pos + 1)));
     } else {
       ShowUsage(argv[0]);
       return 1;
@@ -157,16 +278,11 @@
       &bluetooth);
 
   if (!registration_ticket.empty()) {
-    weave::ErrorPtr error;
-    auto device_id = device->Register(registration_ticket, &error);
-    if (error != nullptr) {
-      LOG(ERROR) << "Fail to register device: " << error->GetMessage();
-    } else {
-      LOG(INFO) << "Device registered: " << device_id;
-    }
+    device->Register(registration_ticket,
+                     base::Bind(&OnRegisterDeviceDone, device.get()));
   }
 
-  CommandHandler handler(device.get());
+  CommandHandler handler(device.get(), &task_runner);
   task_runner.Run();
 
   LOG(INFO) << "exit";
diff --git a/libweave/examples/ubuntu/network_manager.cc b/libweave/examples/ubuntu/network_manager.cc
index 0fdae2d..efc2d5c 100644
--- a/libweave/examples/ubuntu/network_manager.cc
+++ b/libweave/examples/ubuntu/network_manager.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -57,13 +57,11 @@
   callbacks_.push_back(callback);
 }
 
-void NetworkImpl::TryToConnect(
-    const std::string& ssid,
-    const std::string& passphrase,
-    int pid,
-    base::Time until,
-    const SuccessCallback& success_callback,
-    const ErrorCallback& error_callback) {
+void NetworkImpl::TryToConnect(const std::string& ssid,
+                               const std::string& passphrase,
+                               int pid,
+                               base::Time until,
+                               const DoneCallback& callback) {
   if (pid) {
     int status = 0;
     if (pid == waitpid(pid, &status, WNOWAIT)) {
@@ -80,7 +78,8 @@
       close(sockf_d);
 
       if (ssid == essid)
-        return task_runner_->PostDelayedTask(FROM_HERE, success_callback, {});
+        return task_runner_->PostDelayedTask(FROM_HERE,
+                                             base::Bind(callback, nullptr), {});
       pid = 0;  // Try again.
     }
   }
@@ -95,36 +94,32 @@
     Error::AddTo(&error, FROM_HERE, "wifi", "timeout",
                  "Timeout connecting to WiFI network.");
     task_runner_->PostDelayedTask(
-        FROM_HERE, base::Bind(error_callback, base::Owned(error.release())),
-        {});
+        FROM_HERE, base::Bind(callback, base::Passed(&error)), {});
     return;
   }
 
   task_runner_->PostDelayedTask(
-      FROM_HERE, base::Bind(&NetworkImpl::TryToConnect,
-                            weak_ptr_factory_.GetWeakPtr(), ssid, passphrase,
-                            pid, until, success_callback, error_callback),
+      FROM_HERE,
+      base::Bind(&NetworkImpl::TryToConnect, weak_ptr_factory_.GetWeakPtr(),
+                 ssid, passphrase, pid, until, callback),
       base::TimeDelta::FromSeconds(1));
 }
 
 void NetworkImpl::Connect(const std::string& ssid,
                           const std::string& passphrase,
-                          const SuccessCallback& success_callback,
-                          const ErrorCallback& error_callback) {
+                          const DoneCallback& callback) {
   force_bootstrapping_ = false;
   CHECK(!hostapd_started_);
   if (hostapd_started_) {
     ErrorPtr error;
     Error::AddTo(&error, FROM_HERE, "wifi", "busy", "Running Access Point.");
     task_runner_->PostDelayedTask(
-        FROM_HERE, base::Bind(error_callback, base::Owned(error.release())),
-        {});
+        FROM_HERE, base::Bind(callback, base::Passed(&error)), {});
     return;
   }
 
   TryToConnect(ssid, passphrase, 0,
-               base::Time::Now() + base::TimeDelta::FromMinutes(1),
-               success_callback, error_callback);
+               base::Time::Now() + base::TimeDelta::FromMinutes(1), callback);
 }
 
 void NetworkImpl::UpdateNetworkState() {
@@ -132,9 +127,9 @@
   if (force_bootstrapping_)
     return;
   if (std::system("ping talk.google.com -c 1") == 0)
-    network_state_ = State::kConnected;
+    network_state_ = State::kOnline;
   else if (std::system("nmcli dev"))
-    network_state_ = State::kFailure;
+    network_state_ = State::kError;
   else if (std::system("nmcli dev | grep connecting") == 0)
     network_state_ = State::kConnecting;
 
@@ -203,21 +198,22 @@
   return std::system("nmcli dev | grep ^wlan0") == 0;
 }
 
-void NetworkImpl::OpenSslSocket(
-    const std::string& host,
-    uint16_t port,
-    const OpenSslSocketSuccessCallback& success_callback,
-    const ErrorCallback& error_callback) {
+void NetworkImpl::OpenSslSocket(const std::string& host,
+                                uint16_t port,
+                                const OpenSslSocketCallback& callback) {
   // Connect to SSL port instead of upgrading to TLS.
   std::unique_ptr<SSLStream> tls_stream{new SSLStream{task_runner_}};
 
   if (tls_stream->Init(host, port)) {
     task_runner_->PostDelayedTask(
-        FROM_HERE, base::Bind(success_callback, base::Passed(&tls_stream)), {});
+        FROM_HERE, base::Bind(callback, base::Passed(&tls_stream), nullptr),
+        {});
   } else {
     ErrorPtr error;
     Error::AddTo(&error, FROM_HERE, "tls", "tls_init_failed",
                  "Failed to initialize TLS stream.");
+    task_runner_->PostDelayedTask(
+        FROM_HERE, base::Bind(callback, nullptr, base::Passed(&error)), {});
   }
 }
 
diff --git a/libweave/examples/ubuntu/network_manager.h b/libweave/examples/ubuntu/network_manager.h
index b8e589d..3769ab1 100644
--- a/libweave/examples/ubuntu/network_manager.h
+++ b/libweave/examples/ubuntu/network_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -35,14 +35,12 @@
   State GetConnectionState() const override;
   void OpenSslSocket(const std::string& host,
                      uint16_t port,
-                     const OpenSslSocketSuccessCallback& success_callback,
-                     const ErrorCallback& error_callback) override;
+                     const OpenSslSocketCallback& callback) override;
 
   // Wifi implementation.
   void Connect(const std::string& ssid,
                const std::string& passphrase,
-               const SuccessCallback& success_callback,
-               const ErrorCallback& error_callback) override;
+               const DoneCallback& callback) override;
   void StartAccessPoint(const std::string& ssid) override;
   void StopAccessPoint() override;
 
@@ -53,8 +51,7 @@
                     const std::string& passphrase,
                     int pid,
                     base::Time until,
-                    const SuccessCallback& success_callback,
-                    const ErrorCallback& error_callback);
+                    const DoneCallback& callback);
   void UpdateNetworkState();
 
   bool force_bootstrapping_{false};
diff --git a/libweave/examples/ubuntu/prerequisites.sh b/libweave/examples/ubuntu/prerequisites.sh
index f64f06b..bc70fd3 100755
--- a/libweave/examples/ubuntu/prerequisites.sh
+++ b/libweave/examples/ubuntu/prerequisites.sh
@@ -1,5 +1,5 @@
 #!/bin/bash
-# Copyright 2015 The Chromium OS Authors. All rights reserved.
+# Copyright 2015 The Weave Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
@@ -15,6 +15,8 @@
   libavahi-client-dev \
   libcurl4-openssl-dev \
   libexpat1-dev \
+  libnl-3-dev \
+  libnl-route-3-dev \
   libtool \
   ninja-build \
   || exit 1
diff --git a/libweave/examples/ubuntu/ssl_stream.cc b/libweave/examples/ubuntu/ssl_stream.cc
index 58626cc..fc2d36d 100644
--- a/libweave/examples/ubuntu/ssl_stream.cc
+++ b/libweave/examples/ubuntu/ssl_stream.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,14 +23,13 @@
 
 void SSLStream::Read(void* buffer,
                      size_t size_to_read,
-                     const ReadSuccessCallback& success_callback,
-                     const ErrorCallback& error_callback) {
+                     const ReadCallback& callback) {
   int res = SSL_read(ssl_.get(), buffer, size_to_read);
   if (res > 0) {
     task_runner_->PostDelayedTask(
         FROM_HERE,
         base::Bind(&SSLStream::RunDelayedTask, weak_ptr_factory_.GetWeakPtr(),
-                   base::Bind(success_callback, res)),
+                   base::Bind(callback, res, nullptr)),
         {});
     return;
   }
@@ -39,9 +38,8 @@
 
   if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
     task_runner_->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(&SSLStream::Read, weak_ptr_factory_.GetWeakPtr(), buffer,
-                   size_to_read, success_callback, error_callback),
+        FROM_HERE, base::Bind(&SSLStream::Read, weak_ptr_factory_.GetWeakPtr(),
+                              buffer, size_to_read, callback),
         base::TimeDelta::FromSeconds(1));
     return;
   }
@@ -51,17 +49,15 @@
                "SSL error");
   task_runner_->PostDelayedTask(
       FROM_HERE,
-      base::Bind(
-          &SSLStream::RunDelayedTask, weak_ptr_factory_.GetWeakPtr(),
-          base::Bind(error_callback, base::Owned(weave_error.release()))),
+      base::Bind(&SSLStream::RunDelayedTask, weak_ptr_factory_.GetWeakPtr(),
+                 base::Bind(callback, 0, base::Passed(&weave_error))),
       {});
   return;
 }
 
 void SSLStream::Write(const void* buffer,
                       size_t size_to_write,
-                      const SuccessCallback& success_callback,
-                      const ErrorCallback& error_callback) {
+                      const WriteCallback& callback) {
   int res = SSL_write(ssl_.get(), buffer, size_to_write);
   if (res > 0) {
     buffer = static_cast<const char*>(buffer) + res;
@@ -70,15 +66,14 @@
       task_runner_->PostDelayedTask(
           FROM_HERE,
           base::Bind(&SSLStream::RunDelayedTask, weak_ptr_factory_.GetWeakPtr(),
-                     success_callback),
+                     base::Bind(callback, nullptr)),
           {});
       return;
     }
 
     task_runner_->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(&SSLStream::Write, weak_ptr_factory_.GetWeakPtr(), buffer,
-                   size_to_write, success_callback, error_callback),
+        FROM_HERE, base::Bind(&SSLStream::Write, weak_ptr_factory_.GetWeakPtr(),
+                              buffer, size_to_write, callback),
         base::TimeDelta::FromSeconds(1));
 
     return;
@@ -88,9 +83,8 @@
 
   if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
     task_runner_->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(&SSLStream::Write, weak_ptr_factory_.GetWeakPtr(), buffer,
-                   size_to_write, success_callback, error_callback),
+        FROM_HERE, base::Bind(&SSLStream::Write, weak_ptr_factory_.GetWeakPtr(),
+                              buffer, size_to_write, callback),
         base::TimeDelta::FromSeconds(1));
     return;
   }
@@ -100,9 +94,8 @@
                "SSL error");
   task_runner_->PostDelayedTask(
       FROM_HERE,
-      base::Bind(
-          &SSLStream::RunDelayedTask, weak_ptr_factory_.GetWeakPtr(),
-          base::Bind(error_callback, base::Owned(weave_error.release()))),
+      base::Bind(&SSLStream::RunDelayedTask, weak_ptr_factory_.GetWeakPtr(),
+                 base::Bind(callback, base::Passed(&weave_error))),
       {});
   return;
 }
diff --git a/libweave/examples/ubuntu/ssl_stream.h b/libweave/examples/ubuntu/ssl_stream.h
index ac0d76a..ae0e539 100644
--- a/libweave/examples/ubuntu/ssl_stream.h
+++ b/libweave/examples/ubuntu/ssl_stream.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,13 +26,11 @@
 
   void Read(void* buffer,
             size_t size_to_read,
-            const ReadSuccessCallback& success_callback,
-            const ErrorCallback& error_callback) override;
+            const ReadCallback& callback) override;
 
   void Write(const void* buffer,
              size_t size_to_write,
-             const SuccessCallback& success_callback,
-             const ErrorCallback& error_callback) override;
+             const WriteCallback& callback) override;
 
   void CancelPendingOperations() override;
 
diff --git a/libweave/examples/ubuntu/weave.gyp b/libweave/examples/ubuntu/weave.gyp
index a4df01d..a561c9d 100644
--- a/libweave/examples/ubuntu/weave.gyp
+++ b/libweave/examples/ubuntu/weave.gyp
@@ -1,17 +1,42 @@
+# Copyright 2015 The Weave Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'targets': [
     {
       'target_name': 'weave',
       'type': 'executable',
-      'cflags': ['-pthread'],
+      'variables': {
+        'deps': [
+          'avahi-client',
+          'expat',
+          'libcurl',
+          'libcrypto',
+          'openssl',
+        ]
+      },
+      'cflags': [
+        '>!@(pkg-config >(deps) --cflags)',
+        '-pthread',
+      ],
+      'link_settings': {
+        'ldflags+': [
+          '>!@(pkg-config >(deps) --libs-only-L --libs-only-other)',
+        ],
+        'libraries+': [
+          '>!(pkg-config >(deps) --libs-only-l)',
+        ],
+      },
       'sources': [
         'avahi_client.cc',
         'bluez_client.cc',
         'curl_http_client.cc',
+        'event_http_client.cc',
         'event_http_server.cc',
         'event_task_runner.cc',
         'file_config_store.cc',
         'main.cc',
+        'event_network.cc',
         'network_manager.cc',
         'ssl_stream.cc',
       ],
@@ -20,14 +45,8 @@
       ],
       'libraries': [
         '-levent',
-        '-lcrypto',
-        '-lexpat',
-        '-lcurl',
-        '-lpthread',
-        '-lssl',
-        '-lavahi-common',
-        '-lavahi-client',
         '-levent_openssl',
+        '-lpthread',
       ]
     }
   ]
diff --git a/libweave/include/weave/command.h b/libweave/include/weave/command.h
index 911da1e..59a9305 100644
--- a/libweave/include/weave/command.h
+++ b/libweave/include/weave/command.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,39 +12,20 @@
 
 namespace weave {
 
-enum class CommandStatus {
-  kQueued,
-  kInProgress,
-  kPaused,
-  kError,
-  kDone,
-  kCancelled,
-  kAborted,
-  kExpired,
-};
-
-enum class CommandOrigin { kLocal, kCloud };
-
 class Command {
  public:
-  // This interface lets the command to notify clients about changes.
-  class Observer {
-   public:
-    virtual void OnResultsChanged() = 0;
-    virtual void OnStatusChanged() = 0;
-    virtual void OnProgressChanged() = 0;
-    virtual void OnCommandDestroyed() = 0;
-
-   protected:
-    virtual ~Observer() = default;
+  enum class State {
+    kQueued,
+    kInProgress,
+    kPaused,
+    kError,
+    kDone,
+    kCancelled,
+    kAborted,
+    kExpired,
   };
 
-  // Adds an observer for this command. The observer object is not owned by this
-  // class.
-  virtual void AddObserver(Observer* observer) = 0;
-
-  // Removes an observer for this command.
-  virtual void RemoveObserver(Observer* observer) = 0;
+  enum class Origin { kLocal, kCloud };
 
   // Returns the full command ID.
   virtual const std::string& GetID() const = 0;
@@ -52,11 +33,11 @@
   // Returns the full name of the command.
   virtual const std::string& GetName() const = 0;
 
-  // Returns the command status.
-  virtual CommandStatus GetStatus() const = 0;
+  // Returns the command state.
+  virtual Command::State GetState() const = 0;
 
   // Returns the origin of the command.
-  virtual CommandOrigin GetOrigin() const = 0;
+  virtual Command::Origin GetOrigin() const = 0;
 
   // Returns the command parameters.
   virtual std::unique_ptr<base::DictionaryValue> GetParameters() const = 0;
@@ -67,27 +48,35 @@
   // Returns the command results.
   virtual std::unique_ptr<base::DictionaryValue> GetResults() const = 0;
 
+  // Returns the command error.
+  virtual const Error* GetError() const = 0;
+
   // Updates the command progress. The |progress| should match the schema.
   // Returns false if |progress| value is incorrect.
   virtual bool SetProgress(const base::DictionaryValue& progress,
                            ErrorPtr* error) = 0;
 
+  // Sets command into terminal "done" state.
   // Updates the command results. The |results| should match the schema.
   // Returns false if |results| value is incorrect.
-  virtual bool SetResults(const base::DictionaryValue& results,
-                          ErrorPtr* error) = 0;
+  virtual bool Complete(const base::DictionaryValue& results,
+                        ErrorPtr* error) = 0;
+
+  // Sets command into paused state.
+  // This is not terminal state. Command can be resumed with |SetProgress| call.
+  virtual bool Pause(ErrorPtr* error) = 0;
+
+  // Sets command into error state and assign error.
+  // This is not terminal state. Command can be resumed with |SetProgress| call.
+  virtual bool SetError(const Error* command_error, ErrorPtr* error) = 0;
 
   // Aborts command execution.
-  virtual void Abort() = 0;
+  // Sets command into terminal "aborted" state.
+  virtual bool Abort(const Error* command_error, ErrorPtr* error) = 0;
 
   // Cancels command execution.
-  virtual void Cancel() = 0;
-
-  // Marks the command as completed successfully.
-  virtual void Done() = 0;
-
-  // Returns JSON representation of the command.
-  virtual std::unique_ptr<base::DictionaryValue> ToJson() const = 0;
+  // Sets command into terminal "canceled" state.
+  virtual bool Cancel(ErrorPtr* error) = 0;
 
  protected:
   virtual ~Command() = default;
diff --git a/libweave/include/weave/device.h b/libweave/include/weave/device.h
index c81dd04..e761e0c 100644
--- a/libweave/include/weave/device.h
+++ b/libweave/include/weave/device.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -45,14 +45,23 @@
   virtual void AddSettingsChangedCallback(
       const SettingsChangedCallback& callback) = 0;
 
-  // Callback type for AddCommandAddedCallback and AddCommandRemovedCallback.
-  using CommandCallback = base::Callback<void(Command*)>;
+  // Adds provided commands definitions. Can be called multiple times with
+  // condition that definitions do not conflict.
+  // Invalid value is fatal.
+  virtual void AddCommandDefinitionsFromJson(const std::string& json) = 0;
+  virtual void AddCommandDefinitions(const base::DictionaryValue& dict) = 0;
 
-  // Adds notification callback for a new command being added to the queue.
-  virtual void AddCommandAddedCallback(const CommandCallback& callback) = 0;
+  // Callback type for AddCommandHandler.
+  using CommandHandlerCallback =
+      base::Callback<void(const std::weak_ptr<Command>& command)>;
 
-  // Adds notification callback for a command being removed from the queue.
-  virtual void AddCommandRemovedCallback(const CommandCallback& callback) = 0;
+  // Sets handler for new commands added to the queue.
+  // |command_name| is the full command name of the command to handle. e.g.
+  // "base.reboot". Each command can have no more than one handler.
+  // Empty |command_name| sets default handler for all unhanded commands.
+  // No new command handlers can be set after default handler was set.
+  virtual void AddCommandHandler(const std::string& command_name,
+                                 const CommandHandlerCallback& callback) = 0;
 
   // Adds a new command to the command queue.
   virtual bool AddCommand(const base::DictionaryValue& command,
@@ -67,6 +76,24 @@
   // Sets callback which is called when stat is changed.
   virtual void AddStateChangedCallback(const base::Closure& callback) = 0;
 
+  // Adds provided state definitions. Can be called multiple times with
+  // condition that definitions do not conflict.
+  // Invalid value is fatal.
+  virtual void AddStateDefinitionsFromJson(const std::string& json) = 0;
+  virtual void AddStateDefinitions(const base::DictionaryValue& dict) = 0;
+
+  // Sets value of multiple properties of the state.
+  // It's recommended to call this to initialize state defined by
+  // AddStateDefinitions.
+  // Example:
+  //   device->SetStatePropertiesFromJson("{'base':{'firmwareVersion':'123'}}")
+  // Method completely replaces properties included |json| or |dict|.
+  // Properties of the state not included |json| or |dict| will stay unchanged.
+  virtual bool SetStatePropertiesFromJson(const std::string& json,
+                                          ErrorPtr* error) = 0;
+  virtual bool SetStateProperties(const base::DictionaryValue& dict,
+                                  ErrorPtr* error) = 0;
+
   // Returns value of the single property.
   // |name| is full property name, including package name. e.g. "base.network".
   virtual std::unique_ptr<base::Value> GetStateProperty(
@@ -78,10 +105,6 @@
                                 const base::Value& value,
                                 ErrorPtr* error) = 0;
 
-  // Updates a multiple property values.
-  virtual bool SetStateProperties(const base::DictionaryValue& property_set,
-                                  ErrorPtr* error) = 0;
-
   // Returns aggregated state properties across all registered packages.
   virtual std::unique_ptr<base::DictionaryValue> GetState() const = 0;
 
@@ -95,10 +118,10 @@
   virtual void AddGcdStateChangedCallback(
       const GcdStateChangedCallback& callback) = 0;
 
-  // Registers the device. Returns a device ID on success.
+  // Registers the device.
   // This is testing method and should not be used by applications.
-  virtual std::string Register(const std::string& ticket_id,
-                               ErrorPtr* error) = 0;
+  virtual void Register(const std::string& ticket_id,
+                        const DoneCallback& callback) = 0;
 
   // Handler should display pin code to the user.
   using PairingBeginCallback =
diff --git a/libweave/include/weave/enum_to_string.h b/libweave/include/weave/enum_to_string.h
index d6c2b79..67d6154 100644
--- a/libweave/include/weave/enum_to_string.h
+++ b/libweave/include/weave/enum_to_string.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -52,7 +52,7 @@
       return m.name;
     }
   }
-  NOTREACHED();
+  NOTREACHED() << static_cast<int>(id);
   return std::string();
 }
 
diff --git a/libweave/include/weave/error.h b/libweave/include/weave/error.h
index 67f923f..6687d98 100644
--- a/libweave/include/weave/error.h
+++ b/libweave/include/weave/error.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -126,8 +126,13 @@
   DISALLOW_COPY_AND_ASSIGN(Error);
 };
 
-using SuccessCallback = base::Closure;
-using ErrorCallback = base::Callback<void(const Error* error)>;
+// Default callback type for async operations.
+// Function having this callback as argument should call the callback exactly
+// one time.
+// Successfully completed operation should run callback with |error| set to
+// null. Failed operation should run callback with |error| containing error
+// details.
+using DoneCallback = base::Callback<void(ErrorPtr error)>;
 
 }  // namespace weave
 
diff --git a/libweave/include/weave/export.h b/libweave/include/weave/export.h
index 72220a7..1bc27c5 100644
--- a/libweave/include/weave/export.h
+++ b/libweave/include/weave/export.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/include/weave/provider/bluetooth.h b/libweave/include/weave/provider/bluetooth.h
index d87dcc0..e8f3b3c 100644
--- a/libweave/include/weave/provider/bluetooth.h
+++ b/libweave/include/weave/provider/bluetooth.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/include/weave/provider/config_store.h b/libweave/include/weave/provider/config_store.h
index aa7c5e5..4107748 100644
--- a/libweave/include/weave/provider/config_store.h
+++ b/libweave/include/weave/provider/config_store.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -35,15 +35,6 @@
   // recommended to protect data, e.g. using encryption.
   virtual void SaveSettings(const std::string& settings) = 0;
 
-  // Returns command definitions as array of JSONs.
-  virtual std::vector<std::string> LoadCommandDefs() = 0;
-
-  // Returns device state definitions as array of JSONs.
-  virtual std::vector<std::string> LoadStateDefs() = 0;
-
-  // Returns device state defaults as array of JSONs.
-  virtual std::vector<std::string> LoadStateDefaults() = 0;
-
  protected:
   virtual ~ConfigStore() = default;
 };
diff --git a/libweave/include/weave/provider/dns_service_discovery.h b/libweave/include/weave/provider/dns_service_discovery.h
index a6f2057..d96e22d 100644
--- a/libweave/include/weave/provider/dns_service_discovery.h
+++ b/libweave/include/weave/provider/dns_service_discovery.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/include/weave/provider/http_client.h b/libweave/include/weave/provider/http_client.h
index 9671f0d..6edf4bf 100644
--- a/libweave/include/weave/provider/http_client.h
+++ b/libweave/include/weave/provider/http_client.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,34 +17,31 @@
 
 class HttpClient {
  public:
+  enum class Method {
+    kGet,
+    kPatch,
+    kPost,
+    kPut,
+  };
+
   class Response {
    public:
     virtual int GetStatusCode() const = 0;
     virtual std::string GetContentType() const = 0;
-    virtual const std::string& GetData() const = 0;
+    virtual std::string GetData() const = 0;
 
-    // TODO(vitalybuka): Hide when SendRequestAndBlock is removed.
     virtual ~Response() = default;
   };
 
   using Headers = std::vector<std::pair<std::string, std::string>>;
-  using SuccessCallback = base::Callback<void(int, const Response&)>;
-  using ErrorCallback = base::Callback<void(int, const Error*)>;
+  using SendRequestCallback =
+      base::Callback<void(std::unique_ptr<Response> response, ErrorPtr error)>;
 
-  // TODO(vitalybuka): Remove blocking version.
-  virtual std::unique_ptr<Response> SendRequestAndBlock(
-      const std::string& method,
-      const std::string& url,
-      const Headers& headers,
-      const std::string& data,
-      ErrorPtr* error) = 0;
-
-  virtual int SendRequest(const std::string& method,
-                          const std::string& url,
-                          const Headers& headers,
-                          const std::string& data,
-                          const SuccessCallback& success_callback,
-                          const ErrorCallback& error_callback) = 0;
+  virtual void SendRequest(Method method,
+                           const std::string& url,
+                           const Headers& headers,
+                           const std::string& data,
+                           const SendRequestCallback& callback) = 0;
 
  protected:
   virtual ~HttpClient() = default;
diff --git a/libweave/include/weave/provider/http_server.h b/libweave/include/weave/provider/http_server.h
index 1d4ab1e..da53e1f 100644
--- a/libweave/include/weave/provider/http_server.h
+++ b/libweave/include/weave/provider/http_server.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,37 +18,32 @@
  public:
   class Request {
    public:
-    virtual const std::string& GetPath() const = 0;
-    virtual std::string GetFirstHeader(const std::string& name) const = 0;
-    virtual const std::vector<uint8_t>& GetData() const = 0;
-    virtual std::unique_ptr<Stream> GetDataStream() const = 0;
-
-   protected:
     virtual ~Request() = default;
+
+    virtual std::string GetPath() const = 0;
+    virtual std::string GetFirstHeader(const std::string& name) const = 0;
+    virtual std::string GetData() = 0;
+
+    virtual void SendReply(int status_code,
+                           const std::string& data,
+                           const std::string& mime_type) = 0;
   };
 
-  using OnStateChangedCallback = base::Callback<void(const HttpServer& server)>;
+  // Callback type for AddRequestHandler.
+  using RequestHandlerCallback =
+      base::Callback<void(std::unique_ptr<Request> request)>;
 
-  using OnReplyCallback = base::Callback<void(int status_code,
-                                              const std::string& data,
-                                              const std::string& mime_type)>;
-
-  using OnRequestCallback =
-      base::Callback<void(const Request& request,
-                          const OnReplyCallback& callback)>;
-
-  // Adds notification callback for server started/stopped serving requests.
-  virtual void AddOnStateChangedCallback(
-      const OnStateChangedCallback& callback) = 0;
-
-  // Adds callback called on new http/https requests with the given path prefix.
-  virtual void AddRequestHandler(const std::string& path_prefix,
-                                 const OnRequestCallback& callback) = 0;
+  // Adds callback called on new http/https requests with the given path.
+  virtual void AddHttpRequestHandler(
+      const std::string& path,
+      const RequestHandlerCallback& callback) = 0;
+  virtual void AddHttpsRequestHandler(
+      const std::string& path,
+      const RequestHandlerCallback& callback) = 0;
 
   virtual uint16_t GetHttpPort() const = 0;
   virtual uint16_t GetHttpsPort() const = 0;
-  virtual const std::vector<uint8_t>& GetHttpsCertificateFingerprint()
-      const = 0;
+  virtual std::vector<uint8_t> GetHttpsCertificateFingerprint() const = 0;
 
  protected:
   virtual ~HttpServer() = default;
diff --git a/libweave/include/weave/provider/network.h b/libweave/include/weave/provider/network.h
index 8e22166..651155a 100644
--- a/libweave/include/weave/provider/network.h
+++ b/libweave/include/weave/provider/network.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,17 +20,17 @@
  public:
   enum class State {
     kOffline = 0,
-    kFailure,
+    kError,
     kConnecting,
-    kConnected,
+    kOnline,
   };
 
   // Callback type for AddConnectionChangedCallback.
   using ConnectionChangedCallback = base::Closure;
 
   // Callback type for OpenSslSocket.
-  using OpenSslSocketSuccessCallback =
-      base::Callback<void(std::unique_ptr<Stream> stream)>;
+  using OpenSslSocketCallback =
+      base::Callback<void(std::unique_ptr<Stream> stream, ErrorPtr error)>;
 
   // Subscribes to notification about changes in network connectivity. Changes
   // may include but not limited: interface up or down, new IP was assigned,
@@ -42,11 +42,9 @@
   virtual State GetConnectionState() const = 0;
 
   // Opens bidirectional sockets and returns attached stream.
-  virtual void OpenSslSocket(
-      const std::string& host,
-      uint16_t port,
-      const OpenSslSocketSuccessCallback& success_callback,
-      const ErrorCallback& error_callback) = 0;
+  virtual void OpenSslSocket(const std::string& host,
+                             uint16_t port,
+                             const OpenSslSocketCallback& callback) = 0;
 
  protected:
   virtual ~Network() = default;
diff --git a/libweave/include/weave/provider/task_runner.h b/libweave/include/weave/provider/task_runner.h
index daabc7e..0804a10 100644
--- a/libweave/include/weave/provider/task_runner.h
+++ b/libweave/include/weave/provider/task_runner.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/include/weave/provider/test/fake_task_runner.h b/libweave/include/weave/provider/test/fake_task_runner.h
index c0c8e9e..06ccd6b 100644
--- a/libweave/include/weave/provider/test/fake_task_runner.h
+++ b/libweave/include/weave/provider/test/fake_task_runner.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -28,7 +28,7 @@
                        base::TimeDelta delay) override;
 
   bool RunOnce();
-  void Run();
+  void Run(size_t number_of_iterations = 1000);
   void Break();
   base::Clock* GetClock();
 
diff --git a/libweave/include/weave/provider/test/mock_bluetooth.h b/libweave/include/weave/provider/test/mock_bluetooth.h
index d572fa4..334f9de 100644
--- a/libweave/include/weave/provider/test/mock_bluetooth.h
+++ b/libweave/include/weave/provider/test/mock_bluetooth.h
@@ -1,18 +1,6 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Copyright 2015 The Weave 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 LIBWEAVE_INCLUDE_WEAVE_PROVIDER_TEST_MOCK_BLUETOOTH_H_
 #define LIBWEAVE_INCLUDE_WEAVE_PROVIDER_TEST_MOCK_BLUETOOTH_H_
diff --git a/libweave/include/weave/provider/test/mock_config_store.h b/libweave/include/weave/provider/test/mock_config_store.h
index 6eab802..01e1ef9 100644
--- a/libweave/include/weave/provider/test/mock_config_store.h
+++ b/libweave/include/weave/provider/test/mock_config_store.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -41,10 +41,6 @@
   MOCK_METHOD1(LoadDefaults, bool(Settings*));
   MOCK_METHOD0(LoadSettings, std::string());
   MOCK_METHOD1(SaveSettings, void(const std::string&));
-
-  MOCK_METHOD0(LoadCommandDefs, std::vector<std::string>());
-  MOCK_METHOD0(LoadStateDefs, std::vector<std::string>());
-  MOCK_METHOD0(LoadStateDefaults, std::vector<std::string>());
 };
 
 }  // namespace test
diff --git a/libweave/include/weave/provider/test/mock_dns_service_discovery.h b/libweave/include/weave/provider/test/mock_dns_service_discovery.h
index 9c149df..708f3c3 100644
--- a/libweave/include/weave/provider/test/mock_dns_service_discovery.h
+++ b/libweave/include/weave/provider/test/mock_dns_service_discovery.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/include/weave/provider/test/mock_http_client.h b/libweave/include/weave/provider/test/mock_http_client.h
index 21af699..77b9fd0 100644
--- a/libweave/include/weave/provider/test/mock_http_client.h
+++ b/libweave/include/weave/provider/test/mock_http_client.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,32 +20,19 @@
  public:
   MOCK_CONST_METHOD0(GetStatusCode, int());
   MOCK_CONST_METHOD0(GetContentType, std::string());
-  MOCK_CONST_METHOD0(GetData, const std::string&());
+  MOCK_CONST_METHOD0(GetData, std::string());
 };
 
 class MockHttpClient : public HttpClient {
  public:
   ~MockHttpClient() override = default;
 
-  MOCK_METHOD5(MockSendRequest,
-               Response*(const std::string&,
-                         const std::string&,
-                         const Headers&,
-                         const std::string&,
-                         ErrorPtr*));
-
-  std::unique_ptr<Response> SendRequestAndBlock(const std::string& method,
-                                                const std::string& url,
-                                                const Headers& headers,
-                                                const std::string& data,
-                                                ErrorPtr* error) override;
-
-  int SendRequest(const std::string& method,
-                  const std::string& url,
-                  const Headers& headers,
-                  const std::string& data,
-                  const SuccessCallback& success_callback,
-                  const ErrorCallback& error_callback) override;
+  MOCK_METHOD5(SendRequest,
+               void(Method,
+                    const std::string&,
+                    const Headers&,
+                    const std::string&,
+                    const SendRequestCallback&));
 };
 
 }  // namespace test
diff --git a/libweave/include/weave/provider/test/mock_http_server.h b/libweave/include/weave/provider/test/mock_http_server.h
index 3beb4ae..f56470b 100644
--- a/libweave/include/weave/provider/test/mock_http_server.h
+++ b/libweave/include/weave/provider/test/mock_http_server.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,13 +18,13 @@
 
 class MockHttpServer : public HttpServer {
  public:
-  MOCK_METHOD1(AddOnStateChangedCallback, void(const OnStateChangedCallback&));
-  MOCK_METHOD2(AddRequestHandler,
-               void(const std::string&, const OnRequestCallback&));
-
+  MOCK_METHOD2(AddHttpRequestHandler,
+               void(const std::string&, const RequestHandlerCallback&));
+  MOCK_METHOD2(AddHttpsRequestHandler,
+               void(const std::string&, const RequestHandlerCallback&));
   MOCK_CONST_METHOD0(GetHttpPort, uint16_t());
   MOCK_CONST_METHOD0(GetHttpsPort, uint16_t());
-  MOCK_CONST_METHOD0(GetHttpsCertificateFingerprint, std::vector<uint8_t>&());
+  MOCK_CONST_METHOD0(GetHttpsCertificateFingerprint, std::vector<uint8_t>());
 };
 
 }  // namespace test
diff --git a/libweave/include/weave/provider/test/mock_network.h b/libweave/include/weave/provider/test/mock_network.h
index e38dde2..f6ab99a 100644
--- a/libweave/include/weave/provider/test/mock_network.h
+++ b/libweave/include/weave/provider/test/mock_network.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,11 +20,10 @@
   MOCK_METHOD1(AddConnectionChangedCallback,
                void(const ConnectionChangedCallback&));
   MOCK_CONST_METHOD0(GetConnectionState, State());
-  MOCK_METHOD4(OpenSslSocket,
+  MOCK_METHOD3(OpenSslSocket,
                void(const std::string&,
                     uint16_t,
-                    const OpenSslSocketSuccessCallback&,
-                    const ErrorCallback&));
+                    const OpenSslSocketCallback&));
 };
 
 }  // namespace test
diff --git a/libweave/include/weave/provider/test/mock_wifi.h b/libweave/include/weave/provider/test/mock_wifi.h
index 6c53d9a..9fbe10f 100644
--- a/libweave/include/weave/provider/test/mock_wifi.h
+++ b/libweave/include/weave/provider/test/mock_wifi.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,11 +17,10 @@
 
 class MockWifi : public Wifi {
  public:
-  MOCK_METHOD4(Connect,
+  MOCK_METHOD3(Connect,
                void(const std::string&,
                     const std::string&,
-                    const base::Closure&,
-                    const ErrorCallback&));
+                    const DoneCallback&));
   MOCK_METHOD1(StartAccessPoint, void(const std::string&));
   MOCK_METHOD0(StopAccessPoint, void());
 };
diff --git a/libweave/include/weave/provider/wifi.h b/libweave/include/weave/provider/wifi.h
index 51f370c..111bf3c 100644
--- a/libweave/include/weave/provider/wifi.h
+++ b/libweave/include/weave/provider/wifi.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,8 +20,7 @@
   // should post either of callbacks.
   virtual void Connect(const std::string& ssid,
                        const std::string& passphrase,
-                       const SuccessCallback& success_callback,
-                       const ErrorCallback& error_callback) = 0;
+                       const DoneCallback& callback) = 0;
 
   // Starts WiFi access point for wifi setup.
   virtual void StartAccessPoint(const std::string& ssid) = 0;
diff --git a/libweave/include/weave/settings.h b/libweave/include/weave/settings.h
index a2f3ded..e5d21b0 100644
--- a/libweave/include/weave/settings.h
+++ b/libweave/include/weave/settings.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/include/weave/stream.h b/libweave/include/weave/stream.h
index 9d4d6fd..19d38a0 100644
--- a/libweave/include/weave/stream.h
+++ b/libweave/include/weave/stream.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,15 +18,14 @@
   virtual ~InputStream() = default;
 
   // Callback type for Read.
-  using ReadSuccessCallback = base::Callback<void(size_t size)>;
+  using ReadCallback = base::Callback<void(size_t size, ErrorPtr error)>;
 
-  // Implementation should return immediately and post either success_callback
-  // or error_callback. Caller guarantees that buffet is alive until either of
-  // callback is called.
+  // Implementation should return immediately and post callback after
+  // completing operation. Caller guarantees that buffet is alive until callback
+  // is called.
   virtual void Read(void* buffer,
                     size_t size_to_read,
-                    const ReadSuccessCallback& success_callback,
-                    const ErrorCallback& error_callback) = 0;
+                    const ReadCallback& callback) = 0;
 };
 
 // Interface for async input streaming.
@@ -34,14 +33,15 @@
  public:
   virtual ~OutputStream() = default;
 
-  // Implementation should return immediately and post either success_callback
-  // or error_callback. Caller guarantees that buffet is alive until either of
-  // callback is called.
+  using WriteCallback = base::Callback<void(ErrorPtr error)>;
+
+  // Implementation should return immediately and post callback after
+  // completing operation. Caller guarantees that buffet is alive until either
+  // of callback is called.
   // Success callback must be called only after all data is written.
   virtual void Write(const void* buffer,
                      size_t size_to_write,
-                     const SuccessCallback& success_callback,
-                     const ErrorCallback& error_callback) = 0;
+                     const WriteCallback& callback) = 0;
 };
 
 // Interface for async bi-directional streaming.
diff --git a/libweave/include/weave/test/fake_stream.h b/libweave/include/weave/test/fake_stream.h
index 8abb491..ea93da8 100644
--- a/libweave/include/weave/test/fake_stream.h
+++ b/libweave/include/weave/test/fake_stream.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -31,12 +31,10 @@
   void CancelPendingOperations() override;
   void Read(void* buffer,
             size_t size_to_read,
-            const ReadSuccessCallback& success_callback,
-            const ErrorCallback& error_callback) override;
+            const ReadCallback& callback) override;
   void Write(const void* buffer,
              size_t size_to_write,
-             const SuccessCallback& success_callback,
-             const ErrorCallback& error_callback) override;
+             const WriteCallback& callback) override;
 
  private:
   provider::TaskRunner* task_runner_{nullptr};
diff --git a/libweave/include/weave/test/mock_command.h b/libweave/include/weave/test/mock_command.h
index be1b0c3..2b1080e 100644
--- a/libweave/include/weave/test/mock_command.h
+++ b/libweave/include/weave/test/mock_command.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,27 +20,25 @@
  public:
   ~MockCommand() override = default;
 
-  MOCK_METHOD1(AddObserver, void(Observer*));
-  MOCK_METHOD1(RemoveObserver, void(Observer*));
   MOCK_CONST_METHOD0(GetID, const std::string&());
   MOCK_CONST_METHOD0(GetName, const std::string&());
   MOCK_CONST_METHOD0(GetCategory, const std::string&());
-  MOCK_CONST_METHOD0(GetStatus, CommandStatus());
-  MOCK_CONST_METHOD0(GetOrigin, CommandOrigin());
+  MOCK_CONST_METHOD0(GetState, Command::State());
+  MOCK_CONST_METHOD0(GetOrigin, Command::Origin());
   MOCK_CONST_METHOD0(MockGetParameters, const std::string&());
   MOCK_CONST_METHOD0(MockGetProgress, const std::string&());
   MOCK_CONST_METHOD0(MockGetResults, const std::string&());
+  MOCK_CONST_METHOD0(GetError, const Error*());
   MOCK_METHOD2(SetProgress, bool(const base::DictionaryValue&, ErrorPtr*));
-  MOCK_METHOD2(SetResults, bool(const base::DictionaryValue&, ErrorPtr*));
-  MOCK_METHOD0(Abort, void());
-  MOCK_METHOD0(Cancel, void());
-  MOCK_METHOD0(Done, void());
-  MOCK_CONST_METHOD0(MockToJson, const std::string&());
+  MOCK_METHOD2(Complete, bool(const base::DictionaryValue&, ErrorPtr*));
+  MOCK_METHOD1(Pause, bool(ErrorPtr*));
+  MOCK_METHOD2(SetError, bool(const Error*, ErrorPtr*));
+  MOCK_METHOD2(Abort, bool(const Error*, ErrorPtr*));
+  MOCK_METHOD1(Cancel, bool(ErrorPtr*));
 
   std::unique_ptr<base::DictionaryValue> GetParameters() const override;
   std::unique_ptr<base::DictionaryValue> GetProgress() const override;
   std::unique_ptr<base::DictionaryValue> GetResults() const override;
-  std::unique_ptr<base::DictionaryValue> ToJson() const override;
 };
 
 }  // namespace test
diff --git a/libweave/include/weave/test/mock_device.h b/libweave/include/weave/test/mock_device.h
index 480208e..f751f97 100644
--- a/libweave/include/weave/test/mock_device.h
+++ b/libweave/include/weave/test/mock_device.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,27 +21,32 @@
   MOCK_CONST_METHOD0(GetSettings, const Settings&());
   MOCK_METHOD1(AddSettingsChangedCallback,
                void(const SettingsChangedCallback& callback));
-  MOCK_METHOD1(AddCommandAddedCallback, void(const CommandCallback&));
-  MOCK_METHOD1(AddCommandRemovedCallback, void(const CommandCallback&));
+  MOCK_METHOD1(AddCommandDefinitionsFromJson, void(const std::string&));
+  MOCK_METHOD1(AddCommandDefinitions, void(const base::DictionaryValue&));
+  MOCK_METHOD2(AddCommandHandler,
+               void(const std::string&, const CommandHandlerCallback&));
   MOCK_METHOD3(AddCommand,
                bool(const base::DictionaryValue&, std::string*, ErrorPtr*));
   MOCK_METHOD1(FindCommand, Command*(const std::string&));
   MOCK_METHOD1(AddStateChangedCallback, void(const base::Closure& callback));
+  MOCK_METHOD1(AddStateDefinitionsFromJson, void(const std::string&));
+  MOCK_METHOD1(AddStateDefinitions, void(const base::DictionaryValue&));
+  MOCK_METHOD2(SetStatePropertiesFromJson, bool(const std::string&, ErrorPtr*));
+  MOCK_METHOD2(SetStateProperties,
+               bool(const base::DictionaryValue&, ErrorPtr*));
   MOCK_CONST_METHOD1(MockGetStateProperty,
                      base::Value*(const std::string& name));
   MOCK_METHOD3(SetStateProperty,
                bool(const std::string& name,
                     const base::Value& value,
                     ErrorPtr* error));
-  MOCK_METHOD2(SetStateProperties,
-               bool(const base::DictionaryValue& property_set,
-                    ErrorPtr* error));
   MOCK_CONST_METHOD0(MockGetState, base::DictionaryValue*());
   MOCK_CONST_METHOD0(GetGcdState, GcdState());
   MOCK_METHOD1(AddGcdStateChangedCallback,
                void(const GcdStateChangedCallback& callback));
   MOCK_METHOD2(Register,
-               std::string(const std::string& ticket_id, ErrorPtr* error));
+               void(const std::string& ticket_id,
+                    const DoneCallback& callback));
   MOCK_METHOD2(AddPairingChangedCallbacks,
                void(const PairingBeginCallback& begin_callback,
                     const PairingEndCallback& end_callback));
diff --git a/libweave/include/weave/test/unittest_utils.h b/libweave/include/weave/test/unittest_utils.h
index ff8cd2a..304ba01 100644
--- a/libweave/include/weave/test/unittest_utils.h
+++ b/libweave/include/weave/test/unittest_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/libweave.gypi b/libweave/libweave.gypi
index bad1412..1e42f83 100644
--- a/libweave/libweave.gypi
+++ b/libweave/libweave.gypi
@@ -1,9 +1,9 @@
+# Copyright 2015 The Weave Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'variables': {
     'weave_sources': [
-      'external/crypto/p224.cc',
-      'external/crypto/p224_spake.cc',
-      'external/crypto/sha2.cc',
       'src/backoff_entry.cc',
       'src/base_api_handler.cc',
       'src/commands/cloud_command_proxy.cc',
@@ -47,21 +47,21 @@
       'src/states/state_change_queue.cc',
       'src/states/state_manager.cc',
       'src/states/state_package.cc',
+      'src/streams.cc',
       'src/string_utils.cc',
       'src/utils.cc',
+      'third_party/chromium/crypto/p224.cc',
+      'third_party/chromium/crypto/p224_spake.cc',
+      'third_party/chromium/crypto/sha2.cc',
       'third_party/modp_b64/modp_b64.cc',
     ],
     'weave_test_sources': [
       'src/test/fake_stream.cc',
       'src/test/fake_task_runner.cc',
       'src/test/mock_command.cc',
-      'src/test/mock_http_client.cc',
       'src/test/unittest_utils.cc',
     ],
     'weave_unittest_sources': [
-      'external/crypto/p224_spake_unittest.cc',
-      'external/crypto/p224_unittest.cc',
-      'external/crypto/sha2_unittest.cc',
       'src/backoff_entry_unittest.cc',
       'src/base_api_handler_unittest.cc',
       'src/commands/cloud_command_proxy_unittest.cc',
@@ -87,68 +87,72 @@
       'src/states/state_change_queue_unittest.cc',
       'src/states/state_manager_unittest.cc',
       'src/states/state_package_unittest.cc',
+      'src/streams_unittest.cc',
       'src/string_utils_unittest.cc',
       'src/test/weave_testrunner.cc',
+      'third_party/chromium/crypto/p224_spake_unittest.cc',
+      'third_party/chromium/crypto/p224_unittest.cc',
+      'third_party/chromium/crypto/sha2_unittest.cc',
     ],
     'weave_exports_unittest_sources': [
       'src/test/weave_testrunner.cc',
       'src/weave_unittest.cc',
     ],
     'base_sources': [
-      'external/base/bind_helpers.cc',
-      'external/base/callback_internal.cc',
-      'external/base/guid_posix.cc',
-      'external/base/json/json_parser.cc',
-      'external/base/json/json_reader.cc',
-      'external/base/json/json_writer.cc',
-      'external/base/json/string_escape.cc',
-      'external/base/memory/ref_counted.cc',
-      'external/base/logging.cc',
-      'external/base/location.cc',
-      'external/base/memory/weak_ptr.cc',
-      'external/base/memory/weak_ptr.cc',
-      'external/base/rand_util.cc',
-      'external/base/rand_util_posix.cc',
-      'external/base/strings/string_number_conversions.cc',
-      'external/base/strings/string_piece.cc',
-      'external/base/strings/stringprintf.cc',
-      'external/base/strings/string_util.cc',
-      'external/base/strings/string_util_constants.cc',
-      'external/base/strings/utf_string_conversion_utils.cc',
-      'external/base/third_party/dmg_fp/g_fmt.cc',
-      'external/base/third_party/dmg_fp/dtoa.cc',
-      'external/base/third_party/icu/icu_utf.cc',
-      'external/base/time/clock.cc',
-      'external/base/time/time.cc',
-      'external/base/time/time_posix.cc',
-      'external/base/values.cc',
+      'third_party/chromium/base/bind_helpers.cc',
+      'third_party/chromium/base/callback_internal.cc',
+      'third_party/chromium/base/guid_posix.cc',
+      'third_party/chromium/base/json/json_parser.cc',
+      'third_party/chromium/base/json/json_reader.cc',
+      'third_party/chromium/base/json/json_writer.cc',
+      'third_party/chromium/base/json/string_escape.cc',
+      'third_party/chromium/base/memory/ref_counted.cc',
+      'third_party/chromium/base/logging.cc',
+      'third_party/chromium/base/location.cc',
+      'third_party/chromium/base/memory/weak_ptr.cc',
+      'third_party/chromium/base/memory/weak_ptr.cc',
+      'third_party/chromium/base/rand_util.cc',
+      'third_party/chromium/base/rand_util_posix.cc',
+      'third_party/chromium/base/strings/string_number_conversions.cc',
+      'third_party/chromium/base/strings/string_piece.cc',
+      'third_party/chromium/base/strings/stringprintf.cc',
+      'third_party/chromium/base/strings/string_util.cc',
+      'third_party/chromium/base/strings/string_util_constants.cc',
+      'third_party/chromium/base/strings/utf_string_conversion_utils.cc',
+      'third_party/chromium/base/third_party/dmg_fp/g_fmt.cc',
+      'third_party/chromium/base/third_party/dmg_fp/dtoa.cc',
+      'third_party/chromium/base/third_party/icu/icu_utf.cc',
+      'third_party/chromium/base/time/clock.cc',
+      'third_party/chromium/base/time/time.cc',
+      'third_party/chromium/base/time/time_posix.cc',
+      'third_party/chromium/base/values.cc',
     ],
     'base_unittests': [
-      'external/base/bind_unittest.cc',
-      'external/base/callback_list_unittest.cc',
-      'external/base/callback_unittest.cc',
-      'external/base/guid_unittest.cc',
-      'external/base/json/json_parser_unittest.cc',
-      'external/base/json/json_reader_unittest.cc',
-      'external/base/json/json_writer_unittest.cc',
-      'external/base/json/string_escape_unittest.cc',
-      'external/base/logging_unittest.cc',
-      'external/base/memory/ref_counted_unittest.cc',
-      'external/base/memory/scoped_ptr_unittest.cc',
-      'external/base/memory/weak_ptr_unittest.cc',
-      'external/base/move_unittest.cc',
-      'external/base/numerics/safe_numerics_unittest.cc',
-      'external/base/observer_list_unittest.cc',
-      'external/base/rand_util_unittest.cc',
-      'external/base/scoped_clear_errno_unittest.cc',
-      'external/base/strings/string_number_conversions_unittest.cc',
-      'external/base/strings/string_piece_unittest.cc',
-      'external/base/strings/string_util_unittest.cc',
-      'external/base/strings/stringprintf_unittest.cc',
-      'external/base/template_util_unittest.cc',
-      'external/base/time/time_unittest.cc',
-      'external/base/tuple_unittest.cc',
-      'external/base/values_unittest.cc',
+      'third_party/chromium/base/bind_unittest.cc',
+      'third_party/chromium/base/callback_list_unittest.cc',
+      'third_party/chromium/base/callback_unittest.cc',
+      'third_party/chromium/base/guid_unittest.cc',
+      'third_party/chromium/base/json/json_parser_unittest.cc',
+      'third_party/chromium/base/json/json_reader_unittest.cc',
+      'third_party/chromium/base/json/json_writer_unittest.cc',
+      'third_party/chromium/base/json/string_escape_unittest.cc',
+      'third_party/chromium/base/logging_unittest.cc',
+      'third_party/chromium/base/memory/ref_counted_unittest.cc',
+      'third_party/chromium/base/memory/scoped_ptr_unittest.cc',
+      'third_party/chromium/base/memory/weak_ptr_unittest.cc',
+      'third_party/chromium/base/move_unittest.cc',
+      'third_party/chromium/base/numerics/safe_numerics_unittest.cc',
+      'third_party/chromium/base/observer_list_unittest.cc',
+      'third_party/chromium/base/rand_util_unittest.cc',
+      'third_party/chromium/base/scoped_clear_errno_unittest.cc',
+      'third_party/chromium/base/strings/string_number_conversions_unittest.cc',
+      'third_party/chromium/base/strings/string_piece_unittest.cc',
+      'third_party/chromium/base/strings/string_util_unittest.cc',
+      'third_party/chromium/base/strings/stringprintf_unittest.cc',
+      'third_party/chromium/base/template_util_unittest.cc',
+      'third_party/chromium/base/time/time_unittest.cc',
+      'third_party/chromium/base/tuple_unittest.cc',
+      'third_party/chromium/base/values_unittest.cc',
     ],
   },
 }
diff --git a/libweave/libweave_common.gypi b/libweave/libweave_common.gypi
index 84c7a17..125047d 100644
--- a/libweave/libweave_common.gypi
+++ b/libweave/libweave_common.gypi
@@ -1,3 +1,6 @@
+# Copyright 2015 The Weave Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'target_defaults': {
     'configurations': {
@@ -21,7 +24,7 @@
     'include_dirs': [
       '.',
       'include',
-      'external',
+      'third_party/chromium',
       'third_party/include',
       'third_party/modp_b64/modp_b64',
     ],
diff --git a/libweave/libweave_standalone.gyp b/libweave/libweave_standalone.gyp
index 6c825ac..6b73f84 100644
--- a/libweave/libweave_standalone.gyp
+++ b/libweave/libweave_standalone.gyp
@@ -1,3 +1,6 @@
+# Copyright 2015 The Weave Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 {
   'includes': [
     'libweave.gypi',
@@ -6,9 +9,9 @@
     'libraries': [
       '-lcrypto',
       '-lexpat',
-      '-lpthread',
       '-lgtest',
       '-lgmock',
+      '-lpthread',
     ],
   },
   'targets': [
diff --git a/libweave/platform2.gyp b/libweave/platform2.gyp
deleted file mode 100644
index 41b9fd5..0000000
--- a/libweave/platform2.gyp
+++ /dev/null
@@ -1,185 +0,0 @@
-{
-  'includes': [
-    'libweave.gypi',
-  ],
-  'target_defaults': {
-    'variables': {
-      'deps': [
-        'expat',
-        'libcrypto',
-      ],
-    },
-    'include_dirs': [
-      '<(platform2_root)/../weave/libweave/libweave',
-      '<(platform2_root)/../weave/libweave/libweave/include',
-      '<(platform2_root)/../weave/libweave/libweave/third_party/modp_b64/modp_b64/',
-    ],
-  },
-  'targets': [
-    {
-      'target_name': 'libweave_common',
-      'type': 'static_library',
-      'cflags!': ['-fPIE'],
-      'cflags': ['-fPIC'],
-      'variables': {
-        'deps': [
-          'libchrome-<(libbase_ver)',
-        ],
-      },
-      'sources': [
-        '<@(weave_sources)',
-      ],
-    },
-    {
-      'target_name': 'libweave-<(libbase_ver)',
-      'type': 'shared_library',
-      'variables': {
-        'deps': [
-          'libchrome-<(libbase_ver)',
-        ],
-      },
-      'includes': [
-        '../../../platform2/common-mk/deps.gypi',
-      ],
-      'dependencies': [
-        'libweave_common',
-      ],
-      'sources': [
-        'src/empty.cc'
-      ],
-    },
-    {
-      'target_name': 'libweave-test-<(libbase_ver)',
-      'type': 'static_library',
-      'standalone_static_library': 1,
-      'variables': {
-        'deps': [
-          'libchrome-<(libbase_ver)',
-        ],
-      },
-      'sources': [
-        '<@(weave_test_sources)',
-      ],
-      'includes': ['../../../platform2/common-mk/deps.gypi'],
-    },
-    {
-      'target_name': 'libweave_base_common',
-      'type': 'static_library',
-      'cflags!': ['-fPIE'],
-      'cflags': [
-        '-fPIC',
-        '-Wno-format-nonliteral',
-        '-Wno-char-subscripts',
-        '-Wno-deprecated-register',
-      ],
-      'include_dirs': [
-        '../libweave/external',
-      ],
-      'sources': [
-        '<@(weave_sources)',
-        '<@(base_sources)',
-      ],
-    },
-    {
-      'target_name': 'libweave_base',
-      'type': 'shared_library',
-      'include_dirs': [
-        '../libweave/external',
-      ],
-      'includes': [
-        '../../../platform2/common-mk/deps.gypi',
-      ],
-      'dependencies': [
-        'libweave_base_common',
-      ],
-      'sources': [
-        'src/empty.cc'
-      ],
-    },
-    {
-      'target_name': 'libweave_base-test',
-      'type': 'static_library',
-      'standalone_static_library': 1,
-      'include_dirs': [
-        '../libweave/external',
-      ],
-      'sources': [
-        '<@(weave_test_sources)',
-      ],
-      'includes': ['../../../platform2/common-mk/deps.gypi'],
-    },
-  ],
-  'conditions': [
-    ['USE_test == 1', {
-      'targets': [
-        {
-          'target_name': 'libweave_testrunner',
-          'type': 'executable',
-          'variables': {
-            'deps': [
-              'libchrome-<(libbase_ver)',
-            ],
-          },
-          'dependencies': [
-            'libweave_common',
-            'libweave-test-<(libbase_ver)',
-          ],
-          'includes': ['../../../platform2/common-mk/common_test.gypi'],
-          'sources': [
-            '<@(weave_unittest_sources)',
-          ],
-        },
-        {
-          'target_name': 'libweave_base_testrunner',
-          'type': 'executable',
-          'cflags': ['-Wno-format-nonliteral'],
-          'include_dirs': [
-            '../libweave/external',
-          ],
-          'dependencies': [
-            'libweave_base_common',
-            'libweave_base-test',
-          ],
-          'includes': ['../../../platform2/common-mk/common_test.gypi'],
-          'sources': [
-            '<@(weave_unittest_sources)',
-            '<@(base_unittests)',
-          ],
-        },
-        {
-          'target_name': 'libweave_exports_testrunner',
-          'type': 'executable',
-          'variables': {
-            'deps': [
-              'libchrome-<(libbase_ver)',
-            ],
-          },
-          'dependencies': [
-            'libweave-<(libbase_ver)',
-            'libweave-test-<(libbase_ver)',
-          ],
-          'includes': ['../../../platform2/common-mk/common_test.gypi'],
-          'sources': [
-            '<@(weave_exports_unittest_sources)',
-          ],
-        },
-        {
-          'target_name': 'libweave_base_exports_testrunner',
-          'type': 'executable',
-          'cflags': ['-Wno-format-nonliteral'],
-          'include_dirs': [
-            '../libweave/external',
-          ],
-          'dependencies': [
-            'libweave_base',
-            'libweave_base-test',
-          ],
-          'includes': ['../../../platform2/common-mk/common_test.gypi'],
-          'sources': [
-            '<@(weave_exports_unittest_sources)',
-          ],
-        },
-      ],
-    }],
-  ],
-}
diff --git a/libweave/platform2_preinstall.sh b/libweave/platform2_preinstall.sh
deleted file mode 100755
index dfd19c9..0000000
--- a/libweave/platform2_preinstall.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/bash
-# 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.
-
-set -e
-
-OUT=$1
-v=$2
-
-deps=$(<"${OUT}"/gen/libweave-${v}-deps.txt)
-sed \
-  -e "s/@BSLOT@/${v}/g" \
-  -e "s/@PRIVATE_PC@/${deps}/g" \
-  "libweave.pc.in" > "${OUT}/lib/libweave-${v}.pc"
-
-
-deps_test=$(<"${OUT}"/gen/libweave-test-${v}-deps.txt)
-sed \
-  -e "s/@BSLOT@/${v}/g" \
-  -e "s/@PRIVATE_PC@/${deps_test}/g" \
-  "libweave-test.pc.in" > "${OUT}/lib/libweave-test-${v}.pc"
diff --git a/libweave/src/backoff_entry.cc b/libweave/src/backoff_entry.cc
index 4cc1241..6b99d79 100644
--- a/libweave/src/backoff_entry.cc
+++ b/libweave/src/backoff_entry.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/backoff_entry.h b/libweave/src/backoff_entry.h
index 3e7a036..2df0d8a 100644
--- a/libweave/src/backoff_entry.h
+++ b/libweave/src/backoff_entry.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/backoff_entry_unittest.cc b/libweave/src/backoff_entry_unittest.cc
index 598e7c4..9647c74 100644
--- a/libweave/src/backoff_entry_unittest.cc
+++ b/libweave/src/backoff_entry_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/base_api_handler.cc b/libweave/src/base_api_handler.cc
index 69a2838..52b5399 100644
--- a/libweave/src/base_api_handler.cc
+++ b/libweave/src/base_api_handler.cc
@@ -1,15 +1,14 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave 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 "src/base_api_handler.h"
 
 #include <base/bind.h>
+#include <weave/device.h>
 
-#include "src/commands/command_instance.h"
-#include "src/commands/command_manager.h"
+#include "src/commands/schema_constants.h"
 #include "src/device_registration_info.h"
-#include "src/states/state_manager.h"
 
 namespace weave {
 
@@ -20,36 +19,41 @@
 const char kBaseStatePairingEnabled[] = "base.localPairingEnabled";
 }  // namespace
 
-BaseApiHandler::BaseApiHandler(
-    DeviceRegistrationInfo* device_info,
-    const std::shared_ptr<StateManager>& state_manager,
-    const std::shared_ptr<CommandManager>& command_manager)
-    : device_info_{device_info}, state_manager_{state_manager} {
+BaseApiHandler::BaseApiHandler(DeviceRegistrationInfo* device_info,
+                               Device* device)
+    : device_info_{device_info}, device_{device} {
   device_info_->GetMutableConfig()->AddOnChangedCallback(base::Bind(
       &BaseApiHandler::OnConfigChanged, weak_ptr_factory_.GetWeakPtr()));
 
   const auto& settings = device_info_->GetSettings();
   base::DictionaryValue state;
-  state.SetStringWithoutPathExpansion(kBaseStateFirmwareVersion,
-                                      settings.firmware_version);
-  CHECK(state_manager_->SetProperties(state, nullptr));
+  state.SetString(kBaseStateFirmwareVersion, settings.firmware_version);
+  CHECK(device_->SetStateProperties(state, nullptr));
 
-  command_manager->AddCommandAddedCallback(base::Bind(
-      &BaseApiHandler::OnCommandAdded, weak_ptr_factory_.GetWeakPtr()));
+  device->AddCommandDefinitionsFromJson(R"({
+    "base": {
+      "updateBaseConfiguration": {},
+      "updateDeviceInfo": {}
+    }
+  })");
+
+  device_->AddCommandHandler(
+      "base.updateBaseConfiguration",
+      base::Bind(&BaseApiHandler::UpdateBaseConfiguration,
+                 weak_ptr_factory_.GetWeakPtr()));
+
+  device_->AddCommandHandler("base.updateDeviceInfo",
+                             base::Bind(&BaseApiHandler::UpdateDeviceInfo,
+                                        weak_ptr_factory_.GetWeakPtr()));
 }
 
-void BaseApiHandler::OnCommandAdded(Command* command) {
-  if (command->GetStatus() != CommandStatus::kQueued)
+void BaseApiHandler::UpdateBaseConfiguration(
+    const std::weak_ptr<Command>& cmd) {
+  auto command = cmd.lock();
+  if (!command)
     return;
-
-  if (command->GetName() == "base.updateBaseConfiguration")
-    return UpdateBaseConfiguration(command);
-
-  if (command->GetName() == "base.updateDeviceInfo")
-    return UpdateDeviceInfo(command);
-}
-
-void BaseApiHandler::UpdateBaseConfiguration(Command* command) {
+  CHECK(command->GetState() == Command::State::kQueued)
+      << EnumToString(command->GetState());
   command->SetProgress(base::DictionaryValue{}, nullptr);
 
   const auto& settings = device_info_->GetSettings();
@@ -65,28 +69,37 @@
 
   AuthScope auth_scope{AuthScope::kNone};
   if (!StringToEnum(anonymous_access_role, &auth_scope)) {
-    return command->Abort();
+    ErrorPtr error;
+    Error::AddToPrintf(&error, FROM_HERE, errors::commands::kDomain,
+                       errors::commands::kInvalidPropValue,
+                       "Invalid localAnonymousAccessMaxRole value '%s'",
+                       anonymous_access_role.c_str());
+    command->Abort(error.get(), nullptr);
+    return;
   }
 
   device_info_->UpdateBaseConfig(auth_scope, discovery_enabled,
                                  pairing_enabled);
 
-  command->Done();
+  command->Complete({}, nullptr);
 }
 
 void BaseApiHandler::OnConfigChanged(const Settings& settings) {
   base::DictionaryValue state;
-  state.SetStringWithoutPathExpansion(
-      kBaseStateAnonymousAccessRole,
-      EnumToString(settings.local_anonymous_access_role));
-  state.SetBooleanWithoutPathExpansion(kBaseStateDiscoveryEnabled,
-                                       settings.local_discovery_enabled);
-  state.SetBooleanWithoutPathExpansion(kBaseStatePairingEnabled,
-                                       settings.local_pairing_enabled);
-  state_manager_->SetProperties(state, nullptr);
+  state.SetString(kBaseStateAnonymousAccessRole,
+                  EnumToString(settings.local_anonymous_access_role));
+  state.SetBoolean(kBaseStateDiscoveryEnabled,
+                   settings.local_discovery_enabled);
+  state.SetBoolean(kBaseStatePairingEnabled, settings.local_pairing_enabled);
+  device_->SetStateProperties(state, nullptr);
 }
 
-void BaseApiHandler::UpdateDeviceInfo(Command* command) {
+void BaseApiHandler::UpdateDeviceInfo(const std::weak_ptr<Command>& cmd) {
+  auto command = cmd.lock();
+  if (!command)
+    return;
+  CHECK(command->GetState() == Command::State::kQueued)
+      << EnumToString(command->GetState());
   command->SetProgress(base::DictionaryValue{}, nullptr);
 
   const auto& settings = device_info_->GetSettings();
@@ -100,7 +113,7 @@
   parameters->GetString("location", &location);
 
   device_info_->UpdateDeviceInfo(name, description, location);
-  command->Done();
+  command->Complete({}, nullptr);
 }
 
 }  // namespace weave
diff --git a/libweave/src/base_api_handler.h b/libweave/src/base_api_handler.h
index c1a5f2e..8497692 100644
--- a/libweave/src/base_api_handler.h
+++ b/libweave/src/base_api_handler.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,9 +13,8 @@
 namespace weave {
 
 class Command;
-class CommandManager;
+class Device;
 class DeviceRegistrationInfo;
-class StateManager;
 struct Settings;
 
 // Handles commands from 'base' package.
@@ -26,21 +25,18 @@
 //  base.updateBaseConfiguration
 class BaseApiHandler final {
  public:
-  BaseApiHandler(DeviceRegistrationInfo* device_info,
-                 const std::shared_ptr<StateManager>& state_manager,
-                 const std::shared_ptr<CommandManager>& command_manager);
+  BaseApiHandler(DeviceRegistrationInfo* device_info, Device* device);
 
  private:
-  void OnCommandAdded(Command* command);
-  void UpdateBaseConfiguration(Command* command);
-  void UpdateDeviceInfo(Command* command);
+  void UpdateBaseConfiguration(const std::weak_ptr<Command>& command);
+  void UpdateDeviceInfo(const std::weak_ptr<Command>& command);
   bool UpdateState(const std::string& anonymous_access_role,
                    bool discovery_enabled,
                    bool pairing_enabled);
   void OnConfigChanged(const Settings& settings);
 
   DeviceRegistrationInfo* device_info_;
-  std::shared_ptr<StateManager> state_manager_;
+  Device* device_;
 
   base::WeakPtrFactory<BaseApiHandler> weak_ptr_factory_{this};
   DISALLOW_COPY_AND_ASSIGN(BaseApiHandler);
diff --git a/libweave/src/base_api_handler_unittest.cc b/libweave/src/base_api_handler_unittest.cc
index a583dc3..172c8f1 100644
--- a/libweave/src/base_api_handler_unittest.cc
+++ b/libweave/src/base_api_handler_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -9,6 +9,7 @@
 #include <gtest/gtest.h>
 #include <weave/provider/test/mock_config_store.h>
 #include <weave/provider/test/mock_http_client.h>
+#include <weave/test/mock_device.h>
 
 #include "src/commands/command_manager.h"
 #include "src/commands/unittest_utils.h"
@@ -18,6 +19,8 @@
 #include "src/states/state_manager.h"
 
 using testing::_;
+using testing::AnyOf;
+using testing::Eq;
 using testing::Invoke;
 using testing::Return;
 using testing::StrictMock;
@@ -31,58 +34,45 @@
         .WillRepeatedly(Return(true));
 
     command_manager_ = std::make_shared<CommandManager>();
+    command_manager_->Startup();
+
     state_manager_ = std::make_shared<StateManager>(&mock_state_change_queue_);
-    auto state_definition = test::CreateDictionaryValue(R"({
-      'base': {
-        'firmwareVersion': 'string',
-        'localDiscoveryEnabled': 'boolean',
-        'localAnonymousAccessMaxRole': [ 'none', 'viewer', 'user' ],
-        'localPairingEnabled': 'boolean',
-        'network': {
-          'properties': {
-            'name': 'string'
-          }
-        }
-      }
-    })");
-    auto state_defaults = test::CreateDictionaryValue(R"({
-      'base': {
-        'firmwareVersion': '123123',
-        'localDiscoveryEnabled': false,
-        'localAnonymousAccessMaxRole': 'none',
-        'localPairingEnabled': false
-      }
-    })");
-    ASSERT_TRUE(
-        state_manager_->LoadStateDefinition(*state_definition, nullptr));
-    ASSERT_TRUE(state_manager_->LoadStateDefaults(*state_defaults, nullptr));
+    state_manager_->Startup();
+
+    EXPECT_CALL(device_, SetStateProperties(_, _))
+        .WillRepeatedly(
+            Invoke(state_manager_.get(), &StateManager::SetProperties));
+    EXPECT_CALL(device_, AddCommandDefinitionsFromJson(_))
+        .WillRepeatedly(Invoke([this](const std::string& json) {
+          EXPECT_TRUE(command_manager_->LoadCommands(json, nullptr));
+        }));
+
+    EXPECT_CALL(device_, AddCommandHandler(AnyOf("base.updateBaseConfiguration",
+                                                 "base.updateDeviceInfo"),
+                                           _))
+        .WillRepeatedly(
+            Invoke(command_manager_.get(), &CommandManager::AddCommandHandler));
+
     std::unique_ptr<Config> config{new Config{&config_store_}};
     config->Load();
     dev_reg_.reset(new DeviceRegistrationInfo(command_manager_, state_manager_,
                                               std::move(config), nullptr,
                                               &http_client_, nullptr));
-    handler_.reset(
-        new BaseApiHandler{dev_reg_.get(), state_manager_, command_manager_});
-  }
-
-  void LoadCommands(const std::string& command_definitions) {
-    auto json = test::CreateDictionaryValue(command_definitions.c_str());
-    EXPECT_TRUE(command_manager_->LoadBaseCommands(*json, nullptr));
-    EXPECT_TRUE(command_manager_->LoadCommands(*json, nullptr));
+    handler_.reset(new BaseApiHandler{dev_reg_.get(), &device_});
   }
 
   void AddCommand(const std::string& command) {
     auto command_instance = CommandInstance::FromJson(
         test::CreateDictionaryValue(command.c_str()).get(),
-        CommandOrigin::kLocal, command_manager_->GetCommandDictionary(),
+        Command::Origin::kLocal, command_manager_->GetCommandDictionary(),
         nullptr, nullptr);
     EXPECT_TRUE(!!command_instance);
 
     std::string id{base::IntToString(++command_id_)};
     command_instance->SetID(id);
     command_manager_->AddCommand(std::move(command_instance));
-    EXPECT_EQ(CommandStatus::kDone,
-              command_manager_->FindCommand(id)->GetStatus());
+    EXPECT_EQ(Command::State::kDone,
+              command_manager_->FindCommand(id)->GetState());
   }
 
   provider::test::MockConfigStore config_store_;
@@ -92,23 +82,11 @@
   testing::StrictMock<MockStateChangeQueueInterface> mock_state_change_queue_;
   std::shared_ptr<StateManager> state_manager_;
   std::unique_ptr<BaseApiHandler> handler_;
+  StrictMock<test::MockDevice> device_;
   int command_id_{0};
 };
 
 TEST_F(BaseApiHandlerTest, UpdateBaseConfiguration) {
-  LoadCommands(R"({
-    'base': {
-      'updateBaseConfiguration': {
-        'parameters': {
-          'localDiscoveryEnabled': 'boolean',
-          'localAnonymousAccessMaxRole': [ 'none', 'viewer', 'user' ],
-          'localPairingEnabled': 'boolean'
-         },
-         'results': {}
-      }
-    }
-  })");
-
   const Settings& settings = dev_reg_->GetSettings();
 
   AddCommand(R"({
@@ -173,22 +151,6 @@
 }
 
 TEST_F(BaseApiHandlerTest, UpdateDeviceInfo) {
-  LoadCommands(R"({
-    'base': {
-      'updateDeviceInfo': {
-        'parameters': {
-          'description': 'string',
-          'name': {
-            'type': 'string',
-            'minLength': 1
-          },
-          'location': 'string'
-        },
-        'results': {}
-      }
-    }
-  })");
-
   AddCommand(R"({
     'name' : 'base.updateDeviceInfo',
     'parameters': {
diff --git a/libweave/src/bind_lambda.h b/libweave/src/bind_lambda.h
index bceacc5..e6f367c 100644
--- a/libweave/src/bind_lambda.h
+++ b/libweave/src/bind_lambda.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/cloud_command_proxy.cc b/libweave/src/commands/cloud_command_proxy.cc
index 9f6ada3..9ec3d3e 100644
--- a/libweave/src/commands/cloud_command_proxy.cc
+++ b/libweave/src/commands/cloud_command_proxy.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -12,6 +12,7 @@
 #include "src/commands/prop_constraints.h"
 #include "src/commands/prop_types.h"
 #include "src/commands/schema_constants.h"
+#include "src/utils.h"
 
 namespace weave {
 
@@ -32,6 +33,15 @@
   observer_.Add(command_instance);
 }
 
+void CloudCommandProxy::OnErrorChanged() {
+  std::unique_ptr<base::DictionaryValue> patch{new base::DictionaryValue};
+  patch->Set(commands::attributes::kCommand_Error,
+             command_instance_->GetError()
+                 ? ErrorInfoToJson(*command_instance_->GetError()).release()
+                 : base::Value::CreateNullValue().release());
+  QueueCommandUpdate(std::move(patch));
+}
+
 void CloudCommandProxy::OnResultsChanged() {
   std::unique_ptr<base::DictionaryValue> patch{new base::DictionaryValue};
   patch->Set(commands::attributes::kCommand_Results,
@@ -39,10 +49,10 @@
   QueueCommandUpdate(std::move(patch));
 }
 
-void CloudCommandProxy::OnStatusChanged() {
+void CloudCommandProxy::OnStateChanged() {
   std::unique_ptr<base::DictionaryValue> patch{new base::DictionaryValue};
   patch->SetString(commands::attributes::kCommand_State,
-                   EnumToString(command_instance_->GetStatus()));
+                   EnumToString(command_instance_->GetState()));
   QueueCommandUpdate(std::move(patch));
 }
 
@@ -78,7 +88,12 @@
     }
   }
   // Send out an update request to the server, if needed.
-  SendCommandUpdate();
+
+  // Post to accumulate more changes during the current message loop task run.
+  task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(&CloudCommandProxy::SendCommandUpdate,
+                            backoff_weak_ptr_factory_.GetWeakPtr()),
+      {});
 }
 
 void CloudCommandProxy::SendCommandUpdate() {
@@ -124,10 +139,8 @@
   command_update_in_progress_ = true;
   cloud_command_updater_->UpdateCommand(
       command_instance_->GetID(), *update_queue_.front().second,
-      base::Bind(&CloudCommandProxy::OnUpdateCommandFinished,
-                 weak_ptr_factory_.GetWeakPtr(), true),
-      base::Bind(&CloudCommandProxy::OnUpdateCommandFinished,
-                 weak_ptr_factory_.GetWeakPtr(), false));
+      base::Bind(&CloudCommandProxy::OnUpdateCommandDone,
+                 weak_ptr_factory_.GetWeakPtr()));
 }
 
 void CloudCommandProxy::ResendCommandUpdate() {
@@ -135,10 +148,10 @@
   SendCommandUpdate();
 }
 
-void CloudCommandProxy::OnUpdateCommandFinished(bool success) {
+void CloudCommandProxy::OnUpdateCommandDone(ErrorPtr error) {
   command_update_in_progress_ = false;
-  cloud_backoff_entry_->InformOfRequest(success);
-  if (success) {
+  cloud_backoff_entry_->InformOfRequest(!error);
+  if (!error) {
     // Remove the succeeded update from the queue.
     update_queue_.pop_front();
   }
diff --git a/libweave/src/commands/cloud_command_proxy.h b/libweave/src/commands/cloud_command_proxy.h
index c5be02e..ee6358f 100644
--- a/libweave/src/commands/cloud_command_proxy.h
+++ b/libweave/src/commands/cloud_command_proxy.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,6 +17,7 @@
 
 #include "src/backoff_entry.h"
 #include "src/commands/cloud_command_update_interface.h"
+#include "src/commands/command_instance.h"
 #include "src/states/state_change_queue_interface.h"
 
 namespace weave {
@@ -28,7 +29,7 @@
 }
 
 // Command proxy which publishes command updates to the cloud.
-class CloudCommandProxy final : public Command::Observer {
+class CloudCommandProxy final : public CommandInstance::Observer {
  public:
   CloudCommandProxy(CommandInstance* command_instance,
                     CloudCommandUpdateInterface* cloud_command_updater,
@@ -38,10 +39,11 @@
   ~CloudCommandProxy() override = default;
 
   // CommandProxyInterface implementation/overloads.
-  void OnResultsChanged() override;
-  void OnStatusChanged() override;
-  void OnProgressChanged() override;
   void OnCommandDestroyed() override;
+  void OnErrorChanged() override;
+  void OnProgressChanged() override;
+  void OnResultsChanged() override;
+  void OnStateChanged() override;
 
  private:
   using UpdateID = StateChangeQueueInterface::UpdateID;
@@ -61,9 +63,7 @@
   void ResendCommandUpdate();
 
   // Callback invoked by the asynchronous PATCH request to the server.
-  // Called both in a case of successfully updating server command resource
-  // and in case of an error, indicated by the |success| parameter.
-  void OnUpdateCommandFinished(bool success);
+  void OnUpdateCommandDone(ErrorPtr error);
 
   // Callback invoked by the device state change queue to notify of the
   // successful device state update. |update_id| is the ID of the state that
@@ -93,7 +93,7 @@
   // successfully.
   UpdateID last_state_update_id_{0};
 
-  ScopedObserver<Command, Command::Observer> observer_{this};
+  ScopedObserver<CommandInstance, CommandInstance::Observer> observer_{this};
 
   base::WeakPtrFactory<CloudCommandProxy> backoff_weak_ptr_factory_{this};
   base::WeakPtrFactory<CloudCommandProxy> weak_ptr_factory_{this};
diff --git a/libweave/src/commands/cloud_command_proxy_unittest.cc b/libweave/src/commands/cloud_command_proxy_unittest.cc
index e1d96b0..0c04592 100644
--- a/libweave/src/commands/cloud_command_proxy_unittest.cc
+++ b/libweave/src/commands/cloud_command_proxy_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,11 +16,12 @@
 #include "src/commands/unittest_utils.h"
 #include "src/states/mock_state_change_queue_interface.h"
 
-using testing::SaveArg;
+using testing::_;
+using testing::DoAll;
 using testing::Invoke;
 using testing::Return;
 using testing::ReturnPointee;
-using testing::_;
+using testing::SaveArg;
 
 namespace weave {
 
@@ -37,11 +38,10 @@
 
 class MockCloudCommandUpdateInterface : public CloudCommandUpdateInterface {
  public:
-  MOCK_METHOD4(UpdateCommand,
+  MOCK_METHOD3(UpdateCommand,
                void(const std::string&,
                     const base::DictionaryValue&,
-                    const base::Closure&,
-                    const base::Closure&));
+                    const DoneCallback&));
 };
 
 // Test back-off entry that uses the test clock.
@@ -112,7 +112,7 @@
     CHECK(command_json.get());
 
     command_instance_ =
-        CommandInstance::FromJson(command_json.get(), CommandOrigin::kCloud,
+        CommandInstance::FromJson(command_json.get(), Command::Origin::kCloud,
                                   command_dictionary_, nullptr, nullptr);
     CHECK(command_instance_.get());
 
@@ -146,20 +146,21 @@
 
 TEST_F(CloudCommandProxyTest, ImmediateUpdate) {
   const char expected[] = "{'state':'done'}";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _, _));
-  command_instance_->Done();
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _));
+  command_instance_->Complete({}, nullptr);
+  task_runner_.RunOnce();
 }
 
 TEST_F(CloudCommandProxyTest, DelayedUpdate) {
   // Simulate that the current device state has changed.
   current_state_update_id_ = 20;
   // No command update is expected here.
-  command_instance_->Done();
+  command_instance_->Complete({}, nullptr);
   // Still no command update here...
   callbacks_.Notify(19);
   // Now we should get the update...
   const char expected[] = "{'state':'done'}";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _, _));
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _));
   callbacks_.Notify(20);
 }
 
@@ -168,18 +169,18 @@
   //    state=inProgress
   //    progress={...}
   // The first state update is sent immediately, the second should be delayed.
-  base::Closure on_success;
-  EXPECT_CALL(cloud_updater_,
-              UpdateCommand(kCmdID, MatchJson("{'state':'inProgress'}"), _, _))
-      .WillOnce(SaveArg<2>(&on_success));
+  DoneCallback callback;
+  EXPECT_CALL(
+      cloud_updater_,
+      UpdateCommand(
+          kCmdID,
+          MatchJson("{'state':'inProgress', 'progress':{'status':'ready'}}"),
+          _))
+      .WillOnce(SaveArg<2>(&callback));
   EXPECT_TRUE(command_instance_->SetProgress(
       *CreateDictionaryValue("{'status': 'ready'}"), nullptr));
 
-  // Now simulate the first request completing.
-  // The second request should be sent now.
-  const char expected[] = "{'progress':{'status':'ready'}}";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _, _));
-  on_success.Run();
+  task_runner_.RunOnce();
 }
 
 TEST_F(CloudCommandProxyTest, CombineMultiple) {
@@ -198,45 +199,38 @@
     'progress': {'status':'ready'},
     'state':'inProgress'
   })";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _, _));
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _));
   callbacks_.Notify(20);
 }
 
 TEST_F(CloudCommandProxyTest, RetryFailed) {
-  base::Closure on_error;
-  const char expect1[] = "{'state':'inProgress'}";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect1), _, _))
-      .WillOnce(SaveArg<3>(&on_error));
+  DoneCallback callback;
+
+  const char expect[] =
+      "{'state':'inProgress', 'progress': {'status': 'ready'}}";
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect), _))
+      .Times(3)
+      .WillRepeatedly(SaveArg<2>(&callback));
+  auto started = task_runner_.GetClock()->Now();
   EXPECT_TRUE(command_instance_->SetProgress(
       *CreateDictionaryValue("{'status': 'ready'}"), nullptr));
+  task_runner_.Run();
+  ErrorPtr error;
+  Error::AddTo(&error, FROM_HERE, "TEST", "TEST", "TEST");
+  callback.Run(error->Clone());
+  task_runner_.Run();
+  EXPECT_GE(task_runner_.GetClock()->Now() - started,
+            base::TimeDelta::FromSecondsD(0.9));
 
-  // Now pretend the first command update request has failed.
-  // We should retry with both state and progress fields updated this time,
-  // after the initial backoff (which should be 1s in our case).
-  base::TimeDelta expected_delay = base::TimeDelta::FromSeconds(1);
-  on_error.Run();
+  callback.Run(error->Clone());
+  task_runner_.Run();
+  EXPECT_GE(task_runner_.GetClock()->Now() - started,
+            base::TimeDelta::FromSecondsD(2.9));
 
-  // Execute the delayed request. But pretend that it failed too.
-  const char expect2[] = R"({
-    'progress': {'status':'ready'},
-    'state':'inProgress'
-  })";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect2), _, _))
-      .WillOnce(SaveArg<3>(&on_error));
-  task_runner_.RunOnce();
-
-  // Now backoff should be 2 seconds.
-  expected_delay = base::TimeDelta::FromSeconds(2);
-  on_error.Run();
-
-  // Retry the task.
-  base::Closure on_success;
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect2), _, _))
-      .WillOnce(SaveArg<2>(&on_success));
-  task_runner_.RunOnce();
-
-  // Pretend it succeeds this time.
-  on_success.Run();
+  callback.Run(nullptr);
+  task_runner_.Run();
+  EXPECT_GE(task_runner_.GetClock()->Now() - started,
+            base::TimeDelta::FromSecondsD(2.9));
 }
 
 TEST_F(CloudCommandProxyTest, GateOnStateUpdates) {
@@ -247,23 +241,23 @@
   EXPECT_TRUE(command_instance_->SetProgress(
       *CreateDictionaryValue("{'status': 'busy'}"), nullptr));
   current_state_update_id_ = 22;
-  command_instance_->Done();
+  command_instance_->Complete({}, nullptr);
 
   // Device state #20 updated.
-  base::Closure on_success;
+  DoneCallback callback;
   const char expect1[] = R"({
     'progress': {'status':'ready'},
     'state':'inProgress'
   })";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect1), _, _))
-      .WillOnce(SaveArg<2>(&on_success));
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect1), _))
+      .WillOnce(SaveArg<2>(&callback));
   callbacks_.Notify(20);
-  on_success.Run();
+  callback.Run(nullptr);
 
   // Device state #21 updated.
   const char expect2[] = "{'progress': {'status':'busy'}}";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect2), _, _))
-      .WillOnce(SaveArg<2>(&on_success));
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect2), _))
+      .WillOnce(SaveArg<2>(&callback));
   callbacks_.Notify(21);
 
   // Device state #22 updated. Nothing happens here since the previous command
@@ -273,9 +267,9 @@
   // Now the command update is complete, send out the patch that happened after
   // the state #22 was updated.
   const char expect3[] = "{'state': 'done'}";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect3), _, _))
-      .WillOnce(SaveArg<2>(&on_success));
-  on_success.Run();
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect3), _))
+      .WillOnce(SaveArg<2>(&callback));
+  callback.Run(nullptr);
 }
 
 TEST_F(CloudCommandProxyTest, CombineSomeStates) {
@@ -286,25 +280,25 @@
   EXPECT_TRUE(command_instance_->SetProgress(
       *CreateDictionaryValue("{'status': 'busy'}"), nullptr));
   current_state_update_id_ = 22;
-  command_instance_->Done();
+  command_instance_->Complete({}, nullptr);
 
   // Device state 20-21 updated.
-  base::Closure on_success;
+  DoneCallback callback;
   const char expect1[] = R"({
     'progress': {'status':'busy'},
     'state':'inProgress'
   })";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect1), _, _))
-      .WillOnce(SaveArg<2>(&on_success));
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect1), _))
+      .WillOnce(SaveArg<2>(&callback));
   callbacks_.Notify(21);
-  on_success.Run();
+  callback.Run(nullptr);
 
   // Device state #22 updated.
   const char expect2[] = "{'state': 'done'}";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect2), _, _))
-      .WillOnce(SaveArg<2>(&on_success));
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expect2), _))
+      .WillOnce(SaveArg<2>(&callback));
   callbacks_.Notify(22);
-  on_success.Run();
+  callback.Run(nullptr);
 }
 
 TEST_F(CloudCommandProxyTest, CombineAllStates) {
@@ -315,14 +309,14 @@
   EXPECT_TRUE(command_instance_->SetProgress(
       *CreateDictionaryValue("{'status': 'busy'}"), nullptr));
   current_state_update_id_ = 22;
-  command_instance_->Done();
+  command_instance_->Complete({}, nullptr);
 
   // Device state 30 updated.
   const char expected[] = R"({
     'progress': {'status':'busy'},
     'state':'done'
   })";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _, _));
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _));
   callbacks_.Notify(30);
 }
 
@@ -334,16 +328,15 @@
       *CreateDictionaryValue("{'status': 'busy'}"), nullptr));
   EXPECT_TRUE(command_instance_->SetProgress(
       *CreateDictionaryValue("{'status': 'finished'}"), nullptr));
-  EXPECT_TRUE(command_instance_->SetResults(
-      *CreateDictionaryValue("{'sum': 30}"), nullptr));
-  command_instance_->Done();
+  EXPECT_TRUE(command_instance_->Complete(*CreateDictionaryValue("{'sum': 30}"),
+                                          nullptr));
 
   const char expected[] = R"({
     'progress': {'status':'finished'},
     'results': {'sum':30},
     'state':'done'
   })";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _, _));
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _));
   callbacks_.Notify(30);
 }
 
@@ -359,8 +352,9 @@
 
   // As soon as we change the command, the update to the server should be sent.
   const char expected[] = "{'state':'done'}";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _, _));
-  command_instance_->Done();
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _));
+  command_instance_->Complete({}, nullptr);
+  task_runner_.RunOnce();
 }
 
 TEST_F(CloudCommandProxyTest, NonEmptyStateChangeQueue) {
@@ -372,11 +366,11 @@
   CreateCommandInstance();
 
   // No command updates right now.
-  command_instance_->Done();
+  command_instance_->Complete({}, nullptr);
 
   // Only when the state #20 is published we should update the command
   const char expected[] = "{'state':'done'}";
-  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _, _));
+  EXPECT_CALL(cloud_updater_, UpdateCommand(kCmdID, MatchJson(expected), _));
   callbacks_.Notify(20);
 }
 
diff --git a/libweave/src/commands/cloud_command_update_interface.h b/libweave/src/commands/cloud_command_update_interface.h
index 5bd5c11..9538960 100644
--- a/libweave/src/commands/cloud_command_update_interface.h
+++ b/libweave/src/commands/cloud_command_update_interface.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,8 +18,7 @@
  public:
   virtual void UpdateCommand(const std::string& command_id,
                              const base::DictionaryValue& command_patch,
-                             const base::Closure& on_success,
-                             const base::Closure& on_error) = 0;
+                             const DoneCallback& callback) = 0;
 
  protected:
   virtual ~CloudCommandUpdateInterface() = default;
diff --git a/libweave/src/commands/command_definition.cc b/libweave/src/commands/command_definition.cc
index 21e186a..d7ebc83 100644
--- a/libweave/src/commands/command_definition.cc
+++ b/libweave/src/commands/command_definition.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/command_definition.h b/libweave/src/commands/command_definition.h
index 2c5dd77..3bcc07f 100644
--- a/libweave/src/commands/command_definition.h
+++ b/libweave/src/commands/command_definition.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/command_definition_unittest.cc b/libweave/src/commands/command_definition_unittest.cc
index dda2385..77b2754 100644
--- a/libweave/src/commands/command_definition_unittest.cc
+++ b/libweave/src/commands/command_definition_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/command_dictionary.cc b/libweave/src/commands/command_dictionary.cc
index f03e92d..053e7aa 100644
--- a/libweave/src/commands/command_dictionary.cc
+++ b/libweave/src/commands/command_dictionary.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/command_dictionary.h b/libweave/src/commands/command_dictionary.h
index 389d419..8d3d45c 100644
--- a/libweave/src/commands/command_dictionary.h
+++ b/libweave/src/commands/command_dictionary.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/command_dictionary_unittest.cc b/libweave/src/commands/command_dictionary_unittest.cc
index 531a561..819718f 100644
--- a/libweave/src/commands/command_dictionary_unittest.cc
+++ b/libweave/src/commands/command_dictionary_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/command_instance.cc b/libweave/src/commands/command_instance.cc
index a8891f7..f152d82 100644
--- a/libweave/src/commands/command_instance.cc
+++ b/libweave/src/commands/command_instance.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -16,39 +16,57 @@
 #include "src/commands/schema_constants.h"
 #include "src/commands/schema_utils.h"
 #include "src/json_error_codes.h"
+#include "src/utils.h"
 
 namespace weave {
 
 namespace {
 
-const EnumToStringMap<CommandStatus>::Map kMapStatus[] = {
-    {CommandStatus::kQueued, "queued"},
-    {CommandStatus::kInProgress, "inProgress"},
-    {CommandStatus::kPaused, "paused"},
-    {CommandStatus::kError, "error"},
-    {CommandStatus::kDone, "done"},
-    {CommandStatus::kCancelled, "cancelled"},
-    {CommandStatus::kAborted, "aborted"},
-    {CommandStatus::kExpired, "expired"},
+const EnumToStringMap<Command::State>::Map kMapStatus[] = {
+    {Command::State::kQueued, "queued"},
+    {Command::State::kInProgress, "inProgress"},
+    {Command::State::kPaused, "paused"},
+    {Command::State::kError, "error"},
+    {Command::State::kDone, "done"},
+    {Command::State::kCancelled, "cancelled"},
+    {Command::State::kAborted, "aborted"},
+    {Command::State::kExpired, "expired"},
 };
 
-const EnumToStringMap<CommandOrigin>::Map kMapOrigin[] = {
-    {CommandOrigin::kLocal, "local"},
-    {CommandOrigin::kCloud, "cloud"},
+const EnumToStringMap<Command::Origin>::Map kMapOrigin[] = {
+    {Command::Origin::kLocal, "local"},
+    {Command::Origin::kCloud, "cloud"},
 };
 
+bool ReportDestroyedError(ErrorPtr* error) {
+  Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
+               errors::commands::kCommandDestroyed,
+               "Command has been destroyed");
+  return false;
+}
+
+bool ReportInvalidStateTransition(ErrorPtr* error,
+                                  Command::State from,
+                                  Command::State to) {
+  Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                     errors::commands::kInvalidState,
+                     "State switch impossible: '%s' -> '%s'",
+                     EnumToString(from).c_str(), EnumToString(to).c_str());
+  return false;
+}
+
 }  // namespace
 
 template <>
-LIBWEAVE_EXPORT EnumToStringMap<CommandStatus>::EnumToStringMap()
+LIBWEAVE_EXPORT EnumToStringMap<Command::State>::EnumToStringMap()
     : EnumToStringMap(kMapStatus) {}
 
 template <>
-LIBWEAVE_EXPORT EnumToStringMap<CommandOrigin>::EnumToStringMap()
+LIBWEAVE_EXPORT EnumToStringMap<Command::Origin>::EnumToStringMap()
     : EnumToStringMap(kMapOrigin) {}
 
 CommandInstance::CommandInstance(const std::string& name,
-                                 CommandOrigin origin,
+                                 Command::Origin origin,
                                  const CommandDefinition* command_definition,
                                  const ValueMap& parameters)
     : name_{name},
@@ -70,11 +88,11 @@
   return name_;
 }
 
-CommandStatus CommandInstance::GetStatus() const {
-  return status_;
+Command::State CommandInstance::GetState() const {
+  return state_;
 }
 
-CommandOrigin CommandInstance::GetOrigin() const {
+Command::Origin CommandInstance::GetOrigin() const {
   return origin_;
 }
 
@@ -90,8 +108,14 @@
   return TypedValueToJson(results_);
 }
 
+const Error* CommandInstance::GetError() const {
+  return error_.get();
+}
+
 bool CommandInstance::SetProgress(const base::DictionaryValue& progress,
                                   ErrorPtr* error) {
+  if (!command_definition_)
+    return ReportDestroyedError(error);
   ObjectPropType obj_prop_type;
   obj_prop_type.SetObjectSchema(command_definition_->GetProgress()->Clone());
 
@@ -100,16 +124,21 @@
     return false;
 
   // Change status even if progress unchanged, e.g. 0% -> 0%.
-  SetStatus(CommandStatus::kInProgress);
+  if (!SetStatus(State::kInProgress, error))
+    return false;
+
   if (obj != progress_) {
     progress_ = obj;
     FOR_EACH_OBSERVER(Observer, observers_, OnProgressChanged());
   }
+
   return true;
 }
 
-bool CommandInstance::SetResults(const base::DictionaryValue& results,
-                                 ErrorPtr* error) {
+bool CommandInstance::Complete(const base::DictionaryValue& results,
+                               ErrorPtr* error) {
+  if (!command_definition_)
+    return ReportDestroyedError(error);
   ObjectPropType obj_prop_type;
   obj_prop_type.SetObjectSchema(command_definition_->GetResults()->Clone());
 
@@ -121,7 +150,17 @@
     results_ = obj;
     FOR_EACH_OBSERVER(Observer, observers_, OnResultsChanged());
   }
-  return true;
+  // Change status even if result is unchanged.
+  bool result = SetStatus(State::kDone, error);
+  RemoveFromQueue();
+  // The command will be destroyed after that, so do not access any members.
+  return result;
+}
+
+bool CommandInstance::SetError(const Error* command_error, ErrorPtr* error) {
+  error_ = command_error ? command_error->Clone() : nullptr;
+  FOR_EACH_OBSERVER(Observer, observers_, OnErrorChanged());
+  return SetStatus(State::kError, error);
 }
 
 namespace {
@@ -168,7 +207,7 @@
 
 std::unique_ptr<CommandInstance> CommandInstance::FromJson(
     const base::Value* value,
-    CommandOrigin origin,
+    Command::Origin origin,
     const CommandDictionary& dictionary,
     std::string* command_id,
     ErrorPtr* error) {
@@ -235,7 +274,11 @@
             TypedValueToJson(progress_).release());
   json->Set(commands::attributes::kCommand_Results,
             TypedValueToJson(results_).release());
-  json->SetString(commands::attributes::kCommand_State, EnumToString(status_));
+  json->SetString(commands::attributes::kCommand_State, EnumToString(state_));
+  if (error_) {
+    json->Set(commands::attributes::kCommand_Error,
+              ErrorInfoToJson(*error_).release());
+  }
 
   return json;
 }
@@ -248,29 +291,46 @@
   observers_.RemoveObserver(observer);
 }
 
-void CommandInstance::Abort() {
-  SetStatus(CommandStatus::kAborted);
-  RemoveFromQueue();
-  // The command will be destroyed after that, so do not access any members.
+bool CommandInstance::Pause(ErrorPtr* error) {
+  return SetStatus(State::kPaused, error);
 }
 
-void CommandInstance::Cancel() {
-  SetStatus(CommandStatus::kCancelled);
+bool CommandInstance::Abort(const Error* command_error, ErrorPtr* error) {
+  error_ = command_error ? command_error->Clone() : nullptr;
+  FOR_EACH_OBSERVER(Observer, observers_, OnErrorChanged());
+  bool result = SetStatus(State::kAborted, error);
   RemoveFromQueue();
   // The command will be destroyed after that, so do not access any members.
+  return result;
 }
 
-void CommandInstance::Done() {
-  SetStatus(CommandStatus::kDone);
+bool CommandInstance::Cancel(ErrorPtr* error) {
+  bool result = SetStatus(State::kCancelled, error);
   RemoveFromQueue();
   // The command will be destroyed after that, so do not access any members.
+  return result;
 }
 
-void CommandInstance::SetStatus(CommandStatus status) {
-  if (status != status_) {
-    status_ = status;
-    FOR_EACH_OBSERVER(Observer, observers_, OnStatusChanged());
+bool CommandInstance::SetStatus(Command::State status, ErrorPtr* error) {
+  if (status == state_)
+    return true;
+  if (status == State::kQueued)
+    return ReportInvalidStateTransition(error, state_, status);
+  switch (state_) {
+    case State::kDone:
+    case State::kCancelled:
+    case State::kAborted:
+    case State::kExpired:
+      return ReportInvalidStateTransition(error, state_, status);
+    case State::kQueued:
+    case State::kInProgress:
+    case State::kPaused:
+    case State::kError:
+      break;
   }
+  state_ = status;
+  FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
+  return true;
 }
 
 void CommandInstance::RemoveFromQueue() {
diff --git a/libweave/src/commands/command_instance.h b/libweave/src/commands/command_instance.h
index 8a54b05..5a2ebf7 100644
--- a/libweave/src/commands/command_instance.h
+++ b/libweave/src/commands/command_instance.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -31,33 +31,43 @@
 
 class CommandInstance final : public Command {
  public:
+  class Observer {
+   public:
+    virtual void OnCommandDestroyed() = 0;
+    virtual void OnErrorChanged() = 0;
+    virtual void OnProgressChanged() = 0;
+    virtual void OnResultsChanged() = 0;
+    virtual void OnStateChanged() = 0;
+
+   protected:
+    virtual ~Observer() = default;
+  };
+
   // Construct a command instance given the full command |name| which must
   // be in format "<package_name>.<command_name>", a command |category| and
   // a list of parameters and their values specified in |parameters|.
   CommandInstance(const std::string& name,
-                  CommandOrigin origin,
+                  Command::Origin origin,
                   const CommandDefinition* command_definition,
                   const ValueMap& parameters);
   ~CommandInstance() override;
 
   // Command overrides.
-  std::unique_ptr<base::DictionaryValue> ToJson() const override;
-  void AddObserver(Observer* observer) override;
-  void RemoveObserver(Observer* observer) override;
   const std::string& GetID() const override;
   const std::string& GetName() const override;
-  CommandStatus GetStatus() const override;
-  CommandOrigin GetOrigin() const override;
+  Command::State GetState() const override;
+  Command::Origin GetOrigin() const override;
   std::unique_ptr<base::DictionaryValue> GetParameters() const override;
   std::unique_ptr<base::DictionaryValue> GetProgress() const override;
   std::unique_ptr<base::DictionaryValue> GetResults() const override;
+  const Error* GetError() const override;
   bool SetProgress(const base::DictionaryValue& progress,
                    ErrorPtr* error) override;
-  bool SetResults(const base::DictionaryValue& results,
-                  ErrorPtr* error) override;
-  void Abort() override;
-  void Cancel() override;
-  void Done() override;
+  bool Complete(const base::DictionaryValue& results, ErrorPtr* error) override;
+  bool Pause(ErrorPtr* error) override;
+  bool SetError(const Error* command_error, ErrorPtr* error) override;
+  bool Abort(const Error* command_error, ErrorPtr* error) override;
+  bool Cancel(ErrorPtr* error) override;
 
   // Returns command definition.
   const CommandDefinition* GetCommandDefinition() const {
@@ -74,21 +84,32 @@
   // This is used to report parse failures back to the server.
   static std::unique_ptr<CommandInstance> FromJson(
       const base::Value* value,
-      CommandOrigin origin,
+      Command::Origin origin,
       const CommandDictionary& dictionary,
       std::string* command_id,
       ErrorPtr* error);
 
+  std::unique_ptr<base::DictionaryValue> ToJson() const;
+
   // Sets the command ID (normally done by CommandQueue when the command
   // instance is added to it).
   void SetID(const std::string& id) { id_ = id; }
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
   // Sets the pointer to queue this command is part of.
-  void SetCommandQueue(CommandQueue* queue) { queue_ = queue; }
+  void AttachToQueue(CommandQueue* queue) { queue_ = queue; }
+  void DetachFromQueue() {
+    observers_.Clear();
+    queue_ = nullptr;
+    command_definition_ = nullptr;
+  }
 
  private:
   // Helper function to update the command status.
   // Used by Abort(), Cancel(), Done() methods.
-  void SetStatus(CommandStatus status);
+  bool SetStatus(Command::State status, ErrorPtr* error);
   // Helper method that removes this command from the command queue.
   // Note that since the command queue owns the lifetime of the command instance
   // object, removing a command from the queue will also destroy it.
@@ -99,17 +120,19 @@
   // Full command name as "<package_name>.<command_name>".
   std::string name_;
   // The origin of the command, either "local" or "cloud".
-  CommandOrigin origin_ = CommandOrigin::kLocal;
+  Command::Origin origin_ = Command::Origin::kLocal;
   // Command definition.
-  const CommandDefinition* command_definition_;
+  const CommandDefinition* command_definition_{nullptr};
   // Command parameters and their values.
   ValueMap parameters_;
   // Current command execution progress.
   ValueMap progress_;
   // Command results.
   ValueMap results_;
-  // Current command status.
-  CommandStatus status_ = CommandStatus::kQueued;
+  // Current command state.
+  Command::State state_ = Command::State::kQueued;
+  // Error encountered during execution of the command.
+  ErrorPtr error_;
   // Command observers.
   base::ObserverList<Observer> observers_;
   // Pointer to the command queue this command instance is added to.
diff --git a/libweave/src/commands/command_instance_unittest.cc b/libweave/src/commands/command_instance_unittest.cc
index 865ba6e..4e208ed 100644
--- a/libweave/src/commands/command_instance_unittest.cc
+++ b/libweave/src/commands/command_instance_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -76,29 +76,29 @@
   params["phrase"] =
       str_prop.CreateValue(base::StringValue{"iPityDaFool"}, nullptr);
   params["volume"] = int_prop.CreateValue(base::FundamentalValue{5}, nullptr);
-  CommandInstance instance{"robot.speak", CommandOrigin::kCloud,
+  CommandInstance instance{"robot.speak", Command::Origin::kCloud,
                            dict_.FindCommand("robot.speak"), params};
 
   EXPECT_TRUE(
-      instance.SetResults(*CreateDictionaryValue("{'foo': 239}"), nullptr));
+      instance.Complete(*CreateDictionaryValue("{'foo': 239}"), nullptr));
 
   EXPECT_EQ("", instance.GetID());
   EXPECT_EQ("robot.speak", instance.GetName());
-  EXPECT_EQ(CommandOrigin::kCloud, instance.GetOrigin());
+  EXPECT_EQ(Command::Origin::kCloud, instance.GetOrigin());
   EXPECT_JSON_EQ("{'phrase': 'iPityDaFool', 'volume': 5}",
                  *instance.GetParameters());
   EXPECT_JSON_EQ("{'foo': 239}", *instance.GetResults());
 
   CommandInstance instance2{"base.reboot",
-                            CommandOrigin::kLocal,
+                            Command::Origin::kLocal,
                             dict_.FindCommand("base.reboot"),
                             {}};
-  EXPECT_EQ(CommandOrigin::kLocal, instance2.GetOrigin());
+  EXPECT_EQ(Command::Origin::kLocal, instance2.GetOrigin());
 }
 
 TEST_F(CommandInstanceTest, SetID) {
   CommandInstance instance{"base.reboot",
-                           CommandOrigin::kLocal,
+                           Command::Origin::kLocal,
                            dict_.FindCommand("base.reboot"),
                            {}};
   instance.SetID("command_id");
@@ -116,7 +116,7 @@
     'results': {}
   })");
   std::string id;
-  auto instance = CommandInstance::FromJson(json.get(), CommandOrigin::kCloud,
+  auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
                                             dict_, &id, nullptr);
   EXPECT_EQ("abcd", id);
   EXPECT_EQ("abcd", instance->GetID());
@@ -127,7 +127,7 @@
 
 TEST_F(CommandInstanceTest, FromJson_ParamsOmitted) {
   auto json = CreateDictionaryValue("{'name': 'base.reboot'}");
-  auto instance = CommandInstance::FromJson(json.get(), CommandOrigin::kCloud,
+  auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
                                             dict_, nullptr, nullptr);
   EXPECT_EQ("base.reboot", instance->GetName());
   EXPECT_JSON_EQ("{}", *instance->GetParameters());
@@ -136,7 +136,7 @@
 TEST_F(CommandInstanceTest, FromJson_NotObject) {
   auto json = CreateValue("'string'");
   ErrorPtr error;
-  auto instance = CommandInstance::FromJson(json.get(), CommandOrigin::kCloud,
+  auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
                                             dict_, nullptr, &error);
   EXPECT_EQ(nullptr, instance.get());
   EXPECT_EQ("json_object_expected", error->GetCode());
@@ -145,7 +145,7 @@
 TEST_F(CommandInstanceTest, FromJson_NameMissing) {
   auto json = CreateDictionaryValue("{'param': 'value'}");
   ErrorPtr error;
-  auto instance = CommandInstance::FromJson(json.get(), CommandOrigin::kCloud,
+  auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
                                             dict_, nullptr, &error);
   EXPECT_EQ(nullptr, instance.get());
   EXPECT_EQ("parameter_missing", error->GetCode());
@@ -154,7 +154,7 @@
 TEST_F(CommandInstanceTest, FromJson_UnknownCommand) {
   auto json = CreateDictionaryValue("{'name': 'robot.scream'}");
   ErrorPtr error;
-  auto instance = CommandInstance::FromJson(json.get(), CommandOrigin::kCloud,
+  auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
                                             dict_, nullptr, &error);
   EXPECT_EQ(nullptr, instance.get());
   EXPECT_EQ("invalid_command_name", error->GetCode());
@@ -166,7 +166,7 @@
     'parameters': 'hello'
   })");
   ErrorPtr error;
-  auto instance = CommandInstance::FromJson(json.get(), CommandOrigin::kCloud,
+  auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
                                             dict_, nullptr, &error);
   EXPECT_EQ(nullptr, instance.get());
   auto inner = error->GetInnerError();
@@ -183,7 +183,7 @@
     }
   })");
   ErrorPtr error;
-  auto instance = CommandInstance::FromJson(json.get(), CommandOrigin::kCloud,
+  auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
                                             dict_, nullptr, &error);
   EXPECT_EQ(nullptr, instance.get());
   auto first = error->GetFirstError();
@@ -202,20 +202,20 @@
     },
     'results': {}
   })");
-  auto instance = CommandInstance::FromJson(json.get(), CommandOrigin::kCloud,
+  auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
                                             dict_, nullptr, nullptr);
   EXPECT_TRUE(instance->SetProgress(*CreateDictionaryValue("{'progress': 15}"),
                                     nullptr));
   EXPECT_TRUE(instance->SetProgress(*CreateDictionaryValue("{'progress': 15}"),
                                     nullptr));
   instance->SetID("testId");
-  EXPECT_TRUE(instance->SetResults(*CreateDictionaryValue("{'testResult': 17}"),
-                                   nullptr));
+  EXPECT_TRUE(instance->Complete(*CreateDictionaryValue("{'testResult': 17}"),
+                                 nullptr));
 
   json->MergeDictionary(CreateDictionaryValue(R"({
     'id': 'testId',
     'progress': {'progress': 15},
-    'state': 'inProgress',
+    'state': 'done',
     'results': {'testResult': 17}
   })").get());
 
@@ -225,4 +225,31 @@
                *json, *converted);
 }
 
+TEST_F(CommandInstanceTest, ToJsonError) {
+  auto json = CreateDictionaryValue(R"({
+    'name': 'base.reboot',
+    'parameters': {}
+  })");
+  auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
+                                            dict_, nullptr, nullptr);
+  instance->SetID("testId");
+
+  ErrorPtr error;
+  Error::AddTo(&error, FROM_HERE, "DOMAIN", "CODE", "MESSAGE");
+  instance->Abort(error.get(), nullptr);
+
+  json->MergeDictionary(CreateDictionaryValue(R"({
+    'id': 'testId',
+    'state': 'aborted',
+    'progress': {},
+    'results': {},
+    'error': {'code': 'CODE', 'message': 'MESSAGE'}
+  })").get());
+
+  auto converted = instance->ToJson();
+  EXPECT_PRED2([](const base::Value& val1,
+                  const base::Value& val2) { return val1.Equals(&val2); },
+               *json, *converted);
+}
+
 }  // namespace weave
diff --git a/libweave/src/commands/command_manager.cc b/libweave/src/commands/command_manager.cc
index 60b3d1c..70e804b 100644
--- a/libweave/src/commands/command_manager.cc
+++ b/libweave/src/commands/command_manager.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,7 +7,6 @@
 #include <base/values.h>
 #include <weave/enum_to_string.h>
 #include <weave/error.h>
-#include <weave/provider/config_store.h>
 
 #include "src/commands/schema_constants.h"
 #include "src/utils.h"
@@ -93,15 +92,11 @@
   return LoadCommands(*dict, error);
 }
 
-void CommandManager::Startup(provider::ConfigStore* config_store) {
+void CommandManager::Startup() {
   LOG(INFO) << "Initializing CommandManager.";
 
   // Load global standard GCD command dictionary.
   CHECK(LoadBaseCommands(kBaseCommandDefs, nullptr));
-
-  // Loading the rest of commands.
-  for (const auto& defs : config_store->LoadCommandDefs())
-    CHECK(this->LoadCommands(defs, nullptr));
 }
 
 void CommandManager::AddCommand(
@@ -119,8 +114,9 @@
                                 UserRole role,
                                 std::string* id,
                                 ErrorPtr* error) {
-  auto command_instance = CommandInstance::FromJson(
-      &command, CommandOrigin::kLocal, GetCommandDictionary(), nullptr, error);
+  auto command_instance =
+      CommandInstance::FromJson(&command, Command::Origin::kLocal,
+                                GetCommandDictionary(), nullptr, error);
   if (!command_instance)
     return false;
 
@@ -176,13 +172,21 @@
 }
 
 void CommandManager::AddCommandAddedCallback(
-    const Device::CommandCallback& callback) {
+    const CommandQueue::CommandCallback& callback) {
   command_queue_.AddCommandAddedCallback(callback);
 }
 
 void CommandManager::AddCommandRemovedCallback(
-    const Device::CommandCallback& callback) {
+    const CommandQueue::CommandCallback& callback) {
   command_queue_.AddCommandRemovedCallback(callback);
 }
 
+void CommandManager::AddCommandHandler(
+    const std::string& command_name,
+    const Device::CommandHandlerCallback& callback) {
+  CHECK(command_name.empty() || dictionary_.FindCommand(command_name))
+      << "Command undefined: " << command_name;
+  command_queue_.AddCommandHandler(command_name, callback);
+}
+
 }  // namespace weave
diff --git a/libweave/src/commands/command_manager.h b/libweave/src/commands/command_manager.h
index 605f770..e2b212e 100644
--- a/libweave/src/commands/command_manager.h
+++ b/libweave/src/commands/command_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -20,10 +20,6 @@
 
 class CommandInstance;
 
-namespace provider {
-class ConfigStore;
-}
-
 // CommandManager class that will have a list of all the device command
 // schemas as well as the live command queue of pending command instances
 // dispatched to the device.
@@ -37,8 +33,10 @@
                   std::string* id,
                   ErrorPtr* error);
   CommandInstance* FindCommand(const std::string& id);
-  void AddCommandAddedCallback(const Device::CommandCallback& callback);
-  void AddCommandRemovedCallback(const Device::CommandCallback& callback);
+  void AddCommandAddedCallback(const CommandQueue::CommandCallback& callback);
+  void AddCommandRemovedCallback(const CommandQueue::CommandCallback& callback);
+  void AddCommandHandler(const std::string& command_name,
+                         const Device::CommandHandlerCallback& callback);
 
   // Sets callback which is called when command definitions is changed.
   void AddCommandDefChanged(const base::Closure& callback);
@@ -69,10 +67,8 @@
                     ErrorPtr* error);
 
   // Startup method to be called by buffet daemon at startup.
-  // Initializes the object and loads:
-  //   1) the standard GCD command dictionary
-  //   2) static vendor-provided command definitions
-  void Startup(weave::provider::ConfigStore* config_store);
+  // Initializes standard GCD command dictionary.
+  void Startup();
 
   // Adds a new command to the command queue.
   void AddCommand(std::unique_ptr<CommandInstance> command_instance);
diff --git a/libweave/src/commands/command_manager_unittest.cc b/libweave/src/commands/command_manager_unittest.cc
index 3e0d383..aacf30c 100644
--- a/libweave/src/commands/command_manager_unittest.cc
+++ b/libweave/src/commands/command_manager_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -115,11 +115,9 @@
 
 TEST(CommandManager, ShouldLoadStandardAndTestDefinitions) {
   CommandManager manager;
-  provider::test::MockConfigStore config_store;
-  EXPECT_CALL(config_store, LoadCommandDefs())
-      .WillOnce(Return(
-          std::vector<std::string>{kTestVendorCommands, kTestTestCommands}));
-  manager.Startup(&config_store);
+  manager.Startup();
+  ASSERT_TRUE(manager.LoadCommands(kTestVendorCommands, nullptr));
+  ASSERT_TRUE(manager.LoadCommands(kTestTestCommands, nullptr));
   EXPECT_EQ(3, manager.GetCommandDictionary().GetSize());
   EXPECT_NE(nullptr, manager.GetCommandDictionary().FindCommand("robot._jump"));
   EXPECT_NE(nullptr,
diff --git a/libweave/src/commands/command_queue.cc b/libweave/src/commands/command_queue.cc
index e580048..e08527b 100644
--- a/libweave/src/commands/command_queue.cc
+++ b/libweave/src/commands/command_queue.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -13,28 +13,65 @@
 const int kRemoveCommandDelayMin = 5;
 }
 
-void CommandQueue::AddCommandAddedCallback(
-    const Device::CommandCallback& callback) {
+void CommandQueue::AddCommandAddedCallback(const CommandCallback& callback) {
   on_command_added_.push_back(callback);
   // Send all pre-existed commands.
   for (const auto& command : map_)
     callback.Run(command.second.get());
 }
 
-void CommandQueue::AddCommandRemovedCallback(
-    const Device::CommandCallback& callback) {
+void CommandQueue::AddCommandRemovedCallback(const CommandCallback& callback) {
   on_command_removed_.push_back(callback);
 }
 
+void CommandQueue::AddCommandHandler(
+    const std::string& command_name,
+    const Device::CommandHandlerCallback& callback) {
+  if (!command_name.empty()) {
+    CHECK(default_command_callback_.is_null())
+        << "Commands specific handler are not allowed after default one";
+
+    for (const auto& command : map_) {
+      if (command.second->GetState() == Command::State::kQueued &&
+          command.second->GetName() == command_name) {
+        callback.Run(command.second);
+      }
+    }
+
+    CHECK(command_callbacks_.emplace(command_name, callback).second)
+        << command_name << " already has handler";
+
+  } else {
+    for (const auto& command : map_) {
+      if (command.second->GetState() == Command::State::kQueued &&
+          command_callbacks_.find(command.second->GetName()) ==
+              command_callbacks_.end()) {
+        callback.Run(command.second);
+      }
+    }
+
+    CHECK(default_command_callback_.is_null()) << "Already has default handler";
+    default_command_callback_ = callback;
+  }
+}
+
 void CommandQueue::Add(std::unique_ptr<CommandInstance> instance) {
   std::string id = instance->GetID();
   LOG_IF(FATAL, id.empty()) << "Command has no ID";
-  instance->SetCommandQueue(this);
+  instance->AttachToQueue(this);
   auto pair = map_.insert(std::make_pair(id, std::move(instance)));
   LOG_IF(FATAL, !pair.second) << "Command with ID '" << id
                               << "' is already in the queue";
   for (const auto& cb : on_command_added_)
     cb.Run(pair.first->second.get());
+
+  auto it_handler = command_callbacks_.find(pair.first->second->GetName());
+
+  if (it_handler != command_callbacks_.end())
+    it_handler->second.Run(pair.first->second);
+  else if (!default_command_callback_.is_null())
+    default_command_callback_.Run(pair.first->second);
+
   Cleanup();
 }
 
@@ -52,8 +89,8 @@
   auto p = map_.find(id);
   if (p == map_.end())
     return false;
-  std::unique_ptr<CommandInstance> instance{std::move(p->second)};
-  instance->SetCommandQueue(nullptr);
+  std::shared_ptr<CommandInstance> instance = p->second;
+  instance->DetachFromQueue();
   map_.erase(p);
   for (const auto& cb : on_command_removed_)
     cb.Run(instance.get());
diff --git a/libweave/src/commands/command_queue.h b/libweave/src/commands/command_queue.h
index c3e86d5..74ed86f 100644
--- a/libweave/src/commands/command_queue.h
+++ b/libweave/src/commands/command_queue.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -25,11 +25,17 @@
  public:
   CommandQueue() = default;
 
+  // TODO: Remove AddCommandAddedCallback and AddCommandRemovedCallback.
+  using CommandCallback = base::Callback<void(Command* command)>;
+
   // Adds notifications callback for a new command is added to the queue.
-  void AddCommandAddedCallback(const Device::CommandCallback& callback);
+  void AddCommandAddedCallback(const CommandCallback& callback);
 
   // Adds notifications callback for a command is removed from the queue.
-  void AddCommandRemovedCallback(const Device::CommandCallback& callback);
+  void AddCommandRemovedCallback(const CommandCallback& callback);
+
+  void AddCommandHandler(const std::string& command_name,
+                         const Device::CommandHandlerCallback& callback);
 
   // Checks if the command queue is empty.
   bool IsEmpty() const { return map_.empty(); }
@@ -70,14 +76,16 @@
   base::Time test_now_;
 
   // ID-to-CommandInstance map.
-  std::map<std::string, std::unique_ptr<CommandInstance>> map_;
+  std::map<std::string, std::shared_ptr<CommandInstance>> map_;
 
   // Queue of commands to be removed.
   std::queue<std::pair<base::Time, std::string>> remove_queue_;
 
-  using CallbackList = std::vector<Device::CommandCallback>;
+  using CallbackList = std::vector<CommandCallback>;
   CallbackList on_command_added_;
   CallbackList on_command_removed_;
+  std::map<std::string, Device::CommandHandlerCallback> command_callbacks_;
+  Device::CommandHandlerCallback default_command_callback_;
 
   DISALLOW_COPY_AND_ASSIGN(CommandQueue);
 };
diff --git a/libweave/src/commands/command_queue_unittest.cc b/libweave/src/commands/command_queue_unittest.cc
index d119f74..5060071 100644
--- a/libweave/src/commands/command_queue_unittest.cc
+++ b/libweave/src/commands/command_queue_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,7 +24,7 @@
       const std::string& name,
       const std::string& id) {
     std::unique_ptr<CommandInstance> cmd{new CommandInstance{
-        name, CommandOrigin::kLocal, &command_definition_, {}}};
+        name, Command::Origin::kLocal, &command_definition_, {}}};
     cmd->SetID(id);
     return cmd;
   }
diff --git a/libweave/src/commands/object_schema.cc b/libweave/src/commands/object_schema.cc
index c43bd14..b70beff 100644
--- a/libweave/src/commands/object_schema.cc
+++ b/libweave/src/commands/object_schema.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/object_schema.h b/libweave/src/commands/object_schema.h
index c65d21e..504e1dd 100644
--- a/libweave/src/commands/object_schema.h
+++ b/libweave/src/commands/object_schema.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/object_schema_unittest.cc b/libweave/src/commands/object_schema_unittest.cc
index 4108aa8..6417952 100644
--- a/libweave/src/commands/object_schema_unittest.cc
+++ b/libweave/src/commands/object_schema_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/prop_constraints.cc b/libweave/src/commands/prop_constraints.cc
index bb52ba1..b7e9cf6 100644
--- a/libweave/src/commands/prop_constraints.cc
+++ b/libweave/src/commands/prop_constraints.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/prop_constraints.h b/libweave/src/commands/prop_constraints.h
index b2bdebe..53a4d93 100644
--- a/libweave/src/commands/prop_constraints.h
+++ b/libweave/src/commands/prop_constraints.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/prop_types.cc b/libweave/src/commands/prop_types.cc
index c06170a..88a53bd 100644
--- a/libweave/src/commands/prop_types.cc
+++ b/libweave/src/commands/prop_types.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/prop_types.h b/libweave/src/commands/prop_types.h
index 4b0b721..f784eb6 100644
--- a/libweave/src/commands/prop_types.h
+++ b/libweave/src/commands/prop_types.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/prop_values.cc b/libweave/src/commands/prop_values.cc
index 2e78ce5..6f30bee 100644
--- a/libweave/src/commands/prop_values.cc
+++ b/libweave/src/commands/prop_values.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/prop_values.h b/libweave/src/commands/prop_values.h
index 9f7ed72..f0a401e 100644
--- a/libweave/src/commands/prop_values.h
+++ b/libweave/src/commands/prop_values.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/schema_constants.cc b/libweave/src/commands/schema_constants.cc
index 9affe73..c99536b 100644
--- a/libweave/src/commands/schema_constants.cc
+++ b/libweave/src/commands/schema_constants.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -25,6 +25,8 @@
 const char kCommandFailed[] = "command_failed";
 const char kInvalidCommandVisibility[] = "invalid_command_visibility";
 const char kInvalidMinimalRole[] = "invalid_minimal_role";
+const char kCommandDestroyed[] = "command_destroyed";
+const char kInvalidState[] = "invalid_state";
 }  // namespace commands
 }  // namespace errors
 
@@ -56,6 +58,7 @@
 const char kCommand_Progress[] = "progress";
 const char kCommand_Results[] = "results";
 const char kCommand_State[] = "state";
+const char kCommand_Error[] = "error";
 
 const char kCommand_Role[] = "minimalRole";
 const char kCommand_Role_Manager[] = "manager";
@@ -63,9 +66,6 @@
 const char kCommand_Role_User[] = "user";
 const char kCommand_Role_Viewer[] = "viewer";
 
-const char kCommand_ErrorCode[] = "error.code";
-const char kCommand_ErrorMessage[] = "error.message";
-
 const char kCommand_Visibility[] = "visibility";
 const char kCommand_Visibility_None[] = "none";
 const char kCommand_Visibility_Local[] = "local";
diff --git a/libweave/src/commands/schema_constants.h b/libweave/src/commands/schema_constants.h
index 2d4ae07..742245f 100644
--- a/libweave/src/commands/schema_constants.h
+++ b/libweave/src/commands/schema_constants.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -28,6 +28,8 @@
 extern const char kCommandFailed[];
 extern const char kInvalidCommandVisibility[];
 extern const char kInvalidMinimalRole[];
+extern const char kCommandDestroyed[];
+extern const char kInvalidState[];
 }  // namespace commands
 }  // namespace errors
 
@@ -59,6 +61,7 @@
 extern const char kCommand_Progress[];
 extern const char kCommand_Results[];
 extern const char kCommand_State[];
+extern const char kCommand_Error[];
 
 extern const char kCommand_Role[];
 extern const char kCommand_Role_Manager[];
@@ -66,9 +69,6 @@
 extern const char kCommand_Role_User[];
 extern const char kCommand_Role_Viewer[];
 
-extern const char kCommand_ErrorCode[];
-extern const char kCommand_ErrorMessage[];
-
 extern const char kCommand_Visibility[];
 extern const char kCommand_Visibility_None[];
 extern const char kCommand_Visibility_Local[];
diff --git a/libweave/src/commands/schema_utils.cc b/libweave/src/commands/schema_utils.cc
index 1191264..3c3e949 100644
--- a/libweave/src/commands/schema_utils.cc
+++ b/libweave/src/commands/schema_utils.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/schema_utils.h b/libweave/src/commands/schema_utils.h
index 570fb42..826bab8 100644
--- a/libweave/src/commands/schema_utils.h
+++ b/libweave/src/commands/schema_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/schema_utils_unittest.cc b/libweave/src/commands/schema_utils_unittest.cc
index 09122f3..1ea4c90 100644
--- a/libweave/src/commands/schema_utils_unittest.cc
+++ b/libweave/src/commands/schema_utils_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/commands/unittest_utils.h b/libweave/src/commands/unittest_utils.h
index b1cde46..25392ee 100644
--- a/libweave/src/commands/unittest_utils.h
+++ b/libweave/src/commands/unittest_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/config.cc b/libweave/src/config.cc
index 4f59497..9a18bb0 100644
--- a/libweave/src/config.cc
+++ b/libweave/src/config.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -22,6 +22,8 @@
 
 namespace config_keys {
 
+const char kVersion[] = "version";
+
 const char kClientId[] = "client_id";
 const char kClientSecret[] = "client_secret";
 const char kApiKey[] = "api_key";
@@ -44,6 +46,17 @@
 
 namespace {
 
+const int kCurrentConfigVersion = 1;
+
+void MigrateFromV0(base::DictionaryValue* dict) {
+  std::string cloud_id;
+  if (dict->GetString(config_keys::kCloudId, &cloud_id) && !cloud_id.empty())
+    return;
+  scoped_ptr<base::Value> tmp;
+  if (dict->Remove(config_keys::kDeviceId, &tmp))
+    dict->Set(config_keys::kCloudId, std::move(tmp));
+}
+
 Config::Settings CreateDefaultSettings() {
   Config::Settings result;
   result.oauth_url = "https://accounts.google.com/o/oauth2/";
@@ -115,12 +128,25 @@
     return;
 
   auto value = base::JSONReader::Read(json_string);
-  const base::DictionaryValue* dict = nullptr;
+  base::DictionaryValue* dict = nullptr;
   if (!value || !value->GetAsDictionary(&dict)) {
     LOG(ERROR) << "Failed to parse settings.";
     return;
   }
 
+  int loaded_version = 0;
+  dict->GetInteger(config_keys::kVersion, &loaded_version);
+
+  if (loaded_version != kCurrentConfigVersion) {
+    LOG(INFO) << "State version mismatch. expected: " << kCurrentConfigVersion
+              << ", loaded: " << loaded_version;
+    save_ = true;
+  }
+
+  if (loaded_version == 0) {
+    MigrateFromV0(dict);
+  }
+
   std::string tmp;
   bool tmp_bool{false};
 
@@ -184,6 +210,8 @@
     return;
 
   base::DictionaryValue dict;
+  dict.SetInteger(config_keys::kVersion, kCurrentConfigVersion);
+
   dict.SetString(config_keys::kClientId, settings_.client_id);
   dict.SetString(config_keys::kClientSecret, settings_.client_secret);
   dict.SetString(config_keys::kApiKey, settings_.api_key);
diff --git a/libweave/src/config.h b/libweave/src/config.h
index bec99ea..f4c3465 100644
--- a/libweave/src/config.h
+++ b/libweave/src/config.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/config_unittest.cc b/libweave/src/config_unittest.cc
index f65f203..fbfd25f 100644
--- a/libweave/src/config_unittest.cc
+++ b/libweave/src/config_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -79,8 +79,35 @@
   EXPECT_EQ("", GetSettings().secret);
 }
 
+TEST_F(ConfigTest, LoadStateV0) {
+  EXPECT_CALL(config_store_, LoadSettings())
+      .WillOnce(Return(R"({
+    "device_id": "state_device_id"
+  })"));
+
+  EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
+  config_.Load();
+
+  EXPECT_EQ("state_device_id", GetSettings().cloud_id);
+  EXPECT_FALSE(GetSettings().device_id.empty());
+  EXPECT_NE(GetSettings().cloud_id, GetSettings().device_id);
+
+  EXPECT_CALL(config_store_, LoadSettings())
+      .WillOnce(Return(R"({
+    "device_id": "state_device_id",
+    "cloud_id": "state_cloud_id"
+  })"));
+
+  EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
+  config_.Load();
+
+  EXPECT_EQ("state_cloud_id", GetSettings().cloud_id);
+  EXPECT_EQ("state_device_id", GetSettings().device_id);
+}
+
 TEST_F(ConfigTest, LoadState) {
   auto state = R"({
+    "version": 1,
     "api_key": "state_api_key",
     "client_id": "state_client_id",
     "client_secret": "state_client_secret",
@@ -205,6 +232,7 @@
   EXPECT_CALL(config_store_, SaveSettings(_))
       .WillOnce(Invoke([](const std::string& json) {
         auto expected = R"({
+          'version': 1,
           'api_key': 'set_api_key',
           'client_id': 'set_client_id',
           'client_secret': 'set_client_secret',
diff --git a/libweave/src/data_encoding.cc b/libweave/src/data_encoding.cc
index d863b16..b8214aa 100644
--- a/libweave/src/data_encoding.cc
+++ b/libweave/src/data_encoding.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/data_encoding.h b/libweave/src/data_encoding.h
index f952fa1..c074778 100644
--- a/libweave/src/data_encoding.h
+++ b/libweave/src/data_encoding.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/data_encoding_unittest.cc b/libweave/src/data_encoding_unittest.cc
index ac981c3..7e3bfe4 100644
--- a/libweave/src/data_encoding_unittest.cc
+++ b/libweave/src/data_encoding_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2011 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/device_manager.cc b/libweave/src/device_manager.cc
index aedd425..a0c6199 100644
--- a/libweave/src/device_manager.cc
+++ b/libweave/src/device_manager.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -34,10 +34,10 @@
                              provider::Wifi* wifi,
                              provider::Bluetooth* bluetooth) {
   command_manager_ = std::make_shared<CommandManager>();
-  command_manager_->Startup(config_store);
+  command_manager_->Startup();
   state_change_queue_.reset(new StateChangeQueue(kMaxStateChangeQueueSize));
   state_manager_ = std::make_shared<StateManager>(state_change_queue_.get());
-  state_manager_->Startup(config_store);
+  state_manager_->Startup();
 
   std::unique_ptr<Config> config{new Config{config_store}};
   config->Load();
@@ -45,8 +45,7 @@
   device_info_.reset(new DeviceRegistrationInfo(
       command_manager_, state_manager_, std::move(config), task_runner,
       http_client, network));
-  base_api_handler_.reset(
-      new BaseApiHandler{device_info_.get(), state_manager_, command_manager_});
+  base_api_handler_.reset(new BaseApiHandler{device_info_.get(), this});
 
   device_info_->Start();
 
@@ -78,10 +77,9 @@
                                 provider::HttpServer* http_server,
                                 provider::Wifi* wifi,
                                 provider::Bluetooth* bluetooth) {
-  privet_.reset(new privet::Manager{});
-  privet_->Start(task_runner, network, dns_sd, http_server, wifi,
-                 device_info_.get(), command_manager_.get(),
-                 state_manager_.get());
+  privet_.reset(new privet::Manager{task_runner});
+  privet_->Start(network, dns_sd, http_server, wifi, device_info_.get(),
+                 command_manager_.get(), state_manager_.get());
 }
 
 GcdState DeviceManager::GetGcdState() const {
@@ -93,8 +91,12 @@
   device_info_->AddGcdStateChangedCallback(callback);
 }
 
-void DeviceManager::AddStateChangedCallback(const base::Closure& callback) {
-  state_manager_->AddChangedCallback(callback);
+void DeviceManager::AddCommandDefinitionsFromJson(const std::string& json) {
+  CHECK(command_manager_->LoadCommands(json, nullptr));
+}
+
+void DeviceManager::AddCommandDefinitions(const base::DictionaryValue& dict) {
+  CHECK(command_manager_->LoadCommands(dict, nullptr));
 }
 
 bool DeviceManager::AddCommand(const base::DictionaryValue& command,
@@ -107,18 +109,31 @@
   return command_manager_->FindCommand(id);
 }
 
-void DeviceManager::AddCommandAddedCallback(const CommandCallback& callback) {
-  return command_manager_->AddCommandAddedCallback(callback);
+void DeviceManager::AddCommandHandler(const std::string& command_name,
+                                      const CommandHandlerCallback& callback) {
+  return command_manager_->AddCommandHandler(command_name, callback);
 }
 
-void DeviceManager::AddCommandRemovedCallback(const CommandCallback& callback) {
-  return command_manager_->AddCommandRemovedCallback(callback);
+void DeviceManager::AddStateChangedCallback(const base::Closure& callback) {
+  state_manager_->AddChangedCallback(callback);
 }
 
-bool DeviceManager::SetStateProperties(
-    const base::DictionaryValue& property_set,
-    ErrorPtr* error) {
-  return state_manager_->SetProperties(property_set, error);
+void DeviceManager::AddStateDefinitionsFromJson(const std::string& json) {
+  CHECK(state_manager_->LoadStateDefinitionFromJson(json, nullptr));
+}
+
+void DeviceManager::AddStateDefinitions(const base::DictionaryValue& dict) {
+  CHECK(state_manager_->LoadStateDefinition(dict, nullptr));
+}
+
+bool DeviceManager::SetStatePropertiesFromJson(const std::string& json,
+                                               ErrorPtr* error) {
+  return state_manager_->SetPropertiesFromJson(json, error);
+}
+
+bool DeviceManager::SetStateProperties(const base::DictionaryValue& dict,
+                                       ErrorPtr* error) {
+  return state_manager_->SetProperties(dict, error);
 }
 
 std::unique_ptr<base::Value> DeviceManager::GetStateProperty(
@@ -136,9 +151,9 @@
   return state_manager_->GetState();
 }
 
-std::string DeviceManager::Register(const std::string& ticket_id,
-                                    ErrorPtr* error) {
-  return device_info_->RegisterDevice(ticket_id, error);
+void DeviceManager::Register(const std::string& ticket_id,
+                             const DoneCallback& callback) {
+  device_info_->RegisterDevice(ticket_id, callback);
 }
 
 void DeviceManager::AddPairingChangedCallbacks(
diff --git a/libweave/src/device_manager.h b/libweave/src/device_manager.h
index d28d0b0..ccf8778 100644
--- a/libweave/src/device_manager.h
+++ b/libweave/src/device_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -37,14 +37,20 @@
   const Settings& GetSettings() const override;
   void AddSettingsChangedCallback(
       const SettingsChangedCallback& callback) override;
+  void AddCommandDefinitionsFromJson(const std::string& json) override;
+  void AddCommandDefinitions(const base::DictionaryValue& dict) override;
   bool AddCommand(const base::DictionaryValue& command,
                   std::string* id,
                   ErrorPtr* error) override;
   Command* FindCommand(const std::string& id) override;
-  void AddCommandAddedCallback(const CommandCallback& callback) override;
-  void AddCommandRemovedCallback(const CommandCallback& callback) override;
+  void AddCommandHandler(const std::string& command_name,
+                         const CommandHandlerCallback& callback) override;
   void AddStateChangedCallback(const base::Closure& callback) override;
-  bool SetStateProperties(const base::DictionaryValue& property_set,
+  void AddStateDefinitionsFromJson(const std::string& json) override;
+  void AddStateDefinitions(const base::DictionaryValue& dict) override;
+  bool SetStatePropertiesFromJson(const std::string& json,
+                                  ErrorPtr* error) override;
+  bool SetStateProperties(const base::DictionaryValue& dict,
                           ErrorPtr* error) override;
   std::unique_ptr<base::Value> GetStateProperty(
       const std::string& name) const override;
@@ -52,7 +58,8 @@
                         const base::Value& value,
                         ErrorPtr* error) override;
   std::unique_ptr<base::DictionaryValue> GetState() const override;
-  std::string Register(const std::string& ticket_id, ErrorPtr* error) override;
+  void Register(const std::string& ticket_id,
+                const DoneCallback& callback) override;
   GcdState GetGcdState() const override;
   void AddGcdStateChangedCallback(
       const GcdStateChangedCallback& callback) override;
diff --git a/libweave/src/device_registration_info.cc b/libweave/src/device_registration_info.cc
index e9e267a..48361b9 100644
--- a/libweave/src/device_registration_info.cc
+++ b/libweave/src/device_registration_info.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -98,36 +98,49 @@
   return AppendQueryParams(result, params);
 }
 
-void IgnoreCloudError(const Error*) {}
-
-void IgnoreCloudErrorWithCallback(const base::Closure& cb, const Error*) {
+void IgnoreCloudErrorWithCallback(const base::Closure& cb, ErrorPtr) {
   cb.Run();
 }
 
-void IgnoreCloudResult(const base::DictionaryValue&) {}
+void IgnoreCloudError(ErrorPtr) {}
 
-void IgnoreCloudResultWithCallback(const base::Closure& cb,
-                                   const base::DictionaryValue&) {
-  cb.Run();
+void IgnoreCloudResult(const base::DictionaryValue&, ErrorPtr error) {}
+
+void IgnoreCloudResultWithCallback(const DoneCallback& cb,
+                                   const base::DictionaryValue&,
+                                   ErrorPtr error) {
+  cb.Run(std::move(error));
 }
 
 class RequestSender final {
  public:
-  RequestSender(const std::string& method,
+  RequestSender(HttpClient::Method method,
                 const std::string& url,
                 HttpClient* transport)
       : method_{method}, url_{url}, transport_{transport} {}
 
-  std::unique_ptr<provider::HttpClient::Response> SendAndBlock(
-      ErrorPtr* error) {
-    return transport_->SendRequestAndBlock(method_, url_, GetFullHeaders(),
-                                           data_, error);
-  }
-
-  int Send(const HttpClient::SuccessCallback& success_callback,
-           const HttpClient::ErrorCallback& error_callback) {
-    return transport_->SendRequest(method_, url_, GetFullHeaders(), data_,
-                                   success_callback, error_callback);
+  void Send(const HttpClient::SendRequestCallback& callback) {
+    static int debug_id = 0;
+    ++debug_id;
+    VLOG(1) << "Sending request. id:" << debug_id
+            << " method:" << EnumToString(method_) << " url:" << url_;
+    VLOG(2) << "Request data: " << data_;
+    auto on_done = [](
+        int debug_id, const HttpClient::SendRequestCallback& callback,
+        std::unique_ptr<HttpClient::Response> response, ErrorPtr error) {
+      if (error) {
+        VLOG(1) << "Request failed, id=" << debug_id
+                << ", reason: " << error->GetCode()
+                << ", message: " << error->GetMessage();
+        return callback.Run({}, std::move(error));
+      }
+      VLOG(1) << "Request succeeded. id:" << debug_id
+              << " status:" << response->GetStatusCode();
+      VLOG(2) << "Response data: " << response->GetData();
+      callback.Run(std::move(response), nullptr);
+    };
+    transport_->SendRequest(method_, url_, GetFullHeaders(), data_,
+                            base::Bind(on_done, debug_id, callback));
   }
 
   void SetAccessToken(const std::string& access_token) {
@@ -160,7 +173,7 @@
     return headers;
   }
 
-  std::string method_;
+  HttpClient::Method method_;
   std::string url_;
   std::string data_;
   std::string mime_type_;
@@ -289,7 +302,8 @@
     return;  // Assume we're in test
   task_runner_->PostDelayedTask(
       FROM_HERE,
-      base::Bind(&DeviceRegistrationInfo::ConnectToCloud, AsWeakPtr()), delay);
+      base::Bind(&DeviceRegistrationInfo::ConnectToCloud, AsWeakPtr(), nullptr),
+      delay);
 }
 
 bool DeviceRegistrationInfo::HaveRegistrationCredentials() const {
@@ -335,16 +349,12 @@
   return resp;
 }
 
-void DeviceRegistrationInfo::RefreshAccessToken(
-    const base::Closure& success_callback,
-    const ErrorCallback& error_callback) {
+void DeviceRegistrationInfo::RefreshAccessToken(const DoneCallback& callback) {
   LOG(INFO) << "Refreshing access token.";
 
   ErrorPtr error;
-  if (!VerifyRegistrationCredentials(&error)) {
-    error_callback.Run(error.get());
-    return;
-  }
+  if (!VerifyRegistrationCredentials(&error))
+    return callback.Run(std::move(error));
 
   if (oauth2_backoff_entry_->ShouldRejectRequest()) {
     VLOG(1) << "RefreshToken request delayed for "
@@ -352,50 +362,38 @@
             << " due to backoff policy";
     task_runner_->PostDelayedTask(
         FROM_HERE, base::Bind(&DeviceRegistrationInfo::RefreshAccessToken,
-                              AsWeakPtr(), success_callback, error_callback),
+                              AsWeakPtr(), callback),
         oauth2_backoff_entry_->GetTimeUntilRelease());
     return;
   }
 
-  // Make a shared pointer to |success_callback| and |error_callback| since we
-  // are going to share these callbacks with both success and error callbacks
-  // for PostFormData() and if the callbacks have any move-only types,
-  // one of the copies will be bad.
-  auto shared_success_callback =
-      std::make_shared<base::Closure>(success_callback);
-  auto shared_error_callback = std::make_shared<ErrorCallback>(error_callback);
-
-  RequestSender sender{http::kPost, GetOAuthURL("token"), http_client_};
+  RequestSender sender{HttpClient::Method::kPost, GetOAuthURL("token"),
+                       http_client_};
   sender.SetFormData({
       {"refresh_token", GetSettings().refresh_token},
       {"client_id", GetSettings().client_id},
       {"client_secret", GetSettings().client_secret},
       {"grant_type", "refresh_token"},
   });
-  int request_id = sender.Send(
-      base::Bind(&DeviceRegistrationInfo::OnRefreshAccessTokenSuccess,
-                 weak_factory_.GetWeakPtr(), shared_success_callback,
-                 shared_error_callback),
-      base::Bind(&DeviceRegistrationInfo::OnRefreshAccessTokenError,
-                 weak_factory_.GetWeakPtr(), shared_success_callback,
-                 shared_error_callback));
-  VLOG(1) << "Refresh access token request dispatched. Request ID = "
-          << request_id;
+  sender.Send(base::Bind(&DeviceRegistrationInfo::OnRefreshAccessTokenDone,
+                         weak_factory_.GetWeakPtr(), callback));
+  VLOG(1) << "Refresh access token request dispatched";
 }
 
-void DeviceRegistrationInfo::OnRefreshAccessTokenSuccess(
-    const std::shared_ptr<base::Closure>& success_callback,
-    const std::shared_ptr<ErrorCallback>& error_callback,
-    int id,
-    const HttpClient::Response& response) {
-  VLOG(1) << "Refresh access token request with ID " << id << " completed";
-  oauth2_backoff_entry_->InformOfRequest(true);
-  ErrorPtr error;
-  auto json = ParseOAuthResponse(response, &error);
-  if (!json) {
-    error_callback->Run(error.get());
-    return;
+void DeviceRegistrationInfo::OnRefreshAccessTokenDone(
+    const DoneCallback& callback,
+    std::unique_ptr<HttpClient::Response> response,
+    ErrorPtr error) {
+  if (error) {
+    VLOG(1) << "Refresh access token failed";
+    oauth2_backoff_entry_->InformOfRequest(false);
+    return RefreshAccessToken(callback);
   }
+  VLOG(1) << "Refresh access token request completed";
+  oauth2_backoff_entry_->InformOfRequest(true);
+  auto json = ParseOAuthResponse(*response, &error);
+  if (!json)
+    return callback.Run(std::move(error));
 
   int expires_in = 0;
   if (!json->GetString("access_token", &access_token_) ||
@@ -404,8 +402,7 @@
     LOG(ERROR) << "Access token unavailable.";
     Error::AddTo(&error, FROM_HERE, kErrorDomainOAuth2,
                  "unexpected_server_response", "Access token unavailable");
-    error_callback->Run(error.get());
-    return;
+    return callback.Run(std::move(error));
   }
   access_token_expiration_ =
       base::Time::Now() + base::TimeDelta::FromSeconds(expires_in);
@@ -418,17 +415,7 @@
     // Now that we have a new access token, retry the connection.
     StartNotificationChannel();
   }
-  success_callback->Run();
-}
-
-void DeviceRegistrationInfo::OnRefreshAccessTokenError(
-    const std::shared_ptr<base::Closure>& success_callback,
-    const std::shared_ptr<ErrorCallback>& error_callback,
-    int id,
-    const Error* error) {
-  VLOG(1) << "Refresh access token request with ID " << id << " failed";
-  oauth2_backoff_entry_->InformOfRequest(false);
-  RefreshAccessToken(*success_callback, *error_callback);
+  callback.Run(nullptr);
 }
 
 void DeviceRegistrationInfo::StartNotificationChannel() {
@@ -438,8 +425,8 @@
   LOG(INFO) << "Starting notification channel";
 
   // If no TaskRunner assume we're in test.
-  if (!task_runner_) {
-    LOG(INFO) << "No TaskRunner, not starting notification channel";
+  if (!network_) {
+    LOG(INFO) << "No Network, not starting notification channel";
     return;
   }
 
@@ -513,24 +500,27 @@
 }
 
 void DeviceRegistrationInfo::GetDeviceInfo(
-    const CloudRequestCallback& success_callback,
-    const ErrorCallback& error_callback) {
+    const CloudRequestDoneCallback& callback) {
   ErrorPtr error;
   if (!VerifyRegistrationCredentials(&error)) {
-    if (!error_callback.is_null())
-      error_callback.Run(error.get());
-    return;
+    return callback.Run({}, std::move(error));
   }
-  DoCloudRequest(http::kGet, GetDeviceURL(), nullptr, success_callback,
-                 error_callback);
+  DoCloudRequest(HttpClient::Method::kGet, GetDeviceURL(), nullptr, callback);
 }
 
-std::string DeviceRegistrationInfo::RegisterDevice(const std::string& ticket_id,
-                                                   ErrorPtr* error) {
+void DeviceRegistrationInfo::RegisterDeviceError(const DoneCallback& callback,
+                                                 ErrorPtr error) {
+  task_runner_->PostDelayedTask(FROM_HERE,
+                                base::Bind(callback, base::Passed(&error)), {});
+}
+
+void DeviceRegistrationInfo::RegisterDevice(const std::string& ticket_id,
+                                            const DoneCallback& callback) {
+  ErrorPtr error;
   std::unique_ptr<base::DictionaryValue> device_draft =
-      BuildDeviceResource(error);
+      BuildDeviceResource(&error);
   if (!device_draft)
-    return std::string();
+    return RegisterDeviceError(callback, std::move(error));
 
   base::DictionaryValue req_json;
   req_json.SetString("id", ticket_id);
@@ -540,31 +530,48 @@
   auto url = GetServiceURL("registrationTickets/" + ticket_id,
                            {{"key", GetSettings().api_key}});
 
-  RequestSender sender{http::kPatch, url, http_client_};
+  RequestSender sender{HttpClient::Method::kPatch, url, http_client_};
   sender.SetJsonData(req_json);
-  auto response = sender.SendAndBlock(error);
+  sender.Send(base::Bind(&DeviceRegistrationInfo::RegisterDeviceOnTicketSent,
+                         weak_factory_.GetWeakPtr(), ticket_id, callback));
+}
 
-  if (!response)
-    return std::string();
-  auto json_resp = ParseJsonResponse(*response, error);
+void DeviceRegistrationInfo::RegisterDeviceOnTicketSent(
+    const std::string& ticket_id,
+    const DoneCallback& callback,
+    std::unique_ptr<provider::HttpClient::Response> response,
+    ErrorPtr error) {
+  if (error)
+    return RegisterDeviceError(callback, std::move(error));
+  auto json_resp = ParseJsonResponse(*response, &error);
   if (!json_resp)
-    return std::string();
+    return RegisterDeviceError(callback, std::move(error));
+
   if (!IsSuccessful(*response)) {
-    ParseGCDError(json_resp.get(), error);
-    return std::string();
+    ParseGCDError(json_resp.get(), &error);
+    return RegisterDeviceError(callback, std::move(error));
   }
 
-  url = GetServiceURL("registrationTickets/" + ticket_id + "/finalize",
-                      {{"key", GetSettings().api_key}});
-  response = RequestSender{http::kPost, url, http_client_}.SendAndBlock(error);
-  if (!response)
-    return std::string();
-  json_resp = ParseJsonResponse(*response, error);
+  std::string url =
+      GetServiceURL("registrationTickets/" + ticket_id + "/finalize",
+                    {{"key", GetSettings().api_key}});
+  RequestSender{HttpClient::Method::kPost, url, http_client_}.Send(
+      base::Bind(&DeviceRegistrationInfo::RegisterDeviceOnTicketFinalized,
+                 weak_factory_.GetWeakPtr(), callback));
+}
+
+void DeviceRegistrationInfo::RegisterDeviceOnTicketFinalized(
+    const DoneCallback& callback,
+    std::unique_ptr<provider::HttpClient::Response> response,
+    ErrorPtr error) {
+  if (error)
+    return RegisterDeviceError(callback, std::move(error));
+  auto json_resp = ParseJsonResponse(*response, &error);
   if (!json_resp)
-    return std::string();
+    return RegisterDeviceError(callback, std::move(error));
   if (!IsSuccessful(*response)) {
-    ParseGCDError(json_resp.get(), error);
-    return std::string();
+    ParseGCDError(json_resp.get(), &error);
+    return RegisterDeviceError(callback, std::move(error));
   }
 
   std::string auth_code;
@@ -575,15 +582,16 @@
       !json_resp->GetString("robotAccountAuthorizationCode", &auth_code) ||
       !json_resp->GetDictionary("deviceDraft", &device_draft_response) ||
       !device_draft_response->GetString("id", &cloud_id)) {
-    Error::AddTo(error, FROM_HERE, kErrorDomainGCD, "unexpected_response",
+    Error::AddTo(&error, FROM_HERE, kErrorDomainGCD, "unexpected_response",
                  "Device account missing in response");
-    return std::string();
+    return RegisterDeviceError(callback, std::move(error));
   }
 
   UpdateDeviceInfoTimestamp(*device_draft_response);
 
   // Now get access_token and refresh_token
-  RequestSender sender2{http::kPost, GetOAuthURL("token"), http_client_};
+  RequestSender sender2{HttpClient::Method::kPost, GetOAuthURL("token"),
+                        http_client_};
   sender2.SetFormData(
       {{"code", auth_code},
        {"client_id", GetSettings().client_id},
@@ -591,21 +599,29 @@
        {"redirect_uri", "oob"},
        {"scope", "https://www.googleapis.com/auth/clouddevices"},
        {"grant_type", "authorization_code"}});
-  response = sender2.SendAndBlock(error);
+  sender2.Send(base::Bind(&DeviceRegistrationInfo::RegisterDeviceOnAuthCodeSent,
+                          weak_factory_.GetWeakPtr(), cloud_id, robot_account,
+                          callback));
+}
 
-  if (!response)
-    return std::string();
-
-  json_resp = ParseOAuthResponse(*response, error);
+void DeviceRegistrationInfo::RegisterDeviceOnAuthCodeSent(
+    const std::string& cloud_id,
+    const std::string& robot_account,
+    const DoneCallback& callback,
+    std::unique_ptr<provider::HttpClient::Response> response,
+    ErrorPtr error) {
+  if (error)
+    return RegisterDeviceError(callback, std::move(error));
+  auto json_resp = ParseOAuthResponse(*response, &error);
   int expires_in = 0;
   std::string refresh_token;
   if (!json_resp || !json_resp->GetString("access_token", &access_token_) ||
       !json_resp->GetString("refresh_token", &refresh_token) ||
       !json_resp->GetInteger("expires_in", &expires_in) ||
       access_token_.empty() || refresh_token.empty() || expires_in <= 0) {
-    Error::AddTo(error, FROM_HERE, kErrorDomainGCD, "unexpected_response",
+    Error::AddTo(&error, FROM_HERE, kErrorDomainGCD, "unexpected_response",
                  "Device access_token missing in response");
-    return std::string();
+    return RegisterDeviceError(callback, std::move(error));
   }
 
   access_token_expiration_ =
@@ -617,22 +633,22 @@
   change.set_refresh_token(refresh_token);
   change.Commit();
 
+  task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr), {});
+
   StartNotificationChannel();
 
   // We're going to respond with our success immediately and we'll connect to
   // cloud shortly after.
-  ScheduleCloudConnection(base::TimeDelta::FromSeconds(0));
-  return cloud_id;
+  ScheduleCloudConnection({});
 }
 
 void DeviceRegistrationInfo::DoCloudRequest(
-    const std::string& method,
+    HttpClient::Method method,
     const std::string& url,
     const base::DictionaryValue* body,
-    const CloudRequestCallback& success_callback,
-    const ErrorCallback& error_callback) {
+    const CloudRequestDoneCallback& callback) {
   // We make CloudRequestData shared here because we want to make sure
-  // there is only one instance of success_callback and error_calback since
+  // there is only one instance of callback and error_calback since
   // those may have move-only types and making a copy of the callback with
   // move-only types curried-in will invalidate the source callback.
   auto data = std::make_shared<CloudRequestData>();
@@ -640,8 +656,7 @@
   data->url = url;
   if (body)
     base::JSONWriter::Write(*body, &data->body);
-  data->success_callback = success_callback;
-  data->error_callback = error_callback;
+  data->callback = callback;
   SendCloudRequest(data);
 }
 
@@ -651,49 +666,39 @@
   // forget about 5xx when fetching new access token).
   // TODO(antonm): Add support for device removal.
 
-  VLOG(1) << "Sending cloud request '" << data->method << "' to '" << data->url
-          << "' with request body '" << data->body << "'";
   ErrorPtr error;
   if (!VerifyRegistrationCredentials(&error)) {
-    data->error_callback.Run(error.get());
-    return;
+    return data->callback.Run({}, std::move(error));
   }
 
   if (cloud_backoff_entry_->ShouldRejectRequest()) {
     VLOG(1) << "Cloud request delayed for "
             << cloud_backoff_entry_->GetTimeUntilRelease()
             << " due to backoff policy";
-    task_runner_->PostDelayedTask(
+    return task_runner_->PostDelayedTask(
         FROM_HERE, base::Bind(&DeviceRegistrationInfo::SendCloudRequest,
                               AsWeakPtr(), data),
         cloud_backoff_entry_->GetTimeUntilRelease());
-    return;
   }
 
   RequestSender sender{data->method, data->url, http_client_};
   sender.SetData(data->body, http::kJsonUtf8);
   sender.SetAccessToken(access_token_);
-  int request_id =
-      sender.Send(base::Bind(&DeviceRegistrationInfo::OnCloudRequestSuccess,
-                             AsWeakPtr(), data),
-                  base::Bind(&DeviceRegistrationInfo::OnCloudRequestError,
-                             AsWeakPtr(), data));
-  VLOG(1) << "Cloud request with ID " << request_id << " successfully sent";
+  sender.Send(base::Bind(&DeviceRegistrationInfo::OnCloudRequestDone,
+                         AsWeakPtr(), data));
 }
 
-void DeviceRegistrationInfo::OnCloudRequestSuccess(
+void DeviceRegistrationInfo::OnCloudRequestDone(
     const std::shared_ptr<const CloudRequestData>& data,
-    int request_id,
-    const HttpClient::Response& response) {
-  int status_code = response.GetStatusCode();
-  VLOG(1) << "Response for cloud request with ID " << request_id
-          << " received with status code " << status_code;
+    std::unique_ptr<provider::HttpClient::Response> response,
+    ErrorPtr error) {
+  if (error)
+    return RetryCloudRequest(data);
+  int status_code = response->GetStatusCode();
   if (status_code == http::kDenied) {
     cloud_backoff_entry_->InformOfRequest(true);
     RefreshAccessToken(
         base::Bind(&DeviceRegistrationInfo::OnAccessTokenRefreshed, AsWeakPtr(),
-                   data),
-        base::Bind(&DeviceRegistrationInfo::OnAccessTokenError, AsWeakPtr(),
                    data));
     return;
   }
@@ -707,38 +712,26 @@
     return;
   }
 
-  ErrorPtr error;
-  auto json_resp = ParseJsonResponse(response, &error);
+  auto json_resp = ParseJsonResponse(*response, &error);
   if (!json_resp) {
-    data->error_callback.Run(error.get());
     cloud_backoff_entry_->InformOfRequest(true);
-    return;
+    return data->callback.Run({}, std::move(error));
   }
 
-  if (!IsSuccessful(response)) {
+  if (!IsSuccessful(*response)) {
     ParseGCDError(json_resp.get(), &error);
     if (status_code == http::kForbidden &&
         error->HasError(kErrorDomainGCDServer, "rateLimitExceeded")) {
       // If we exceeded server quota, retry the request later.
-      RetryCloudRequest(data);
-      return;
+      return RetryCloudRequest(data);
     }
     cloud_backoff_entry_->InformOfRequest(true);
-    data->error_callback.Run(error.get());
-    return;
+    return data->callback.Run({}, std::move(error));
   }
 
   cloud_backoff_entry_->InformOfRequest(true);
   SetGcdState(GcdState::kConnected);
-  data->success_callback.Run(*json_resp);
-}
-
-void DeviceRegistrationInfo::OnCloudRequestError(
-    const std::shared_ptr<const CloudRequestData>& data,
-    int request_id,
-    const Error* error) {
-  VLOG(1) << "Cloud request with ID " << request_id << " failed";
-  RetryCloudRequest(data);
+  data->callback.Run(*json_resp, nullptr);
 }
 
 void DeviceRegistrationInfo::RetryCloudRequest(
@@ -750,32 +743,34 @@
 }
 
 void DeviceRegistrationInfo::OnAccessTokenRefreshed(
-    const std::shared_ptr<const CloudRequestData>& data) {
+    const std::shared_ptr<const CloudRequestData>& data,
+    ErrorPtr error) {
+  if (error) {
+    CheckAccessTokenError(error->Clone());
+    return data->callback.Run({}, std::move(error));
+  }
   SendCloudRequest(data);
 }
 
-void DeviceRegistrationInfo::OnAccessTokenError(
-    const std::shared_ptr<const CloudRequestData>& data,
-    const Error* error) {
-  CheckAccessTokenError(error);
-  data->error_callback.Run(error);
-}
-
-void DeviceRegistrationInfo::CheckAccessTokenError(const Error* error) {
-  if (error->HasError(kErrorDomainOAuth2, "invalid_grant"))
+void DeviceRegistrationInfo::CheckAccessTokenError(ErrorPtr error) {
+  if (error && error->HasError(kErrorDomainOAuth2, "invalid_grant"))
     MarkDeviceUnregistered();
 }
 
-void DeviceRegistrationInfo::ConnectToCloud() {
+void DeviceRegistrationInfo::ConnectToCloud(ErrorPtr error) {
+  if (error) {
+    if (error->HasError(kErrorDomainOAuth2, "invalid_grant"))
+      MarkDeviceUnregistered();
+    return;
+  }
+
   connected_to_cloud_ = false;
   if (!VerifyRegistrationCredentials(nullptr))
     return;
 
   if (access_token_.empty()) {
     RefreshAccessToken(
-        base::Bind(&DeviceRegistrationInfo::ConnectToCloud, AsWeakPtr()),
-        base::Bind(&DeviceRegistrationInfo::CheckAccessTokenError,
-                   AsWeakPtr()));
+        base::Bind(&DeviceRegistrationInfo::ConnectToCloud, AsWeakPtr()));
     return;
   }
 
@@ -785,16 +780,16 @@
   //   3) abort any commands that we've previously marked as "in progress"
   //      or as being in an error state; publish queued commands
   UpdateDeviceResource(
-      base::Bind(&DeviceRegistrationInfo::OnConnectedToCloud, AsWeakPtr()),
-      base::Bind(&IgnoreCloudError));
+      base::Bind(&DeviceRegistrationInfo::OnConnectedToCloud, AsWeakPtr()));
 }
 
-void DeviceRegistrationInfo::OnConnectedToCloud() {
+void DeviceRegistrationInfo::OnConnectedToCloud(ErrorPtr error) {
+  if (error)
+    return;
   LOG(INFO) << "Device connected to cloud server";
   connected_to_cloud_ = true;
   FetchCommands(base::Bind(&DeviceRegistrationInfo::ProcessInitialCommandList,
-                           AsWeakPtr()),
-                base::Bind(&IgnoreCloudError));
+                           AsWeakPtr()));
   // In case there are any pending state updates since we sent off the initial
   // UpdateDeviceResource() request, update the server with any state changes.
   PublishStateUpdates();
@@ -810,8 +805,7 @@
   change.Commit();
 
   if (HaveRegistrationCredentials()) {
-    UpdateDeviceResource(base::Bind(&base::DoNothing),
-                         base::Bind(&IgnoreCloudError));
+    UpdateDeviceResource(base::Bind(&IgnoreCloudError));
   }
 }
 
@@ -848,39 +842,27 @@
 void DeviceRegistrationInfo::UpdateCommand(
     const std::string& command_id,
     const base::DictionaryValue& command_patch,
-    const base::Closure& on_success,
-    const base::Closure& on_error) {
-  DoCloudRequest(http::kPatch, GetServiceURL("commands/" + command_id),
-                 &command_patch,
-                 base::Bind(&IgnoreCloudResultWithCallback, on_success),
-                 base::Bind(&IgnoreCloudErrorWithCallback, on_error));
+    const DoneCallback& callback) {
+  DoCloudRequest(HttpClient::Method::kPatch,
+                 GetServiceURL("commands/" + command_id), &command_patch,
+                 base::Bind(&IgnoreCloudResultWithCallback, callback));
 }
 
 void DeviceRegistrationInfo::NotifyCommandAborted(const std::string& command_id,
                                                   ErrorPtr error) {
   base::DictionaryValue command_patch;
   command_patch.SetString(commands::attributes::kCommand_State,
-                          EnumToString(CommandStatus::kAborted));
+                          EnumToString(Command::State::kAborted));
   if (error) {
-    command_patch.SetString(commands::attributes::kCommand_ErrorCode,
-                            Join(":", error->GetDomain(), error->GetCode()));
-    std::vector<std::string> messages;
-    const Error* current_error = error.get();
-    while (current_error) {
-      messages.push_back(current_error->GetMessage());
-      current_error = current_error->GetInnerError();
-    }
-    command_patch.SetString(commands::attributes::kCommand_ErrorMessage,
-                            Join(";", messages));
+    command_patch.Set(commands::attributes::kCommand_Error,
+                      ErrorInfoToJson(*error).release());
   }
-  UpdateCommand(command_id, command_patch, base::Bind(&base::DoNothing),
-                base::Bind(&base::DoNothing));
+  UpdateCommand(command_id, command_patch, base::Bind(&IgnoreCloudError));
 }
 
 void DeviceRegistrationInfo::UpdateDeviceResource(
-    const base::Closure& on_success,
-    const ErrorCallback& on_failure) {
-  queued_resource_update_callbacks_.emplace_back(on_success, on_failure);
+    const DoneCallback& callback) {
+  queued_resource_update_callbacks_.emplace_back(callback);
   if (!in_progress_resource_update_callbacks_.empty()) {
     VLOG(1) << "Another request is already pending.";
     return;
@@ -900,10 +882,8 @@
     // the request to guard against out-of-order requests overwriting settings
     // specified by later requests.
     VLOG(1) << "Getting the last device resource timestamp from server...";
-    GetDeviceInfo(
-        base::Bind(&DeviceRegistrationInfo::OnDeviceInfoRetrieved, AsWeakPtr()),
-        base::Bind(&DeviceRegistrationInfo::OnUpdateDeviceResourceError,
-                   AsWeakPtr()));
+    GetDeviceInfo(base::Bind(&DeviceRegistrationInfo::OnDeviceInfoRetrieved,
+                             AsWeakPtr()));
     return;
   }
 
@@ -918,23 +898,22 @@
   std::unique_ptr<base::DictionaryValue> device_resource =
       BuildDeviceResource(&error);
   if (!device_resource) {
-    OnUpdateDeviceResourceError(error.get());
-    return;
+    return OnUpdateDeviceResourceError(std::move(error));
   }
 
   std::string url = GetDeviceURL(
       {}, {{"lastUpdateTimeMs", last_device_resource_updated_timestamp_}});
 
-  DoCloudRequest(
-      http::kPut, url, device_resource.get(),
-      base::Bind(&DeviceRegistrationInfo::OnUpdateDeviceResourceSuccess,
-                 AsWeakPtr()),
-      base::Bind(&DeviceRegistrationInfo::OnUpdateDeviceResourceError,
-                 AsWeakPtr()));
+  DoCloudRequest(HttpClient::Method::kPut, url, device_resource.get(),
+                 base::Bind(&DeviceRegistrationInfo::OnUpdateDeviceResourceDone,
+                            AsWeakPtr()));
 }
 
 void DeviceRegistrationInfo::OnDeviceInfoRetrieved(
-    const base::DictionaryValue& device_info) {
+    const base::DictionaryValue& device_info,
+    ErrorPtr error) {
+  if (error)
+    return OnUpdateDeviceResourceError(std::move(error));
   if (UpdateDeviceInfoTimestamp(device_info))
     StartQueuedUpdateDeviceResource();
 }
@@ -953,27 +932,28 @@
   return true;
 }
 
-void DeviceRegistrationInfo::OnUpdateDeviceResourceSuccess(
-    const base::DictionaryValue& device_info) {
+void DeviceRegistrationInfo::OnUpdateDeviceResourceDone(
+    const base::DictionaryValue& device_info,
+    ErrorPtr error) {
+  if (error)
+    return OnUpdateDeviceResourceError(std::move(error));
   UpdateDeviceInfoTimestamp(device_info);
   // Make a copy of the callback list so that if the callback triggers another
   // call to UpdateDeviceResource(), we do not modify the list we are iterating
   // over.
   auto callback_list = std::move(in_progress_resource_update_callbacks_);
-  for (const auto& callback_pair : callback_list)
-    callback_pair.first.Run();
+  for (const auto& callback : callback_list)
+    callback.Run(nullptr);
   StartQueuedUpdateDeviceResource();
 }
 
-void DeviceRegistrationInfo::OnUpdateDeviceResourceError(const Error* error) {
+void DeviceRegistrationInfo::OnUpdateDeviceResourceError(ErrorPtr error) {
   if (error->HasError(kErrorDomainGCDServer, "invalid_last_update_time_ms")) {
     // If the server rejected our previous request, retrieve the latest
     // timestamp from the server and retry.
     VLOG(1) << "Getting the last device resource timestamp from server...";
-    GetDeviceInfo(
-        base::Bind(&DeviceRegistrationInfo::OnDeviceInfoRetrieved, AsWeakPtr()),
-        base::Bind(&DeviceRegistrationInfo::OnUpdateDeviceResourceError,
-                   AsWeakPtr()));
+    GetDeviceInfo(base::Bind(&DeviceRegistrationInfo::OnDeviceInfoRetrieved,
+                             AsWeakPtr()));
     return;
   }
 
@@ -981,28 +961,24 @@
   // call to UpdateDeviceResource(), we do not modify the list we are iterating
   // over.
   auto callback_list = std::move(in_progress_resource_update_callbacks_);
-  for (const auto& callback_pair : callback_list)
-    callback_pair.second.Run(error);
+  for (const auto& callback : callback_list)
+    callback.Run(error->Clone());
 
   StartQueuedUpdateDeviceResource();
 }
 
-void DeviceRegistrationInfo::OnFetchCommandsSuccess(
-    const base::Callback<void(const base::ListValue&)>& callback,
-    const base::DictionaryValue& json) {
+void DeviceRegistrationInfo::OnFetchCommandsDone(
+    const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback,
+    const base::DictionaryValue& json,
+    ErrorPtr error) {
   OnFetchCommandsReturned();
+  if (error)
+    return callback.Run({}, std::move(error));
   const base::ListValue* commands{nullptr};
-  if (!json.GetList("commands", &commands)) {
+  if (!json.GetList("commands", &commands))
     VLOG(2) << "No commands in the response.";
-  }
   const base::ListValue empty;
-  callback.Run(commands ? *commands : empty);
-}
-
-void DeviceRegistrationInfo::OnFetchCommandsError(const ErrorCallback& callback,
-                                                  const Error* error) {
-  OnFetchCommandsReturned();
-  callback.Run(error);
+  callback.Run(commands ? *commands : empty, nullptr);
 }
 
 void DeviceRegistrationInfo::OnFetchCommandsReturned() {
@@ -1013,17 +989,15 @@
 }
 
 void DeviceRegistrationInfo::FetchCommands(
-    const base::Callback<void(const base::ListValue&)>& on_success,
-    const ErrorCallback& on_failure) {
+    const base::Callback<void(const base::ListValue&, ErrorPtr error)>&
+        callback) {
   fetch_commands_request_sent_ = true;
   fetch_commands_request_queued_ = false;
   DoCloudRequest(
-      http::kGet,
+      HttpClient::Method::kGet,
       GetServiceURL("commands/queue", {{"deviceId", GetSettings().cloud_id}}),
-      nullptr, base::Bind(&DeviceRegistrationInfo::OnFetchCommandsSuccess,
-                          AsWeakPtr(), on_success),
-      base::Bind(&DeviceRegistrationInfo::OnFetchCommandsError, AsWeakPtr(),
-                 on_failure));
+      nullptr, base::Bind(&DeviceRegistrationInfo::OnFetchCommandsDone,
+                          AsWeakPtr(), callback));
 }
 
 void DeviceRegistrationInfo::FetchAndPublishCommands() {
@@ -1033,12 +1007,14 @@
   }
 
   FetchCommands(base::Bind(&DeviceRegistrationInfo::PublishCommands,
-                           weak_factory_.GetWeakPtr()),
-                base::Bind(&IgnoreCloudError));
+                           weak_factory_.GetWeakPtr()));
 }
 
 void DeviceRegistrationInfo::ProcessInitialCommandList(
-    const base::ListValue& commands) {
+    const base::ListValue& commands,
+    ErrorPtr error) {
+  if (error)
+    return;
   for (const base::Value* command : commands) {
     const base::DictionaryValue* command_dict{nullptr};
     if (!command->GetAsDictionary(&command_dict)) {
@@ -1062,9 +1038,9 @@
       std::unique_ptr<base::DictionaryValue> cmd_copy{command_dict->DeepCopy()};
       cmd_copy->SetString("state", "aborted");
       // TODO(wiley) We could consider handling this error case more gracefully.
-      DoCloudRequest(http::kPut, GetServiceURL("commands/" + command_id),
-                     cmd_copy.get(), base::Bind(&IgnoreCloudResult),
-                     base::Bind(&IgnoreCloudError));
+      DoCloudRequest(HttpClient::Method::kPut,
+                     GetServiceURL("commands/" + command_id), cmd_copy.get(),
+                     base::Bind(&IgnoreCloudResult));
     } else {
       // Normal command, publish it to local clients.
       PublishCommand(*command_dict);
@@ -1072,7 +1048,10 @@
   }
 }
 
-void DeviceRegistrationInfo::PublishCommands(const base::ListValue& commands) {
+void DeviceRegistrationInfo::PublishCommands(const base::ListValue& commands,
+                                             ErrorPtr error) {
+  if (error)
+    return;
   for (const base::Value* command : commands) {
     const base::DictionaryValue* command_dict{nullptr};
     if (!command->GetAsDictionary(&command_dict)) {
@@ -1088,8 +1067,8 @@
   std::string command_id;
   ErrorPtr error;
   auto command_instance = CommandInstance::FromJson(
-      &command, CommandOrigin::kCloud, command_manager_->GetCommandDictionary(),
-      &command_id, &error);
+      &command, Command::Origin::kCloud,
+      command_manager_->GetCommandDictionary(), &command_id, &error);
   if (!command_instance) {
     LOG(WARNING) << "Failed to parse a command instance: " << command;
     if (!command_id.empty())
@@ -1153,28 +1132,26 @@
   body.Set("patches", patches.release());
 
   device_state_update_pending_ = true;
-  DoCloudRequest(
-      http::kPost, GetDeviceURL("patchState"), &body,
-      base::Bind(&DeviceRegistrationInfo::OnPublishStateSuccess, AsWeakPtr(),
-                 update_id),
-      base::Bind(&DeviceRegistrationInfo::OnPublishStateError, AsWeakPtr()));
+  DoCloudRequest(HttpClient::Method::kPost, GetDeviceURL("patchState"), &body,
+                 base::Bind(&DeviceRegistrationInfo::OnPublishStateDone,
+                            AsWeakPtr(), update_id));
 }
 
-void DeviceRegistrationInfo::OnPublishStateSuccess(
+void DeviceRegistrationInfo::OnPublishStateDone(
     StateChangeQueueInterface::UpdateID update_id,
-    const base::DictionaryValue& reply) {
+    const base::DictionaryValue& reply,
+    ErrorPtr error) {
   device_state_update_pending_ = false;
+  if (error) {
+    LOG(ERROR) << "Permanent failure while trying to update device state";
+    return;
+  }
   state_manager_->NotifyStateUpdatedOnServer(update_id);
   // See if there were more pending state updates since the previous request
   // had been sent out.
   PublishStateUpdates();
 }
 
-void DeviceRegistrationInfo::OnPublishStateError(const Error* error) {
-  LOG(ERROR) << "Permanent failure while trying to update device state";
-  device_state_update_pending_ = false;
-}
-
 void DeviceRegistrationInfo::SetGcdState(GcdState new_state) {
   VLOG_IF(1, new_state != gcd_state_) << "Changing registration status to "
                                       << EnumToString(new_state);
@@ -1188,8 +1165,7 @@
   if (!HaveRegistrationCredentials() || !connected_to_cloud_)
     return;
 
-  UpdateDeviceResource(base::Bind(&base::DoNothing),
-                       base::Bind(&IgnoreCloudError));
+  UpdateDeviceResource(base::Bind(&IgnoreCloudError));
 }
 
 void DeviceRegistrationInfo::OnStateChanged() {
@@ -1222,8 +1198,9 @@
   // the moment of the last poll and the time we successfully told the server
   // to send new commands over the new notification channel.
   UpdateDeviceResource(
-      base::Bind(&DeviceRegistrationInfo::FetchAndPublishCommands, AsWeakPtr()),
-      base::Bind(&IgnoreCloudError));
+      base::Bind(&IgnoreCloudErrorWithCallback,
+                 base::Bind(&DeviceRegistrationInfo::FetchAndPublishCommands,
+                            AsWeakPtr())));
 }
 
 void DeviceRegistrationInfo::OnDisconnected() {
@@ -1234,15 +1211,13 @@
   pull_channel_->UpdatePullInterval(
       base::TimeDelta::FromSeconds(kPollingPeriodSeconds));
   current_notification_channel_ = pull_channel_.get();
-  UpdateDeviceResource(base::Bind(&base::DoNothing),
-                       base::Bind(&IgnoreCloudError));
+  UpdateDeviceResource(base::Bind(&IgnoreCloudError));
 }
 
 void DeviceRegistrationInfo::OnPermanentFailure() {
   LOG(ERROR) << "Failed to establish notification channel.";
   notification_channel_starting_ = false;
   RefreshAccessToken(
-      base::Bind(&base::DoNothing),
       base::Bind(&DeviceRegistrationInfo::CheckAccessTokenError, AsWeakPtr()));
 }
 
@@ -1251,6 +1226,8 @@
   if (!connected_to_cloud_)
     return;
 
+  VLOG(1) << "Command notification received: " << command;
+
   if (!command.empty()) {
     // GCD spec indicates that the command parameter in notification object
     // "may be empty if command size is too big".
diff --git a/libweave/src/device_registration_info.h b/libweave/src/device_registration_info.h
index c797102..8eb0ec3 100644
--- a/libweave/src/device_registration_info.h
+++ b/libweave/src/device_registration_info.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -50,8 +50,9 @@
 class DeviceRegistrationInfo : public NotificationDelegate,
                                public CloudCommandUpdateInterface {
  public:
-  using CloudRequestCallback =
-      base::Callback<void(const base::DictionaryValue& response)>;
+  using CloudRequestDoneCallback =
+      base::Callback<void(const base::DictionaryValue& response,
+                          ErrorPtr error)>;
 
   DeviceRegistrationInfo(const std::shared_ptr<CommandManager>& command_manager,
                          const std::shared_ptr<StateManager>& state_manager,
@@ -64,7 +65,8 @@
 
   void AddGcdStateChangedCallback(
       const Device::GcdStateChangedCallback& callback);
-  std::string RegisterDevice(const std::string& ticket_id, ErrorPtr* error);
+  void RegisterDevice(const std::string& ticket_id,
+                      const DoneCallback& callback);
 
   void UpdateDeviceInfo(const std::string& name,
                         const std::string& description,
@@ -79,8 +81,7 @@
                            const std::string& service_url,
                            ErrorPtr* error);
 
-  void GetDeviceInfo(const CloudRequestCallback& success_callback,
-                     const ErrorCallback& error_callback);
+  void GetDeviceInfo(const CloudRequestDoneCallback& callback);
 
   // Returns the GCD service request URL. If |subpath| is specified, it is
   // appended to the base URL which is normally
@@ -118,8 +119,7 @@
   // Updates a command (override from CloudCommandUpdateInterface).
   void UpdateCommand(const std::string& command_id,
                      const base::DictionaryValue& command_patch,
-                     const base::Closure& on_success,
-                     const base::Closure& on_error) override;
+                     const DoneCallback& callback) override;
 
   // TODO(vitalybuka): remove getters and pass config to dependent code.
   const Config::Settings& GetSettings() const { return config_->GetSettings(); }
@@ -141,25 +141,18 @@
   // Initiates the connection to the cloud server.
   // Device will do required start up chores and then start to listen
   // to new commands.
-  void ConnectToCloud();
+  void ConnectToCloud(ErrorPtr error);
   // Notification called when ConnectToCloud() succeeds.
-  void OnConnectedToCloud();
+  void OnConnectedToCloud(ErrorPtr error);
 
   // Forcibly refreshes the access token.
-  void RefreshAccessToken(const base::Closure& success_callback,
-                          const ErrorCallback& error_callback);
+  void RefreshAccessToken(const DoneCallback& callback);
 
   // Callbacks for RefreshAccessToken().
-  void OnRefreshAccessTokenSuccess(
-      const std::shared_ptr<base::Closure>& success_callback,
-      const std::shared_ptr<ErrorCallback>& error_callback,
-      int id,
-      const provider::HttpClient::Response& response);
-  void OnRefreshAccessTokenError(
-      const std::shared_ptr<base::Closure>& success_callback,
-      const std::shared_ptr<ErrorCallback>& error_callback,
-      int id,
-      const Error* error);
+  void OnRefreshAccessTokenDone(
+      const DoneCallback& callback,
+      std::unique_ptr<provider::HttpClient::Response> response,
+      ErrorPtr error);
 
   // Parse the OAuth response, and sets registration status to
   // kInvalidCredentials if our registration is no longer valid.
@@ -176,45 +169,39 @@
   // and device removal.  It is a recommended way to do cloud API
   // requests.
   // TODO(antonm): Consider moving into some other class.
-  void DoCloudRequest(const std::string& method,
+  void DoCloudRequest(provider::HttpClient::Method method,
                       const std::string& url,
                       const base::DictionaryValue* body,
-                      const CloudRequestCallback& success_callback,
-                      const ErrorCallback& error_callback);
+                      const CloudRequestDoneCallback& callback);
 
   // Helper for DoCloudRequest().
   struct CloudRequestData {
-    std::string method;
+    provider::HttpClient::Method method;
     std::string url;
     std::string body;
-    CloudRequestCallback success_callback;
-    ErrorCallback error_callback;
+    CloudRequestDoneCallback callback;
   };
   void SendCloudRequest(const std::shared_ptr<const CloudRequestData>& data);
-  void OnCloudRequestSuccess(
+  void OnCloudRequestDone(
       const std::shared_ptr<const CloudRequestData>& data,
-      int request_id,
-      const provider::HttpClient::Response& response);
-  void OnCloudRequestError(const std::shared_ptr<const CloudRequestData>& data,
-                           int request_id,
-                           const Error* error);
+      std::unique_ptr<provider::HttpClient::Response> response,
+      ErrorPtr error);
   void RetryCloudRequest(const std::shared_ptr<const CloudRequestData>& data);
   void OnAccessTokenRefreshed(
-      const std::shared_ptr<const CloudRequestData>& data);
-  void OnAccessTokenError(const std::shared_ptr<const CloudRequestData>& data,
-                          const Error* error);
-  void CheckAccessTokenError(const Error* error);
+      const std::shared_ptr<const CloudRequestData>& data,
+      ErrorPtr error);
+  void CheckAccessTokenError(ErrorPtr error);
 
-  void UpdateDeviceResource(const base::Closure& on_success,
-                            const ErrorCallback& on_failure);
+  void UpdateDeviceResource(const DoneCallback& callback);
   void StartQueuedUpdateDeviceResource();
-  // Success/failure callbacks for UpdateDeviceResource().
-  void OnUpdateDeviceResourceSuccess(const base::DictionaryValue& device_info);
-  void OnUpdateDeviceResourceError(const Error* error);
+  void OnUpdateDeviceResourceDone(const base::DictionaryValue& device_info,
+                                  ErrorPtr error);
+  void OnUpdateDeviceResourceError(ErrorPtr error);
 
   // Callback from GetDeviceInfo() to retrieve the device resource timestamp
   // and retry UpdateDeviceResource() call.
-  void OnDeviceInfoRetrieved(const base::DictionaryValue& device_info);
+  void OnDeviceInfoRetrieved(const base::DictionaryValue& device_info,
+                             ErrorPtr error);
 
   // Extracts the timestamp from the device resource and sets it to
   // |last_device_resource_updated_timestamp_|.
@@ -223,13 +210,11 @@
   bool UpdateDeviceInfoTimestamp(const base::DictionaryValue& device_info);
 
   void FetchCommands(
-      const base::Callback<void(const base::ListValue&)>& on_success,
-      const ErrorCallback& on_failure);
-  // Success/failure callbacks for FetchCommands().
-  void OnFetchCommandsSuccess(
-      const base::Callback<void(const base::ListValue&)>& callback,
-      const base::DictionaryValue& json);
-  void OnFetchCommandsError(const ErrorCallback& callback, const Error* error);
+      const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback);
+  void OnFetchCommandsDone(
+      const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback,
+      const base::DictionaryValue& json,
+      ErrorPtr);
   // Called when FetchCommands completes (with either success or error).
   // This method reschedules any pending/queued fetch requests.
   void OnFetchCommandsReturned();
@@ -237,9 +222,10 @@
   // Processes the command list that is fetched from the server on connection.
   // Aborts commands which are in transitional states and publishes queued
   // commands which are queued.
-  void ProcessInitialCommandList(const base::ListValue& commands);
+  void ProcessInitialCommandList(const base::ListValue& commands,
+                                 ErrorPtr error);
 
-  void PublishCommands(const base::ListValue& commands);
+  void PublishCommands(const base::ListValue& commands, ErrorPtr error);
   void PublishCommand(const base::DictionaryValue& command);
 
   // Helper function to pull the pending command list from the server using
@@ -247,9 +233,10 @@
   void FetchAndPublishCommands();
 
   void PublishStateUpdates();
-  void OnPublishStateSuccess(StateChangeQueueInterface::UpdateID update_id,
-                             const base::DictionaryValue& reply);
-  void OnPublishStateError(const Error* error);
+  void OnPublishStateDone(StateChangeQueueInterface::UpdateID update_id,
+                          const base::DictionaryValue& reply,
+                          ErrorPtr error);
+  void OnPublishStateError(ErrorPtr error);
 
   // If unrecoverable error occurred (e.g. error parsing command instance),
   // notify the server that the command is aborted by the device.
@@ -277,6 +264,23 @@
   // Wipes out the device registration information and stops server connections.
   void MarkDeviceUnregistered();
 
+  void RegisterDeviceError(const DoneCallback& callback, ErrorPtr error);
+  void RegisterDeviceOnTicketSent(
+      const std::string& ticket_id,
+      const DoneCallback& callback,
+      std::unique_ptr<provider::HttpClient::Response> response,
+      ErrorPtr error);
+  void RegisterDeviceOnTicketFinalized(
+      const DoneCallback& callback,
+      std::unique_ptr<provider::HttpClient::Response> response,
+      ErrorPtr error);
+  void RegisterDeviceOnAuthCodeSent(
+      const std::string& cloud_id,
+      const std::string& robot_account,
+      const DoneCallback& callback,
+      std::unique_ptr<provider::HttpClient::Response> response,
+      ErrorPtr error);
+
   // Transient data
   std::string access_token_;
   base::Time access_token_expiration_;
@@ -313,13 +317,12 @@
   // another one was in flight.
   bool fetch_commands_request_queued_{false};
 
-  using ResourceUpdateCallbackList =
-      std::vector<std::pair<base::Closure, ErrorCallback>>;
-  // Success/error callbacks for device resource update request currently in
-  // flight to the cloud server.
+  using ResourceUpdateCallbackList = std::vector<DoneCallback>;
+  // Callbacks for device resource update request currently in flight to the
+  // cloud server.
   ResourceUpdateCallbackList in_progress_resource_update_callbacks_;
-  // Success/error callbacks for device resource update requests queued while
-  // another request is in flight to the cloud server.
+  // Callbacks for device resource update requests queued while another request
+  // is in flight to the cloud server.
   ResourceUpdateCallbackList queued_resource_update_callbacks_;
 
   std::unique_ptr<NotificationChannel> primary_notification_channel_;
diff --git a/libweave/src/device_registration_info_unittest.cc b/libweave/src/device_registration_info_unittest.cc
index f5c53fa..060e5c4 100644
--- a/libweave/src/device_registration_info_unittest.cc
+++ b/libweave/src/device_registration_info_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -8,6 +8,7 @@
 #include <base/json/json_writer.h>
 #include <base/values.h>
 #include <gtest/gtest.h>
+#include <weave/provider/test/fake_task_runner.h>
 #include <weave/provider/test/mock_config_store.h>
 #include <weave/provider/test/mock_http_client.h>
 
@@ -76,12 +77,14 @@
   return {};
 }
 
-HttpClient::Response* ReplyWithJson(int status_code, const base::Value& json) {
+std::unique_ptr<HttpClient::Response> ReplyWithJson(int status_code,
+                                                    const base::Value& json) {
   std::string text;
   base::JSONWriter::WriteWithOptions(
       json, base::JSONWriter::OPTIONS_PRETTY_PRINT, &text);
 
-  MockHttpClientResponse* response = new StrictMock<MockHttpClientResponse>;
+  std::unique_ptr<MockHttpClientResponse> response{
+      new StrictMock<MockHttpClientResponse>};
   EXPECT_CALL(*response, GetStatusCode())
       .Times(AtLeast(1))
       .WillRepeatedly(Return(status_code));
@@ -90,8 +93,8 @@
       .WillRepeatedly(Return(http::kJsonUtf8));
   EXPECT_CALL(*response, GetData())
       .Times(AtLeast(1))
-      .WillRepeatedly(ReturnRefOfCopy(text));
-  return response;
+      .WillRepeatedly(Return(text));
+  return std::move(response);
 }
 
 std::pair<std::string, std::string> GetAuthHeader() {
@@ -123,7 +126,7 @@
     std::unique_ptr<Config> config{new Config{&config_store_}};
     config_ = config.get();
     dev_reg_.reset(new DeviceRegistrationInfo{command_manager_, state_manager_,
-                                              std::move(config), nullptr,
+                                              std::move(config), &task_runner_,
                                               &http_client_, nullptr});
 
     ReloadDefaults();
@@ -163,18 +166,19 @@
   }
 
   void PublishCommands(const base::ListValue& commands) {
-    return dev_reg_->PublishCommands(commands);
+    dev_reg_->PublishCommands(commands, nullptr);
   }
 
   bool RefreshAccessToken(ErrorPtr* error) const {
     bool succeeded = false;
-    auto on_success = [&succeeded]() { succeeded = true; };
-    auto on_failure = [&error](const Error* in_error) {
-      if (error)
-        *error = in_error->Clone();
+    auto callback = [&succeeded, &error](ErrorPtr in_error) {
+      if (error) {
+        *error = std::move(in_error);
+        return;
+      }
+      succeeded = true;
     };
-    dev_reg_->RefreshAccessToken(base::Bind(on_success),
-                                 base::Bind(on_failure));
+    dev_reg_->RefreshAccessToken(base::Bind(callback));
     return succeeded;
   }
 
@@ -182,6 +186,7 @@
 
   GcdState GetGcdState() const { return dev_reg_->GetGcdState(); }
 
+  provider::test::FakeTaskRunner task_runner_;
   provider::test::MockConfigStore config_store_;
   StrictMock<MockHttpClient> http_client_;
   base::DictionaryValue data_;
@@ -192,7 +197,6 @@
   std::shared_ptr<StateManager> state_manager_;
 };
 
-////////////////////////////////////////////////////////////////////////////////
 TEST_F(DeviceRegistrationInfoTest, GetServiceURL) {
   EXPECT_EQ(test_data::kServiceURL, dev_reg_->GetServiceURL());
   std::string url = test_data::kServiceURL;
@@ -230,10 +234,13 @@
   EXPECT_FALSE(dev_reg_->HaveRegistrationCredentials());
   ReloadSettings();
 
-  EXPECT_CALL(http_client_,
-              MockSendRequest(http::kPost, dev_reg_->GetOAuthURL("token"),
-                              HttpClient::Headers{GetFormHeader()}, _, _))
-      .WillOnce(WithArgs<3>(Invoke([](const std::string& data) {
+  EXPECT_CALL(
+      http_client_,
+      SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
+                  HttpClient::Headers{GetFormHeader()}, _, _))
+      .WillOnce(WithArgs<3, 4>(Invoke([](
+          const std::string& data,
+          const HttpClient::SendRequestCallback& callback) {
         EXPECT_EQ("refresh_token", GetFormField(data, "grant_type"));
         EXPECT_EQ(test_data::kRefreshToken,
                   GetFormField(data, "refresh_token"));
@@ -245,7 +252,7 @@
         json.SetString("access_token", test_data::kAccessToken);
         json.SetInteger("expires_in", 3600);
 
-        return ReplyWithJson(200, json);
+        callback.Run(ReplyWithJson(200, json), nullptr);
       })));
 
   EXPECT_TRUE(RefreshAccessToken(nullptr));
@@ -256,10 +263,13 @@
   ReloadSettings();
   EXPECT_EQ(GcdState::kConnecting, GetGcdState());
 
-  EXPECT_CALL(http_client_,
-              MockSendRequest(http::kPost, dev_reg_->GetOAuthURL("token"),
-                              HttpClient::Headers{GetFormHeader()}, _, _))
-      .WillOnce(WithArgs<3>(Invoke([](const std::string& data) {
+  EXPECT_CALL(
+      http_client_,
+      SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
+                  HttpClient::Headers{GetFormHeader()}, _, _))
+      .WillOnce(WithArgs<3, 4>(Invoke([](
+          const std::string& data,
+          const HttpClient::SendRequestCallback& callback) {
         EXPECT_EQ("refresh_token", GetFormField(data, "grant_type"));
         EXPECT_EQ(test_data::kRefreshToken,
                   GetFormField(data, "refresh_token"));
@@ -269,7 +279,7 @@
 
         base::DictionaryValue json;
         json.SetString("error", "unable_to_authenticate");
-        return ReplyWithJson(400, json);
+        callback.Run(ReplyWithJson(400, json), nullptr);
       })));
 
   ErrorPtr error;
@@ -282,10 +292,13 @@
   ReloadSettings();
   EXPECT_EQ(GcdState::kConnecting, GetGcdState());
 
-  EXPECT_CALL(http_client_,
-              MockSendRequest(http::kPost, dev_reg_->GetOAuthURL("token"),
-                              HttpClient::Headers{GetFormHeader()}, _, _))
-      .WillOnce(WithArgs<3>(Invoke([](const std::string& data) {
+  EXPECT_CALL(
+      http_client_,
+      SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
+                  HttpClient::Headers{GetFormHeader()}, _, _))
+      .WillOnce(WithArgs<3, 4>(Invoke([](
+          const std::string& data,
+          const HttpClient::SendRequestCallback& callback) {
         EXPECT_EQ("refresh_token", GetFormField(data, "grant_type"));
         EXPECT_EQ(test_data::kRefreshToken,
                   GetFormField(data, "refresh_token"));
@@ -295,7 +308,7 @@
 
         base::DictionaryValue json;
         json.SetString("error", "invalid_grant");
-        return ReplyWithJson(400, json);
+        callback.Run(ReplyWithJson(400, json), nullptr);
       })));
 
   ErrorPtr error;
@@ -308,30 +321,31 @@
   ReloadSettings();
   SetAccessToken();
 
-  EXPECT_CALL(http_client_,
-              MockSendRequest(
-                  http::kGet, dev_reg_->GetDeviceURL(),
+  EXPECT_CALL(
+      http_client_,
+      SendRequest(HttpClient::Method::kGet, dev_reg_->GetDeviceURL(),
                   HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
-      .WillOnce(WithArgs<3>(Invoke([](const std::string& data) {
-        base::DictionaryValue json;
-        json.SetString("channel.supportedType", "xmpp");
-        json.SetString("deviceKind", "vendor");
-        json.SetString("id", test_data::kDeviceId);
-        json.SetString("kind", "clouddevices#device");
-        return ReplyWithJson(200, json);
-      })));
+      .WillOnce(WithArgs<3, 4>(
+          Invoke([](const std::string& data,
+                    const HttpClient::SendRequestCallback& callback) {
+            base::DictionaryValue json;
+            json.SetString("channel.supportedType", "xmpp");
+            json.SetString("deviceKind", "vendor");
+            json.SetString("id", test_data::kDeviceId);
+            json.SetString("kind", "clouddevices#device");
+            callback.Run(ReplyWithJson(200, json), nullptr);
+          })));
 
   bool succeeded = false;
-  auto on_success = [&succeeded, this](const base::DictionaryValue& info) {
+  auto callback = [&succeeded, this](const base::DictionaryValue& info,
+                                     ErrorPtr error) {
+    EXPECT_FALSE(error);
     std::string id;
     EXPECT_TRUE(info.GetString("id", &id));
     EXPECT_EQ(test_data::kDeviceId, id);
     succeeded = true;
   };
-  auto on_failure = [](const Error* error) {
-    FAIL() << "Should not be called";
-  };
-  dev_reg_->GetDeviceInfo(base::Bind(on_success), base::Bind(on_failure));
+  dev_reg_->GetDeviceInfo(base::Bind(callback));
   EXPECT_TRUE(succeeded);
 }
 
@@ -371,11 +385,13 @@
 
   std::string ticket_url = dev_reg_->GetServiceURL("registrationTickets/") +
                            test_data::kClaimTicketId;
-  EXPECT_CALL(
-      http_client_,
-      MockSendRequest(http::kPatch, ticket_url + "?key=" + test_data::kApiKey,
-                      HttpClient::Headers{GetJsonHeader()}, _, _))
-      .WillOnce(WithArgs<3>(Invoke([](const std::string& data) {
+  EXPECT_CALL(http_client_,
+              SendRequest(HttpClient::Method::kPatch,
+                          ticket_url + "?key=" + test_data::kApiKey,
+                          HttpClient::Headers{GetJsonHeader()}, _, _))
+      .WillOnce(WithArgs<3, 4>(Invoke([](
+          const std::string& data,
+          const HttpClient::SendRequestCallback& callback) {
         auto json = test::CreateDictionaryValue(data);
         EXPECT_NE(nullptr, json.get());
         std::string value;
@@ -435,32 +451,36 @@
         device_draft->SetString("kind", "clouddevices#device");
         json_resp.Set("deviceDraft", device_draft);
 
-        return ReplyWithJson(200, json_resp);
+        callback.Run(ReplyWithJson(200, json_resp), nullptr);
       })));
 
   EXPECT_CALL(http_client_,
-              MockSendRequest(http::kPost, ticket_url + "/finalize?key=" +
-                                               test_data::kApiKey,
-                              HttpClient::Headers{}, _, _))
-      .WillOnce(InvokeWithoutArgs([]() {
-        base::DictionaryValue json;
-        json.SetString("id", test_data::kClaimTicketId);
-        json.SetString("kind", "clouddevices#registrationTicket");
-        json.SetString("oauthClientId", test_data::kClientId);
-        json.SetString("userEmail", "user@email.com");
-        json.SetString("deviceDraft.id", test_data::kDeviceId);
-        json.SetString("deviceDraft.kind", "clouddevices#device");
-        json.SetString("deviceDraft.channel.supportedType", "xmpp");
-        json.SetString("robotAccountEmail", test_data::kRobotAccountEmail);
-        json.SetString("robotAccountAuthorizationCode",
-                       test_data::kRobotAccountAuthCode);
-        return ReplyWithJson(200, json);
-      }));
+              SendRequest(HttpClient::Method::kPost,
+                          ticket_url + "/finalize?key=" + test_data::kApiKey,
+                          HttpClient::Headers{}, _, _))
+      .WillOnce(WithArgs<4>(
+          Invoke([](const HttpClient::SendRequestCallback& callback) {
+            base::DictionaryValue json;
+            json.SetString("id", test_data::kClaimTicketId);
+            json.SetString("kind", "clouddevices#registrationTicket");
+            json.SetString("oauthClientId", test_data::kClientId);
+            json.SetString("userEmail", "user@email.com");
+            json.SetString("deviceDraft.id", test_data::kDeviceId);
+            json.SetString("deviceDraft.kind", "clouddevices#device");
+            json.SetString("deviceDraft.channel.supportedType", "xmpp");
+            json.SetString("robotAccountEmail", test_data::kRobotAccountEmail);
+            json.SetString("robotAccountAuthorizationCode",
+                           test_data::kRobotAccountAuthCode);
+            callback.Run(ReplyWithJson(200, json), nullptr);
+          })));
 
-  EXPECT_CALL(http_client_,
-              MockSendRequest(http::kPost, dev_reg_->GetOAuthURL("token"),
-                              HttpClient::Headers{GetFormHeader()}, _, _))
-      .WillOnce(WithArgs<3>(Invoke([](const std::string& data) {
+  EXPECT_CALL(
+      http_client_,
+      SendRequest(HttpClient::Method::kPost, dev_reg_->GetOAuthURL("token"),
+                  HttpClient::Headers{GetFormHeader()}, _, _))
+      .WillOnce(WithArgs<3, 4>(Invoke([](
+          const std::string& data,
+          const HttpClient::SendRequestCallback& callback) {
         EXPECT_EQ("authorization_code", GetFormField(data, "grant_type"));
         EXPECT_EQ(test_data::kRobotAccountAuthCode, GetFormField(data, "code"));
         EXPECT_EQ(test_data::kClientId, GetFormField(data, "client_id"));
@@ -477,20 +497,26 @@
         json.SetString("refresh_token", test_data::kRefreshToken);
         json.SetInteger("expires_in", 3600);
 
-        return ReplyWithJson(200, json);
+        callback.Run(ReplyWithJson(200, json), nullptr);
       })));
 
-  std::string cloud_id =
-      dev_reg_->RegisterDevice(test_data::kClaimTicketId, nullptr);
+  bool done = false;
+  dev_reg_->RegisterDevice(
+      test_data::kClaimTicketId, base::Bind([this, &done](ErrorPtr error) {
+        EXPECT_FALSE(error);
+        done = true;
+        task_runner_.Break();
+        EXPECT_EQ(GcdState::kConnecting, GetGcdState());
 
-  EXPECT_EQ(test_data::kDeviceId, cloud_id);
-  EXPECT_EQ(GcdState::kConnecting, GetGcdState());
-
-  // Validate the device info saved to storage...
-  EXPECT_EQ(test_data::kDeviceId, dev_reg_->GetSettings().cloud_id);
-  EXPECT_EQ(test_data::kRefreshToken, dev_reg_->GetSettings().refresh_token);
-  EXPECT_EQ(test_data::kRobotAccountEmail,
-            dev_reg_->GetSettings().robot_account);
+        // Validate the device info saved to storage...
+        EXPECT_EQ(test_data::kDeviceId, dev_reg_->GetSettings().cloud_id);
+        EXPECT_EQ(test_data::kRefreshToken,
+                  dev_reg_->GetSettings().refresh_token);
+        EXPECT_EQ(test_data::kRobotAccountEmail,
+                  dev_reg_->GetSettings().robot_account);
+      }));
+  task_runner_.Run();
+  EXPECT_TRUE(done);
 }
 
 TEST_F(DeviceRegistrationInfoTest, OOBRegistrationStatus) {
@@ -502,83 +528,100 @@
   EXPECT_EQ(GcdState::kConnecting, GetGcdState());
 }
 
-TEST_F(DeviceRegistrationInfoTest, UpdateCommand) {
-  ReloadSettings();
-  SetAccessToken();
+class DeviceRegistrationInfoUpdateCommandTest
+    : public DeviceRegistrationInfoTest {
+ protected:
+  void SetUp() override {
+    DeviceRegistrationInfoTest::SetUp();
 
-  auto json_cmds = CreateDictionaryValue(R"({
-    'robot': {
-      '_jump': {
-        'parameters': {'_height': 'integer'},
-        'progress': {'progress': 'integer'},
-        'results': {'status': 'string'},
-        'minimalRole': 'user'
+    ReloadSettings();
+    SetAccessToken();
+
+    auto json_cmds = CreateDictionaryValue(R"({
+      'robot': {
+        '_jump': {
+          'parameters': {'_height': 'integer'},
+          'progress': {'progress': 'integer'},
+          'results': {'status': 'string'},
+          'minimalRole': 'user'
+        }
       }
-    }
-  })");
-  EXPECT_TRUE(command_manager_->LoadCommands(*json_cmds, nullptr));
+    })");
+    EXPECT_TRUE(command_manager_->LoadCommands(*json_cmds, nullptr));
 
-  const std::string command_url = dev_reg_->GetServiceURL("commands/1234");
+    command_url_ = dev_reg_->GetServiceURL("commands/1234");
 
-  auto commands_json = CreateValue(R"([{
-    'name':'robot._jump',
-    'id':'1234',
-    'parameters': {'_height': 100},
-    'minimalRole': 'user'
-  }])");
-  ASSERT_NE(nullptr, commands_json.get());
-  const base::ListValue* command_list = nullptr;
-  ASSERT_TRUE(commands_json->GetAsList(&command_list));
-  PublishCommands(*command_list);
-  auto command = command_manager_->FindCommand("1234");
-  ASSERT_NE(nullptr, command);
+    auto commands_json = CreateValue(R"([{
+      'name':'robot._jump',
+      'id':'1234',
+      'parameters': {'_height': 100},
+      'minimalRole': 'user'
+    }])");
+    ASSERT_NE(nullptr, commands_json.get());
+    const base::ListValue* command_list = nullptr;
+    ASSERT_TRUE(commands_json->GetAsList(&command_list));
+    PublishCommands(*command_list);
+    command_ = command_manager_->FindCommand("1234");
+    ASSERT_NE(nullptr, command_);
+  }
 
-  EXPECT_CALL(http_client_,
-              MockSendRequest(
-                  http::kPatch, command_url,
+  void TearDown() override {
+    task_runner_.RunOnce();
+    DeviceRegistrationInfoTest::TearDown();
+  }
+
+  Command* command_{nullptr};
+  std::string command_url_;
+};
+
+TEST_F(DeviceRegistrationInfoUpdateCommandTest, SetProgress) {
+  EXPECT_CALL(
+      http_client_,
+      SendRequest(HttpClient::Method::kPatch, command_url_,
                   HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
-      .WillOnce(WithArgs<3>(Invoke([](const std::string& data) {
-        EXPECT_JSON_EQ(R"({"results":{"status":"Ok"}})",
+      .WillOnce(WithArgs<3, 4>(Invoke([](
+          const std::string& data,
+          const HttpClient::SendRequestCallback& callback) {
+        EXPECT_JSON_EQ((R"({"state":"inProgress","progress":{"progress":18}})"),
                        *CreateDictionaryValue(data));
         base::DictionaryValue json;
-        return ReplyWithJson(200, json);
+        callback.Run(ReplyWithJson(200, json), nullptr);
+      })));
+  EXPECT_TRUE(command_->SetProgress(*CreateDictionaryValue("{'progress':18}"),
+                                    nullptr));
+}
+
+TEST_F(DeviceRegistrationInfoUpdateCommandTest, Complete) {
+  EXPECT_CALL(
+      http_client_,
+      SendRequest(HttpClient::Method::kPatch, command_url_,
+                  HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
+      .WillOnce(WithArgs<3, 4>(Invoke([](
+          const std::string& data,
+          const HttpClient::SendRequestCallback& callback) {
+        EXPECT_JSON_EQ(R"({"state":"done", "results":{"status":"Ok"}})",
+                       *CreateDictionaryValue(data));
+        base::DictionaryValue json;
+        callback.Run(ReplyWithJson(200, json), nullptr);
       })));
   EXPECT_TRUE(
-      command->SetResults(*CreateDictionaryValue("{'status': 'Ok'}"), nullptr));
-  Mock::VerifyAndClearExpectations(&http_client_);
+      command_->Complete(*CreateDictionaryValue("{'status': 'Ok'}"), nullptr));
+}
 
-  EXPECT_CALL(http_client_,
-              MockSendRequest(
-                  http::kPatch, command_url,
+TEST_F(DeviceRegistrationInfoUpdateCommandTest, Cancel) {
+  EXPECT_CALL(
+      http_client_,
+      SendRequest(HttpClient::Method::kPatch, command_url_,
                   HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
-      .WillOnce(WithArgs<3>(Invoke([](const std::string& data) {
-        EXPECT_JSON_EQ(R"({"state":"inProgress"})",
-                       *CreateDictionaryValue(data));
-        base::DictionaryValue json;
-        return ReplyWithJson(200, json);
-      })))
-      .WillOnce(WithArgs<3>(Invoke([](const std::string& data) {
-        EXPECT_JSON_EQ(R"({"progress":{"progress":18}})",
-                       *CreateDictionaryValue(data));
-        base::DictionaryValue json;
-        return ReplyWithJson(200, json);
-      })));
-  EXPECT_TRUE(
-      command->SetProgress(*CreateDictionaryValue("{'progress':18}"), nullptr));
-  Mock::VerifyAndClearExpectations(&http_client_);
-
-  EXPECT_CALL(http_client_,
-              MockSendRequest(
-                  http::kPatch, command_url,
-                  HttpClient::Headers{GetAuthHeader(), GetJsonHeader()}, _, _))
-      .WillOnce(WithArgs<3>(Invoke([](const std::string& data) {
+      .WillOnce(WithArgs<3, 4>(Invoke([](
+          const std::string& data,
+          const HttpClient::SendRequestCallback& callback) {
         EXPECT_JSON_EQ(R"({"state":"cancelled"})",
                        *CreateDictionaryValue(data));
         base::DictionaryValue json;
-        return ReplyWithJson(200, json);
+        callback.Run(ReplyWithJson(200, json), nullptr);
       })));
-  command->Cancel();
-  Mock::VerifyAndClearExpectations(&http_client_);
+  EXPECT_TRUE(command_->Cancel(nullptr));
 }
 
 }  // namespace weave
diff --git a/libweave/src/empty.cc b/libweave/src/empty.cc
index 1ad31f8..108f420 100644
--- a/libweave/src/empty.cc
+++ b/libweave/src/empty.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/error.cc b/libweave/src/error.cc
index 92ee42e..89071df 100644
--- a/libweave/src/error.cc
+++ b/libweave/src/error.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/error_unittest.cc b/libweave/src/error_unittest.cc
index 36028fb..85633de 100644
--- a/libweave/src/error_unittest.cc
+++ b/libweave/src/error_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/http_constants.cc b/libweave/src/http_constants.cc
index e80aae4..ecea880 100644
--- a/libweave/src/http_constants.cc
+++ b/libweave/src/http_constants.cc
@@ -1,17 +1,15 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave 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 "src/http_constants.h"
 
+#include <weave/enum_to_string.h>
+#include <weave/provider/http_client.h>
+
 namespace weave {
 namespace http {
 
-const char kGet[] = "GET";
-const char kPatch[] = "PATCH";
-const char kPost[] = "POST";
-const char kPut[] = "PUT";
-
 const char kAuthorization[] = "Authorization";
 const char kContentType[] = "Content-Type";
 
@@ -21,4 +19,21 @@
 const char kWwwFormUrlEncoded[] = "application/x-www-form-urlencoded";
 
 }  // namespace http
+
+using provider::HttpClient;
+
+namespace {
+
+const weave::EnumToStringMap<HttpClient::Method>::Map kMapMethod[] = {
+    {HttpClient::Method::kGet, "GET"},
+    {HttpClient::Method::kPost, "POST"},
+    {HttpClient::Method::kPut, "PUT"},
+    {HttpClient::Method::kPatch, "PATCH"}};
+
+}  // namespace
+
+template <>
+LIBWEAVE_EXPORT EnumToStringMap<HttpClient::Method>::EnumToStringMap()
+    : EnumToStringMap(kMapMethod) {}
+
 }  // namespace weave
diff --git a/libweave/src/http_constants.h b/libweave/src/http_constants.h
index f219a3c..ef84fc8 100644
--- a/libweave/src/http_constants.h
+++ b/libweave/src/http_constants.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,11 +18,6 @@
 const int kServiceUnavailable = 503;
 const int kNotSupported = 501;
 
-extern const char kGet[];
-extern const char kPatch[];
-extern const char kPost[];
-extern const char kPut[];
-
 extern const char kAuthorization[];
 extern const char kContentType[];
 
diff --git a/libweave/src/json_error_codes.cc b/libweave/src/json_error_codes.cc
index 2a53ab5..9bfaef0 100644
--- a/libweave/src/json_error_codes.cc
+++ b/libweave/src/json_error_codes.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/json_error_codes.h b/libweave/src/json_error_codes.h
index 193f6b2..5d3f07b 100644
--- a/libweave/src/json_error_codes.h
+++ b/libweave/src/json_error_codes.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/notification_channel.h b/libweave/src/notification/notification_channel.h
index 972629f..5fb7993 100644
--- a/libweave/src/notification/notification_channel.h
+++ b/libweave/src/notification/notification_channel.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/notification_delegate.h b/libweave/src/notification/notification_delegate.h
index ad49ab8..feb9d17 100644
--- a/libweave/src/notification/notification_delegate.h
+++ b/libweave/src/notification/notification_delegate.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/notification_parser.cc b/libweave/src/notification/notification_parser.cc
index 8f23757..25c525f 100644
--- a/libweave/src/notification/notification_parser.cc
+++ b/libweave/src/notification/notification_parser.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/notification_parser.h b/libweave/src/notification/notification_parser.h
index e77c9e7..ed62d10 100644
--- a/libweave/src/notification/notification_parser.h
+++ b/libweave/src/notification/notification_parser.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/notification_parser_unittest.cc b/libweave/src/notification/notification_parser_unittest.cc
index fae7e0d..146208f 100644
--- a/libweave/src/notification/notification_parser_unittest.cc
+++ b/libweave/src/notification/notification_parser_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/pull_channel.cc b/libweave/src/notification/pull_channel.cc
index f71066c..b192df6 100644
--- a/libweave/src/notification/pull_channel.cc
+++ b/libweave/src/notification/pull_channel.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/pull_channel.h b/libweave/src/notification/pull_channel.h
index 8f33f8f..a48b9f6 100644
--- a/libweave/src/notification/pull_channel.h
+++ b/libweave/src/notification/pull_channel.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/xml_node.cc b/libweave/src/notification/xml_node.cc
index d838613..60f24b3 100644
--- a/libweave/src/notification/xml_node.cc
+++ b/libweave/src/notification/xml_node.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/xml_node.h b/libweave/src/notification/xml_node.h
index 9977fb4..bb5a514 100644
--- a/libweave/src/notification/xml_node.h
+++ b/libweave/src/notification/xml_node.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/xml_node_unittest.cc b/libweave/src/notification/xml_node_unittest.cc
index 09d25d3..34ccfb0 100644
--- a/libweave/src/notification/xml_node_unittest.cc
+++ b/libweave/src/notification/xml_node_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/xmpp_channel.cc b/libweave/src/notification/xmpp_channel.cc
index fae2022..bca4c3b 100644
--- a/libweave/src/notification/xmpp_channel.cc
+++ b/libweave/src/notification/xmpp_channel.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -106,10 +106,12 @@
   }
 }
 
-void XmppChannel::OnMessageRead(size_t size) {
+void XmppChannel::OnMessageRead(size_t size, ErrorPtr error) {
+  read_pending_ = false;
+  if (error)
+    return Restart();
   std::string msg(read_socket_data_.data(), size);
   VLOG(2) << "Received XMPP packet: '" << msg << "'";
-  read_pending_ = false;
 
   if (!size)
     return Restart();
@@ -286,14 +288,24 @@
   LOG(INFO) << "Starting XMPP connection to " << kDefaultXmppHost << ":"
             << kDefaultXmppPort;
 
-  network_->OpenSslSocket(
-      kDefaultXmppHost, kDefaultXmppPort,
-      base::Bind(&XmppChannel::OnSslSocketReady,
-                 task_ptr_factory_.GetWeakPtr()),
-      base::Bind(&XmppChannel::OnSslError, task_ptr_factory_.GetWeakPtr()));
+  network_->OpenSslSocket(kDefaultXmppHost, kDefaultXmppPort,
+                          base::Bind(&XmppChannel::OnSslSocketReady,
+                                     task_ptr_factory_.GetWeakPtr()));
 }
 
-void XmppChannel::OnSslSocketReady(std::unique_ptr<Stream> stream) {
+void XmppChannel::OnSslSocketReady(std::unique_ptr<Stream> stream,
+                                   ErrorPtr error) {
+  if (error) {
+    LOG(ERROR) << "TLS handshake failed. Restarting XMPP connection";
+    backoff_entry_.InformOfRequest(false);
+
+    LOG(INFO) << "Delaying connection to XMPP server for "
+              << backoff_entry_.GetTimeUntilRelease();
+    return task_runner_->PostDelayedTask(
+        FROM_HERE, base::Bind(&XmppChannel::CreateSslSocket,
+                              task_ptr_factory_.GetWeakPtr()),
+        backoff_entry_.GetTimeUntilRelease());
+  }
   CHECK(XmppState::kConnecting == state_);
   backoff_entry_.InformOfRequest(true);
   stream_ = std::move(stream);
@@ -302,18 +314,6 @@
   ScheduleRegularPing();
 }
 
-void XmppChannel::OnSslError(const Error* error) {
-  LOG(ERROR) << "TLS handshake failed. Restarting XMPP connection";
-  backoff_entry_.InformOfRequest(false);
-
-  LOG(INFO) << "Delaying connection to XMPP server for "
-            << backoff_entry_.GetTimeUntilRelease();
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&XmppChannel::CreateSslSocket, task_ptr_factory_.GetWeakPtr()),
-      backoff_entry_.GetTimeUntilRelease());
-}
-
 void XmppChannel::SendMessage(const std::string& message) {
   CHECK(stream_) << "No XMPP socket stream available";
   if (write_pending_) {
@@ -327,13 +327,13 @@
   write_pending_ = true;
   stream_->Write(
       write_socket_data_.data(), write_socket_data_.size(),
-      base::Bind(&XmppChannel::OnMessageSent, task_ptr_factory_.GetWeakPtr()),
-      base::Bind(&XmppChannel::OnWriteError, task_ptr_factory_.GetWeakPtr()));
+      base::Bind(&XmppChannel::OnMessageSent, task_ptr_factory_.GetWeakPtr()));
 }
 
-void XmppChannel::OnMessageSent() {
-  ErrorPtr error;
+void XmppChannel::OnMessageSent(ErrorPtr error) {
   write_pending_ = false;
+  if (error)
+    return Restart();
   if (queued_write_data_.empty()) {
     WaitForMessage();
   } else {
@@ -348,18 +348,7 @@
   read_pending_ = true;
   stream_->Read(
       read_socket_data_.data(), read_socket_data_.size(),
-      base::Bind(&XmppChannel::OnMessageRead, task_ptr_factory_.GetWeakPtr()),
-      base::Bind(&XmppChannel::OnReadError, task_ptr_factory_.GetWeakPtr()));
-}
-
-void XmppChannel::OnReadError(const Error* error) {
-  read_pending_ = false;
-  Restart();
-}
-
-void XmppChannel::OnWriteError(const Error* error) {
-  write_pending_ = false;
-  Restart();
+      base::Bind(&XmppChannel::OnMessageRead, task_ptr_factory_.GetWeakPtr()));
 }
 
 std::string XmppChannel::GetName() const {
diff --git a/libweave/src/notification/xmpp_channel.h b/libweave/src/notification/xmpp_channel.h
index 27e1f1b..e6185d9 100644
--- a/libweave/src/notification/xmpp_channel.h
+++ b/libweave/src/notification/xmpp_channel.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -97,15 +97,12 @@
   void RestartXmppStream();
 
   void CreateSslSocket();
-  void OnSslSocketReady(std::unique_ptr<Stream> stream);
-  void OnSslError(const Error* error);
+  void OnSslSocketReady(std::unique_ptr<Stream> stream, ErrorPtr error);
 
   void WaitForMessage();
 
-  void OnMessageRead(size_t size);
-  void OnMessageSent();
-  void OnReadError(const Error* error);
-  void OnWriteError(const Error* error);
+  void OnMessageRead(size_t size, ErrorPtr error);
+  void OnMessageSent(ErrorPtr error);
   void Restart();
   void CloseStream();
 
diff --git a/libweave/src/notification/xmpp_channel_unittest.cc b/libweave/src/notification/xmpp_channel_unittest.cc
index c6e0be1..674fe22 100644
--- a/libweave/src/notification/xmpp_channel_unittest.cc
+++ b/libweave/src/notification/xmpp_channel_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -88,8 +88,9 @@
         stream_{new test::FakeStream{task_runner_}},
         fake_stream_{stream_.get()} {}
 
-  void Connect(const base::Callback<void(std::unique_ptr<Stream>)>& callback) {
-    callback.Run(std::move(stream_));
+  void Connect(const base::Callback<void(std::unique_ptr<Stream>,
+                                         ErrorPtr error)>& callback) {
+    callback.Run(std::move(stream_), nullptr);
   }
 
   XmppState state() const { return state_; }
@@ -121,7 +122,7 @@
 class XmppChannelTest : public ::testing::Test {
  protected:
   XmppChannelTest() {
-    EXPECT_CALL(network_, OpenSslSocket("talk.google.com", 5223, _, _))
+    EXPECT_CALL(network_, OpenSslSocket("talk.google.com", 5223, _))
         .WillOnce(
             WithArgs<2>(Invoke(&xmpp_client_, &FakeXmppChannel::Connect)));
   }
diff --git a/libweave/src/notification/xmpp_iq_stanza_handler.cc b/libweave/src/notification/xmpp_iq_stanza_handler.cc
index 4b21e2d..8753030 100644
--- a/libweave/src/notification/xmpp_iq_stanza_handler.cc
+++ b/libweave/src/notification/xmpp_iq_stanza_handler.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/xmpp_iq_stanza_handler.h b/libweave/src/notification/xmpp_iq_stanza_handler.h
index 9a463d4..052e5d2 100644
--- a/libweave/src/notification/xmpp_iq_stanza_handler.h
+++ b/libweave/src/notification/xmpp_iq_stanza_handler.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/xmpp_iq_stanza_handler_unittest.cc b/libweave/src/notification/xmpp_iq_stanza_handler_unittest.cc
index 72f3719..052b7c5 100644
--- a/libweave/src/notification/xmpp_iq_stanza_handler_unittest.cc
+++ b/libweave/src/notification/xmpp_iq_stanza_handler_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/xmpp_stream_parser.cc b/libweave/src/notification/xmpp_stream_parser.cc
index 607e4d6..de3b8f1 100644
--- a/libweave/src/notification/xmpp_stream_parser.cc
+++ b/libweave/src/notification/xmpp_stream_parser.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/xmpp_stream_parser.h b/libweave/src/notification/xmpp_stream_parser.h
index 43dfda7..41faaff 100644
--- a/libweave/src/notification/xmpp_stream_parser.h
+++ b/libweave/src/notification/xmpp_stream_parser.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/notification/xmpp_stream_parser_unittest.cc b/libweave/src/notification/xmpp_stream_parser_unittest.cc
index 3c9bade..f6466fe 100644
--- a/libweave/src/notification/xmpp_stream_parser_unittest.cc
+++ b/libweave/src/notification/xmpp_stream_parser_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/cloud_delegate.cc b/libweave/src/privet/cloud_delegate.cc
index 309876a..b4ab288 100644
--- a/libweave/src/privet/cloud_delegate.cc
+++ b/libweave/src/privet/cloud_delegate.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -31,7 +31,8 @@
 
 const int kMaxDeviceRegistrationTimeMinutes = 5;
 
-Command* ReturnNotFound(const std::string& command_id, ErrorPtr* error) {
+CommandInstance* ReturnNotFound(const std::string& command_id,
+                                ErrorPtr* error) {
   Error::AddToPrintf(error, FROM_HERE, errors::kDomain, errors::kNotFound,
                      "Command not found, ID='%s'", command_id.c_str());
   return nullptr;
@@ -153,8 +154,7 @@
 
   void AddCommand(const base::DictionaryValue& command,
                   const UserInfo& user_info,
-                  const CommandSuccessCallback& success_callback,
-                  const ErrorCallback& error_callback) override {
+                  const CommandDoneCallback& callback) override {
     CHECK(user_info.scope() != AuthScope::kNone);
     CHECK_NE(user_info.user_id(), 0u);
 
@@ -165,46 +165,41 @@
       Error::AddToPrintf(&error, FROM_HERE, errors::kDomain,
                          errors::kInvalidParams, "Invalid role: '%s'",
                          str_scope.c_str());
-      return error_callback.Run(error.get());
+      return callback.Run({}, std::move(error));
     }
 
     std::string id;
     if (!command_manager_->AddCommand(command, role, &id, &error))
-      return error_callback.Run(error.get());
+      return callback.Run({}, std::move(error));
 
     command_owners_[id] = user_info.user_id();
-    success_callback.Run(*command_manager_->FindCommand(id)->ToJson());
+    callback.Run(*command_manager_->FindCommand(id)->ToJson(), nullptr);
   }
 
   void GetCommand(const std::string& id,
                   const UserInfo& user_info,
-                  const CommandSuccessCallback& success_callback,
-                  const ErrorCallback& error_callback) override {
+                  const CommandDoneCallback& callback) override {
     CHECK(user_info.scope() != AuthScope::kNone);
     ErrorPtr error;
     auto command = GetCommandInternal(id, user_info, &error);
     if (!command)
-      return error_callback.Run(error.get());
-    success_callback.Run(*command->ToJson());
+      return callback.Run({}, std::move(error));
+    callback.Run(*command->ToJson(), nullptr);
   }
 
   void CancelCommand(const std::string& id,
                      const UserInfo& user_info,
-                     const CommandSuccessCallback& success_callback,
-                     const ErrorCallback& error_callback) override {
+                     const CommandDoneCallback& callback) override {
     CHECK(user_info.scope() != AuthScope::kNone);
     ErrorPtr error;
     auto command = GetCommandInternal(id, user_info, &error);
-    if (!command)
-      return error_callback.Run(error.get());
-
-    command->Cancel();
-    success_callback.Run(*command->ToJson());
+    if (!command || !command->Cancel(&error))
+      return callback.Run({}, std::move(error));
+    callback.Run(*command->ToJson(), nullptr);
   }
 
   void ListCommands(const UserInfo& user_info,
-                    const CommandSuccessCallback& success_callback,
-                    const ErrorCallback& error_callback) override {
+                    const CommandDoneCallback& callback) override {
     CHECK(user_info.scope() != AuthScope::kNone);
 
     base::ListValue list_value;
@@ -219,7 +214,7 @@
     base::DictionaryValue commands_json;
     commands_json.Set("commands", list_value.DeepCopy());
 
-    success_callback.Run(commands_json);
+    callback.Run(commands_json, nullptr);
   }
 
  private:
@@ -285,24 +280,31 @@
       return;
     }
 
-    if (!device_->RegisterDevice(ticket_id, &error).empty()) {
-      backoff_entry_.InformOfRequest(true);
-      setup_state_ = SetupState(SetupState::kSuccess);
-      return;
-    }
-
-    // Registration failed. Retry with backoff.
-    backoff_entry_.InformOfRequest(false);
-    task_runner_->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(&CloudDelegateImpl::CallManagerRegisterDevice,
-                   setup_weak_factory_.GetWeakPtr(), ticket_id, deadline),
-        backoff_entry_.GetTimeUntilRelease());
+    device_->RegisterDevice(
+        ticket_id,
+        base::Bind(&CloudDelegateImpl::RegisterDeviceDone,
+                   setup_weak_factory_.GetWeakPtr(), ticket_id, deadline));
   }
 
-  Command* GetCommandInternal(const std::string& command_id,
-                              const UserInfo& user_info,
-                              ErrorPtr* error) const {
+  void RegisterDeviceDone(const std::string& ticket_id,
+                          const base::Time& deadline,
+                          ErrorPtr error) {
+    if (error) {
+      // Registration failed. Retry with backoff.
+      backoff_entry_.InformOfRequest(false);
+      return task_runner_->PostDelayedTask(
+          FROM_HERE,
+          base::Bind(&CloudDelegateImpl::CallManagerRegisterDevice,
+                     setup_weak_factory_.GetWeakPtr(), ticket_id, deadline),
+          backoff_entry_.GetTimeUntilRelease());
+    }
+    backoff_entry_.InformOfRequest(true);
+    setup_state_ = SetupState(SetupState::kSuccess);
+  }
+
+  CommandInstance* GetCommandInternal(const std::string& command_id,
+                                      const UserInfo& user_info,
+                                      ErrorPtr* error) const {
     if (user_info.scope() != AuthScope::kOwner) {
       auto it = command_owners_.find(command_id);
       if (it == command_owners_.end())
diff --git a/libweave/src/privet/cloud_delegate.h b/libweave/src/privet/cloud_delegate.h
index 74456d3..3875580 100644
--- a/libweave/src/privet/cloud_delegate.h
+++ b/libweave/src/privet/cloud_delegate.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -39,8 +39,9 @@
   CloudDelegate();
   virtual ~CloudDelegate();
 
-  using CommandSuccessCallback =
-      base::Callback<void(const base::DictionaryValue& commands)>;
+  using CommandDoneCallback =
+      base::Callback<void(const base::DictionaryValue& commands,
+                          ErrorPtr error)>;
 
   class Observer {
    public:
@@ -107,25 +108,21 @@
   // Adds command created from the given JSON representation.
   virtual void AddCommand(const base::DictionaryValue& command,
                           const UserInfo& user_info,
-                          const CommandSuccessCallback& success_callback,
-                          const ErrorCallback& error_callback) = 0;
+                          const CommandDoneCallback& callback) = 0;
 
   // Returns command with the given ID.
   virtual void GetCommand(const std::string& id,
                           const UserInfo& user_info,
-                          const CommandSuccessCallback& success_callback,
-                          const ErrorCallback& error_callback) = 0;
+                          const CommandDoneCallback& callback) = 0;
 
   // Cancels command with the given ID.
   virtual void CancelCommand(const std::string& id,
                              const UserInfo& user_info,
-                             const CommandSuccessCallback& success_callback,
-                             const ErrorCallback& error_callback) = 0;
+                             const CommandDoneCallback& callback) = 0;
 
   // Lists commands.
   virtual void ListCommands(const UserInfo& user_info,
-                            const CommandSuccessCallback& success_callback,
-                            const ErrorCallback& error_callback) = 0;
+                            const CommandDoneCallback& callback) = 0;
 
   void AddObserver(Observer* observer) { observer_list_.AddObserver(observer); }
   void RemoveObserver(Observer* observer) {
diff --git a/libweave/src/privet/constants.cc b/libweave/src/privet/constants.cc
index ade70af..3cc3e4a 100644
--- a/libweave/src/privet/constants.cc
+++ b/libweave/src/privet/constants.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/constants.h b/libweave/src/privet/constants.h
index 256cf9e..0668879 100644
--- a/libweave/src/privet/constants.h
+++ b/libweave/src/privet/constants.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/device_delegate.cc b/libweave/src/privet/device_delegate.cc
index 3b2e247..78aa3c4 100644
--- a/libweave/src/privet/device_delegate.cc
+++ b/libweave/src/privet/device_delegate.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -15,7 +15,8 @@
 
 class DeviceDelegateImpl : public DeviceDelegate {
  public:
-  DeviceDelegateImpl() = default;
+  DeviceDelegateImpl(uint16_t http_port, uint16_t https_port)
+      : http_port_{http_port}, https_port_{https_port} {}
   ~DeviceDelegateImpl() override = default;
 
   std::pair<uint16_t, uint16_t> GetHttpEnpoint() const override {
@@ -45,8 +46,11 @@
 DeviceDelegate::~DeviceDelegate() {}
 
 // static
-std::unique_ptr<DeviceDelegate> DeviceDelegate::CreateDefault() {
-  return std::unique_ptr<DeviceDelegate>(new DeviceDelegateImpl());
+std::unique_ptr<DeviceDelegate> DeviceDelegate::CreateDefault(
+    uint16_t http_port,
+    uint16_t https_port) {
+  return std::unique_ptr<DeviceDelegate>(
+      new DeviceDelegateImpl(http_port, https_port));
 }
 
 }  // namespace privet
diff --git a/libweave/src/privet/device_delegate.h b/libweave/src/privet/device_delegate.h
index 12996d7..3f13b22 100644
--- a/libweave/src/privet/device_delegate.h
+++ b/libweave/src/privet/device_delegate.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -38,7 +38,8 @@
   virtual void SetHttpsPort(uint16_t port) = 0;
 
   // Create default instance.
-  static std::unique_ptr<DeviceDelegate> CreateDefault();
+  static std::unique_ptr<DeviceDelegate> CreateDefault(uint16_t http_port,
+                                                       uint16_t https_port);
 };
 
 }  // namespace privet
diff --git a/libweave/src/privet/mock_delegates.h b/libweave/src/privet/mock_delegates.h
index 48227ae..159fa09 100644
--- a/libweave/src/privet/mock_delegates.h
+++ b/libweave/src/privet/mock_delegates.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -154,25 +154,19 @@
   MOCK_CONST_METHOD0(GetCloudId, std::string());
   MOCK_CONST_METHOD0(GetState, const base::DictionaryValue&());
   MOCK_CONST_METHOD0(GetCommandDef, const base::DictionaryValue&());
-  MOCK_METHOD4(AddCommand,
+  MOCK_METHOD3(AddCommand,
                void(const base::DictionaryValue&,
                     const UserInfo&,
-                    const CommandSuccessCallback&,
-                    const ErrorCallback&));
-  MOCK_METHOD4(GetCommand,
+                    const CommandDoneCallback&));
+  MOCK_METHOD3(GetCommand,
                void(const std::string&,
                     const UserInfo&,
-                    const CommandSuccessCallback&,
-                    const ErrorCallback&));
-  MOCK_METHOD4(CancelCommand,
+                    const CommandDoneCallback&));
+  MOCK_METHOD3(CancelCommand,
                void(const std::string&,
                     const UserInfo&,
-                    const CommandSuccessCallback&,
-                    const ErrorCallback&));
-  MOCK_METHOD3(ListCommands,
-               void(const UserInfo&,
-                    const CommandSuccessCallback&,
-                    const ErrorCallback&));
+                    const CommandDoneCallback&));
+  MOCK_METHOD2(ListCommands, void(const UserInfo&, const CommandDoneCallback&));
 
   MockCloudDelegate() {
     EXPECT_CALL(*this, GetDeviceId()).WillRepeatedly(Return("TestId"));
diff --git a/libweave/src/privet/openssl_utils.cc b/libweave/src/privet/openssl_utils.cc
index c590a34..2a98fa8 100644
--- a/libweave/src/privet/openssl_utils.cc
+++ b/libweave/src/privet/openssl_utils.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/openssl_utils.h b/libweave/src/privet/openssl_utils.h
index 0206681..a67d023 100644
--- a/libweave/src/privet/openssl_utils.h
+++ b/libweave/src/privet/openssl_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/privet_handler.cc b/libweave/src/privet/privet_handler.cc
index 47ee8f7..2180cad 100644
--- a/libweave/src/privet/privet_handler.cc
+++ b/libweave/src/privet/privet_handler.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,6 +23,7 @@
 #include "src/privet/security_delegate.h"
 #include "src/privet/wifi_delegate.h"
 #include "src/string_utils.h"
+#include "src/utils.h"
 
 namespace weave {
 namespace privet {
@@ -90,8 +91,6 @@
 
 const char kAuthorizationHeaderPrefix[] = "Privet";
 
-const char kErrorCodeKey[] = "code";
-const char kErrorMessageKey[] = "message";
 const char kErrorDebugInfoKey[] = "debugInfo";
 
 const char kSetupStartSsidKey[] = "ssid";
@@ -156,13 +155,6 @@
   return SplitAtFirst(auth_header, " ", true).second;
 }
 
-std::unique_ptr<base::DictionaryValue> ErrorInfoToJson(const Error& error) {
-  std::unique_ptr<base::DictionaryValue> output{new base::DictionaryValue};
-  output->SetString(kErrorMessageKey, error.GetMessage());
-  output->SetString(kErrorCodeKey, error.GetCode());
-  return output;
-}
-
 // Creates JSON similar to GCD server error format.
 std::unique_ptr<base::DictionaryValue> ErrorToJson(const Error& error) {
   std::unique_ptr<base::DictionaryValue> output{ErrorInfoToJson(error)};
@@ -182,7 +174,7 @@
 }
 
 template <class T>
-void SetState(const T& state, base::DictionaryValue* parent) {
+void SetStateProperties(const T& state, base::DictionaryValue* parent) {
   if (!state.error()) {
     parent->SetString(kStatusKey, EnumToString(state.status()));
     return;
@@ -206,23 +198,20 @@
 }
 
 void OnCommandRequestSucceeded(const PrivetHandler::RequestCallback& callback,
-                               const base::DictionaryValue& output) {
-  callback.Run(http::kOk, output);
-}
+                               const base::DictionaryValue& output,
+                               ErrorPtr error) {
+  if (!error)
+    return callback.Run(http::kOk, output);
 
-void OnCommandRequestFailed(const PrivetHandler::RequestCallback& callback,
-                            const Error* error) {
   if (error->HasError("gcd", "unknown_command")) {
-    ErrorPtr new_error = error->Clone();
-    Error::AddTo(&new_error, FROM_HERE, errors::kDomain, errors::kNotFound,
+    Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kNotFound,
                  "Unknown command ID");
-    return ReturnError(*new_error, callback);
+    return ReturnError(*error, callback);
   }
   if (error->HasError("gcd", "access_denied")) {
-    ErrorPtr new_error = error->Clone();
-    Error::AddTo(&new_error, FROM_HERE, errors::kDomain, errors::kAccessDenied,
+    Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kAccessDenied,
                  error->GetMessage());
-    return ReturnError(*new_error, callback);
+    return ReturnError(*error, callback);
   }
   return ReturnError(*error, callback);
 }
@@ -330,7 +319,7 @@
     DCHECK(!state.IsStatusEqual(ConnectionState::kOnline));
     result->SetString(kInfoWifiHostedSsidKey, hosted_ssid);
   }
-  SetState(state, result.get());
+  SetStateProperties(state, result.get());
   return result;
 }
 
@@ -338,7 +327,7 @@
     const CloudDelegate& cloud) {
   std::unique_ptr<base::DictionaryValue> gcd(new base::DictionaryValue());
   gcd->SetString(kInfoIdKey, cloud.GetCloudId());
-  SetState(cloud.GetConnectionState(), gcd.get());
+  SetStateProperties(cloud.GetConnectionState(), gcd.get());
   return gcd;
 }
 
@@ -351,6 +340,18 @@
 
 }  // namespace
 
+std::vector<std::string> PrivetHandler::GetHttpPaths() const {
+  // TODO(vitalybuka): Should be subset only.
+  return GetHttpsPaths();
+}
+
+std::vector<std::string> PrivetHandler::GetHttpsPaths() const {
+  std::vector<std::string> result;
+  for (const auto& pair : handlers_)
+    result.push_back(pair.first);
+  return result;
+}
+
 PrivetHandler::PrivetHandler(CloudDelegate* cloud,
                              DeviceDelegate* device,
                              SecurityDelegate* security,
@@ -718,7 +719,7 @@
   if (!state.IsStatusEqual(SetupState::kNone)) {
     base::DictionaryValue* gcd = new base::DictionaryValue;
     output.Set(kGcdKey, gcd);
-    SetState(state, gcd);
+    SetStateProperties(state, gcd);
     if (state.IsStatusEqual(SetupState::kSuccess))
       gcd->SetString(kInfoIdKey, cloud_->GetCloudId());
   }
@@ -728,7 +729,7 @@
     if (!state.IsStatusEqual(SetupState::kNone)) {
       base::DictionaryValue* wifi = new base::DictionaryValue;
       output.Set(kWifiKey, wifi);
-      SetState(state, wifi);
+      SetStateProperties(state, wifi);
       if (state.IsStatusEqual(SetupState::kSuccess))
         wifi->SetString(kInfoWifiSsidKey, wifi_->GetCurrentlyConnectedSsid());
     }
@@ -764,8 +765,7 @@
                                           const UserInfo& user_info,
                                           const RequestCallback& callback) {
   cloud_->AddCommand(input, user_info,
-                     base::Bind(&OnCommandRequestSucceeded, callback),
-                     base::Bind(&OnCommandRequestFailed, callback));
+                     base::Bind(&OnCommandRequestSucceeded, callback));
 }
 
 void PrivetHandler::HandleCommandsStatus(const base::DictionaryValue& input,
@@ -780,16 +780,14 @@
     return ReturnError(*error, callback);
   }
   cloud_->GetCommand(id, user_info,
-                     base::Bind(&OnCommandRequestSucceeded, callback),
-                     base::Bind(&OnCommandRequestFailed, callback));
+                     base::Bind(&OnCommandRequestSucceeded, callback));
 }
 
 void PrivetHandler::HandleCommandsList(const base::DictionaryValue& input,
                                        const UserInfo& user_info,
                                        const RequestCallback& callback) {
   cloud_->ListCommands(user_info,
-                       base::Bind(&OnCommandRequestSucceeded, callback),
-                       base::Bind(&OnCommandRequestFailed, callback));
+                       base::Bind(&OnCommandRequestSucceeded, callback));
 }
 
 void PrivetHandler::HandleCommandsCancel(const base::DictionaryValue& input,
@@ -804,8 +802,7 @@
     return ReturnError(*error, callback);
   }
   cloud_->CancelCommand(id, user_info,
-                        base::Bind(&OnCommandRequestSucceeded, callback),
-                        base::Bind(&OnCommandRequestFailed, callback));
+                        base::Bind(&OnCommandRequestSucceeded, callback));
 }
 
 }  // namespace privet
diff --git a/libweave/src/privet/privet_handler.h b/libweave/src/privet/privet_handler.h
index ce32485..7818d87 100644
--- a/libweave/src/privet/privet_handler.h
+++ b/libweave/src/privet/privet_handler.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -48,6 +48,9 @@
   void OnCommandDefsChanged() override;
   void OnStateChanged() override;
 
+  std::vector<std::string> GetHttpPaths() const;
+  std::vector<std::string> GetHttpsPaths() const;
+
   // Handles HTTP/HTTPS Privet request.
   // |api| is the path from the HTTP request, e.g /privet/info.
   // |auth_header| is the Authentication header from HTTP request.
diff --git a/libweave/src/privet/privet_handler_unittest.cc b/libweave/src/privet/privet_handler_unittest.cc
index 6829984..6f123e2 100644
--- a/libweave/src/privet/privet_handler_unittest.cc
+++ b/libweave/src/privet/privet_handler_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -24,6 +24,7 @@
 using testing::Invoke;
 using testing::Return;
 using testing::SetArgPointee;
+using testing::WithArgs;
 
 namespace weave {
 namespace privet {
@@ -648,8 +649,11 @@
   base::DictionaryValue command;
   LoadTestJson(kInput, &command);
   LoadTestJson("{'id':'5'}", &command);
-  EXPECT_CALL(cloud_, AddCommand(_, _, _, _))
-      .WillOnce(RunCallback<2, const base::DictionaryValue&>(command));
+  EXPECT_CALL(cloud_, AddCommand(_, _, _))
+      .WillOnce(WithArgs<2>(Invoke(
+          [&command](const CloudDelegate::CommandDoneCallback& callback) {
+            callback.Run(command, nullptr);
+          })));
 
   EXPECT_PRED2(IsEqualJson, "{'name':'test', 'id':'5'}",
                HandleRequest("/privet/v3/commands/execute", kInput));
@@ -660,16 +664,22 @@
   base::DictionaryValue command;
   LoadTestJson(kInput, &command);
   LoadTestJson("{'name':'test'}", &command);
-  EXPECT_CALL(cloud_, GetCommand(_, _, _, _))
-      .WillOnce(RunCallback<2, const base::DictionaryValue&>(command));
+  EXPECT_CALL(cloud_, GetCommand(_, _, _))
+      .WillOnce(WithArgs<2>(Invoke(
+          [&command](const CloudDelegate::CommandDoneCallback& callback) {
+            callback.Run(command, nullptr);
+          })));
 
   EXPECT_PRED2(IsEqualJson, "{'name':'test', 'id':'5'}",
                HandleRequest("/privet/v3/commands/status", kInput));
 
   ErrorPtr error;
   Error::AddTo(&error, FROM_HERE, errors::kDomain, "notFound", "");
-  EXPECT_CALL(cloud_, GetCommand(_, _, _, _))
-      .WillOnce(RunCallback<3>(error.get()));
+  EXPECT_CALL(cloud_, GetCommand(_, _, _))
+      .WillOnce(WithArgs<2>(
+          Invoke([&error](const CloudDelegate::CommandDoneCallback& callback) {
+            callback.Run({}, std::move(error));
+          })));
 
   EXPECT_PRED2(IsEqualError, CodeWithReason(404, "notFound"),
                HandleRequest("/privet/v3/commands/status", "{'id': '15'}"));
@@ -679,16 +689,22 @@
   const char kExpected[] = "{'id': '5', 'name':'test', 'state':'cancelled'}";
   base::DictionaryValue command;
   LoadTestJson(kExpected, &command);
-  EXPECT_CALL(cloud_, CancelCommand(_, _, _, _))
-      .WillOnce(RunCallback<2, const base::DictionaryValue&>(command));
+  EXPECT_CALL(cloud_, CancelCommand(_, _, _))
+      .WillOnce(WithArgs<2>(Invoke(
+          [&command](const CloudDelegate::CommandDoneCallback& callback) {
+            callback.Run(command, nullptr);
+          })));
 
   EXPECT_PRED2(IsEqualJson, kExpected,
                HandleRequest("/privet/v3/commands/cancel", "{'id': '8'}"));
 
   ErrorPtr error;
   Error::AddTo(&error, FROM_HERE, errors::kDomain, "notFound", "");
-  EXPECT_CALL(cloud_, CancelCommand(_, _, _, _))
-      .WillOnce(RunCallback<3>(error.get()));
+  EXPECT_CALL(cloud_, CancelCommand(_, _, _))
+      .WillOnce(WithArgs<2>(
+          Invoke([&error](const CloudDelegate::CommandDoneCallback& callback) {
+            callback.Run({}, std::move(error));
+          })));
 
   EXPECT_PRED2(IsEqualError, CodeWithReason(404, "notFound"),
                HandleRequest("/privet/v3/commands/cancel", "{'id': '11'}"));
@@ -704,8 +720,11 @@
   base::DictionaryValue commands;
   LoadTestJson(kExpected, &commands);
 
-  EXPECT_CALL(cloud_, ListCommands(_, _, _))
-      .WillOnce(RunCallback<1, const base::DictionaryValue&>(commands));
+  EXPECT_CALL(cloud_, ListCommands(_, _))
+      .WillOnce(WithArgs<1>(Invoke(
+          [&commands](const CloudDelegate::CommandDoneCallback& callback) {
+            callback.Run(commands, nullptr);
+          })));
 
   EXPECT_PRED2(IsEqualJson, kExpected,
                HandleRequest("/privet/v3/commands/list", "{}"));
diff --git a/libweave/src/privet/privet_manager.cc b/libweave/src/privet/privet_manager.cc
index ac47c6c..7a97570 100644
--- a/libweave/src/privet/privet_manager.cc
+++ b/libweave/src/privet/privet_manager.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -17,6 +17,7 @@
 #include <base/values.h>
 #include <weave/provider/network.h>
 
+#include "src/bind_lambda.h"
 #include "src/device_registration_info.h"
 #include "src/http_constants.h"
 #include "src/privet/cloud_delegate.h"
@@ -24,6 +25,7 @@
 #include "src/privet/device_delegate.h"
 #include "src/privet/privet_handler.h"
 #include "src/privet/publisher.h"
+#include "src/streams.h"
 #include "src/string_utils.h"
 
 namespace weave {
@@ -35,12 +37,11 @@
 using provider::HttpServer;
 using provider::Wifi;
 
-Manager::Manager() {}
+Manager::Manager(TaskRunner* task_runner) : task_runner_{task_runner} {}
 
 Manager::~Manager() {}
 
-void Manager::Start(TaskRunner* task_runner,
-                    Network* network,
+void Manager::Start(Network* network,
                     DnsServiceDiscovery* dns_sd,
                     HttpServer* http_server,
                     Wifi* wifi,
@@ -49,16 +50,19 @@
                     StateManager* state_manager) {
   disable_security_ = device->GetSettings().disable_security;
 
-  device_ = DeviceDelegate::CreateDefault();
-  cloud_ = CloudDelegate::CreateDefault(task_runner, device, command_manager,
+  device_ = DeviceDelegate::CreateDefault(http_server->GetHttpPort(),
+                                          http_server->GetHttpsPort());
+  cloud_ = CloudDelegate::CreateDefault(task_runner_, device, command_manager,
                                         state_manager);
   cloud_observer_.Add(cloud_.get());
   security_.reset(new SecurityManager(
       device->GetSettings().secret, device->GetSettings().pairing_modes,
-      device->GetSettings().embedded_code, disable_security_, task_runner));
+      device->GetSettings().embedded_code, disable_security_, task_runner_));
+  security_->SetCertificateFingerprint(
+      http_server->GetHttpsCertificateFingerprint());
   if (device->GetSettings().secret.empty()) {
     // TODO(vitalybuka): Post all Config::Transaction to avoid following.
-    task_runner->PostDelayedTask(
+    task_runner_->PostDelayedTask(
         FROM_HERE,
         base::Bind(&Manager::SaveDeviceSecret, weak_ptr_factory_.GetWeakPtr(),
                    base::Unretained(device->GetMutableConfig())),
@@ -70,7 +74,7 @@
   if (wifi && device->GetSettings().wifi_auto_setup_enabled) {
     VLOG(1) << "Enabling WiFi bootstrapping.";
     wifi_bootstrap_manager_.reset(new WifiBootstrapManager(
-        device->GetMutableConfig(), task_runner, network, wifi, cloud_.get()));
+        device->GetMutableConfig(), task_runner_, network, wifi, cloud_.get()));
     wifi_bootstrap_manager_->Init();
   }
 
@@ -83,11 +87,17 @@
                                           security_.get(),
                                           wifi_bootstrap_manager_.get()));
 
-  http_server->AddOnStateChangedCallback(base::Bind(
-      &Manager::OnHttpServerStatusChanged, weak_ptr_factory_.GetWeakPtr()));
-  http_server->AddRequestHandler("/privet/",
-                                 base::Bind(&Manager::PrivetRequestHandler,
-                                            weak_ptr_factory_.GetWeakPtr()));
+  for (const auto& path : privet_handler_->GetHttpPaths()) {
+    http_server->AddHttpRequestHandler(
+        path, base::Bind(&Manager::PrivetRequestHandler,
+                         weak_ptr_factory_.GetWeakPtr()));
+  }
+
+  for (const auto& path : privet_handler_->GetHttpsPaths()) {
+    http_server->AddHttpsRequestHandler(
+        path, base::Bind(&Manager::PrivetRequestHandler,
+                         weak_ptr_factory_.GetWeakPtr()));
+  }
 }
 
 std::string Manager::GetCurrentlyConnectedSsid() const {
@@ -107,43 +117,52 @@
 }
 
 void Manager::PrivetRequestHandler(
-    const HttpServer::Request& request,
-    const HttpServer::OnReplyCallback& callback) {
-  std::string auth_header = request.GetFirstHeader(http::kAuthorization);
-  if (auth_header.empty() && disable_security_)
-    auth_header = "Privet anonymous";
-  std::string data(request.GetData().begin(), request.GetData().end());
-  VLOG(3) << "Input: " << data;
-
-  base::DictionaryValue empty;
-  std::unique_ptr<base::Value> value;
-  const base::DictionaryValue* dictionary = &empty;
+    std::unique_ptr<provider::HttpServer::Request> req) {
+  std::shared_ptr<provider::HttpServer::Request> request{std::move(req)};
 
   std::string content_type =
-      SplitAtFirst(request.GetFirstHeader(http::kContentType), ";", true).first;
-  if (content_type == http::kJson) {
-    value.reset(base::JSONReader::Read(data).release());
-    if (value)
-      value->GetAsDictionary(&dictionary);
-  }
+      SplitAtFirst(request->GetFirstHeader(http::kContentType), ";", true)
+          .first;
 
-  privet_handler_->HandleRequest(
-      request.GetPath(), auth_header, dictionary,
-      base::Bind(&Manager::PrivetResponseHandler,
-                 weak_ptr_factory_.GetWeakPtr(), callback));
+  return PrivetRequestHandlerWithData(request, content_type == http::kJson
+                                                   ? request->GetData()
+                                                   : std::string{});
 }
 
-void Manager::PrivetResponseHandler(const HttpServer::OnReplyCallback& callback,
-                                    int status,
-                                    const base::DictionaryValue& output) {
+void Manager::PrivetRequestHandlerWithData(
+    const std::shared_ptr<provider::HttpServer::Request>& request,
+    const std::string& data) {
+  std::string auth_header = request->GetFirstHeader(http::kAuthorization);
+  if (auth_header.empty() && disable_security_)
+    auth_header = "Privet anonymous";
+
+  base::DictionaryValue empty;
+  auto value = base::JSONReader::Read(data);
+  const base::DictionaryValue* dictionary = &empty;
+  if (value)
+    value->GetAsDictionary(&dictionary);
+
+  VLOG(3) << "Input: " << *dictionary;
+
+  privet_handler_->HandleRequest(
+      request->GetPath(), auth_header, dictionary,
+      base::Bind(&Manager::PrivetResponseHandler,
+                 weak_ptr_factory_.GetWeakPtr(), request));
+}
+
+void Manager::PrivetResponseHandler(
+    const std::shared_ptr<provider::HttpServer::Request>& request,
+    int status,
+    const base::DictionaryValue& output) {
   VLOG(3) << "status: " << status << ", Output: " << output;
   std::string data;
   base::JSONWriter::WriteWithOptions(
       output, base::JSONWriter::OPTIONS_PRETTY_PRINT, &data);
-  callback.Run(status, data, http::kJson);
+  request->SendReply(status, data, http::kJson);
 }
 
 void Manager::OnChanged() {
+  VLOG(1) << "Manager::OnChanged";
   if (publisher_)
     publisher_->Update();
 }
@@ -152,16 +171,6 @@
   OnChanged();
 }
 
-void Manager::OnHttpServerStatusChanged(const HttpServer& server) {
-  if (device_->GetHttpEnpoint().first != server.GetHttpPort()) {
-    device_->SetHttpPort(server.GetHttpPort());
-    if (publisher_)  // Only HTTP port is published
-      publisher_->Update();
-  }
-  device_->SetHttpsPort(server.GetHttpsPort());
-  security_->SetCertificateFingerprint(server.GetHttpsCertificateFingerprint());
-}
-
 void Manager::SaveDeviceSecret(Config* config) {
   Config::Transaction transaction(config);
   transaction.set_secret(security_->GetSecret());
diff --git a/libweave/src/privet/privet_manager.h b/libweave/src/privet/privet_manager.h
index 35fb6f9..3174ca0 100644
--- a/libweave/src/privet/privet_manager.h
+++ b/libweave/src/privet/privet_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -44,11 +44,10 @@
 
 class Manager : public CloudDelegate::Observer {
  public:
-  Manager();
+  explicit Manager(provider::TaskRunner* task_runner);
   ~Manager() override;
 
-  void Start(provider::TaskRunner* task_runner,
-             provider::Network* network,
+  void Start(provider::Network* network,
              provider::DnsServiceDiscovery* dns_sd,
              provider::HttpServer* http_server,
              provider::Wifi* wifi,
@@ -67,21 +66,24 @@
   void OnDeviceInfoChanged() override;
 
   void PrivetRequestHandler(
-      const provider::HttpServer::Request& request,
-      const provider::HttpServer::OnReplyCallback& callback);
+      std::unique_ptr<provider::HttpServer::Request> request);
+
+  void PrivetRequestHandlerWithData(
+      const std::shared_ptr<provider::HttpServer::Request>& request,
+      const std::string& data);
 
   void PrivetResponseHandler(
-      const provider::HttpServer::OnReplyCallback& callback,
+      const std::shared_ptr<provider::HttpServer::Request>& request,
       int status,
       const base::DictionaryValue& output);
 
   void OnChanged();
   void OnConnectivityChanged();
 
-  void OnHttpServerStatusChanged(const provider::HttpServer& server);
   void SaveDeviceSecret(Config* config);
 
   bool disable_security_{false};
+  provider::TaskRunner* task_runner_{nullptr};
   std::unique_ptr<CloudDelegate> cloud_;
   std::unique_ptr<DeviceDelegate> device_;
   std::unique_ptr<SecurityManager> security_;
diff --git a/libweave/src/privet/privet_types.cc b/libweave/src/privet/privet_types.cc
index 8d380f0..ab026e1 100644
--- a/libweave/src/privet/privet_types.cc
+++ b/libweave/src/privet/privet_types.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -58,9 +58,9 @@
 
 const EnumToStringMap<Network::State>::Map kNetworkStateMap[] = {
     {Network::State::kOffline, "offline"},
-    {Network::State::kFailure, "failure"},
+    {Network::State::kError, "error"},
     {Network::State::kConnecting, "connecting"},
-    {Network::State::kConnected, "connected"},
+    {Network::State::kOnline, "online"},
 };
 
 }  // namespace
diff --git a/libweave/src/privet/privet_types.h b/libweave/src/privet/privet_types.h
index 9c05be1..bc8e400 100644
--- a/libweave/src/privet/privet_types.h
+++ b/libweave/src/privet/privet_types.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/publisher.cc b/libweave/src/privet/publisher.cc
index 37da735..f323dac 100644
--- a/libweave/src/privet/publisher.cc
+++ b/libweave/src/privet/publisher.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -50,7 +50,7 @@
   std::string model_id{cloud_->GetModelId()};
   DCHECK_EQ(model_id.size(), 5U);
 
-  VLOG(1) << "Starting peerd advertising.";
+  VLOG(2) << "DNS-SD update requested";
   const uint16_t port = device_->GetHttpEnpoint().first;
   DCHECK_NE(port, 0);
 
@@ -77,6 +77,7 @@
   auto new_data = std::make_pair(port, txt_record);
   if (published_ == new_data)
     return;
+  VLOG(1) << "Updating DNS-SD";
   published_ = new_data;
   dns_sd_->PublishService(kPrivetServiceType, port, txt_record);
 }
@@ -85,7 +86,7 @@
   if (!published_.first)
     return;
   published_ = {};
-  VLOG(1) << "Stopping service publishing.";
+  VLOG(1) << "Stopping service publishing";
   dns_sd_->StopPublishing(kPrivetServiceType);
 }
 
diff --git a/libweave/src/privet/publisher.h b/libweave/src/privet/publisher.h
index ce417a7..5de3345 100644
--- a/libweave/src/privet/publisher.h
+++ b/libweave/src/privet/publisher.h
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/security_delegate.h b/libweave/src/privet/security_delegate.h
index e56dee7..1d28ba3 100644
--- a/libweave/src/privet/security_delegate.h
+++ b/libweave/src/privet/security_delegate.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/security_manager.cc b/libweave/src/privet/security_manager.cc
index 9b8e853..8b1500c 100644
--- a/libweave/src/privet/security_manager.cc
+++ b/libweave/src/privet/security_manager.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -18,11 +18,11 @@
 #include <base/time/time.h>
 #include <weave/provider/task_runner.h>
 
-#include "external/crypto/p224_spake.h"
 #include "src/data_encoding.h"
 #include "src/privet/constants.h"
 #include "src/privet/openssl_utils.h"
 #include "src/string_utils.h"
+#include "third_party/chromium/crypto/p224_spake.h"
 
 namespace weave {
 namespace privet {
diff --git a/libweave/src/privet/security_manager.h b/libweave/src/privet/security_manager.h
index c1adfc8..c99201b 100644
--- a/libweave/src/privet/security_manager.h
+++ b/libweave/src/privet/security_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/security_manager_unittest.cc b/libweave/src/privet/security_manager_unittest.cc
index 7e4ea05..7229710 100644
--- a/libweave/src/privet/security_manager_unittest.cc
+++ b/libweave/src/privet/security_manager_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -21,9 +21,9 @@
 #include <gtest/gtest.h>
 #include <weave/provider/test/fake_task_runner.h>
 
-#include "external/crypto/p224_spake.h"
 #include "src/data_encoding.h"
 #include "src/privet/openssl_utils.h"
+#include "third_party/chromium/crypto/p224_spake.h"
 
 using testing::Eq;
 using testing::_;
diff --git a/libweave/src/privet/wifi_bootstrap_manager.cc b/libweave/src/privet/wifi_bootstrap_manager.cc
index 7e439e3..255ae6b 100644
--- a/libweave/src/privet/wifi_bootstrap_manager.cc
+++ b/libweave/src/privet/wifi_bootstrap_manager.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -59,7 +59,7 @@
 }
 
 void WifiBootstrapManager::StartBootstrapping() {
-  if (network_->GetConnectionState() == Network::State::kConnected) {
+  if (network_->GetConnectionState() == Network::State::kOnline) {
     // If one of the devices we monitor for connectivity is online, we need not
     // start an AP.  For most devices, this is a situation which happens in
     // testing when we have an ethernet connection.  If you need to always
@@ -96,22 +96,12 @@
           << ", pass=" << passphrase << ").";
   UpdateState(State::kConnecting);
   task_runner_->PostDelayedTask(
-      FROM_HERE, base::Bind(&WifiBootstrapManager::OnConnectError,
-                            tasks_weak_factory_.GetWeakPtr(), nullptr),
+      FROM_HERE, base::Bind(&WifiBootstrapManager::OnConnectTimeout,
+                            tasks_weak_factory_.GetWeakPtr()),
       base::TimeDelta::FromMinutes(3));
   wifi_->Connect(ssid, passphrase,
-                 base::Bind(&WifiBootstrapManager::OnConnectSuccess,
-                            tasks_weak_factory_.GetWeakPtr(), ssid),
-                 base::Bind(&WifiBootstrapManager::OnConnectError,
-                            tasks_weak_factory_.GetWeakPtr()));
-}
-
-void WifiBootstrapManager::OnConnectError(const Error* error) {
-  ErrorPtr new_error = error ? error->Clone() : nullptr;
-  Error::AddTo(&new_error, FROM_HERE, errors::kDomain, errors::kInvalidState,
-               "Failed to connect to provided network");
-  setup_state_ = SetupState{std::move(new_error)};
-  StartBootstrapping();
+                 base::Bind(&WifiBootstrapManager::OnConnectDone,
+                            tasks_weak_factory_.GetWeakPtr(), ssid));
 }
 
 void WifiBootstrapManager::EndConnecting() {}
@@ -122,7 +112,7 @@
   // connectivity state.  See OnConnectivityChange().
   UpdateState(State::kMonitoring);
 
-  if (network_->GetConnectionState() == Network::State::kConnected) {
+  if (network_->GetConnectionState() == Network::State::kOnline) {
     monitor_until_ = {};
   } else {
     if (monitor_until_.is_null()) {
@@ -204,7 +194,14 @@
   return {WifiType::kWifi24};
 }
 
-void WifiBootstrapManager::OnConnectSuccess(const std::string& ssid) {
+void WifiBootstrapManager::OnConnectDone(const std::string& ssid,
+                                         ErrorPtr error) {
+  if (error) {
+    Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kInvalidState,
+                 "Failed to connect to provided network");
+    setup_state_ = SetupState{std::move(error)};
+    return StartBootstrapping();
+  }
   VLOG(1) << "Wifi was connected successfully";
   Config::Transaction change{config_};
   change.set_last_configured_ssid(ssid);
@@ -213,6 +210,14 @@
   StartMonitoring();
 }
 
+void WifiBootstrapManager::OnConnectTimeout() {
+  ErrorPtr error;
+  Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kInvalidState,
+               "Timeout connecting to provided network");
+  setup_state_ = SetupState{std::move(error)};
+  return StartBootstrapping();
+}
+
 void WifiBootstrapManager::OnBootstrapTimeout() {
   VLOG(1) << "Bootstrapping has timed out.";
   StartMonitoring();
@@ -225,7 +230,7 @@
 
   if (state_ == State::kMonitoring ||  // Reset monitoring timeout.
       (state_ != State::kDisabled &&
-       network_->GetConnectionState() == Network::State::kConnected)) {
+       network_->GetConnectionState() == Network::State::kOnline)) {
     StartMonitoring();
   }
 }
@@ -246,7 +251,7 @@
     case Network::State::kOffline:
       connection_state_ = ConnectionState{ConnectionState::kOffline};
       return;
-    case Network::State::kFailure: {
+    case Network::State::kError: {
       // TODO(wiley) Pull error information from somewhere.
       ErrorPtr error;
       Error::AddTo(&error, FROM_HERE, errors::kDomain, errors::kInvalidState,
@@ -257,7 +262,7 @@
     case Network::State::kConnecting:
       connection_state_ = ConnectionState{ConnectionState::kConnecting};
       return;
-    case Network::State::kConnected:
+    case Network::State::kOnline:
       connection_state_ = ConnectionState{ConnectionState::kOnline};
       return;
   }
diff --git a/libweave/src/privet/wifi_bootstrap_manager.h b/libweave/src/privet/wifi_bootstrap_manager.h
index 6423b9d..4ff2d3e 100644
--- a/libweave/src/privet/wifi_bootstrap_manager.h
+++ b/libweave/src/privet/wifi_bootstrap_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -87,8 +87,8 @@
   // to return to monitoring mode periodically in case our connectivity issues
   // were temporary.
   void OnBootstrapTimeout();
-  void OnConnectSuccess(const std::string& ssid);
-  void OnConnectError(const Error* error);
+  void OnConnectDone(const std::string& ssid, ErrorPtr error);
+  void OnConnectTimeout();
   void OnConnectivityChange();
   void OnMonitorTimeout();
   void UpdateConnectionState();
diff --git a/libweave/src/privet/wifi_delegate.h b/libweave/src/privet/wifi_delegate.h
index 0cdf219..ae5e520 100644
--- a/libweave/src/privet/wifi_delegate.h
+++ b/libweave/src/privet/wifi_delegate.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/wifi_ssid_generator.cc b/libweave/src/privet/wifi_ssid_generator.cc
index 4a1a589..a049c20 100644
--- a/libweave/src/privet/wifi_ssid_generator.cc
+++ b/libweave/src/privet/wifi_ssid_generator.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/wifi_ssid_generator.h b/libweave/src/privet/wifi_ssid_generator.h
index 90f1493..d720fb5 100644
--- a/libweave/src/privet/wifi_ssid_generator.h
+++ b/libweave/src/privet/wifi_ssid_generator.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/privet/wifi_ssid_generator_unittest.cc b/libweave/src/privet/wifi_ssid_generator_unittest.cc
index 85e2c1b..3bd561f 100644
--- a/libweave/src/privet/wifi_ssid_generator_unittest.cc
+++ b/libweave/src/privet/wifi_ssid_generator_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/registration_status.cc b/libweave/src/registration_status.cc
index 167eb0f..852f3ea 100644
--- a/libweave/src/registration_status.cc
+++ b/libweave/src/registration_status.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/states/error_codes.cc b/libweave/src/states/error_codes.cc
index 4cf1699..4b12a45 100644
--- a/libweave/src/states/error_codes.cc
+++ b/libweave/src/states/error_codes.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/states/error_codes.h b/libweave/src/states/error_codes.h
index c5f876f..676a199 100644
--- a/libweave/src/states/error_codes.h
+++ b/libweave/src/states/error_codes.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/states/mock_state_change_queue_interface.h b/libweave/src/states/mock_state_change_queue_interface.h
index 2eab88a..c3f50c0 100644
--- a/libweave/src/states/mock_state_change_queue_interface.h
+++ b/libweave/src/states/mock_state_change_queue_interface.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/states/state_change_queue.cc b/libweave/src/states/state_change_queue.cc
index 4a2906c..288dadc 100644
--- a/libweave/src/states/state_change_queue.cc
+++ b/libweave/src/states/state_change_queue.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/states/state_change_queue.h b/libweave/src/states/state_change_queue.h
index a4b85d1..00b827f 100644
--- a/libweave/src/states/state_change_queue.h
+++ b/libweave/src/states/state_change_queue.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/states/state_change_queue_interface.h b/libweave/src/states/state_change_queue_interface.h
index a50d0ba..e3b3650 100644
--- a/libweave/src/states/state_change_queue_interface.h
+++ b/libweave/src/states/state_change_queue_interface.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/states/state_change_queue_unittest.cc b/libweave/src/states/state_change_queue_unittest.cc
index 427ff5b..9f26071 100644
--- a/libweave/src/states/state_change_queue_unittest.cc
+++ b/libweave/src/states/state_change_queue_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/states/state_manager.cc b/libweave/src/states/state_manager.cc
index cd94736..f629aa1 100644
--- a/libweave/src/states/state_manager.cc
+++ b/libweave/src/states/state_manager.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -6,7 +6,6 @@
 
 #include <base/logging.h>
 #include <base/values.h>
-#include <weave/provider/config_store.h>
 
 #include "src/json_error_codes.h"
 #include "src/states/error_codes.h"
@@ -54,22 +53,14 @@
   callback.Run();  // Force to read current state.
 }
 
-void StateManager::Startup(provider::ConfigStore* config_store) {
+void StateManager::Startup() {
   LOG(INFO) << "Initializing StateManager.";
 
   // Load standard device state definition.
   CHECK(LoadBaseStateDefinition(kBaseStateDefs, nullptr));
 
-  // Load component-specific device state definitions.
-  for (const auto& state_def : config_store->LoadStateDefs())
-    CHECK(LoadStateDefinition(state_def, nullptr));
-
   // Load standard device state defaults.
-  CHECK(LoadStateDefaults(kBaseStateDefaults, nullptr));
-
-  // Load component-specific device state defaults.
-  for (const auto& json : config_store->LoadStateDefaults())
-    CHECK(LoadStateDefaults(json, nullptr));
+  CHECK(SetPropertiesFromJson(kBaseStateDefaults, nullptr));
 
   for (const auto& cb : on_changed_)
     cb.Run();
@@ -85,23 +76,6 @@
   return dict;
 }
 
-bool StateManager::SetProperties(const base::DictionaryValue& property_set,
-                                 ErrorPtr* error) {
-  base::Time timestamp = base::Time::Now();
-  bool all_success = true;
-  for (base::DictionaryValue::Iterator it(property_set); !it.IsAtEnd();
-       it.Advance()) {
-    if (!SetPropertyValue(it.key(), it.value(), timestamp, error)) {
-      // Remember that an error occurred but keep going and update the rest of
-      // the properties if possible.
-      all_success = false;
-    }
-  }
-  for (const auto& cb : on_changed_)
-    cb.Run();
-  return all_success;
-}
-
 bool StateManager::SetProperty(const std::string& name,
                                const base::Value& value,
                                ErrorPtr* error) {
@@ -176,8 +150,8 @@
 
 bool StateManager::LoadStateDefinition(const base::DictionaryValue& dict,
                                        ErrorPtr* error) {
-  base::DictionaryValue::Iterator iter(dict);
-  while (!iter.IsAtEnd()) {
+  for (base::DictionaryValue::Iterator iter(dict); !iter.IsAtEnd();
+       iter.Advance()) {
     std::string package_name = iter.key();
     if (package_name.empty()) {
       Error::AddTo(error, FROM_HERE, errors::kErrorDomain,
@@ -196,14 +170,13 @@
     CHECK(package) << "Unable to create state package " << package_name;
     if (!package->AddSchemaFromJson(package_dict, error))
       return false;
-    iter.Advance();
   }
 
   return true;
 }
 
-bool StateManager::LoadStateDefinition(const std::string& json,
-                                       ErrorPtr* error) {
+bool StateManager::LoadStateDefinitionFromJson(const std::string& json,
+                                               ErrorPtr* error) {
   std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
   if (!dict)
     return false;
@@ -230,44 +203,49 @@
   return true;
 }
 
-bool StateManager::LoadStateDefaults(const base::DictionaryValue& dict,
-                                     ErrorPtr* error) {
-  base::DictionaryValue::Iterator iter(dict);
-  while (!iter.IsAtEnd()) {
-    std::string package_name = iter.key();
-    if (package_name.empty()) {
+bool StateManager::SetProperties(const base::DictionaryValue& dict,
+                                 ErrorPtr* error) {
+  base::Time timestamp = base::Time::Now();
+  bool all_success = true;
+  for (base::DictionaryValue::Iterator iter(dict); !iter.IsAtEnd();
+       iter.Advance()) {
+    if (iter.key().empty()) {
       Error::AddTo(error, FROM_HERE, errors::kErrorDomain,
                    errors::kInvalidPackageError, "State package name is empty");
-      return false;
+      all_success = false;
+      continue;
     }
+
     const base::DictionaryValue* package_dict = nullptr;
     if (!iter.value().GetAsDictionary(&package_dict)) {
       Error::AddToPrintf(error, FROM_HERE, errors::json::kDomain,
                          errors::json::kObjectExpected,
                          "State package '%s' must be an object",
-                         package_name.c_str());
-      return false;
+                         iter.key().c_str());
+      all_success = false;
+      continue;
     }
-    StatePackage* package = FindPackage(package_name);
-    if (package == nullptr) {
-      Error::AddToPrintf(error, FROM_HERE, errors::json::kDomain,
-                         errors::json::kObjectExpected,
-                         "Providing values for undefined state package '%s'",
-                         package_name.c_str());
-      return false;
+
+    for (base::DictionaryValue::Iterator it_prop(*package_dict);
+         !it_prop.IsAtEnd(); it_prop.Advance()) {
+      if (!SetPropertyValue(iter.key() + "." + it_prop.key(), it_prop.value(),
+                            timestamp, error)) {
+        all_success = false;
+        continue;
+      }
     }
-    if (!package->AddValuesFromJson(package_dict, error))
-      return false;
-    iter.Advance();
   }
-  return true;
+  for (const auto& cb : on_changed_)
+    cb.Run();
+  return all_success;
 }
 
-bool StateManager::LoadStateDefaults(const std::string& json, ErrorPtr* error) {
+bool StateManager::SetPropertiesFromJson(const std::string& json,
+                                         ErrorPtr* error) {
   std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
   if (!dict)
     return false;
-  if (!LoadStateDefaults(*dict, error)) {
+  if (!SetProperties(*dict, error)) {
     Error::AddToPrintf(error, FROM_HERE, errors::kErrorDomain,
                        errors::kSchemaError, "Failed to load defaults: '%s'",
                        json.c_str());
diff --git a/libweave/src/states/state_manager.h b/libweave/src/states/state_manager.h
index 5a64d78..2e2e002 100644
--- a/libweave/src/states/state_manager.h
+++ b/libweave/src/states/state_manager.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,10 +26,6 @@
 
 namespace weave {
 
-namespace provider {
-class ConfigStore;
-}
-
 // StateManager is the class that aggregates the device state fragments
 // provided by device daemons and makes the aggregate device state available
 // to the GCD cloud server and local clients.
@@ -39,8 +35,10 @@
   ~StateManager();
 
   void AddChangedCallback(const base::Closure& callback);
-  bool SetProperties(const base::DictionaryValue& property_set,
-                     ErrorPtr* error);
+  bool LoadStateDefinition(const base::DictionaryValue& dict, ErrorPtr* error);
+  bool LoadStateDefinitionFromJson(const std::string& json, ErrorPtr* error);
+  bool SetProperties(const base::DictionaryValue& dict, ErrorPtr* error);
+  bool SetPropertiesFromJson(const std::string& json, ErrorPtr* error);
   std::unique_ptr<base::Value> GetProperty(const std::string& name) const;
   bool SetProperty(const std::string& name,
                    const base::Value& value,
@@ -49,7 +47,7 @@
 
   // Initializes the state manager and load device state fragments.
   // Called by Buffet daemon at startup.
-  void Startup(provider::ConfigStore* config_store);
+  void Startup();
 
   // Returns the recorded state changes since last time this method has been
   // called.
@@ -75,25 +73,11 @@
                         const base::Time& timestamp,
                         ErrorPtr* error);
 
-  // Loads a device state fragment from a JSON object.
-  bool LoadStateDefinition(const base::DictionaryValue& dict,
-                           ErrorPtr* error);
-
-  // Loads a device state fragment JSON.
-  bool LoadStateDefinition(const std::string& json,
-                           ErrorPtr* error);
-
   // Loads the base device state fragment JSON. This state fragment
   // defines the standard state properties from the 'base' package as defined
   // by GCD specification.
   bool LoadBaseStateDefinition(const std::string& json, ErrorPtr* error);
 
-  // Loads state default values from JSON object.
-  bool LoadStateDefaults(const base::DictionaryValue& dict, ErrorPtr* error);
-
-  // Loads state default values from JSON.
-  bool LoadStateDefaults(const std::string& json, ErrorPtr* error);
-
   // Finds a package by its name. Returns nullptr if not found.
   StatePackage* FindPackage(const std::string& package_name);
   const StatePackage* FindPackage(const std::string& package_name) const;
diff --git a/libweave/src/states/state_manager_unittest.cc b/libweave/src/states/state_manager_unittest.cc
index 522e0e6..b62dc10 100644
--- a/libweave/src/states/state_manager_unittest.cc
+++ b/libweave/src/states/state_manager_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -59,17 +59,17 @@
     // Initial expectations.
     EXPECT_CALL(mock_state_change_queue_, IsEmpty()).Times(0);
     EXPECT_CALL(mock_state_change_queue_, NotifyPropertiesUpdated(_, _))
-        .Times(0);
+        .WillRepeatedly(Return(true));
     EXPECT_CALL(mock_state_change_queue_, GetAndClearRecordedStateChanges())
         .Times(0);
     mgr_.reset(new StateManager(&mock_state_change_queue_));
 
-    EXPECT_CALL(*this, OnStateChanged()).Times(1);
+    EXPECT_CALL(*this, OnStateChanged()).Times(2);
     mgr_->AddChangedCallback(
         base::Bind(&StateManagerTest::OnStateChanged, base::Unretained(this)));
 
     LoadStateDefinition(GetTestSchema().get(), nullptr);
-    ASSERT_TRUE(mgr_->LoadStateDefaults(*GetTestValues().get(), nullptr));
+    ASSERT_TRUE(mgr_->SetProperties(*GetTestValues().get(), nullptr));
   }
   void TearDown() override { mgr_.reset(); }
 
@@ -133,18 +133,13 @@
 }
 
 TEST_F(StateManagerTest, Startup) {
-  provider::test::MockConfigStore config_store;
   StateManager manager(&mock_state_change_queue_);
 
-  EXPECT_CALL(config_store, LoadStateDefs())
-      .WillOnce(Return(std::vector<std::string>{
-          {R"({"power": {"battery_level":"integer"}})"}}));
-
-  EXPECT_CALL(config_store, LoadStateDefaults())
-      .WillOnce(Return(
-          std::vector<std::string>{{R"({"power": {"battery_level":44}})"}}));
-
-  manager.Startup(&config_store);
+  manager.Startup();
+  ASSERT_TRUE(manager.LoadStateDefinitionFromJson(
+      R"({"power": {"battery_level":"integer"}})", nullptr));
+  ASSERT_TRUE(manager.SetPropertiesFromJson(
+      R"({"power": {"battery_level":44}})", nullptr));
 
   auto expected = R"({
     'base': {
@@ -243,7 +238,7 @@
 
   EXPECT_CALL(*this, OnStateChanged()).Times(1);
   ASSERT_TRUE(mgr_->SetProperties(
-      *CreateDictionaryValue("{'base.manufacturer': 'No Name'}"), nullptr));
+      *CreateDictionaryValue("{'base':{'manufacturer':'No Name'}}"), nullptr));
 
   auto expected = R"({
     'base': {
diff --git a/libweave/src/states/state_package.cc b/libweave/src/states/state_package.cc
index b6a5353..0b8e219 100644
--- a/libweave/src/states/state_package.cc
+++ b/libweave/src/states/state_package.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/states/state_package.h b/libweave/src/states/state_package.h
index ea640a1..e8e3964 100644
--- a/libweave/src/states/state_package.h
+++ b/libweave/src/states/state_package.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/states/state_package_unittest.cc b/libweave/src/states/state_package_unittest.cc
index f830997..f958bf5 100644
--- a/libweave/src/states/state_package_unittest.cc
+++ b/libweave/src/states/state_package_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/streams.cc b/libweave/src/streams.cc
new file mode 100644
index 0000000..22527c9
--- /dev/null
+++ b/libweave/src/streams.cc
@@ -0,0 +1,72 @@
+// Copyright 2015 The Weave 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 "src/streams.h"
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <weave/provider/task_runner.h>
+#include <weave/stream.h>
+
+namespace weave {
+
+namespace {}  // namespace
+
+MemoryStream::MemoryStream(const std::vector<uint8_t>& data,
+                           provider::TaskRunner* task_runner)
+    : data_{data}, task_runner_{task_runner} {}
+
+void MemoryStream::Read(void* buffer,
+                        size_t size_to_read,
+                        const ReadCallback& callback) {
+  CHECK_LE(read_position_, data_.size());
+  size_t size_read = std::min(size_to_read, data_.size() - read_position_);
+  if (size_read > 0)
+    memcpy(buffer, data_.data() + read_position_, size_read);
+  read_position_ += size_read;
+  task_runner_->PostDelayedTask(FROM_HERE,
+                                base::Bind(callback, size_read, nullptr), {});
+}
+
+void MemoryStream::Write(const void* buffer,
+                         size_t size_to_write,
+                         const WriteCallback& callback) {
+  data_.insert(data_.end(), static_cast<const char*>(buffer),
+               static_cast<const char*>(buffer) + size_to_write);
+  task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr), {});
+}
+
+StreamCopier::StreamCopier(InputStream* source, OutputStream* destination)
+    : source_{source}, destination_{destination}, buffer_(4096) {}
+
+void StreamCopier::Copy(const InputStream::ReadCallback& callback) {
+  source_->Read(buffer_.data(), buffer_.size(),
+                base::Bind(&StreamCopier::OnReadDone,
+                           weak_ptr_factory_.GetWeakPtr(), callback));
+}
+
+void StreamCopier::OnReadDone(const InputStream::ReadCallback& callback,
+                              size_t size,
+                              ErrorPtr error) {
+  if (error)
+    return callback.Run(0, std::move(error));
+
+  size_done_ += size;
+  if (size) {
+    return destination_->Write(
+        buffer_.data(), size,
+        base::Bind(&StreamCopier::OnWriteDone, weak_ptr_factory_.GetWeakPtr(),
+                   callback));
+  }
+  callback.Run(size_done_, nullptr);
+}
+
+void StreamCopier::OnWriteDone(const InputStream::ReadCallback& callback,
+                               ErrorPtr error) {
+  if (error)
+    return callback.Run(size_done_, std::move(error));
+  Copy(callback);
+}
+
+}  // namespace weave
diff --git a/libweave/src/streams.h b/libweave/src/streams.h
new file mode 100644
index 0000000..990f47c
--- /dev/null
+++ b/libweave/src/streams.h
@@ -0,0 +1,61 @@
+// Copyright 2015 The Weave 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 LIBWEAVE_SRC_STREAMS_H_
+#define LIBWEAVE_SRC_STREAMS_H_
+
+#include <base/memory/weak_ptr.h>
+#include <weave/stream.h>
+
+namespace weave {
+
+namespace provider {
+class TaskRunner;
+}
+
+class MemoryStream : public InputStream, public OutputStream {
+ public:
+  MemoryStream(const std::vector<uint8_t>& data,
+               provider::TaskRunner* task_runner);
+
+  void Read(void* buffer,
+            size_t size_to_read,
+            const ReadCallback& callback) override;
+
+  void Write(const void* buffer,
+             size_t size_to_write,
+             const WriteCallback& callback) override;
+
+  const std::vector<uint8_t>& GetData() const { return data_; }
+
+ private:
+  std::vector<uint8_t> data_;
+  provider::TaskRunner* task_runner_{nullptr};
+  size_t read_position_{0};
+};
+
+class StreamCopier {
+ public:
+  StreamCopier(InputStream* source, OutputStream* destination);
+
+  void Copy(const InputStream::ReadCallback& callback);
+
+ private:
+  void OnWriteDone(const InputStream::ReadCallback& callback, ErrorPtr error);
+  void OnReadDone(const InputStream::ReadCallback& callback,
+                  size_t size,
+                  ErrorPtr error);
+
+  InputStream* source_{nullptr};
+  OutputStream* destination_{nullptr};
+
+  size_t size_done_{0};
+  std::vector<uint8_t> buffer_;
+
+  base::WeakPtrFactory<StreamCopier> weak_ptr_factory_{this};
+};
+
+}  // namespace weave
+
+#endif  // LIBWEAVE_SRC_STREAMS_H_
diff --git a/libweave/src/streams_unittest.cc b/libweave/src/streams_unittest.cc
new file mode 100644
index 0000000..fba6ca5
--- /dev/null
+++ b/libweave/src/streams_unittest.cc
@@ -0,0 +1,39 @@
+// Copyright 2015 The Weave 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 "src/streams.h"
+
+#include <functional>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <weave/provider/test/fake_task_runner.h>
+
+#include <src/bind_lambda.h>
+
+namespace weave {
+
+TEST(Stream, CopyStreams) {
+  provider::test::FakeTaskRunner task_runner;
+  std::vector<uint8_t> test_data(1024 * 1024);
+  for (size_t i = 0; i < test_data.size(); ++i)
+    test_data[i] = static_cast<uint8_t>(std::hash<size_t>()(i));
+  MemoryStream source{test_data, &task_runner};
+  MemoryStream destination{{}, &task_runner};
+
+  bool done = false;
+
+  auto callback = base::Bind(
+      [&test_data, &done, &destination](size_t size, ErrorPtr error) {
+        EXPECT_FALSE(error);
+        done = true;
+        EXPECT_EQ(test_data, destination.GetData());
+      });
+  StreamCopier copier{&source, &destination};
+  copier.Copy(callback);
+
+  task_runner.Run(test_data.size());
+  EXPECT_TRUE(done);
+}
+
+}  // namespace weave
diff --git a/libweave/src/string_utils.cc b/libweave/src/string_utils.cc
index 67688b9..b5e71b8 100644
--- a/libweave/src/string_utils.cc
+++ b/libweave/src/string_utils.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/string_utils.h b/libweave/src/string_utils.h
index 5771971..e349d9d 100644
--- a/libweave/src/string_utils.h
+++ b/libweave/src/string_utils.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/string_utils_unittest.cc b/libweave/src/string_utils_unittest.cc
index 2473a79..c26d7d8 100644
--- a/libweave/src/string_utils_unittest.cc
+++ b/libweave/src/string_utils_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2014 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/test/fake_stream.cc b/libweave/src/test/fake_stream.cc
index 786aa01..1101779 100644
--- a/libweave/src/test/fake_stream.cc
+++ b/libweave/src/test/fake_stream.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -30,33 +30,30 @@
 
 void FakeStream::Read(void* buffer,
                       size_t size_to_read,
-                      const ReadSuccessCallback& success_callback,
-                      const ErrorCallback& error_callback) {
+                      const ReadCallback& callback) {
   if (read_data_.empty()) {
     task_runner_->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(&FakeStream::Read, base::Unretained(this), buffer,
-                    size_to_read, success_callback, error_callback),
+        FROM_HERE, base::Bind(&FakeStream::Read, base::Unretained(this), buffer,
+                              size_to_read, callback),
         base::TimeDelta::FromSeconds(0));
     return;
   }
   size_t size = std::min(size_to_read, read_data_.size());
   memcpy(buffer, read_data_.data(), size);
   read_data_ = read_data_.substr(size);
-  task_runner_->PostDelayedTask(FROM_HERE, base::Bind(success_callback, size),
+  task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, size, nullptr),
                                 base::TimeDelta::FromSeconds(0));
 }
 
 void FakeStream::Write(const void* buffer,
                        size_t size_to_write,
-                       const SuccessCallback& success_callback,
-                       const ErrorCallback& error_callback) {
+                       const WriteCallback& callback) {
   size_t size = std::min(size_to_write, write_data_.size());
   EXPECT_EQ(
       write_data_.substr(0, size),
       std::string(reinterpret_cast<const char*>(buffer), size_to_write));
   write_data_ = write_data_.substr(size);
-  task_runner_->PostDelayedTask(FROM_HERE, success_callback,
+  task_runner_->PostDelayedTask(FROM_HERE, base::Bind(callback, nullptr),
                                 base::TimeDelta::FromSeconds(0));
 }
 
diff --git a/libweave/src/test/fake_task_runner.cc b/libweave/src/test/fake_task_runner.cc
index 4026880..88e078b 100644
--- a/libweave/src/test/fake_task_runner.cc
+++ b/libweave/src/test/fake_task_runner.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -32,9 +32,9 @@
   return true;
 }
 
-void FakeTaskRunner::Run() {
+void FakeTaskRunner::Run(size_t number_of_iterations) {
   break_ = false;
-  for (size_t i = 0; i < 1000 && !break_ && RunOnce(); ++i) {
+  for (size_t i = 0; i < number_of_iterations && !break_ && RunOnce(); ++i) {
   }
 }
 
diff --git a/libweave/src/test/mock_command.cc b/libweave/src/test/mock_command.cc
index 43b528b..a8564c4 100644
--- a/libweave/src/test/mock_command.cc
+++ b/libweave/src/test/mock_command.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -26,9 +26,5 @@
   return CreateDictionaryValue(MockGetResults());
 }
 
-std::unique_ptr<base::DictionaryValue> MockCommand::ToJson() const {
-  return CreateDictionaryValue(MockToJson());
-}
-
 }  // namespace test
 }  // namespace weave
diff --git a/libweave/src/test/mock_http_client.cc b/libweave/src/test/mock_http_client.cc
deleted file mode 100644
index b17645c..0000000
--- a/libweave/src/test/mock_http_client.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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 <weave/provider/test/mock_http_client.h>
-
-#include <memory>
-#include <string>
-
-namespace weave {
-namespace provider {
-namespace test {
-
-std::unique_ptr<HttpClient::Response> MockHttpClient::SendRequestAndBlock(
-    const std::string& method,
-    const std::string& url,
-    const Headers& headers,
-    const std::string& data,
-    ErrorPtr* error) {
-  return std::unique_ptr<Response>{
-      MockSendRequest(method, url, headers, data, error)};
-}
-
-int MockHttpClient::SendRequest(const std::string& method,
-                                const std::string& url,
-                                const Headers& headers,
-                                const std::string& data,
-                                const SuccessCallback& success_callback,
-                                const ErrorCallback& error_callback) {
-  ErrorPtr error;
-  std::unique_ptr<Response> response{
-      MockSendRequest(method, url, headers, data, &error)};
-  if (response) {
-    success_callback.Run(0, *response);
-  } else {
-    error_callback.Run(0, error.get());
-  }
-  return 0;
-}
-
-}  // namespace test
-}  // namespace provider
-}  // namespace weave
diff --git a/libweave/src/test/unittest_utils.cc b/libweave/src/test/unittest_utils.cc
index 0078894..b9858e4 100644
--- a/libweave/src/test/unittest_utils.cc
+++ b/libweave/src/test/unittest_utils.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/test/weave_testrunner.cc b/libweave/src/test/weave_testrunner.cc
index 5de7cae..00e3432 100644
--- a/libweave/src/test/weave_testrunner.cc
+++ b/libweave/src/test/weave_testrunner.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/libweave/src/utils.cc b/libweave/src/utils.cc
index 6827db5..24ce1ca 100644
--- a/libweave/src/utils.cc
+++ b/libweave/src/utils.cc
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -23,6 +23,9 @@
 
 const size_t kMaxStrLen = 1700;  // Log messages are limited to 2000 chars.
 
+const char kErrorCodeKey[] = "code";
+const char kErrorMessageKey[] = "message";
+
 }  // anonymous namespace
 
 namespace errors {
@@ -62,4 +65,11 @@
   return result;
 }
 
+std::unique_ptr<base::DictionaryValue> ErrorInfoToJson(const Error& error) {
+  std::unique_ptr<base::DictionaryValue> output{new base::DictionaryValue};
+  output->SetString(kErrorMessageKey, error.GetMessage());
+  output->SetString(kErrorCodeKey, error.GetCode());
+  return output;
+}
+
 }  // namespace weave
diff --git a/libweave/src/utils.h b/libweave/src/utils.h
index 29ea138..bd69e94 100644
--- a/libweave/src/utils.h
+++ b/libweave/src/utils.h
@@ -1,4 +1,4 @@
-// Copyright 2014 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -30,6 +30,8 @@
     const std::string& json_string,
     ErrorPtr* error);
 
+std::unique_ptr<base::DictionaryValue> ErrorInfoToJson(const Error& error);
+
 }  // namespace weave
 
 #endif  // LIBWEAVE_SRC_UTILS_H_
diff --git a/libweave/src/weave_unittest.cc b/libweave/src/weave_unittest.cc
index f0f8a58..0bfbe0b 100644
--- a/libweave/src/weave_unittest.cc
+++ b/libweave/src/weave_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2015 The Chromium OS Authors. All rights reserved.
+// Copyright 2015 The Weave Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -14,6 +14,7 @@
 #include <weave/provider/test/mock_http_server.h>
 #include <weave/provider/test/mock_network.h>
 #include <weave/provider/test/mock_wifi.h>
+#include <weave/test/mock_command.h>
 #include <weave/test/mock_device.h>
 #include <weave/test/unittest_utils.h>
 
@@ -35,10 +36,11 @@
 
 namespace weave {
 
+using provider::HttpClient;
+using provider::Network;
+using provider::test::MockHttpClientResponse;
 using test::CreateDictionaryValue;
 using test::ValueToString;
-using provider::test::MockHttpClientResponse;
-using provider::Network;
 
 const char kCommandDefs[] = R"({
   "base": {
@@ -135,13 +137,14 @@
  protected:
   void SetUp() override {}
 
-  void ExpectRequest(const std::string& method,
+  void ExpectRequest(HttpClient::Method method,
                      const std::string& url,
                      const std::string& json_response) {
-    EXPECT_CALL(http_client_, MockSendRequest(method, url, _, _, _))
-        .WillOnce(InvokeWithoutArgs([json_response]() {
-          provider::test::MockHttpClientResponse* response =
-              new StrictMock<provider::test::MockHttpClientResponse>;
+    EXPECT_CALL(http_client_, SendRequest(method, url, _, _, _))
+        .WillOnce(WithArgs<4>(Invoke([json_response](
+            const HttpClient::SendRequestCallback& callback) {
+          std::unique_ptr<provider::test::MockHttpClientResponse> response{
+              new StrictMock<provider::test::MockHttpClientResponse>};
           EXPECT_CALL(*response, GetStatusCode())
               .Times(AtLeast(1))
               .WillRepeatedly(Return(200));
@@ -150,22 +153,13 @@
               .WillRepeatedly(Return("application/json; charset=utf-8"));
           EXPECT_CALL(*response, GetData())
               .Times(AtLeast(1))
-              .WillRepeatedly(ReturnRefOfCopy(json_response));
-          return response;
-        }));
+              .WillRepeatedly(Return(json_response));
+          callback.Run(std::move(response), nullptr);
+        })));
   }
 
   void InitConfigStore() {
     EXPECT_CALL(config_store_, SaveSettings("")).WillRepeatedly(Return());
-
-    EXPECT_CALL(config_store_, LoadCommandDefs())
-        .WillOnce(Return(std::vector<std::string>{kCommandDefs}));
-
-    EXPECT_CALL(config_store_, LoadStateDefs())
-        .WillOnce(Return(std::vector<std::string>{kStateDefs}));
-
-    EXPECT_CALL(config_store_, LoadStateDefaults())
-        .WillOnce(Return(std::vector<std::string>{kStateDefaults}));
   }
 
   void InitNetwork() {
@@ -201,6 +195,7 @@
     }
 
     EXPECT_CALL(dns_sd_, PublishService("_privet._tcp", 11, MatchTxt(txt)))
+        .Times(AtMost(1))
         .WillOnce(Return());
   }
 
@@ -208,17 +203,18 @@
     EXPECT_CALL(http_server_, GetHttpPort()).WillRepeatedly(Return(11));
     EXPECT_CALL(http_server_, GetHttpsPort()).WillRepeatedly(Return(12));
     EXPECT_CALL(http_server_, GetHttpsCertificateFingerprint())
-        .WillRepeatedly(ReturnRefOfCopy(std::vector<uint8_t>{1, 2, 3}));
-    EXPECT_CALL(http_server_, AddRequestHandler(_, _))
-        .WillRepeatedly(
-            Invoke([this](const std::string& path_prefix,
-                          const provider::HttpServer::OnRequestCallback& cb) {
+        .WillRepeatedly(Return(std::vector<uint8_t>{1, 2, 3}));
+    EXPECT_CALL(http_server_, AddHttpRequestHandler(_, _))
+        .WillRepeatedly(Invoke(
+            [this](const std::string& path_prefix,
+                   const provider::HttpServer::RequestHandlerCallback& cb) {
               http_server_request_cb_.push_back(cb);
             }));
-    EXPECT_CALL(http_server_, AddOnStateChangedCallback(_))
+    EXPECT_CALL(http_server_, AddHttpsRequestHandler(_, _))
         .WillRepeatedly(Invoke(
-            [this](const provider::HttpServer::OnStateChangedCallback& cb) {
-              http_server_changed_cb_.push_back(cb);
+            [this](const std::string& path_prefix,
+                   const provider::HttpServer::RequestHandlerCallback& cb) {
+              http_server_request_cb_.push_back(cb);
             }));
   }
 
@@ -236,8 +232,9 @@
                                     &http_client_, &network_, &dns_sd_,
                                     &http_server_, &wifi_, &bluetooth_);
 
-    for (const auto& cb : http_server_changed_cb_)
-      cb.Run(http_server_);
+    device_->AddCommandDefinitionsFromJson(kCommandDefs);
+    device_->AddStateDefinitionsFromJson(kStateDefs);
+    device_->SetStatePropertiesFromJson(kStateDefaults, nullptr);
 
     task_runner_.Run();
   }
@@ -250,9 +247,8 @@
     }
   }
 
-  std::vector<provider::HttpServer::OnStateChangedCallback>
-      http_server_changed_cb_;
-  std::vector<provider::HttpServer::OnRequestCallback> http_server_request_cb_;
+  std::vector<provider::HttpServer::RequestHandlerCallback>
+      http_server_request_cb_;
 
   StrictMock<provider::test::MockConfigStore> config_store_;
   StrictMock<provider::test::FakeTaskRunner> task_runner_;
@@ -268,10 +264,11 @@
   std::unique_ptr<weave::Device> device_;
 };
 
-TEST_F(WeaveTest, MockDevice) {
+TEST_F(WeaveTest, Mocks) {
   // Test checks if mock implements entire interface and mock can be
   // instantiated.
   test::MockDevice device;
+  test::MockCommand command;
 }
 
 TEST_F(WeaveTest, StartMinimal) {
@@ -290,9 +287,7 @@
   device_ = weave::Device::Create(&config_store_, &task_runner_, &http_client_,
                                   &network_, &dns_sd_, &http_server_, nullptr,
                                   &bluetooth_);
-
-  for (const auto& cb : http_server_changed_cb_)
-    cb.Run(http_server_);
+  device_->AddCommandDefinitionsFromJson(kCommandDefs);
 
   task_runner_.Run();
 }
@@ -312,14 +307,14 @@
 }
 
 TEST_F(WeaveBasicTest, Register) {
-  EXPECT_CALL(network_, OpenSslSocket(_, _, _, _)).WillRepeatedly(Return());
+  EXPECT_CALL(network_, OpenSslSocket(_, _, _)).WillRepeatedly(Return());
   StartDevice();
 
   auto draft = CreateDictionaryValue(kDeviceResource);
   auto response = CreateDictionaryValue(kRegistrationResponse);
   response->Set("deviceDraft", draft->DeepCopy());
   ExpectRequest(
-      "PATCH",
+      HttpClient::Method::kPatch,
       "https://www.googleapis.com/clouddevices/v1/registrationTickets/"
       "TICKET_ID?key=TEST_API_KEY",
       ValueToString(*response));
@@ -327,17 +322,26 @@
   response = CreateDictionaryValue(kRegistrationFinalResponse);
   response->Set("deviceDraft", draft->DeepCopy());
   ExpectRequest(
-      "POST",
+      HttpClient::Method::kPost,
       "https://www.googleapis.com/clouddevices/v1/registrationTickets/"
       "TICKET_ID/finalize?key=TEST_API_KEY",
       ValueToString(*response));
 
-  ExpectRequest("POST", "https://accounts.google.com/o/oauth2/token",
+  ExpectRequest(HttpClient::Method::kPost,
+                "https://accounts.google.com/o/oauth2/token",
                 kAuthTokenResponse);
 
   InitDnsSdPublishing(true, "DB");
 
-  EXPECT_EQ("CLOUD_ID", device_->Register("TICKET_ID", nullptr));
+  bool done = false;
+  device_->Register("TICKET_ID", base::Bind([this, &done](ErrorPtr error) {
+                      EXPECT_FALSE(error);
+                      done = true;
+                      task_runner_.Break();
+                      EXPECT_EQ("CLOUD_ID", device_->GetSettings().cloud_id);
+                    }));
+  task_runner_.Run();
+  EXPECT_TRUE(done);
 }
 
 class WeaveWiFiSetupTest : public WeaveTest {
@@ -351,7 +355,7 @@
     InitDnsSd();
 
     EXPECT_CALL(network_, GetConnectionState())
-        .WillRepeatedly(Return(provider::Network::State::kConnected));
+        .WillRepeatedly(Return(provider::Network::State::kOnline));
   }
 };
 
@@ -360,7 +364,7 @@
 
   // Short disconnect.
   NotifyNetworkChanged(provider::Network::State::kOffline, {});
-  NotifyNetworkChanged(provider::Network::State::kConnected,
+  NotifyNetworkChanged(provider::Network::State::kOnline,
                        base::TimeDelta::FromSeconds(10));
   task_runner_.Run();
 
@@ -408,7 +412,7 @@
     task_runner_.Run();
   }
 
-  NotifyNetworkChanged(Network::State::kConnected, {});
+  NotifyNetworkChanged(Network::State::kOnline, {});
   task_runner_.Run();
 }
 
diff --git a/libweave/third_party/chromium/LICENSE b/libweave/third_party/chromium/LICENSE
new file mode 100644
index 0000000..a32e00c
--- /dev/null
+++ b/libweave/third_party/chromium/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/libweave/third_party/chromium/LICENSE.chromium_os b/libweave/third_party/chromium/LICENSE.chromium_os
new file mode 100644
index 0000000..0aa7fc9
--- /dev/null
+++ b/libweave/third_party/chromium/LICENSE.chromium_os
@@ -0,0 +1,27 @@
+// Copyright (c) 2006-2009 The Chromium OS Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/libweave/external/base/base_export.h b/libweave/third_party/chromium/base/base_export.h
similarity index 100%
rename from libweave/external/base/base_export.h
rename to libweave/third_party/chromium/base/base_export.h
diff --git a/libweave/external/base/basictypes.h b/libweave/third_party/chromium/base/basictypes.h
similarity index 100%
rename from libweave/external/base/basictypes.h
rename to libweave/third_party/chromium/base/basictypes.h
diff --git a/libweave/external/base/bind.h b/libweave/third_party/chromium/base/bind.h
similarity index 100%
rename from libweave/external/base/bind.h
rename to libweave/third_party/chromium/base/bind.h
diff --git a/libweave/external/base/bind_helpers.cc b/libweave/third_party/chromium/base/bind_helpers.cc
similarity index 100%
rename from libweave/external/base/bind_helpers.cc
rename to libweave/third_party/chromium/base/bind_helpers.cc
diff --git a/libweave/external/base/bind_helpers.h b/libweave/third_party/chromium/base/bind_helpers.h
similarity index 100%
rename from libweave/external/base/bind_helpers.h
rename to libweave/third_party/chromium/base/bind_helpers.h
diff --git a/libweave/external/base/bind_internal.h b/libweave/third_party/chromium/base/bind_internal.h
similarity index 100%
rename from libweave/external/base/bind_internal.h
rename to libweave/third_party/chromium/base/bind_internal.h
diff --git a/libweave/external/base/bind_unittest.cc b/libweave/third_party/chromium/base/bind_unittest.cc
similarity index 100%
rename from libweave/external/base/bind_unittest.cc
rename to libweave/third_party/chromium/base/bind_unittest.cc
diff --git a/libweave/external/base/build/build_config.h b/libweave/third_party/chromium/base/build/build_config.h
similarity index 100%
rename from libweave/external/base/build/build_config.h
rename to libweave/third_party/chromium/base/build/build_config.h
diff --git a/libweave/external/base/callback.h b/libweave/third_party/chromium/base/callback.h
similarity index 100%
rename from libweave/external/base/callback.h
rename to libweave/third_party/chromium/base/callback.h
diff --git a/libweave/external/base/callback_forward.h b/libweave/third_party/chromium/base/callback_forward.h
similarity index 100%
rename from libweave/external/base/callback_forward.h
rename to libweave/third_party/chromium/base/callback_forward.h
diff --git a/libweave/external/base/callback_internal.cc b/libweave/third_party/chromium/base/callback_internal.cc
similarity index 100%
rename from libweave/external/base/callback_internal.cc
rename to libweave/third_party/chromium/base/callback_internal.cc
diff --git a/libweave/external/base/callback_internal.h b/libweave/third_party/chromium/base/callback_internal.h
similarity index 100%
rename from libweave/external/base/callback_internal.h
rename to libweave/third_party/chromium/base/callback_internal.h
diff --git a/libweave/external/base/callback_list.h b/libweave/third_party/chromium/base/callback_list.h
similarity index 100%
rename from libweave/external/base/callback_list.h
rename to libweave/third_party/chromium/base/callback_list.h
diff --git a/libweave/external/base/callback_list_unittest.cc b/libweave/third_party/chromium/base/callback_list_unittest.cc
similarity index 100%
rename from libweave/external/base/callback_list_unittest.cc
rename to libweave/third_party/chromium/base/callback_list_unittest.cc
diff --git a/libweave/external/base/callback_unittest.cc b/libweave/third_party/chromium/base/callback_unittest.cc
similarity index 100%
rename from libweave/external/base/callback_unittest.cc
rename to libweave/third_party/chromium/base/callback_unittest.cc
diff --git a/libweave/external/base/command_line.h b/libweave/third_party/chromium/base/command_line.h
similarity index 100%
rename from libweave/external/base/command_line.h
rename to libweave/third_party/chromium/base/command_line.h
diff --git a/libweave/external/base/compiler_specific.h b/libweave/third_party/chromium/base/compiler_specific.h
similarity index 100%
rename from libweave/external/base/compiler_specific.h
rename to libweave/third_party/chromium/base/compiler_specific.h
diff --git a/libweave/external/base/gtest_prod_util.h b/libweave/third_party/chromium/base/gtest_prod_util.h
similarity index 100%
rename from libweave/external/base/gtest_prod_util.h
rename to libweave/third_party/chromium/base/gtest_prod_util.h
diff --git a/libweave/external/base/guid.h b/libweave/third_party/chromium/base/guid.h
similarity index 100%
rename from libweave/external/base/guid.h
rename to libweave/third_party/chromium/base/guid.h
diff --git a/libweave/external/base/guid_posix.cc b/libweave/third_party/chromium/base/guid_posix.cc
similarity index 100%
rename from libweave/external/base/guid_posix.cc
rename to libweave/third_party/chromium/base/guid_posix.cc
diff --git a/libweave/external/base/guid_unittest.cc b/libweave/third_party/chromium/base/guid_unittest.cc
similarity index 100%
rename from libweave/external/base/guid_unittest.cc
rename to libweave/third_party/chromium/base/guid_unittest.cc
diff --git a/libweave/external/base/json/json_parser.cc b/libweave/third_party/chromium/base/json/json_parser.cc
similarity index 100%
rename from libweave/external/base/json/json_parser.cc
rename to libweave/third_party/chromium/base/json/json_parser.cc
diff --git a/libweave/external/base/json/json_parser.h b/libweave/third_party/chromium/base/json/json_parser.h
similarity index 100%
rename from libweave/external/base/json/json_parser.h
rename to libweave/third_party/chromium/base/json/json_parser.h
diff --git a/libweave/external/base/json/json_parser_unittest.cc b/libweave/third_party/chromium/base/json/json_parser_unittest.cc
similarity index 100%
rename from libweave/external/base/json/json_parser_unittest.cc
rename to libweave/third_party/chromium/base/json/json_parser_unittest.cc
diff --git a/libweave/external/base/json/json_reader.cc b/libweave/third_party/chromium/base/json/json_reader.cc
similarity index 100%
rename from libweave/external/base/json/json_reader.cc
rename to libweave/third_party/chromium/base/json/json_reader.cc
diff --git a/libweave/external/base/json/json_reader.h b/libweave/third_party/chromium/base/json/json_reader.h
similarity index 100%
rename from libweave/external/base/json/json_reader.h
rename to libweave/third_party/chromium/base/json/json_reader.h
diff --git a/libweave/external/base/json/json_reader_unittest.cc b/libweave/third_party/chromium/base/json/json_reader_unittest.cc
similarity index 100%
rename from libweave/external/base/json/json_reader_unittest.cc
rename to libweave/third_party/chromium/base/json/json_reader_unittest.cc
diff --git a/libweave/external/base/json/json_writer.cc b/libweave/third_party/chromium/base/json/json_writer.cc
similarity index 100%
rename from libweave/external/base/json/json_writer.cc
rename to libweave/third_party/chromium/base/json/json_writer.cc
diff --git a/libweave/external/base/json/json_writer.h b/libweave/third_party/chromium/base/json/json_writer.h
similarity index 100%
rename from libweave/external/base/json/json_writer.h
rename to libweave/third_party/chromium/base/json/json_writer.h
diff --git a/libweave/external/base/json/json_writer_unittest.cc b/libweave/third_party/chromium/base/json/json_writer_unittest.cc
similarity index 100%
rename from libweave/external/base/json/json_writer_unittest.cc
rename to libweave/third_party/chromium/base/json/json_writer_unittest.cc
diff --git a/libweave/external/base/json/string_escape.cc b/libweave/third_party/chromium/base/json/string_escape.cc
similarity index 100%
rename from libweave/external/base/json/string_escape.cc
rename to libweave/third_party/chromium/base/json/string_escape.cc
diff --git a/libweave/external/base/json/string_escape.h b/libweave/third_party/chromium/base/json/string_escape.h
similarity index 100%
rename from libweave/external/base/json/string_escape.h
rename to libweave/third_party/chromium/base/json/string_escape.h
diff --git a/libweave/external/base/json/string_escape_unittest.cc b/libweave/third_party/chromium/base/json/string_escape_unittest.cc
similarity index 100%
rename from libweave/external/base/json/string_escape_unittest.cc
rename to libweave/third_party/chromium/base/json/string_escape_unittest.cc
diff --git a/libweave/external/base/location.cc b/libweave/third_party/chromium/base/location.cc
similarity index 100%
rename from libweave/external/base/location.cc
rename to libweave/third_party/chromium/base/location.cc
diff --git a/libweave/external/base/location.h b/libweave/third_party/chromium/base/location.h
similarity index 100%
rename from libweave/external/base/location.h
rename to libweave/third_party/chromium/base/location.h
diff --git a/libweave/external/base/logging.cc b/libweave/third_party/chromium/base/logging.cc
similarity index 98%
rename from libweave/external/base/logging.cc
rename to libweave/third_party/chromium/base/logging.cc
index c576d18..1fd047f 100644
--- a/libweave/external/base/logging.cc
+++ b/libweave/third_party/chromium/base/logging.cc
@@ -83,11 +83,6 @@
   return std::max(-1, LOG_INFO - GetMinLogLevel());
 }
 
-int GetVlogLevelHelper(const char* file, size_t N) {
-  DCHECK_GT(N, 0U);
-  return 0;
-}
-
 void SetLogItems(bool enable_process_id, bool enable_thread_id,
                  bool enable_timestamp, bool enable_tickcount) {
   g_log_timestamp = enable_timestamp;
diff --git a/libweave/external/base/logging.h b/libweave/third_party/chromium/base/logging.h
similarity index 99%
rename from libweave/external/base/logging.h
rename to libweave/third_party/chromium/base/logging.h
index 0096d9d..61e9f9d 100644
--- a/libweave/external/base/logging.h
+++ b/libweave/third_party/chromium/base/logging.h
@@ -195,7 +195,9 @@
 // __FILE__).
 
 // Note that |N| is the size *with* the null terminator.
-int GetVlogLevelHelper(const char* file_start, size_t N);
+inline int GetVlogLevelHelper(const char* file_start, size_t N) {
+  return GetVlogVerbosity();
+}
 
 template <size_t N>
 int GetVlogLevel(const char (&file)[N]) {
diff --git a/libweave/external/base/logging_unittest.cc b/libweave/third_party/chromium/base/logging_unittest.cc
similarity index 100%
rename from libweave/external/base/logging_unittest.cc
rename to libweave/third_party/chromium/base/logging_unittest.cc
diff --git a/libweave/external/base/macros.h b/libweave/third_party/chromium/base/macros.h
similarity index 100%
rename from libweave/external/base/macros.h
rename to libweave/third_party/chromium/base/macros.h
diff --git a/libweave/external/base/memory/ref_counted.cc b/libweave/third_party/chromium/base/memory/ref_counted.cc
similarity index 100%
rename from libweave/external/base/memory/ref_counted.cc
rename to libweave/third_party/chromium/base/memory/ref_counted.cc
diff --git a/libweave/external/base/memory/ref_counted.h b/libweave/third_party/chromium/base/memory/ref_counted.h
similarity index 100%
rename from libweave/external/base/memory/ref_counted.h
rename to libweave/third_party/chromium/base/memory/ref_counted.h
diff --git a/libweave/external/base/memory/ref_counted_unittest.cc b/libweave/third_party/chromium/base/memory/ref_counted_unittest.cc
similarity index 100%
rename from libweave/external/base/memory/ref_counted_unittest.cc
rename to libweave/third_party/chromium/base/memory/ref_counted_unittest.cc
diff --git a/libweave/external/base/memory/scoped_ptr.h b/libweave/third_party/chromium/base/memory/scoped_ptr.h
similarity index 100%
rename from libweave/external/base/memory/scoped_ptr.h
rename to libweave/third_party/chromium/base/memory/scoped_ptr.h
diff --git a/libweave/external/base/memory/scoped_ptr_unittest.cc b/libweave/third_party/chromium/base/memory/scoped_ptr_unittest.cc
similarity index 100%
rename from libweave/external/base/memory/scoped_ptr_unittest.cc
rename to libweave/third_party/chromium/base/memory/scoped_ptr_unittest.cc
diff --git a/libweave/external/base/memory/weak_ptr.cc b/libweave/third_party/chromium/base/memory/weak_ptr.cc
similarity index 100%
rename from libweave/external/base/memory/weak_ptr.cc
rename to libweave/third_party/chromium/base/memory/weak_ptr.cc
diff --git a/libweave/external/base/memory/weak_ptr.h b/libweave/third_party/chromium/base/memory/weak_ptr.h
similarity index 100%
rename from libweave/external/base/memory/weak_ptr.h
rename to libweave/third_party/chromium/base/memory/weak_ptr.h
diff --git a/libweave/external/base/memory/weak_ptr_unittest.cc b/libweave/third_party/chromium/base/memory/weak_ptr_unittest.cc
similarity index 100%
rename from libweave/external/base/memory/weak_ptr_unittest.cc
rename to libweave/third_party/chromium/base/memory/weak_ptr_unittest.cc
diff --git a/libweave/external/base/move.h b/libweave/third_party/chromium/base/move.h
similarity index 100%
rename from libweave/external/base/move.h
rename to libweave/third_party/chromium/base/move.h
diff --git a/libweave/external/base/move_unittest.cc b/libweave/third_party/chromium/base/move_unittest.cc
similarity index 100%
rename from libweave/external/base/move_unittest.cc
rename to libweave/third_party/chromium/base/move_unittest.cc
diff --git a/libweave/external/base/numerics/safe_conversions.h b/libweave/third_party/chromium/base/numerics/safe_conversions.h
similarity index 100%
rename from libweave/external/base/numerics/safe_conversions.h
rename to libweave/third_party/chromium/base/numerics/safe_conversions.h
diff --git a/libweave/external/base/numerics/safe_conversions_impl.h b/libweave/third_party/chromium/base/numerics/safe_conversions_impl.h
similarity index 100%
rename from libweave/external/base/numerics/safe_conversions_impl.h
rename to libweave/third_party/chromium/base/numerics/safe_conversions_impl.h
diff --git a/libweave/external/base/numerics/safe_math.h b/libweave/third_party/chromium/base/numerics/safe_math.h
similarity index 100%
rename from libweave/external/base/numerics/safe_math.h
rename to libweave/third_party/chromium/base/numerics/safe_math.h
diff --git a/libweave/external/base/numerics/safe_math_impl.h b/libweave/third_party/chromium/base/numerics/safe_math_impl.h
similarity index 100%
rename from libweave/external/base/numerics/safe_math_impl.h
rename to libweave/third_party/chromium/base/numerics/safe_math_impl.h
diff --git a/libweave/external/base/numerics/safe_numerics_unittest.cc b/libweave/third_party/chromium/base/numerics/safe_numerics_unittest.cc
similarity index 100%
rename from libweave/external/base/numerics/safe_numerics_unittest.cc
rename to libweave/third_party/chromium/base/numerics/safe_numerics_unittest.cc
diff --git a/libweave/external/base/observer_list.h b/libweave/third_party/chromium/base/observer_list.h
similarity index 100%
rename from libweave/external/base/observer_list.h
rename to libweave/third_party/chromium/base/observer_list.h
diff --git a/libweave/external/base/observer_list_unittest.cc b/libweave/third_party/chromium/base/observer_list_unittest.cc
similarity index 100%
rename from libweave/external/base/observer_list_unittest.cc
rename to libweave/third_party/chromium/base/observer_list_unittest.cc
diff --git a/libweave/external/base/posix/eintr_wrapper.h b/libweave/third_party/chromium/base/posix/eintr_wrapper.h
similarity index 100%
rename from libweave/external/base/posix/eintr_wrapper.h
rename to libweave/third_party/chromium/base/posix/eintr_wrapper.h
diff --git a/libweave/external/base/rand_util.cc b/libweave/third_party/chromium/base/rand_util.cc
similarity index 100%
rename from libweave/external/base/rand_util.cc
rename to libweave/third_party/chromium/base/rand_util.cc
diff --git a/libweave/external/base/rand_util.h b/libweave/third_party/chromium/base/rand_util.h
similarity index 100%
rename from libweave/external/base/rand_util.h
rename to libweave/third_party/chromium/base/rand_util.h
diff --git a/libweave/external/base/rand_util_posix.cc b/libweave/third_party/chromium/base/rand_util_posix.cc
similarity index 100%
rename from libweave/external/base/rand_util_posix.cc
rename to libweave/third_party/chromium/base/rand_util_posix.cc
diff --git a/libweave/external/base/rand_util_unittest.cc b/libweave/third_party/chromium/base/rand_util_unittest.cc
similarity index 100%
rename from libweave/external/base/rand_util_unittest.cc
rename to libweave/third_party/chromium/base/rand_util_unittest.cc
diff --git a/libweave/external/base/scoped_clear_errno.h b/libweave/third_party/chromium/base/scoped_clear_errno.h
similarity index 100%
rename from libweave/external/base/scoped_clear_errno.h
rename to libweave/third_party/chromium/base/scoped_clear_errno.h
diff --git a/libweave/external/base/scoped_clear_errno_unittest.cc b/libweave/third_party/chromium/base/scoped_clear_errno_unittest.cc
similarity index 100%
rename from libweave/external/base/scoped_clear_errno_unittest.cc
rename to libweave/third_party/chromium/base/scoped_clear_errno_unittest.cc
diff --git a/libweave/external/base/scoped_observer.h b/libweave/third_party/chromium/base/scoped_observer.h
similarity index 100%
rename from libweave/external/base/scoped_observer.h
rename to libweave/third_party/chromium/base/scoped_observer.h
diff --git a/libweave/external/base/strings/string_number_conversions.cc b/libweave/third_party/chromium/base/strings/string_number_conversions.cc
similarity index 100%
rename from libweave/external/base/strings/string_number_conversions.cc
rename to libweave/third_party/chromium/base/strings/string_number_conversions.cc
diff --git a/libweave/external/base/strings/string_number_conversions.h b/libweave/third_party/chromium/base/strings/string_number_conversions.h
similarity index 100%
rename from libweave/external/base/strings/string_number_conversions.h
rename to libweave/third_party/chromium/base/strings/string_number_conversions.h
diff --git a/libweave/external/base/strings/string_number_conversions_unittest.cc b/libweave/third_party/chromium/base/strings/string_number_conversions_unittest.cc
similarity index 100%
rename from libweave/external/base/strings/string_number_conversions_unittest.cc
rename to libweave/third_party/chromium/base/strings/string_number_conversions_unittest.cc
diff --git a/libweave/external/base/strings/string_piece.cc b/libweave/third_party/chromium/base/strings/string_piece.cc
similarity index 100%
rename from libweave/external/base/strings/string_piece.cc
rename to libweave/third_party/chromium/base/strings/string_piece.cc
diff --git a/libweave/external/base/strings/string_piece.h b/libweave/third_party/chromium/base/strings/string_piece.h
similarity index 100%
rename from libweave/external/base/strings/string_piece.h
rename to libweave/third_party/chromium/base/strings/string_piece.h
diff --git a/libweave/external/base/strings/string_piece_unittest.cc b/libweave/third_party/chromium/base/strings/string_piece_unittest.cc
similarity index 100%
rename from libweave/external/base/strings/string_piece_unittest.cc
rename to libweave/third_party/chromium/base/strings/string_piece_unittest.cc
diff --git a/libweave/external/base/strings/string_util.cc b/libweave/third_party/chromium/base/strings/string_util.cc
similarity index 100%
rename from libweave/external/base/strings/string_util.cc
rename to libweave/third_party/chromium/base/strings/string_util.cc
diff --git a/libweave/external/base/strings/string_util.h b/libweave/third_party/chromium/base/strings/string_util.h
similarity index 100%
rename from libweave/external/base/strings/string_util.h
rename to libweave/third_party/chromium/base/strings/string_util.h
diff --git a/libweave/external/base/strings/string_util_constants.cc b/libweave/third_party/chromium/base/strings/string_util_constants.cc
similarity index 100%
rename from libweave/external/base/strings/string_util_constants.cc
rename to libweave/third_party/chromium/base/strings/string_util_constants.cc
diff --git a/libweave/external/base/strings/string_util_posix.h b/libweave/third_party/chromium/base/strings/string_util_posix.h
similarity index 100%
rename from libweave/external/base/strings/string_util_posix.h
rename to libweave/third_party/chromium/base/strings/string_util_posix.h
diff --git a/libweave/external/base/strings/string_util_unittest.cc b/libweave/third_party/chromium/base/strings/string_util_unittest.cc
similarity index 100%
rename from libweave/external/base/strings/string_util_unittest.cc
rename to libweave/third_party/chromium/base/strings/string_util_unittest.cc
diff --git a/libweave/external/base/strings/stringprintf.cc b/libweave/third_party/chromium/base/strings/stringprintf.cc
similarity index 100%
rename from libweave/external/base/strings/stringprintf.cc
rename to libweave/third_party/chromium/base/strings/stringprintf.cc
diff --git a/libweave/external/base/strings/stringprintf.h b/libweave/third_party/chromium/base/strings/stringprintf.h
similarity index 100%
rename from libweave/external/base/strings/stringprintf.h
rename to libweave/third_party/chromium/base/strings/stringprintf.h
diff --git a/libweave/external/base/strings/stringprintf_unittest.cc b/libweave/third_party/chromium/base/strings/stringprintf_unittest.cc
similarity index 100%
rename from libweave/external/base/strings/stringprintf_unittest.cc
rename to libweave/third_party/chromium/base/strings/stringprintf_unittest.cc
diff --git a/libweave/external/base/strings/utf_string_conversion_utils.cc b/libweave/third_party/chromium/base/strings/utf_string_conversion_utils.cc
similarity index 100%
rename from libweave/external/base/strings/utf_string_conversion_utils.cc
rename to libweave/third_party/chromium/base/strings/utf_string_conversion_utils.cc
diff --git a/libweave/external/base/strings/utf_string_conversion_utils.h b/libweave/third_party/chromium/base/strings/utf_string_conversion_utils.h
similarity index 100%
rename from libweave/external/base/strings/utf_string_conversion_utils.h
rename to libweave/third_party/chromium/base/strings/utf_string_conversion_utils.h
diff --git a/libweave/external/base/template_util.h b/libweave/third_party/chromium/base/template_util.h
similarity index 100%
rename from libweave/external/base/template_util.h
rename to libweave/third_party/chromium/base/template_util.h
diff --git a/libweave/external/base/template_util_unittest.cc b/libweave/third_party/chromium/base/template_util_unittest.cc
similarity index 100%
rename from libweave/external/base/template_util_unittest.cc
rename to libweave/third_party/chromium/base/template_util_unittest.cc
diff --git a/libweave/external/base/third_party/dmg_fp/LICENSE b/libweave/third_party/chromium/base/third_party/dmg_fp/LICENSE
similarity index 100%
rename from libweave/external/base/third_party/dmg_fp/LICENSE
rename to libweave/third_party/chromium/base/third_party/dmg_fp/LICENSE
diff --git a/libweave/external/base/third_party/dmg_fp/README.chromium b/libweave/third_party/chromium/base/third_party/dmg_fp/README.chromium
similarity index 100%
rename from libweave/external/base/third_party/dmg_fp/README.chromium
rename to libweave/third_party/chromium/base/third_party/dmg_fp/README.chromium
diff --git a/libweave/external/base/third_party/dmg_fp/dmg_fp.h b/libweave/third_party/chromium/base/third_party/dmg_fp/dmg_fp.h
similarity index 100%
rename from libweave/external/base/third_party/dmg_fp/dmg_fp.h
rename to libweave/third_party/chromium/base/third_party/dmg_fp/dmg_fp.h
diff --git a/libweave/external/base/third_party/dmg_fp/dtoa.cc b/libweave/third_party/chromium/base/third_party/dmg_fp/dtoa.cc
similarity index 100%
rename from libweave/external/base/third_party/dmg_fp/dtoa.cc
rename to libweave/third_party/chromium/base/third_party/dmg_fp/dtoa.cc
diff --git a/libweave/external/base/third_party/dmg_fp/dtoa_wrapper.cc b/libweave/third_party/chromium/base/third_party/dmg_fp/dtoa_wrapper.cc
similarity index 100%
rename from libweave/external/base/third_party/dmg_fp/dtoa_wrapper.cc
rename to libweave/third_party/chromium/base/third_party/dmg_fp/dtoa_wrapper.cc
diff --git a/libweave/external/base/third_party/dmg_fp/g_fmt.cc b/libweave/third_party/chromium/base/third_party/dmg_fp/g_fmt.cc
similarity index 100%
rename from libweave/external/base/third_party/dmg_fp/g_fmt.cc
rename to libweave/third_party/chromium/base/third_party/dmg_fp/g_fmt.cc
diff --git a/libweave/external/base/third_party/icu/LICENSE b/libweave/third_party/chromium/base/third_party/icu/LICENSE
similarity index 100%
rename from libweave/external/base/third_party/icu/LICENSE
rename to libweave/third_party/chromium/base/third_party/icu/LICENSE
diff --git a/libweave/external/base/third_party/icu/README.chromium b/libweave/third_party/chromium/base/third_party/icu/README.chromium
similarity index 100%
rename from libweave/external/base/third_party/icu/README.chromium
rename to libweave/third_party/chromium/base/third_party/icu/README.chromium
diff --git a/libweave/external/base/third_party/icu/icu_utf.cc b/libweave/third_party/chromium/base/third_party/icu/icu_utf.cc
similarity index 100%
rename from libweave/external/base/third_party/icu/icu_utf.cc
rename to libweave/third_party/chromium/base/third_party/icu/icu_utf.cc
diff --git a/libweave/external/base/third_party/icu/icu_utf.h b/libweave/third_party/chromium/base/third_party/icu/icu_utf.h
similarity index 100%
rename from libweave/external/base/third_party/icu/icu_utf.h
rename to libweave/third_party/chromium/base/third_party/icu/icu_utf.h
diff --git a/libweave/external/base/time/clock.cc b/libweave/third_party/chromium/base/time/clock.cc
similarity index 100%
rename from libweave/external/base/time/clock.cc
rename to libweave/third_party/chromium/base/time/clock.cc
diff --git a/libweave/external/base/time/clock.h b/libweave/third_party/chromium/base/time/clock.h
similarity index 100%
rename from libweave/external/base/time/clock.h
rename to libweave/third_party/chromium/base/time/clock.h
diff --git a/libweave/external/base/time/default_clock.cc b/libweave/third_party/chromium/base/time/default_clock.cc
similarity index 100%
rename from libweave/external/base/time/default_clock.cc
rename to libweave/third_party/chromium/base/time/default_clock.cc
diff --git a/libweave/external/base/time/default_clock.h b/libweave/third_party/chromium/base/time/default_clock.h
similarity index 100%
rename from libweave/external/base/time/default_clock.h
rename to libweave/third_party/chromium/base/time/default_clock.h
diff --git a/libweave/external/base/time/time.cc b/libweave/third_party/chromium/base/time/time.cc
similarity index 100%
rename from libweave/external/base/time/time.cc
rename to libweave/third_party/chromium/base/time/time.cc
diff --git a/libweave/external/base/time/time.h b/libweave/third_party/chromium/base/time/time.h
similarity index 100%
rename from libweave/external/base/time/time.h
rename to libweave/third_party/chromium/base/time/time.h
diff --git a/libweave/external/base/time/time_posix.cc b/libweave/third_party/chromium/base/time/time_posix.cc
similarity index 100%
rename from libweave/external/base/time/time_posix.cc
rename to libweave/third_party/chromium/base/time/time_posix.cc
diff --git a/libweave/external/base/time/time_unittest.cc b/libweave/third_party/chromium/base/time/time_unittest.cc
similarity index 100%
rename from libweave/external/base/time/time_unittest.cc
rename to libweave/third_party/chromium/base/time/time_unittest.cc
diff --git a/libweave/external/base/tuple.h b/libweave/third_party/chromium/base/tuple.h
similarity index 100%
rename from libweave/external/base/tuple.h
rename to libweave/third_party/chromium/base/tuple.h
diff --git a/libweave/external/base/tuple_unittest.cc b/libweave/third_party/chromium/base/tuple_unittest.cc
similarity index 100%
rename from libweave/external/base/tuple_unittest.cc
rename to libweave/third_party/chromium/base/tuple_unittest.cc
diff --git a/libweave/external/base/values.cc b/libweave/third_party/chromium/base/values.cc
similarity index 100%
rename from libweave/external/base/values.cc
rename to libweave/third_party/chromium/base/values.cc
diff --git a/libweave/external/base/values.h b/libweave/third_party/chromium/base/values.h
similarity index 100%
rename from libweave/external/base/values.h
rename to libweave/third_party/chromium/base/values.h
diff --git a/libweave/external/base/values_unittest.cc b/libweave/third_party/chromium/base/values_unittest.cc
similarity index 100%
rename from libweave/external/base/values_unittest.cc
rename to libweave/third_party/chromium/base/values_unittest.cc
diff --git a/libweave/external/crypto/p224.cc b/libweave/third_party/chromium/crypto/p224.cc
similarity index 99%
rename from libweave/external/crypto/p224.cc
rename to libweave/third_party/chromium/crypto/p224.cc
index 8ec4034..81bce3a 100644
--- a/libweave/external/crypto/p224.cc
+++ b/libweave/third_party/chromium/crypto/p224.cc
@@ -7,11 +7,10 @@
 //
 // See http://www.imperialviolet.org/2010/12/04/ecc.html ([1]) for background.
 
-#include "external/crypto/p224.h"
+#include "third_party/chromium/crypto/p224.h"
 
 #include <string.h>
 
-namespace weave {
 namespace crypto {
 namespace p224 {
 
@@ -767,4 +766,3 @@
 
 }  // namespace p224
 }  // namespace crypto
-}  // namespace weave
diff --git a/libweave/external/crypto/p224.h b/libweave/third_party/chromium/crypto/p224.h
similarity index 90%
rename from libweave/external/crypto/p224.h
rename to libweave/third_party/chromium/crypto/p224.h
index ea15acf..7574389 100644
--- a/libweave/external/crypto/p224.h
+++ b/libweave/third_party/chromium/crypto/p224.h
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef LIBWEAVE_EXTERNAL_CRYPTO_P224_H_
-#define LIBWEAVE_EXTERNAL_CRYPTO_P224_H_
+#ifndef LIBWEAVE_THIRD_PARTY_CHROMIUM_P224_H_
+#define LIBWEAVE_THIRD_PARTY_CHROMIUM_P224_H_
 
 #include <string>
 
 #include <base/basictypes.h>
 
-namespace weave {
 namespace crypto {
 
 // P224 implements an elliptic curve group, commonly known as P224 and defined
@@ -54,6 +53,5 @@
 
 }  // namespace p224
 }  // namespace crypto
-}  // namespace weave
 
-#endif  // LIBWEAVE_EXTERNAL_CRYPTO_P224_H_
+#endif  // LIBWEAVE_THIRD_PARTY_CHROMIUM_P224_H_
diff --git a/libweave/external/crypto/p224_spake.cc b/libweave/third_party/chromium/crypto/p224_spake.cc
similarity index 98%
rename from libweave/external/crypto/p224_spake.cc
rename to libweave/third_party/chromium/crypto/p224_spake.cc
index 1db768e..6d82322 100644
--- a/libweave/external/crypto/p224_spake.cc
+++ b/libweave/third_party/chromium/crypto/p224_spake.cc
@@ -5,16 +5,15 @@
 // This code implements SPAKE2, a variant of EKE:
 //  http://www.di.ens.fr/~pointche/pub.php?reference=AbPo04
 
-#include "external/crypto/p224_spake.h"
+#include "third_party/chromium/crypto/p224_spake.h"
 
 #include <algorithm>
 
 #include <base/logging.h>
 #include <base/rand_util.h>
 
-#include "external/crypto/p224.h"
+#include "third_party/chromium/crypto/p224.h"
 
-namespace weave {
 namespace crypto {
 
 namespace {
@@ -283,4 +282,3 @@
 }
 
 }  // namespace crypto
-}  // namespace weave
diff --git a/libweave/external/crypto/p224_spake.h b/libweave/third_party/chromium/crypto/p224_spake.h
similarity index 94%
rename from libweave/external/crypto/p224_spake.h
rename to libweave/third_party/chromium/crypto/p224_spake.h
index 14bde6d..dcfd0fe 100644
--- a/libweave/external/crypto/p224_spake.h
+++ b/libweave/third_party/chromium/crypto/p224_spake.h
@@ -2,17 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef LIBWEAVE_EXTERNAL_CRYPTO_P224_SPAKE_H_
-#define LIBWEAVE_EXTERNAL_CRYPTO_P224_SPAKE_H_
+#ifndef LIBWEAVE_THIRD_PARTY_CHROMIUM_P224_SPAKE_H_
 
 #include <string>
 
 #include <base/gtest_prod_util.h>
 
-#include "external/crypto/p224.h"
-#include "external/crypto/sha2.h"
+#include "third_party/chromium/crypto/p224.h"
+#include "third_party/chromium/crypto/sha2.h"
 
-namespace weave {
 namespace crypto {
 
 // P224EncryptedKeyExchange implements SPAKE2, a variant of Encrypted
@@ -124,6 +122,5 @@
 };
 
 }  // namespace crypto
-}  // namespace weave
 
-#endif  // LIBWEAVE_EXTERNAL_CRYPTO_P224_SPAKE_H_
+#endif  // LIBWEAVE_THIRD_PARTY_CHROMIUM_P224_SPAKE_H_
diff --git a/libweave/external/crypto/p224_spake_unittest.cc b/libweave/third_party/chromium/crypto/p224_spake_unittest.cc
similarity index 98%
rename from libweave/external/crypto/p224_spake_unittest.cc
rename to libweave/third_party/chromium/crypto/p224_spake_unittest.cc
index 21cda88..9a9f9d2 100644
--- a/libweave/external/crypto/p224_spake_unittest.cc
+++ b/libweave/third_party/chromium/crypto/p224_spake_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "external/crypto/p224_spake.h"
+#include "third_party/chromium/crypto/p224_spake.h"
 
 #include <string>
 
@@ -10,7 +10,6 @@
 #include <base/strings/string_number_conversions.h>
 #include <gtest/gtest.h>
 
-namespace weave {
 namespace crypto {
 
 namespace {
@@ -173,4 +172,3 @@
 }
 
 }  // namespace crypto
-}  // namespace weave
diff --git a/libweave/external/crypto/p224_unittest.cc b/libweave/third_party/chromium/crypto/p224_unittest.cc
similarity index 99%
rename from libweave/external/crypto/p224_unittest.cc
rename to libweave/third_party/chromium/crypto/p224_unittest.cc
index 6bfda46..0540dbb 100644
--- a/libweave/external/crypto/p224_unittest.cc
+++ b/libweave/third_party/chromium/crypto/p224_unittest.cc
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "third_party/chromium/crypto/p224.h"
+
 #include <stdio.h>
 #include <string.h>
 
 #include <gtest/gtest.h>
 
-#include "external/crypto/p224.h"
-
-namespace weave {
 namespace crypto {
 
 using p224::Point;
@@ -823,4 +822,3 @@
 }
 
 }  // namespace crypto
-}  // namespace weave
diff --git a/libweave/external/crypto/sha2.cc b/libweave/third_party/chromium/crypto/sha2.cc
similarity index 91%
rename from libweave/external/crypto/sha2.cc
rename to libweave/third_party/chromium/crypto/sha2.cc
index 4ad40ba..7dcef0b 100644
--- a/libweave/external/crypto/sha2.cc
+++ b/libweave/third_party/chromium/crypto/sha2.cc
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "external/crypto/sha2.h"
+#include "third_party/chromium/crypto/sha2.h"
 
 #include <algorithm>
 #include <openssl/sha.h>
 
 #include <base/memory/scoped_ptr.h>
 
-namespace weave {
 namespace crypto {
 
 void SHA256HashString(const std::string& str, uint8_t* output, size_t len) {
@@ -29,4 +28,3 @@
 }
 
 }  // namespace crypto
-}  // namespace weave
diff --git a/libweave/external/crypto/sha2.h b/libweave/third_party/chromium/crypto/sha2.h
similarity index 83%
rename from libweave/external/crypto/sha2.h
rename to libweave/third_party/chromium/crypto/sha2.h
index 920a798..8e05388 100644
--- a/libweave/external/crypto/sha2.h
+++ b/libweave/third_party/chromium/crypto/sha2.h
@@ -2,12 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef LIBWEAVE_EXTERNAL_CRYPTO_SHA2_H_
-#define LIBWEAVE_EXTERNAL_CRYPTO_SHA2_H_
+#ifndef LIBWEAVE_THIRD_PARTY_CHROMIUM_SHA2_H_
+#define LIBWEAVE_THIRD_PARTY_CHROMIUM_SHA2_H_
 
 #include <string>
 
-namespace weave {
 namespace crypto {
 
 // These functions perform SHA-256 operations.
@@ -26,6 +25,5 @@
 std::string SHA256HashString(const std::string& str);
 
 }  // namespace crypto
-}  // namespace weave
 
-#endif  // LIBWEAVE_EXTERNAL_CRYPTO_SHA2_H_
+#endif  // LIBWEAVE_THIRD_PARTY_CHROMIUM_SHA2_H_
diff --git a/libweave/external/crypto/sha2_unittest.cc b/libweave/third_party/chromium/crypto/sha2_unittest.cc
similarity index 98%
rename from libweave/external/crypto/sha2_unittest.cc
rename to libweave/third_party/chromium/crypto/sha2_unittest.cc
index cd89043..0c30f45 100644
--- a/libweave/external/crypto/sha2_unittest.cc
+++ b/libweave/third_party/chromium/crypto/sha2_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "external/crypto/sha2.h"
+#include "third_party/chromium/crypto/sha2.h"
 
 #include <base/basictypes.h>
 #include <gtest/gtest.h>