git merge --no-ff remotes/weave/master

Change-Id: I15f4cbdaaedeeebceb69631265ce3b7862876405
diff --git a/libweave/README b/libweave/README
index 7e519b9..b772553 100644
--- a/libweave/README
+++ b/libweave/README
@@ -34,16 +34,16 @@
   libweave_standalone.gyp
   libweave_common.gypi
 
-Quick start on Ubuntu
----------------------
+Quick start on Debian/Ubuntu
+----------------------------
 
 Install prerequisites:
 
-  examples/ubuntu/prerequisites.sh
+  examples/prerequisites.sh
 
 Build library, tests, run tests, build example:
 
-  examples/ubuntu/build.sh
+  examples/build.sh
 
 Execute example:
 
@@ -115,3 +115,7 @@
   git push origin HEAD:refs/for/master
 
 Go to the url from the output of "push" and add reviewers.
+
+Known Issues
+------------
+* No big-endian support. Pairing fails on big-endian hardware.
diff --git a/libweave/examples/ubuntu/build.sh b/libweave/examples/build.sh
similarity index 82%
rename from libweave/examples/ubuntu/build.sh
rename to libweave/examples/build.sh
index cc585cb..2bd398ef 100755
--- a/libweave/examples/ubuntu/build.sh
+++ b/libweave/examples/build.sh
@@ -4,11 +4,11 @@
 # found in the LICENSE file.
 
 DIR=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
-ROOT_DIR=$(cd -P -- "$(dirname -- "$0")/../.." && pwd -P)
+ROOT_DIR=$(cd -P -- "$(dirname -- "$0")/.." && pwd -P)
 
 cd $ROOT_DIR
 
-gyp -Ilibweave_common.gypi --toplevel-dir=. --depth=. -f ninja $DIR/weave.gyp
+gyp -Ilibweave_common.gypi --toplevel-dir=. --depth=. -f ninja $DIR/daemon/daemon.gyp
 
 if [ -z "$BUILD_CONFIG" ]; then
    export BUILD_CONFIG=Debug
@@ -16,7 +16,7 @@
 
 export BUILD_TARGET=$*
 if [ -z "$BUILD_TARGET" ]; then
-   export BUILD_TARGET="weave libweave_testrunner libweave_exports_testrunner"
+   export BUILD_TARGET="weave_daemon libweave_testrunner libweave_exports_testrunner"
 fi
 
 export CORES=`cat /proc/cpuinfo | grep processor | wc -l`
diff --git a/libweave/examples/ubuntu/README b/libweave/examples/daemon/README
similarity index 94%
rename from libweave/examples/ubuntu/README
rename to libweave/examples/daemon/README
index af67036..48a4e95 100644
--- a/libweave/examples/ubuntu/README
+++ b/libweave/examples/daemon/README
@@ -5,9 +5,9 @@
 Building
 --------
   - prepare environment
-        examples/ubuntu/prerequisites.sh
+        examples/prerequisites.sh
   - build daemon
-        examples/ubuntu/build.sh
+        examples/build.sh
   - binaries for daemon is at
         out/Debug/weave
 
@@ -48,7 +48,7 @@
 ------------------------
   - copy the ticket "id" generated above
         93019287-6b26-04a0-22ee-d55ad23a4226
-  - go to ubuntu terminal, register ubuntu and start daemon with
+  - go to terminal, register and start the daemon with
 
         sudo out/Debug/weave --registration_ticket=93019287-6b26-04a0-22ee-d55ad23a4226
 
@@ -75,8 +75,8 @@
         chrome: https://chrome.google.com/webstore/detail/weave-device-manager/pcdgflbjckpjmlofgopidgdfonmnodfm
         [need your whitelisted EAP account]
 
-Send Command to Ubuntu Device
------------------------------
+Send Command to the Daemon
+--------------------------
   - go to the oauthplayground used for registration ticket command
      in "Step 3", send base.identify with
         HTTP Method: POST
diff --git a/libweave/examples/daemon/daemon.gyp b/libweave/examples/daemon/daemon.gyp
new file mode 100644
index 0000000..f421601
--- /dev/null
+++ b/libweave/examples/daemon/daemon.gyp
@@ -0,0 +1,18 @@
+# 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_daemon',
+      'type': 'executable',
+      'sources': [
+        'main.cc',
+      ],
+      'dependencies': [
+        '../../libweave_standalone.gyp:libweave',
+        '../provider/provider.gyp:libweave_provider',
+      ]
+    }
+  ]
+}
diff --git a/libweave/examples/daemon/ledflasher_handler.h b/libweave/examples/daemon/ledflasher_handler.h
new file mode 100644
index 0000000..812a3d4
--- /dev/null
+++ b/libweave/examples/daemon/ledflasher_handler.h
@@ -0,0 +1,132 @@
+// 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 <weave/device.h>
+
+#include <base/bind.h>
+#include <base/memory/weak_ptr.h>
+
+#include <bitset>
+
+namespace weave {
+namespace examples {
+namespace daemon {
+
+namespace {
+// Supported LED count on this device
+const size_t kLedCount = 3;
+}  // namespace
+
+// LedFlasherHandler is a complete command handler example that shows
+// how to handle commands that modify device state.
+class LedFlasherHandler {
+ public:
+  LedFlasherHandler() {}
+  void Register(Device* device) {
+    device_ = device;
+
+    device->AddStateDefinitionsFromJson(R"({
+      "_ledflasher": {"_leds": {"items": "boolean"}}
+    })");
+
+    device->SetStatePropertiesFromJson(R"({
+      "_ledflasher":{"_leds": [false, false, false]}
+    })",
+                                       nullptr);
+
+    device->AddCommandDefinitionsFromJson(R"({
+      "_ledflasher": {
+         "_set":{
+           "parameters": {
+             "_led": {"minimum": 1, "maximum": 3},
+             "_on": "boolean"
+           }
+         },
+         "_toggle":{
+           "parameters": {
+             "_led": {"minimum": 1, "maximum": 3}
+           }
+        }
+      }
+    })");
+    device->AddCommandHandler(
+        "_ledflasher._toggle",
+        base::Bind(&LedFlasherHandler::OnFlasherToggleCommand,
+                   weak_ptr_factory_.GetWeakPtr()));
+    device->AddCommandHandler(
+        "_ledflasher._set", base::Bind(&LedFlasherHandler::OnFlasherSetCommand,
+                                       weak_ptr_factory_.GetWeakPtr()));
+  }
+
+ private:
+  void OnFlasherSetCommand(const std::weak_ptr<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->Complete({}, nullptr);
+      return;
+    }
+    ErrorPtr error;
+    Error::AddTo(&error, FROM_HERE, "example", "invalid_parameter_value",
+                 "Invalid parameters");
+    cmd->Abort(error.get(), nullptr);
+  }
+
+  void OnFlasherToggleCommand(const std::weak_ptr<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;
+    }
+    ErrorPtr error;
+    Error::AddTo(&error, FROM_HERE, "example", "invalid_parameter_value",
+                 "Invalid parameters");
+    cmd->Abort(error.get(), nullptr);
+  }
+
+  void UpdateLedState(void) {
+    base::ListValue list;
+    for (uint32_t i = 0; i < led_status_.size(); i++)
+      list.AppendBoolean(led_status_[i] ? true : false);
+
+    device_->SetStateProperty("_ledflasher._leds", list, nullptr);
+  }
+
+  Device* device_{nullptr};
+
+  // Simulate LED status on this device so client app could explore
+  // Each bit represents one device, indexing from LSB
+  std::bitset<kLedCount> led_status_{0};
+  base::WeakPtrFactory<LedFlasherHandler> weak_ptr_factory_{this};
+};
+
+}  // namespace daemon
+}  // namespace examples
+}  // namespace weave
diff --git a/libweave/examples/daemon/main.cc b/libweave/examples/daemon/main.cc
new file mode 100644
index 0000000..263e730
--- /dev/null
+++ b/libweave/examples/daemon/main.cc
@@ -0,0 +1,118 @@
+// 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 <weave/device.h>
+#include <weave/error.h>
+
+#include <base/bind.h>
+
+#include "examples/daemon/ledflasher_handler.h"
+#include "examples/daemon/sample_handler.h"
+
+#include "examples/provider/avahi_client.h"
+#include "examples/provider/bluez_client.h"
+#include "examples/provider/curl_http_client.h"
+#include "examples/provider/event_http_server.h"
+#include "examples/provider/event_network.h"
+#include "examples/provider/event_task_runner.h"
+#include "examples/provider/file_config_store.h"
+#include "examples/provider/wifi_manager.h"
+
+namespace {
+
+// Supported LED count on this device
+const size_t kLedCount = 3;
+
+void ShowUsage(const std::string& name) {
+  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 "
+                "given ticket\n"
+             << "\t--disable_privet             Disable local privet\n";
+}
+
+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) {
+  bool force_bootstrapping = false;
+  bool disable_security = false;
+  bool disable_privet = false;
+  std::string registration_ticket;
+  for (int i = 1; i < argc; ++i) {
+    std::string arg = argv[i];
+    if (arg == "-h" || arg == "--help") {
+      ShowUsage(argv[0]);
+      return 0;
+    } else if (arg == "-b" || arg == "--bootstrapping") {
+      force_bootstrapping = true;
+    } else if (arg == "-d" || arg == "--disable_security") {
+      disable_security = true;
+    } else if (arg == "--disable_privet") {
+      disable_privet = true;
+    } else if (arg.find("--registration_ticket") != std::string::npos) {
+      auto pos = arg.find("=");
+      if (pos == std::string::npos) {
+        ShowUsage(argv[0]);
+        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;
+    }
+  }
+
+  weave::examples::FileConfigStore config_store{disable_security};
+  weave::examples::EventTaskRunner task_runner;
+  weave::examples::CurlHttpClient http_client{&task_runner};
+  weave::examples::EventNetworkImpl network{&task_runner};
+  weave::examples::BluetoothImpl bluetooth;
+  std::unique_ptr<weave::examples::AvahiClient> dns_sd;
+  std::unique_ptr<weave::examples::HttpServerImpl> http_server;
+  std::unique_ptr<weave::examples::WifiImpl> wifi;
+
+  if (!disable_privet) {
+    dns_sd.reset(new weave::examples::AvahiClient);
+    http_server.reset(new weave::examples::HttpServerImpl{&task_runner});
+    if (weave::examples::WifiImpl::HasWifiCapability())
+      wifi.reset(
+          new weave::examples::WifiImpl{&task_runner, force_bootstrapping});
+  }
+  std::unique_ptr<weave::Device> device{weave::Device::Create(
+      &config_store, &task_runner, &http_client, &network, dns_sd.get(),
+      http_server.get(), wifi.get(), &bluetooth)};
+
+  if (!registration_ticket.empty()) {
+    device->Register(registration_ticket,
+                     base::Bind(&OnRegisterDeviceDone, device.get()));
+  }
+
+  weave::examples::daemon::SampleHandler sample{&task_runner};
+  weave::examples::daemon::LedFlasherHandler ledFlasher;
+  sample.Register(device.get());
+  ledFlasher.Register(device.get());
+
+  task_runner.Run();
+
+  LOG(INFO) << "exit";
+  return 0;
+}
diff --git a/libweave/examples/daemon/sample_handler.h b/libweave/examples/daemon/sample_handler.h
new file mode 100644
index 0000000..eca3452
--- /dev/null
+++ b/libweave/examples/daemon/sample_handler.h
@@ -0,0 +1,160 @@
+// 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 <weave/device.h>
+#include <weave/provider/task_runner.h>
+
+#include <base/bind.h>
+#include <base/memory/weak_ptr.h>
+
+namespace weave {
+namespace examples {
+namespace daemon {
+
+// SampleHandler is a command handler example.
+// It implements the following commands:
+// - _hello: handle a command with an argument and set its results.
+// - _ping: update device state.
+// - _countdown: handle long running command and report progress.
+class SampleHandler {
+ public:
+  SampleHandler(provider::TaskRunner* task_runner)
+      : task_runner_{task_runner} {}
+  void Register(Device* device) {
+    device_ = device;
+
+    device->AddCommandDefinitionsFromJson(R"({
+      "_sample": {
+        "_hello": {
+          "minimalRole": "user",
+          "parameters": {
+            "_name": "string"
+          },
+          "results": { "_reply": "string" }
+        },
+        "_ping": {
+          "minimalRole": "user",
+          "results": {}
+        },
+        "_countdown": {
+          "minimalRole": "user",
+          "parameters": {
+            "_seconds": {"minimum": 1, "maximum": 25}
+          },
+          "progress": { "_seconds_left": "integer"},
+          "results": {}
+        }
+      }
+    })");
+
+    device->AddStateDefinitionsFromJson(R"({
+      "_sample": {"_ping_count":"integer"}
+    })");
+
+    device->SetStatePropertiesFromJson(R"({
+      "_sample": {"_ping_count": 0}
+    })",
+                                       nullptr);
+
+    device->AddCommandHandler("_sample._hello",
+                              base::Bind(&SampleHandler::OnHelloCommand,
+                                         weak_ptr_factory_.GetWeakPtr()));
+    device->AddCommandHandler("_sample._ping",
+                              base::Bind(&SampleHandler::OnPingCommand,
+                                         weak_ptr_factory_.GetWeakPtr()));
+    device->AddCommandHandler("_sample._countdown",
+                              base::Bind(&SampleHandler::OnCountdownCommand,
+                                         weak_ptr_factory_.GetWeakPtr()));
+  }
+
+ private:
+  void OnHelloCommand(const std::weak_ptr<Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
+
+    std::string name;
+    if (!cmd->GetParameters()->GetString("_name", &name)) {
+      ErrorPtr error;
+      Error::AddTo(&error, FROM_HERE, "example", "invalid_parameter_value",
+                   "Name is missing");
+      cmd->Abort(error.get(), nullptr);
+      return;
+    }
+
+    base::DictionaryValue result;
+    result.SetString("_reply", "Hello " + name);
+    cmd->Complete(result, nullptr);
+    LOG(INFO) << cmd->GetName() << " command finished: " << result;
+  }
+
+  void OnPingCommand(const std::weak_ptr<Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
+
+    base::DictionaryValue state;
+    state.SetInteger("_sample._ping_count", ++ping_count_);
+    device_->SetStateProperties(state, nullptr);
+    LOG(INFO) << "New state: " << *device_->GetState();
+
+    base::DictionaryValue result;
+    cmd->Complete(result, nullptr);
+
+    LOG(INFO) << cmd->GetName() << " command finished: " << result;
+  }
+
+  void OnCountdownCommand(const std::weak_ptr<Command>& command) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+    LOG(INFO) << "received command: " << cmd->GetName();
+
+    int seconds;
+    if (!cmd->GetParameters()->GetInteger("_seconds", &seconds))
+      seconds = 10;
+
+    LOG(INFO) << "starting countdown";
+    DoTick(cmd, seconds);
+  }
+
+  void DoTick(const std::weak_ptr<Command>& command, int seconds) {
+    auto cmd = command.lock();
+    if (!cmd)
+      return;
+
+    if (seconds > 0) {
+      std::string todo;
+      cmd->GetParameters()->GetString("_todo", &todo);
+      LOG(INFO) << "countdown tick: " << seconds << " seconds left";
+
+      base::DictionaryValue progress;
+      progress.SetInteger("_seconds_left", seconds);
+      cmd->SetProgress(progress, nullptr);
+      task_runner_->PostDelayedTask(
+          FROM_HERE,
+          base::Bind(&SampleHandler::DoTick, weak_ptr_factory_.GetWeakPtr(),
+                     command, --seconds),
+          base::TimeDelta::FromSeconds(1));
+      return;
+    }
+
+    base::DictionaryValue result;
+    cmd->Complete(result, nullptr);
+    LOG(INFO) << "countdown finished";
+    LOG(INFO) << cmd->GetName() << " command finished: " << result;
+  }
+
+  Device* device_{nullptr};
+  provider::TaskRunner* task_runner_{nullptr};
+
+  int ping_count_{0};
+  base::WeakPtrFactory<SampleHandler> weak_ptr_factory_{this};
+};
+
+}  // namespace daemon
+}  // namespace examples
+}  // namespace weave
diff --git a/libweave/examples/ubuntu/prerequisites.sh b/libweave/examples/prerequisites.sh
similarity index 96%
rename from libweave/examples/ubuntu/prerequisites.sh
rename to libweave/examples/prerequisites.sh
index bc70fd3..d1661d0 100755
--- a/libweave/examples/ubuntu/prerequisites.sh
+++ b/libweave/examples/prerequisites.sh
@@ -4,7 +4,7 @@
 # found in the LICENSE file.
 
 DIR=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
-ROOT_DIR=$(cd -P -- "$(dirname -- "$0")/../.." && pwd -P)
+ROOT_DIR=$(cd -P -- "$(dirname -- "$0")/.." && pwd -P)
 
 sudo apt-get install ${APT_GET_OPTS} \
   autoconf \
diff --git a/libweave/examples/ubuntu/avahi_client.cc b/libweave/examples/provider/avahi_client.cc
similarity index 98%
rename from libweave/examples/ubuntu/avahi_client.cc
rename to libweave/examples/provider/avahi_client.cc
index 3bc1823..18ad555 100644
--- a/libweave/examples/ubuntu/avahi_client.cc
+++ b/libweave/examples/provider/avahi_client.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 "examples/ubuntu/avahi_client.h"
+#include "examples/provider/avahi_client.h"
 
 #include <cstdlib>
 #include <vector>
diff --git a/libweave/examples/ubuntu/avahi_client.h b/libweave/examples/provider/avahi_client.h
similarity index 88%
rename from libweave/examples/ubuntu/avahi_client.h
rename to libweave/examples/provider/avahi_client.h
index 64e0eb1..0ca28db 100644
--- a/libweave/examples/ubuntu/avahi_client.h
+++ b/libweave/examples/provider/avahi_client.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef LIBWEAVE_EXAMPLES_UBUNTU_AVAHI_CLIENT_H_
-#define LIBWEAVE_EXAMPLES_UBUNTU_AVAHI_CLIENT_H_
+#ifndef LIBWEAVE_EXAMPLES_PROVIDER_AVAHI_CLIENT_H_
+#define LIBWEAVE_EXAMPLES_PROVIDER_AVAHI_CLIENT_H_
 
 #include <map>
 #include <string>
@@ -44,4 +44,4 @@
 }  // namespace examples
 }  // namespace weave
 
-#endif  // LIBWEAVE_EXAMPLES_UBUNTU_AVAHI_CLIENT_H_
+#endif  // LIBWEAVE_EXAMPLES_PROVIDER_AVAHI_CLIENT_H_
diff --git a/libweave/examples/ubuntu/bluez_client.cc b/libweave/examples/provider/bluez_client.cc
similarity index 87%
rename from libweave/examples/ubuntu/bluez_client.cc
rename to libweave/examples/provider/bluez_client.cc
index 35defde..5342bcb 100644
--- a/libweave/examples/ubuntu/bluez_client.cc
+++ b/libweave/examples/provider/bluez_client.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 "examples/ubuntu/bluez_client.h"
+#include "examples/provider/bluez_client.h"
 
 namespace weave {
 namespace examples {
diff --git a/libweave/examples/ubuntu/bluez_client.h b/libweave/examples/provider/bluez_client.h
similarity index 74%
rename from libweave/examples/ubuntu/bluez_client.h
rename to libweave/examples/provider/bluez_client.h
index e0bc2f2..922f325 100644
--- a/libweave/examples/ubuntu/bluez_client.h
+++ b/libweave/examples/provider/bluez_client.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef LIBWEAVE_EXAMPLES_UBUNTU_BLUEZ_CLIENT_H_
-#define LIBWEAVE_EXAMPLES_UBUNTU_BLUEZ_CLIENT_H_
+#ifndef LIBWEAVE_EXAMPLES_PROVIDER_BLUEZ_CLIENT_H_
+#define LIBWEAVE_EXAMPLES_PROVIDER_BLUEZ_CLIENT_H_
 
 #include <weave/provider/bluetooth.h>
 
@@ -21,4 +21,4 @@
 }  // namespace examples
 }  // namespace weave
 
-#endif  // LIBWEAVE_EXAMPLES_UBUNTU_BLUEZ_CLIENT_H_
+#endif  // LIBWEAVE_EXAMPLES_PROVIDER_BLUEZ_CLIENT_H_
diff --git a/libweave/examples/ubuntu/curl_http_client.cc b/libweave/examples/provider/curl_http_client.cc
similarity index 94%
rename from libweave/examples/ubuntu/curl_http_client.cc
rename to libweave/examples/provider/curl_http_client.cc
index 0ddf57a..b440f39 100644
--- a/libweave/examples/ubuntu/curl_http_client.cc
+++ b/libweave/examples/provider/curl_http_client.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 "examples/ubuntu/curl_http_client.h"
+#include "examples/provider/curl_http_client.h"
 
 #include <base/bind.h>
 #include <curl/curl.h>
@@ -46,11 +46,11 @@
 
   switch (method) {
     case Method::kGet:
-    CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 1L));
-    break;
+      CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPGET, 1L));
+      break;
     case Method::kPost:
-    CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPPOST, 1L));
-    break;
+      CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_HTTPPOST, 1L));
+      break;
     case Method::kPatch:
     case Method::kPut:
       CHECK_EQ(CURLE_OK, curl_easy_setopt(curl.get(), CURLOPT_CUSTOMREQUEST,
diff --git a/libweave/examples/ubuntu/curl_http_client.h b/libweave/examples/provider/curl_http_client.h
similarity index 85%
rename from libweave/examples/ubuntu/curl_http_client.h
rename to libweave/examples/provider/curl_http_client.h
index daf9b0c..b2f4bca 100644
--- a/libweave/examples/ubuntu/curl_http_client.h
+++ b/libweave/examples/provider/curl_http_client.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef LIBWEAVE_EXAMPLES_UBUNTU_CURL_HTTP_CLIENT_H_
-#define LIBWEAVE_EXAMPLES_UBUNTU_CURL_HTTP_CLIENT_H_
+#ifndef LIBWEAVE_EXAMPLES_PROVIDER_CURL_HTTP_CLIENT_H_
+#define LIBWEAVE_EXAMPLES_PROVIDER_CURL_HTTP_CLIENT_H_
 
 #include <string>
 
@@ -39,4 +39,4 @@
 }  // namespace examples
 }  // namespace weave
 
-#endif  // LIBWEAVE_EXAMPLES_UBUNTU_CURL_HTTP_CLIENT_H_
+#endif  // LIBWEAVE_EXAMPLES_PROVIDER_CURL_HTTP_CLIENT_H_
diff --git a/libweave/examples/ubuntu/event_http_client.cc b/libweave/examples/provider/event_http_client.cc
similarity index 97%
rename from libweave/examples/ubuntu/event_http_client.cc
rename to libweave/examples/provider/event_http_client.cc
index 22b68f2..b38bd55 100644
--- a/libweave/examples/ubuntu/event_http_client.cc
+++ b/libweave/examples/provider/event_http_client.cc
@@ -2,8 +2,8 @@
 // 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 "examples/provider/event_http_client.h"
+#include "examples/provider/event_task_runner.h"
 
 #include <weave/enum_to_string.h>
 
diff --git a/libweave/examples/ubuntu/event_http_client.h b/libweave/examples/provider/event_http_client.h
similarity index 83%
rename from libweave/examples/ubuntu/event_http_client.h
rename to libweave/examples/provider/event_http_client.h
index 6ec2db6..7ad117e 100644
--- a/libweave/examples/ubuntu/event_http_client.h
+++ b/libweave/examples/provider/event_http_client.h
@@ -2,8 +2,8 @@
 // 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_
+#ifndef LIBWEAVE_EXAMPLES_PROVIDER_EVENT_HTTP_CLIENT_H_
+#define LIBWEAVE_EXAMPLES_PROVIDER_EVENT_HTTP_CLIENT_H_
 
 #include <string>
 
@@ -35,4 +35,4 @@
 }  // namespace examples
 }  // namespace weave
 
-#endif  // LIBWEAVE_EXAMPLES_UBUNTU_EVENT_HTTP_CLIENT_H_
+#endif  // LIBWEAVE_EXAMPLES_PROVIDER_EVENT_HTTP_CLIENT_H_
diff --git a/libweave/examples/ubuntu/event_http_server.cc b/libweave/examples/provider/event_http_server.cc
similarity index 98%
rename from libweave/examples/ubuntu/event_http_server.cc
rename to libweave/examples/provider/event_http_server.cc
index 95fea21..bb55c50 100644
--- a/libweave/examples/ubuntu/event_http_server.cc
+++ b/libweave/examples/provider/event_http_server.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 "examples/ubuntu/event_http_server.h"
+#include "examples/provider/event_http_server.h"
 
 #include <vector>
 
@@ -11,7 +11,7 @@
 #include <event2/bufferevent_ssl.h>
 #include <openssl/err.h>
 
-#include "examples/ubuntu/event_task_runner.h"
+#include "examples/provider/event_task_runner.h"
 
 namespace weave {
 namespace examples {
@@ -159,9 +159,7 @@
 void HttpServerImpl::ProcessReply(std::shared_ptr<RequestImpl> request,
                                   int status_code,
                                   const std::string& data,
-                                  const std::string& mime_type) {
-
-}
+                                  const std::string& mime_type) {}
 
 void HttpServerImpl::AddHttpRequestHandler(
     const std::string& path,
diff --git a/libweave/examples/ubuntu/event_http_server.h b/libweave/examples/provider/event_http_server.h
similarity index 93%
rename from libweave/examples/ubuntu/event_http_server.h
rename to libweave/examples/provider/event_http_server.h
index 4828b72..e51c6f4 100644
--- a/libweave/examples/ubuntu/event_http_server.h
+++ b/libweave/examples/provider/event_http_server.h
@@ -2,8 +2,8 @@
 // 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_SERVER_H_
-#define LIBWEAVE_EXAMPLES_UBUNTU_EVENT_HTTP_SERVER_H_
+#ifndef LIBWEAVE_EXAMPLES_PROVIDER_EVENT_HTTP_SERVER_H_
+#define LIBWEAVE_EXAMPLES_PROVIDER_EVENT_HTTP_SERVER_H_
 
 #include <event2/http.h>
 #include <evhttp.h>
@@ -70,4 +70,4 @@
 }  // namespace examples
 }  // namespace weave
 
-#endif  // LIBWEAVE_EXAMPLES_UBUNTU_EVENT_HTTP_SERVER_H_
+#endif  // LIBWEAVE_EXAMPLES_PROVIDER_EVENT_HTTP_SERVER_H_
diff --git a/libweave/examples/ubuntu/event_network.cc b/libweave/examples/provider/event_network.cc
similarity index 77%
rename from libweave/examples/ubuntu/event_network.cc
rename to libweave/examples/provider/event_network.cc
index b411f80..d86db69 100644
--- a/libweave/examples/ubuntu/event_network.cc
+++ b/libweave/examples/provider/event_network.cc
@@ -1,8 +1,8 @@
-// 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 "examples/ubuntu/event_network.h"
+#include "examples/provider/event_network.h"
 
 #include <weave/enum_to_string.h>
 
@@ -10,8 +10,8 @@
 #include <event2/dns.h>
 #include <event2/bufferevent.h>
 
-#include "examples/ubuntu/event_task_runner.h"
-#include "examples/ubuntu/ssl_stream.h"
+#include "examples/provider/event_task_runner.h"
+#include "examples/provider/ssl_stream.h"
 
 namespace weave {
 namespace examples {
@@ -19,6 +19,7 @@
 namespace {
 const char kNetworkProbeHostname[] = "talk.google.com";
 const int kNetworkProbePort = 5223;
+const int kNetworkProbeTimeoutS = 2;
 }  // namespace
 
 void EventNetworkImpl::Deleter::operator()(evdns_base* dns_base) {
@@ -43,6 +44,8 @@
   std::unique_ptr<bufferevent, Deleter> bev{
       bufferevent_socket_new(task_runner_->GetEventBase(), -1,
                              BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS)};
+  timeval timeout{kNetworkProbeTimeoutS, 0};
+  bufferevent_set_timeouts(bev.get(), &timeout, &timeout);
   bufferevent_setcb(
       bev.get(), nullptr, nullptr,
       [](struct bufferevent* buf, short events, void* ctx) {
@@ -77,10 +80,17 @@
 
 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();
+  if (state != network_state_) {
+    LOG(INFO) << "network state updated: " << weave::EnumToString(state);
+    network_state_ = state;
+
+    // In general it's better to send false notification than miss one.
+    // However current implementation can only send them very often on every
+    // UpdateNetworkStateCallback or just here, guarder with this if condition.
+    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,
@@ -95,20 +105,7 @@
 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)), {});
-  }
+  SSLStream::Connect(task_runner_, host, port, callback);
 }
 
 }  // namespace examples
diff --git a/libweave/examples/ubuntu/event_network.h b/libweave/examples/provider/event_network.h
similarity index 100%
rename from libweave/examples/ubuntu/event_network.h
rename to libweave/examples/provider/event_network.h
diff --git a/libweave/examples/ubuntu/event_task_runner.cc b/libweave/examples/provider/event_task_runner.cc
similarity index 96%
rename from libweave/examples/ubuntu/event_task_runner.cc
rename to libweave/examples/provider/event_task_runner.cc
index b781ff4..c14a934 100644
--- a/libweave/examples/ubuntu/event_task_runner.cc
+++ b/libweave/examples/provider/event_task_runner.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 "examples/ubuntu/event_task_runner.h"
+#include "examples/provider/event_task_runner.h"
 
 #include <signal.h>
 
diff --git a/libweave/examples/ubuntu/event_task_runner.h b/libweave/examples/provider/event_task_runner.h
similarity index 90%
rename from libweave/examples/ubuntu/event_task_runner.h
rename to libweave/examples/provider/event_task_runner.h
index 77d7e72..473441e 100644
--- a/libweave/examples/ubuntu/event_task_runner.h
+++ b/libweave/examples/provider/event_task_runner.h
@@ -2,8 +2,8 @@
 // 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_TASK_RUNNER_H_
-#define LIBWEAVE_EXAMPLES_UBUNTU_EVENT_TASK_RUNNER_H_
+#ifndef LIBWEAVE_EXAMPLES_PROVIDER_EVENT_TASK_RUNNER_H_
+#define LIBWEAVE_EXAMPLES_PROVIDER_EVENT_TASK_RUNNER_H_
 
 #include <queue>
 #include <utility>
@@ -56,4 +56,4 @@
 }  // namespace examples
 }  // namespace weave
 
-#endif  // LIBWEAVE_EXAMPLES_UBUNTU_EVENT_TASK_RUNNER_H_
+#endif  // LIBWEAVE_EXAMPLES_PROVIDER_EVENT_TASK_RUNNER_H_
diff --git a/libweave/examples/ubuntu/file_config_store.cc b/libweave/examples/provider/file_config_store.cc
similarity index 97%
rename from libweave/examples/ubuntu/file_config_store.cc
rename to libweave/examples/provider/file_config_store.cc
index d3f10ef..44f2c30 100644
--- a/libweave/examples/ubuntu/file_config_store.cc
+++ b/libweave/examples/provider/file_config_store.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 "examples/ubuntu/file_config_store.h"
+#include "examples/provider/file_config_store.h"
 
 #include <sys/stat.h>
 #include <sys/utsname.h>
diff --git a/libweave/examples/ubuntu/file_config_store.h b/libweave/examples/provider/file_config_store.h
similarity index 79%
rename from libweave/examples/ubuntu/file_config_store.h
rename to libweave/examples/provider/file_config_store.h
index 1f3d181..e4d16a9 100644
--- a/libweave/examples/ubuntu/file_config_store.h
+++ b/libweave/examples/provider/file_config_store.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef LIBWEAVE_EXAMPLES_UBUNTU_FILE_CONFIG_STORE_H_
-#define LIBWEAVE_EXAMPLES_UBUNTU_FILE_CONFIG_STORE_H_
+#ifndef LIBWEAVE_EXAMPLES_PROVIDER_FILE_CONFIG_STORE_H_
+#define LIBWEAVE_EXAMPLES_PROVIDER_FILE_CONFIG_STORE_H_
 
 #include <map>
 #include <string>
@@ -29,4 +29,4 @@
 }  // namespace examples
 }  // namespace weave
 
-#endif  // LIBWEAVE_EXAMPLES_UBUNTU_FILE_CONFIG_STORE_H_
+#endif  // LIBWEAVE_EXAMPLES_PROVIDER_FILE_CONFIG_STORE_H_
diff --git a/libweave/examples/provider/provider.gyp b/libweave/examples/provider/provider.gyp
new file mode 100644
index 0000000..9dcf184
--- /dev/null
+++ b/libweave/examples/provider/provider.gyp
@@ -0,0 +1,59 @@
+# 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': 'libweave_provider',
+      'type': 'static_library',
+      'variables': {
+        'deps': [
+          'avahi-client',
+          'expat',
+          'libcurl',
+          'libcrypto',
+          'openssl',
+        ]
+      },
+      'cflags': [
+        '>!@(pkg-config >(deps) --cflags)',
+        '-pthread',
+      ],
+      'sources': [
+        'avahi_client.cc',
+        'bluez_client.cc',
+        'curl_http_client.cc',
+        'event_http_client.cc',
+        'event_http_server.cc',
+        'event_network.cc',
+        'event_task_runner.cc',
+        'file_config_store.cc',
+        'wifi_manager.cc',
+        'ssl_stream.cc',
+      ],
+      'dependencies': [
+        '../../libweave_standalone.gyp:libweave',
+      ],
+      'direct_dependent_settings' : {
+        'variables': {
+          'parent_deps': [
+            '<@(deps)'
+          ]
+        },
+        'link_settings': {
+          'ldflags+': [
+            '>!@(pkg-config >(parent_deps) --libs-only-L --libs-only-other)',
+          ],
+          'libraries+': [
+            '>!(pkg-config >(parent_deps) --libs-only-l)',
+          ],
+        },
+        'libraries': [
+          '-levent',
+          '-levent_openssl',
+          '-lpthread',
+        ]
+      }
+    }
+  ]
+}
diff --git a/libweave/examples/provider/ssl_stream.cc b/libweave/examples/provider/ssl_stream.cc
new file mode 100644
index 0000000..eea28c0
--- /dev/null
+++ b/libweave/examples/provider/ssl_stream.cc
@@ -0,0 +1,207 @@
+// 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/provider/ssl_stream.h"
+
+#include <openssl/err.h>
+
+#include <base/bind.h>
+#include <base/bind_helpers.h>
+#include <weave/provider/task_runner.h>
+
+namespace weave {
+namespace examples {
+
+namespace {
+
+void AddSslError(ErrorPtr* error,
+                 const tracked_objects::Location& location,
+                 const std::string& error_code,
+                 unsigned long ssl_error_code) {
+  ERR_load_BIO_strings();
+  SSL_load_error_strings();
+  Error::AddToPrintf(error, location, "ssl_stream", error_code, "%s: %s",
+                     ERR_lib_error_string(ssl_error_code),
+                     ERR_reason_error_string(ssl_error_code));
+}
+
+void RetryAsyncTask(provider::TaskRunner* task_runner,
+                    const tracked_objects::Location& location,
+                    const base::Closure& task) {
+  task_runner->PostDelayedTask(FROM_HERE, task,
+                               base::TimeDelta::FromMilliseconds(100));
+}
+
+}  // namespace
+
+void SSLStream::SslDeleter::operator()(BIO* bio) const {
+  BIO_free(bio);
+}
+
+void SSLStream::SslDeleter::operator()(SSL* ssl) const {
+  SSL_free(ssl);
+}
+
+void SSLStream::SslDeleter::operator()(SSL_CTX* ctx) const {
+  SSL_CTX_free(ctx);
+}
+
+SSLStream::SSLStream(provider::TaskRunner* task_runner,
+                     std::unique_ptr<BIO, SslDeleter> stream_bio)
+    : task_runner_{task_runner} {
+  ctx_.reset(SSL_CTX_new(TLSv1_2_client_method()));
+  CHECK(ctx_);
+  ssl_.reset(SSL_new(ctx_.get()));
+
+  SSL_set_bio(ssl_.get(), stream_bio.get(), stream_bio.get());
+  stream_bio.release();  // Owned by ssl now.
+  SSL_set_connect_state(ssl_.get());
+}
+
+SSLStream::~SSLStream() {
+  CancelPendingOperations();
+}
+
+void SSLStream::RunTask(const base::Closure& task) {
+  task.Run();
+}
+
+void SSLStream::Read(void* buffer,
+                     size_t size_to_read,
+                     const ReadCallback& callback) {
+  int res = SSL_read(ssl_.get(), buffer, size_to_read);
+  if (res > 0) {
+    task_runner_->PostDelayedTask(
+        FROM_HERE,
+        base::Bind(&SSLStream::RunTask, weak_ptr_factory_.GetWeakPtr(),
+                   base::Bind(callback, res, nullptr)),
+        {});
+    return;
+  }
+
+  int err = SSL_get_error(ssl_.get(), res);
+
+  if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
+    return RetryAsyncTask(
+        task_runner_, FROM_HERE,
+        base::Bind(&SSLStream::Read, weak_ptr_factory_.GetWeakPtr(), buffer,
+                   size_to_read, callback));
+  }
+
+  ErrorPtr weave_error;
+  AddSslError(&weave_error, FROM_HERE, "read_failed", err);
+  return task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&SSLStream::RunTask, weak_ptr_factory_.GetWeakPtr(),
+                 base::Bind(callback, 0, base::Passed(&weave_error))),
+      {});
+}
+
+void SSLStream::Write(const void* buffer,
+                      size_t size_to_write,
+                      const WriteCallback& callback) {
+  int res = SSL_write(ssl_.get(), buffer, size_to_write);
+  if (res > 0) {
+    buffer = static_cast<const char*>(buffer) + res;
+    size_to_write -= res;
+    if (size_to_write == 0) {
+      return task_runner_->PostDelayedTask(
+          FROM_HERE,
+          base::Bind(&SSLStream::RunTask, weak_ptr_factory_.GetWeakPtr(),
+                     base::Bind(callback, nullptr)),
+          {});
+    }
+
+    return RetryAsyncTask(
+        task_runner_, FROM_HERE,
+        base::Bind(&SSLStream::Write, weak_ptr_factory_.GetWeakPtr(), buffer,
+                   size_to_write, callback));
+  }
+
+  int err = SSL_get_error(ssl_.get(), res);
+
+  if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
+    return RetryAsyncTask(
+        task_runner_, FROM_HERE,
+        base::Bind(&SSLStream::Write, weak_ptr_factory_.GetWeakPtr(), buffer,
+                   size_to_write, callback));
+  }
+
+  ErrorPtr weave_error;
+  AddSslError(&weave_error, FROM_HERE, "write_failed", err);
+  task_runner_->PostDelayedTask(
+      FROM_HERE, base::Bind(&SSLStream::RunTask, weak_ptr_factory_.GetWeakPtr(),
+                            base::Bind(callback, base::Passed(&weave_error))),
+      {});
+}
+
+void SSLStream::CancelPendingOperations() {
+  weak_ptr_factory_.InvalidateWeakPtrs();
+}
+
+void SSLStream::Connect(
+    provider::TaskRunner* task_runner,
+    const std::string& host,
+    uint16_t port,
+    const provider::Network::OpenSslSocketCallback& callback) {
+  SSL_library_init();
+
+  char end_point[255];
+  snprintf(end_point, sizeof(end_point), "%s:%u", host.c_str(), port);
+
+  std::unique_ptr<BIO, SslDeleter> stream_bio(BIO_new_connect(end_point));
+  CHECK(stream_bio);
+  BIO_set_nbio(stream_bio.get(), 1);
+
+  std::unique_ptr<SSLStream> stream{
+      new SSLStream{task_runner, std::move(stream_bio)}};
+  ConnectBio(std::move(stream), callback);
+}
+
+void SSLStream::ConnectBio(
+    std::unique_ptr<SSLStream> stream,
+    const provider::Network::OpenSslSocketCallback& callback) {
+  BIO* bio = SSL_get_rbio(stream->ssl_.get());
+  if (BIO_do_connect(bio) == 1)
+    return DoHandshake(std::move(stream), callback);
+
+  auto task_runner = stream->task_runner_;
+  if (BIO_should_retry(bio)) {
+    return RetryAsyncTask(
+        task_runner, FROM_HERE,
+        base::Bind(&SSLStream::ConnectBio, base::Passed(&stream), callback));
+  }
+
+  ErrorPtr error;
+  AddSslError(&error, FROM_HERE, "connect_failed", ERR_get_error());
+  task_runner->PostDelayedTask(
+      FROM_HERE, base::Bind(callback, nullptr, base::Passed(&error)), {});
+}
+
+void SSLStream::DoHandshake(
+    std::unique_ptr<SSLStream> stream,
+    const provider::Network::OpenSslSocketCallback& callback) {
+  int res = SSL_do_handshake(stream->ssl_.get());
+  auto task_runner = stream->task_runner_;
+  if (res == 1) {
+    return task_runner->PostDelayedTask(
+        FROM_HERE, base::Bind(callback, base::Passed(&stream), nullptr), {});
+  }
+
+  res = SSL_get_error(stream->ssl_.get(), res);
+
+  if (res == SSL_ERROR_WANT_READ || res == SSL_ERROR_WANT_WRITE) {
+    return RetryAsyncTask(
+        task_runner, FROM_HERE,
+        base::Bind(&SSLStream::DoHandshake, base::Passed(&stream), callback));
+  }
+
+  ErrorPtr error;
+  AddSslError(&error, FROM_HERE, "handshake_failed", res);
+  task_runner->PostDelayedTask(
+      FROM_HERE, base::Bind(callback, nullptr, base::Passed(&error)), {});
+}
+
+}  // namespace examples
+}  // namespace weave
diff --git a/libweave/examples/provider/ssl_stream.h b/libweave/examples/provider/ssl_stream.h
new file mode 100644
index 0000000..f1ef4a6
--- /dev/null
+++ b/libweave/examples/provider/ssl_stream.h
@@ -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.
+
+#ifndef LIBWEAVE_EXAMPLES_PROVIDER_SSL_STREAM_H_
+#define LIBWEAVE_EXAMPLES_PROVIDER_SSL_STREAM_H_
+
+#include <openssl/ssl.h>
+
+#include <base/memory/weak_ptr.h>
+#include <weave/provider/network.h>
+#include <weave/stream.h>
+
+namespace weave {
+
+namespace provider {
+class TaskRunner;
+}
+
+namespace examples {
+
+class SSLStream : public Stream {
+ public:
+  ~SSLStream() override;
+
+  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;
+
+  void CancelPendingOperations() override;
+
+  static void Connect(provider::TaskRunner* task_runner,
+                      const std::string& host,
+                      uint16_t port,
+                      const provider::Network::OpenSslSocketCallback& callback);
+
+ private:
+  struct SslDeleter {
+    void operator()(BIO* bio) const;
+    void operator()(SSL* ssl) const;
+    void operator()(SSL_CTX* ctx) const;
+  };
+
+  SSLStream(provider::TaskRunner* task_runner,
+            std::unique_ptr<BIO, SslDeleter> stream_bio);
+
+  static void ConnectBio(
+      std::unique_ptr<SSLStream> stream,
+      const provider::Network::OpenSslSocketCallback& callback);
+  static void DoHandshake(
+      std::unique_ptr<SSLStream> stream,
+      const provider::Network::OpenSslSocketCallback& callback);
+
+  // Send task to this method with WeakPtr if callback should not be executed
+  // after SSLStream is destroyed.
+  void RunTask(const base::Closure& task);
+
+  provider::TaskRunner* task_runner_{nullptr};
+  std::unique_ptr<SSL_CTX, SslDeleter> ctx_;
+  std::unique_ptr<SSL, SslDeleter> ssl_;
+
+  base::WeakPtrFactory<SSLStream> weak_ptr_factory_{this};
+};
+
+}  // namespace examples
+}  // namespace weave
+
+#endif  // LIBWEAVE_EXAMPLES_PROVIDER_SSL_STREAM_H_
diff --git a/libweave/examples/provider/wifi_manager.cc b/libweave/examples/provider/wifi_manager.cc
new file mode 100644
index 0000000..ac1c604
--- /dev/null
+++ b/libweave/examples/provider/wifi_manager.cc
@@ -0,0 +1,167 @@
+// 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/provider/wifi_manager.h"
+
+#include <arpa/inet.h>
+#include <linux/wireless.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+#include <fstream>
+
+#include <base/bind.h>
+#include <weave/provider/task_runner.h>
+
+#include "examples/provider/ssl_stream.h"
+
+namespace weave {
+namespace examples {
+
+namespace {
+
+int ForkCmd(const std::string& path, const std::vector<std::string>& args) {
+  int pid = fork();
+  if (pid != 0)
+    return pid;
+
+  std::vector<const char*> args_vector;
+  args_vector.push_back(path.c_str());
+  for (auto& i : args)
+    args_vector.push_back(i.c_str());
+  args_vector.push_back(nullptr);
+
+  execvp(path.c_str(), const_cast<char**>(args_vector.data()));
+  NOTREACHED();
+  return 0;
+}
+
+}  // namespace
+
+WifiImpl::WifiImpl(provider::TaskRunner* task_runner, bool force_bootstrapping)
+    : force_bootstrapping_{force_bootstrapping}, task_runner_{task_runner} {
+  StopAccessPoint();
+}
+WifiImpl::~WifiImpl() {
+  StopAccessPoint();
+}
+
+void WifiImpl::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)) {
+      int sockf_d = socket(AF_INET, SOCK_DGRAM, 0);
+      CHECK_GE(sockf_d, 0) << strerror(errno);
+
+      iwreq wreq = {};
+      snprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "wlan0");
+      std::string essid(' ', IW_ESSID_MAX_SIZE + 1);
+      wreq.u.essid.pointer = &essid[0];
+      wreq.u.essid.length = essid.size();
+      CHECK_GE(ioctl(sockf_d, SIOCGIWESSID, &wreq), 0) << strerror(errno);
+      essid.resize(wreq.u.essid.length);
+      close(sockf_d);
+
+      if (ssid == essid)
+        return task_runner_->PostDelayedTask(FROM_HERE,
+                                             base::Bind(callback, nullptr), {});
+      pid = 0;  // Try again.
+    }
+  }
+
+  if (pid == 0) {
+    pid = ForkCmd("nmcli",
+                  {"dev", "wifi", "connect", ssid, "password", passphrase});
+  }
+
+  if (base::Time::Now() >= until) {
+    ErrorPtr error;
+    Error::AddTo(&error, FROM_HERE, "wifi", "timeout",
+                 "Timeout connecting to WiFI network.");
+    task_runner_->PostDelayedTask(
+        FROM_HERE, base::Bind(callback, base::Passed(&error)), {});
+    return;
+  }
+
+  task_runner_->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&WifiImpl::TryToConnect, weak_ptr_factory_.GetWeakPtr(), ssid,
+                 passphrase, pid, until, callback),
+      base::TimeDelta::FromSeconds(1));
+}
+
+void WifiImpl::Connect(const std::string& ssid,
+                       const std::string& passphrase,
+                       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(callback, base::Passed(&error)), {});
+    return;
+  }
+
+  TryToConnect(ssid, passphrase, 0,
+               base::Time::Now() + base::TimeDelta::FromMinutes(1), callback);
+}
+
+void WifiImpl::StartAccessPoint(const std::string& ssid) {
+  if (hostapd_started_)
+    return;
+
+  // Release wlan0 interface.
+  CHECK_EQ(0, std::system("nmcli nm wifi off"));
+  CHECK_EQ(0, std::system("rfkill unblock wlan"));
+  sleep(1);
+
+  std::string hostapd_conf = "/tmp/weave_hostapd.conf";
+  {
+    std::ofstream ofs(hostapd_conf);
+    ofs << "interface=wlan0" << std::endl;
+    ofs << "channel=1" << std::endl;
+    ofs << "ssid=" << ssid << std::endl;
+  }
+
+  CHECK_EQ(0, std::system(("hostapd -B -K " + hostapd_conf).c_str()));
+  hostapd_started_ = true;
+
+  for (size_t i = 0; i < 10; ++i) {
+    if (0 == std::system("ifconfig wlan0 192.168.76.1/24"))
+      break;
+    sleep(1);
+  }
+
+  std::string dnsmasq_conf = "/tmp/weave_dnsmasq.conf";
+  {
+    std::ofstream ofs(dnsmasq_conf.c_str());
+    ofs << "port=0" << std::endl;
+    ofs << "bind-interfaces" << std::endl;
+    ofs << "log-dhcp" << std::endl;
+    ofs << "dhcp-range=192.168.76.10,192.168.76.100" << std::endl;
+    ofs << "interface=wlan0" << std::endl;
+    ofs << "dhcp-leasefile=" << dnsmasq_conf << ".leases" << std::endl;
+  }
+
+  CHECK_EQ(0, std::system(("dnsmasq --conf-file=" + dnsmasq_conf).c_str()));
+}
+
+void WifiImpl::StopAccessPoint() {
+  base::IgnoreResult(std::system("pkill -f dnsmasq.*/tmp/weave"));
+  base::IgnoreResult(std::system("pkill -f hostapd.*/tmp/weave"));
+  CHECK_EQ(0, std::system("nmcli nm wifi on"));
+  hostapd_started_ = false;
+}
+
+bool WifiImpl::HasWifiCapability() {
+  return std::system("nmcli dev | grep ^wlan0") == 0;
+}
+
+}  // namespace examples
+}  // namespace weave
diff --git a/libweave/examples/provider/wifi_manager.h b/libweave/examples/provider/wifi_manager.h
new file mode 100644
index 0000000..849beef
--- /dev/null
+++ b/libweave/examples/provider/wifi_manager.h
@@ -0,0 +1,55 @@
+// 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_WIFI_MANAGER_H_
+#define LIBWEAVE_EXAMPLES_UBUNTU_WIFI_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include <base/memory/weak_ptr.h>
+#include <base/time/time.h>
+#include <weave/provider/wifi.h>
+
+namespace weave {
+
+namespace provider {
+class TaskRunner;
+}
+
+namespace examples {
+
+// Basic weave::Wifi implementation.
+// Production version of SSL socket needs secure server certificate check.
+class WifiImpl : public provider::Wifi {
+ public:
+  explicit WifiImpl(provider::TaskRunner* task_runner,
+                    bool force_bootstrapping);
+  ~WifiImpl();
+
+  // Wifi implementation.
+  void Connect(const std::string& ssid,
+               const std::string& passphrase,
+               const DoneCallback& callback) override;
+  void StartAccessPoint(const std::string& ssid) override;
+  void StopAccessPoint() override;
+
+  static bool HasWifiCapability();
+
+ private:
+  void TryToConnect(const std::string& ssid,
+                    const std::string& passphrase,
+                    int pid,
+                    base::Time until,
+                    const DoneCallback& callback);
+  bool force_bootstrapping_{false};
+  bool hostapd_started_{false};
+  provider::TaskRunner* task_runner_{nullptr};
+  base::WeakPtrFactory<WifiImpl> weak_ptr_factory_{this};
+};
+
+}  // namespace examples
+}  // namespace weave
+
+#endif  // LIBWEAVE_EXAMPLES_UBUNTU_WIFI_MANAGER_H_
diff --git a/libweave/examples/ubuntu/main.cc b/libweave/examples/ubuntu/main.cc
deleted file mode 100644
index cd6dc06..0000000
--- a/libweave/examples/ubuntu/main.cc
+++ /dev/null
@@ -1,290 +0,0 @@
-// 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>
-#include <weave/error.h>
-
-#include "examples/ubuntu/avahi_client.h"
-#include "examples/ubuntu/bluez_client.h"
-#include "examples/ubuntu/curl_http_client.h"
-#include "examples/ubuntu/event_http_server.h"
-#include "examples/ubuntu/event_task_runner.h"
-#include "examples/ubuntu/file_config_store.h"
-#include "examples/ubuntu/network_manager.h"
-
-namespace {
-
-// Supported LED count on this device
-const size_t kLedCount = 3;
-
-void ShowUsage(const std::string& name) {
-  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 "
-                                                "given ticket\n";
-}
-
-class CommandHandler {
- public:
-  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 DoGreet(const std::weak_ptr<weave::Command>& command, int todo) {
-    auto cmd = command.lock();
-    if (!cmd)
-      return;
-
-    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;
-    }
-
-    if (todo-- > 0) {
-      LOG(INFO) << "Hello " << name;
-
-      base::DictionaryValue progress;
-      progress.SetInteger("_todo", todo);
-      cmd->SetProgress(progress, nullptr);
-
-      base::DictionaryValue state;
-      state.SetInteger("_greeter._greetings_counter", ++counter_);
-      device_->SetStateProperties(state, nullptr);
-    }
-
-    if (todo > 0) {
-      task_runner_->PostDelayedTask(
-          FROM_HERE, base::Bind(&CommandHandler::DoGreet,
-                                weak_ptr_factory_.GetWeakPtr(), command, todo),
-          base::TimeDelta::FromSeconds(1));
-      return;
-    }
-
-    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();
-  }
-
-  void OnGreetCommand(const std::weak_ptr<weave::Command>& command) {
-    auto cmd = command.lock();
-    if (!cmd)
-      return;
-    LOG(INFO) << "received command: " << cmd->GetName();
-
-    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->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) {
-    base::ListValue list;
-    for (uint32_t i = 0; i < led_status_.size(); i++)
-      list.AppendBoolean(led_status_[i] ? true : false);
-
-    device_->SetStateProperty("_ledflasher._leds", list, nullptr);
-  }
-
-  weave::Device* device_{nullptr};
-  weave::provider::TaskRunner* task_runner_{nullptr};
-
-  int counter_{0};
-
-  // Simulate LED status on this device so client app could explore
-  // Each bit represents one device, indexing from LSB
-  std::bitset<kLedCount> led_status_{0};
-
-  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) {
-  bool force_bootstrapping = false;
-  bool disable_security = false;
-  std::string registration_ticket;
-  for (int i = 1; i < argc; ++i) {
-    std::string arg = argv[i];
-    if (arg == "-h" || arg == "--help") {
-      ShowUsage(argv[0]);
-      return 0;
-    } else if (arg == "-b" || arg == "--bootstrapping") {
-      force_bootstrapping = true;
-    } else if (arg == "-d" || arg == "--disable_security") {
-      disable_security = true;
-    } else if (arg.find("--registration_ticket") != std::string::npos) {
-      auto pos = arg.find("=");
-      if (pos == std::string::npos) {
-        ShowUsage(argv[0]);
-        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;
-    }
-  }
-
-  weave::examples::FileConfigStore config_store{disable_security};
-  weave::examples::EventTaskRunner task_runner;
-  weave::examples::CurlHttpClient http_client{&task_runner};
-  weave::examples::NetworkImpl network{&task_runner, force_bootstrapping};
-  weave::examples::AvahiClient dns_sd;
-  weave::examples::HttpServerImpl http_server{&task_runner};
-  weave::examples::BluetoothImpl bluetooth;
-
-  auto device = weave::Device::Create(
-      &config_store, &task_runner, &http_client, &network, &dns_sd,
-      &http_server,
-      weave::examples::NetworkImpl::HasWifiCapability() ? &network : nullptr,
-      &bluetooth);
-
-  if (!registration_ticket.empty()) {
-    device->Register(registration_ticket,
-                     base::Bind(&OnRegisterDeviceDone, device.get()));
-  }
-
-  CommandHandler handler(device.get(), &task_runner);
-  task_runner.Run();
-
-  LOG(INFO) << "exit";
-  return 0;
-}
diff --git a/libweave/examples/ubuntu/network_manager.cc b/libweave/examples/ubuntu/network_manager.cc
deleted file mode 100644
index efc2d5c..0000000
--- a/libweave/examples/ubuntu/network_manager.cc
+++ /dev/null
@@ -1,221 +0,0 @@
-// 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/network_manager.h"
-
-#include <arpa/inet.h>
-#include <linux/wireless.h>
-#include <sys/ioctl.h>
-#include <sys/wait.h>
-
-#include <fstream>
-
-#include <base/bind.h>
-#include <weave/provider/task_runner.h>
-
-#include "examples/ubuntu/ssl_stream.h"
-
-namespace weave {
-namespace examples {
-
-namespace {
-
-int ForkCmd(const std::string& path, const std::vector<std::string>& args) {
-  int pid = fork();
-  if (pid != 0)
-    return pid;
-
-  std::vector<const char*> args_vector;
-  args_vector.push_back(path.c_str());
-  for (auto& i : args)
-    args_vector.push_back(i.c_str());
-  args_vector.push_back(nullptr);
-
-  execvp(path.c_str(), const_cast<char**>(args_vector.data()));
-  NOTREACHED();
-  return 0;
-}
-
-}  // namespace
-
-NetworkImpl::NetworkImpl(provider::TaskRunner* task_runner,
-                         bool force_bootstrapping)
-    : force_bootstrapping_{force_bootstrapping}, task_runner_{task_runner} {
-  SSL_load_error_strings();
-  SSL_library_init();
-
-  StopAccessPoint();
-  UpdateNetworkState();
-}
-NetworkImpl::~NetworkImpl() {
-  StopAccessPoint();
-}
-
-void NetworkImpl::AddConnectionChangedCallback(
-    const ConnectionChangedCallback& callback) {
-  callbacks_.push_back(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)) {
-      int sockf_d = socket(AF_INET, SOCK_DGRAM, 0);
-      CHECK_GE(sockf_d, 0) << strerror(errno);
-
-      iwreq wreq = {};
-      snprintf(wreq.ifr_name, sizeof(wreq.ifr_name), "wlan0");
-      std::string essid(' ', IW_ESSID_MAX_SIZE + 1);
-      wreq.u.essid.pointer = &essid[0];
-      wreq.u.essid.length = essid.size();
-      CHECK_GE(ioctl(sockf_d, SIOCGIWESSID, &wreq), 0) << strerror(errno);
-      essid.resize(wreq.u.essid.length);
-      close(sockf_d);
-
-      if (ssid == essid)
-        return task_runner_->PostDelayedTask(FROM_HERE,
-                                             base::Bind(callback, nullptr), {});
-      pid = 0;  // Try again.
-    }
-  }
-
-  if (pid == 0) {
-    pid = ForkCmd("nmcli",
-                  {"dev", "wifi", "connect", ssid, "password", passphrase});
-  }
-
-  if (base::Time::Now() >= until) {
-    ErrorPtr error;
-    Error::AddTo(&error, FROM_HERE, "wifi", "timeout",
-                 "Timeout connecting to WiFI network.");
-    task_runner_->PostDelayedTask(
-        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, callback),
-      base::TimeDelta::FromSeconds(1));
-}
-
-void NetworkImpl::Connect(const std::string& ssid,
-                          const std::string& passphrase,
-                          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(callback, base::Passed(&error)), {});
-    return;
-  }
-
-  TryToConnect(ssid, passphrase, 0,
-               base::Time::Now() + base::TimeDelta::FromMinutes(1), callback);
-}
-
-void NetworkImpl::UpdateNetworkState() {
-  network_state_ = Network::State::kOffline;
-  if (force_bootstrapping_)
-    return;
-  if (std::system("ping talk.google.com -c 1") == 0)
-    network_state_ = State::kOnline;
-  else if (std::system("nmcli dev"))
-    network_state_ = State::kError;
-  else if (std::system("nmcli dev | grep connecting") == 0)
-    network_state_ = State::kConnecting;
-
-  task_runner_->PostDelayedTask(FROM_HERE,
-                                base::Bind(&NetworkImpl::UpdateNetworkState,
-                                           weak_ptr_factory_.GetWeakPtr()),
-                                base::TimeDelta::FromSeconds(10));
-  for (const auto& cb : callbacks_)
-    cb.Run();
-}
-
-provider::Network::State NetworkImpl::GetConnectionState() const {
-  return network_state_;
-}
-
-void NetworkImpl::StartAccessPoint(const std::string& ssid) {
-  if (hostapd_started_)
-    return;
-
-  network_state_ = State::kOffline;
-
-  // Release wlan0 interface.
-  CHECK_EQ(0, std::system("nmcli nm wifi off"));
-  CHECK_EQ(0, std::system("rfkill unblock wlan"));
-  sleep(1);
-
-  std::string hostapd_conf = "/tmp/weave_hostapd.conf";
-  {
-    std::ofstream ofs(hostapd_conf);
-    ofs << "interface=wlan0" << std::endl;
-    ofs << "channel=1" << std::endl;
-    ofs << "ssid=" << ssid << std::endl;
-  }
-
-  CHECK_EQ(0, std::system(("hostapd -B -K " + hostapd_conf).c_str()));
-  hostapd_started_ = true;
-
-  for (size_t i = 0; i < 10; ++i) {
-    if (0 == std::system("ifconfig wlan0 192.168.76.1/24"))
-      break;
-    sleep(1);
-  }
-
-  std::string dnsmasq_conf = "/tmp/weave_dnsmasq.conf";
-  {
-    std::ofstream ofs(dnsmasq_conf.c_str());
-    ofs << "port=0" << std::endl;
-    ofs << "bind-interfaces" << std::endl;
-    ofs << "log-dhcp" << std::endl;
-    ofs << "dhcp-range=192.168.76.10,192.168.76.100" << std::endl;
-    ofs << "interface=wlan0" << std::endl;
-    ofs << "dhcp-leasefile=" << dnsmasq_conf << ".leases" << std::endl;
-  }
-
-  CHECK_EQ(0, std::system(("dnsmasq --conf-file=" + dnsmasq_conf).c_str()));
-}
-
-void NetworkImpl::StopAccessPoint() {
-  base::IgnoreResult(std::system("pkill -f dnsmasq.*/tmp/weave"));
-  base::IgnoreResult(std::system("pkill -f hostapd.*/tmp/weave"));
-  CHECK_EQ(0, std::system("nmcli nm wifi on"));
-  hostapd_started_ = false;
-}
-
-bool NetworkImpl::HasWifiCapability() {
-  return std::system("nmcli dev | grep ^wlan0") == 0;
-}
-
-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(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/network_manager.h b/libweave/examples/ubuntu/network_manager.h
deleted file mode 100644
index 3769ab1..0000000
--- a/libweave/examples/ubuntu/network_manager.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// 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_NETWORK_MANAGER_H_
-#define LIBWEAVE_EXAMPLES_UBUNTU_NETWORK_MANAGER_H_
-
-#include <string>
-#include <vector>
-
-#include <base/memory/weak_ptr.h>
-#include <base/time/time.h>
-#include <weave/provider/network.h>
-#include <weave/provider/wifi.h>
-
-namespace weave {
-
-namespace provider {
-class TaskRunner;
-}
-
-namespace examples {
-
-// Basic weave::Network implementation.
-// Production version of SSL socket needs secure server certificate check.
-class NetworkImpl : public provider::Network, public provider::Wifi {
- public:
-  explicit NetworkImpl(provider::TaskRunner* task_runner,
-                       bool force_bootstrapping);
-  ~NetworkImpl();
-
-  // Network implementation.
-  void AddConnectionChangedCallback(
-      const ConnectionChangedCallback& callback) override;
-  State GetConnectionState() const override;
-  void OpenSslSocket(const std::string& host,
-                     uint16_t port,
-                     const OpenSslSocketCallback& callback) override;
-
-  // Wifi implementation.
-  void Connect(const std::string& ssid,
-               const std::string& passphrase,
-               const DoneCallback& callback) override;
-  void StartAccessPoint(const std::string& ssid) override;
-  void StopAccessPoint() override;
-
-  static bool HasWifiCapability();
-
- private:
-  void TryToConnect(const std::string& ssid,
-                    const std::string& passphrase,
-                    int pid,
-                    base::Time until,
-                    const DoneCallback& callback);
-  void UpdateNetworkState();
-
-  bool force_bootstrapping_{false};
-  bool hostapd_started_{false};
-  provider::TaskRunner* task_runner_{nullptr};
-  std::vector<ConnectionChangedCallback> callbacks_;
-  provider::Network::State network_state_{provider::Network::State::kOffline};
-
-  base::WeakPtrFactory<NetworkImpl> weak_ptr_factory_{this};
-};
-
-}  // namespace examples
-}  // namespace weave
-
-#endif  // LIBWEAVE_EXAMPLES_UBUNTU_NETWORK_MANAGER_H_
diff --git a/libweave/examples/ubuntu/ssl_stream.cc b/libweave/examples/ubuntu/ssl_stream.cc
deleted file mode 100644
index fc2d36d..0000000
--- a/libweave/examples/ubuntu/ssl_stream.cc
+++ /dev/null
@@ -1,144 +0,0 @@
-// 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/ssl_stream.h"
-
-#include <base/bind.h>
-#include <weave/provider/task_runner.h>
-
-namespace weave {
-namespace examples {
-
-SSLStream::SSLStream(provider::TaskRunner* task_runner)
-    : task_runner_{task_runner} {}
-
-SSLStream::~SSLStream() {
-  CancelPendingOperations();
-}
-
-void SSLStream::RunDelayedTask(const base::Closure& task) {
-  task.Run();
-}
-
-void SSLStream::Read(void* buffer,
-                     size_t size_to_read,
-                     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(callback, res, nullptr)),
-        {});
-    return;
-  }
-
-  int err = SSL_get_error(ssl_.get(), res);
-
-  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, callback),
-        base::TimeDelta::FromSeconds(1));
-    return;
-  }
-
-  ErrorPtr weave_error;
-  Error::AddTo(&weave_error, FROM_HERE, "ssl", "socket_read_failed",
-               "SSL error");
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      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 WriteCallback& callback) {
-  int res = SSL_write(ssl_.get(), buffer, size_to_write);
-  if (res > 0) {
-    buffer = static_cast<const char*>(buffer) + res;
-    size_to_write -= res;
-    if (size_to_write == 0) {
-      task_runner_->PostDelayedTask(
-          FROM_HERE,
-          base::Bind(&SSLStream::RunDelayedTask, weak_ptr_factory_.GetWeakPtr(),
-                     base::Bind(callback, nullptr)),
-          {});
-      return;
-    }
-
-    task_runner_->PostDelayedTask(
-        FROM_HERE, base::Bind(&SSLStream::Write, weak_ptr_factory_.GetWeakPtr(),
-                              buffer, size_to_write, callback),
-        base::TimeDelta::FromSeconds(1));
-
-    return;
-  }
-
-  int err = SSL_get_error(ssl_.get(), res);
-
-  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, callback),
-        base::TimeDelta::FromSeconds(1));
-    return;
-  }
-
-  ErrorPtr weave_error;
-  Error::AddTo(&weave_error, FROM_HERE, "ssl", "socket_write_failed",
-               "SSL error");
-  task_runner_->PostDelayedTask(
-      FROM_HERE,
-      base::Bind(&SSLStream::RunDelayedTask, weak_ptr_factory_.GetWeakPtr(),
-                 base::Bind(callback, base::Passed(&weave_error))),
-      {});
-  return;
-}
-
-void SSLStream::CancelPendingOperations() {
-  weak_ptr_factory_.InvalidateWeakPtrs();
-}
-
-bool SSLStream::Init(const std::string& host, uint16_t port) {
-  ctx_.reset(SSL_CTX_new(TLSv1_2_client_method()));
-  CHECK(ctx_);
-  ssl_.reset(SSL_new(ctx_.get()));
-
-  char end_point[255];
-  snprintf(end_point, sizeof(end_point), "%s:%u", host.c_str(), port);
-  BIO* stream_bio = BIO_new_connect(end_point);
-  CHECK(stream_bio);
-  BIO_set_nbio(stream_bio, 1);
-
-  while (BIO_do_connect(stream_bio) != 1) {
-    CHECK(BIO_should_retry(stream_bio));
-    sleep(1);
-  }
-
-  SSL_set_bio(ssl_.get(), stream_bio, stream_bio);
-  SSL_set_connect_state(ssl_.get());
-
-  for (;;) {
-    int res = SSL_do_handshake(ssl_.get());
-    if (res) {
-      return true;
-    }
-
-    res = SSL_get_error(ssl_.get(), res);
-
-    if (res != SSL_ERROR_WANT_READ || res != SSL_ERROR_WANT_WRITE) {
-      return false;
-    }
-
-    sleep(1);
-  }
-  return false;
-}
-
-}  // namespace examples
-}  // namespace weave
diff --git a/libweave/examples/ubuntu/ssl_stream.h b/libweave/examples/ubuntu/ssl_stream.h
deleted file mode 100644
index ae0e539..0000000
--- a/libweave/examples/ubuntu/ssl_stream.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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_SSL_STREAM_H_
-#define LIBWEAVE_EXAMPLES_UBUNTU_SSL_STREAM_H_
-
-#include <openssl/ssl.h>
-
-#include <base/memory/weak_ptr.h>
-#include <weave/stream.h>
-
-namespace weave {
-
-namespace provider {
-class TaskRunner;
-}
-
-namespace examples {
-
-class SSLStream : public Stream {
- public:
-  explicit SSLStream(provider::TaskRunner* task_runner);
-
-  ~SSLStream() override;
-
-  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;
-
-  void CancelPendingOperations() override;
-
-  bool Init(const std::string& host, uint16_t port);
-
- private:
-  void RunDelayedTask(const base::Closure& task);
-
-  provider::TaskRunner* task_runner_{nullptr};
-  std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)> ctx_{nullptr, SSL_CTX_free};
-  std::unique_ptr<SSL, decltype(&SSL_free)> ssl_{nullptr, SSL_free};
-
-  base::WeakPtrFactory<SSLStream> weak_ptr_factory_{this};
-};
-
-}  // namespace examples
-}  // namespace weave
-
-#endif  // LIBWEAVE_EXAMPLES_UBUNTU_SSL_STREAM_H_
diff --git a/libweave/examples/ubuntu/weave.gyp b/libweave/examples/ubuntu/weave.gyp
deleted file mode 100644
index a561c9d..0000000
--- a/libweave/examples/ubuntu/weave.gyp
+++ /dev/null
@@ -1,53 +0,0 @@
-# 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',
-      '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',
-      ],
-      'dependencies': [
-        '../../libweave_standalone.gyp:libweave',
-      ],
-      'libraries': [
-        '-levent',
-        '-levent_openssl',
-        '-lpthread',
-      ]
-    }
-  ]
-}
diff --git a/libweave/libweave_common.gypi b/libweave/libweave_common.gypi
index 125047d..bf81f76 100644
--- a/libweave/libweave_common.gypi
+++ b/libweave/libweave_common.gypi
@@ -18,6 +18,7 @@
         ],
         'cflags': [
           '-Og',
+          '-g3',
         ],
       },
     },
diff --git a/libweave/src/device_registration_info.cc b/libweave/src/device_registration_info.cc
index 48361b9..8945b66 100644
--- a/libweave/src/device_registration_info.cc
+++ b/libweave/src/device_registration_info.cc
@@ -41,7 +41,16 @@
 namespace {
 
 const int kPollingPeriodSeconds = 7;
-const int kBackupPollingPeriodSeconds = 30;
+const int kBackupPollingPeriodMinutes = 30;
+
+namespace fetch_reason {
+
+const char kDeviceStart[] = "device_start";  // Initial queue fetch at startup.
+const char kRegularPull[] = "regular_pull";  // Regular fetch before XMPP is up.
+const char kNewCommand[] = "new_command";    // A new command is available.
+const char kJustInCase[] = "just_in_case";   // Backup fetch when XMPP is live.
+
+}  // namespace fetch_reason
 
 using provider::HttpClient;
 
@@ -789,7 +798,7 @@
   LOG(INFO) << "Device connected to cloud server";
   connected_to_cloud_ = true;
   FetchCommands(base::Bind(&DeviceRegistrationInfo::ProcessInitialCommandList,
-                           AsWeakPtr()));
+                           AsWeakPtr()), fetch_reason::kDeviceStart);
   // In case there are any pending state updates since we sent off the initial
   // UpdateDeviceResource() request, update the server with any state changes.
   PublishStateUpdates();
@@ -985,29 +994,33 @@
   fetch_commands_request_sent_ = false;
   // If we have additional requests queued, send them out now.
   if (fetch_commands_request_queued_)
-    FetchAndPublishCommands();
+    FetchAndPublishCommands(queued_fetch_reason_);
 }
 
 void DeviceRegistrationInfo::FetchCommands(
-    const base::Callback<void(const base::ListValue&, ErrorPtr error)>&
-        callback) {
+    const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback,
+    const std::string& reason) {
   fetch_commands_request_sent_ = true;
   fetch_commands_request_queued_ = false;
   DoCloudRequest(
       HttpClient::Method::kGet,
-      GetServiceURL("commands/queue", {{"deviceId", GetSettings().cloud_id}}),
+      GetServiceURL("commands/queue", {{"deviceId", GetSettings().cloud_id},
+                                       {"reason", reason}}),
       nullptr, base::Bind(&DeviceRegistrationInfo::OnFetchCommandsDone,
                           AsWeakPtr(), callback));
 }
 
-void DeviceRegistrationInfo::FetchAndPublishCommands() {
+void DeviceRegistrationInfo::FetchAndPublishCommands(
+    const std::string& reason) {
   if (fetch_commands_request_sent_) {
     fetch_commands_request_queued_ = true;
+    queued_fetch_reason_ = reason;
     return;
   }
 
   FetchCommands(base::Bind(&DeviceRegistrationInfo::PublishCommands,
-                           weak_factory_.GetWeakPtr()));
+                           weak_factory_.GetWeakPtr()),
+                reason);
 }
 
 void DeviceRegistrationInfo::ProcessInitialCommandList(
@@ -1183,7 +1196,7 @@
   CHECK_EQ(primary_notification_channel_->GetName(), channel_name);
   notification_channel_starting_ = false;
   pull_channel_->UpdatePullInterval(
-      base::TimeDelta::FromSeconds(kBackupPollingPeriodSeconds));
+      base::TimeDelta::FromMinutes(kBackupPollingPeriodMinutes));
   current_notification_channel_ = primary_notification_channel_.get();
 
   // If we have not successfully connected to the cloud server and we have not
@@ -1200,7 +1213,7 @@
   UpdateDeviceResource(
       base::Bind(&IgnoreCloudErrorWithCallback,
                  base::Bind(&DeviceRegistrationInfo::FetchAndPublishCommands,
-                            AsWeakPtr())));
+                            AsWeakPtr(), fetch_reason::kRegularPull)));
 }
 
 void DeviceRegistrationInfo::OnDisconnected() {
@@ -1222,7 +1235,8 @@
 }
 
 void DeviceRegistrationInfo::OnCommandCreated(
-    const base::DictionaryValue& command) {
+    const base::DictionaryValue& command,
+    const std::string& channel_name) {
   if (!connected_to_cloud_)
     return;
 
@@ -1234,10 +1248,21 @@
     PublishCommand(command);
     return;
   }
+
+  // If this request comes from a Pull channel while the primary notification
+  // channel (XMPP) is active, we are doing a backup poll, so mark the request
+  // appropriately.
+  bool just_in_case =
+    (channel_name == kPullChannelName) &&
+    (current_notification_channel_ == primary_notification_channel_.get());
+
+  std::string reason =
+      just_in_case ? fetch_reason::kJustInCase : fetch_reason::kNewCommand;
+
   // If the command was too big to be delivered over a notification channel,
   // or OnCommandCreated() was initiated from the Pull notification,
   // perform a manual command fetch from the server here.
-  FetchAndPublishCommands();
+  FetchAndPublishCommands(reason);
 }
 
 void DeviceRegistrationInfo::OnDeviceDeleted(const std::string& cloud_id) {
diff --git a/libweave/src/device_registration_info.h b/libweave/src/device_registration_info.h
index 8eb0ec3..f3bacc6 100644
--- a/libweave/src/device_registration_info.h
+++ b/libweave/src/device_registration_info.h
@@ -210,7 +210,8 @@
   bool UpdateDeviceInfoTimestamp(const base::DictionaryValue& device_info);
 
   void FetchCommands(
-      const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback);
+      const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback,
+      const std::string& reason);
   void OnFetchCommandsDone(
       const base::Callback<void(const base::ListValue&, ErrorPtr)>& callback,
       const base::DictionaryValue& json,
@@ -230,7 +231,9 @@
 
   // Helper function to pull the pending command list from the server using
   // FetchCommands() and make them available on D-Bus with PublishCommands().
-  void FetchAndPublishCommands();
+  // |backup_fetch| is set to true when performing backup ("just-in-case")
+  // command fetch while XMPP channel is up and running.
+  void FetchAndPublishCommands(const std::string& reason);
 
   void PublishStateUpdates();
   void OnPublishStateDone(StateChangeQueueInterface::UpdateID update_id,
@@ -258,7 +261,8 @@
   void OnConnected(const std::string& channel_name) override;
   void OnDisconnected() override;
   void OnPermanentFailure() override;
-  void OnCommandCreated(const base::DictionaryValue& command) override;
+  void OnCommandCreated(const base::DictionaryValue& command,
+                        const std::string& channel_name) override;
   void OnDeviceDeleted(const std::string& cloud_id) override;
 
   // Wipes out the device registration information and stops server connections.
@@ -316,6 +320,8 @@
   // Set to true when another command queue fetch request is queued while
   // another one was in flight.
   bool fetch_commands_request_queued_{false};
+  // Specifies the reason for queued command fetch request.
+  std::string queued_fetch_reason_;
 
   using ResourceUpdateCallbackList = std::vector<DoneCallback>;
   // Callbacks for device resource update request currently in flight to the
diff --git a/libweave/src/device_registration_info_unittest.cc b/libweave/src/device_registration_info_unittest.cc
index 060e5c4..a885b7b 100644
--- a/libweave/src/device_registration_info_unittest.cc
+++ b/libweave/src/device_registration_info_unittest.cc
@@ -596,14 +596,14 @@
       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);
-      })));
+      .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_->Complete(*CreateDictionaryValue("{'status': 'Ok'}"), nullptr));
 }
diff --git a/libweave/src/notification/notification_delegate.h b/libweave/src/notification/notification_delegate.h
index feb9d17..719d76d 100644
--- a/libweave/src/notification/notification_delegate.h
+++ b/libweave/src/notification/notification_delegate.h
@@ -18,7 +18,8 @@
   virtual void OnDisconnected() = 0;
   virtual void OnPermanentFailure() = 0;
   // Called when a new command is sent via the notification channel.
-  virtual void OnCommandCreated(const base::DictionaryValue& command) = 0;
+  virtual void OnCommandCreated(const base::DictionaryValue& command,
+                                const std::string& channel_name) = 0;
   // Called when DEVICE_DELETED notification is received.
   virtual void OnDeviceDeleted(const std::string& cloud_id) = 0;
 
diff --git a/libweave/src/notification/notification_parser.cc b/libweave/src/notification/notification_parser.cc
index 25c525f..0a27f1c 100644
--- a/libweave/src/notification/notification_parser.cc
+++ b/libweave/src/notification/notification_parser.cc
@@ -12,14 +12,15 @@
 
 // Processes COMMAND_CREATED notifications.
 bool ParseCommandCreated(const base::DictionaryValue& notification,
-                         NotificationDelegate* delegate) {
+                         NotificationDelegate* delegate,
+                         const std::string& channel_name) {
   const base::DictionaryValue* command = nullptr;
   if (!notification.GetDictionary("command", &command)) {
     LOG(ERROR) << "COMMAND_CREATED notification is missing 'command' property";
     return false;
   }
 
-  delegate->OnCommandCreated(*command);
+  delegate->OnCommandCreated(*command, channel_name);
   return true;
 }
 
@@ -39,7 +40,8 @@
 }  // anonymous namespace
 
 bool ParseNotificationJson(const base::DictionaryValue& notification,
-                           NotificationDelegate* delegate) {
+                           NotificationDelegate* delegate,
+                           const std::string& channel_name) {
   CHECK(delegate);
 
   std::string kind;
@@ -57,7 +59,7 @@
   }
 
   if (type == "COMMAND_CREATED")
-    return ParseCommandCreated(notification, delegate);
+    return ParseCommandCreated(notification, delegate, channel_name);
 
   if (type == "DEVICE_DELETED")
     return ParseDeviceDeleted(notification, delegate);
diff --git a/libweave/src/notification/notification_parser.h b/libweave/src/notification/notification_parser.h
index ed62d10..01932c7 100644
--- a/libweave/src/notification/notification_parser.h
+++ b/libweave/src/notification/notification_parser.h
@@ -17,7 +17,8 @@
 // the appropriate method from the |delegate|.
 // Returns false if unexpected or malformed notification is received.
 bool ParseNotificationJson(const base::DictionaryValue& notification,
-                           NotificationDelegate* delegate);
+                           NotificationDelegate* delegate,
+                           const std::string& channel_name);
 
 }  // namespace weave
 
diff --git a/libweave/src/notification/notification_parser_unittest.cc b/libweave/src/notification/notification_parser_unittest.cc
index 146208f..d0b43fe 100644
--- a/libweave/src/notification/notification_parser_unittest.cc
+++ b/libweave/src/notification/notification_parser_unittest.cc
@@ -17,12 +17,17 @@
 
 using test::CreateDictionaryValue;
 
+MATCHER_P(MatchDict, str, "") {
+  return arg.Equals(CreateDictionaryValue(str).get());
+}
+
 class MockNotificationDelegate : public NotificationDelegate {
  public:
   MOCK_METHOD1(OnConnected, void(const std::string&));
   MOCK_METHOD0(OnDisconnected, void());
   MOCK_METHOD0(OnPermanentFailure, void());
-  MOCK_METHOD1(OnCommandCreated, void(const base::DictionaryValue& command));
+  MOCK_METHOD2(OnCommandCreated, void(const base::DictionaryValue& command,
+                                      const std::string& channel_name));
   MOCK_METHOD1(OnDeviceDeleted, void(const std::string&));
 };
 
@@ -51,14 +56,6 @@
     "commandId": "command_id"
   })");
 
-  base::DictionaryValue command_instance;
-  auto on_command = [&command_instance](const base::DictionaryValue& command) {
-    command_instance.MergeDictionary(&command);
-  };
-
-  EXPECT_CALL(delegate_, OnCommandCreated(_)).WillOnce(Invoke(on_command));
-  EXPECT_TRUE(ParseNotificationJson(*json, &delegate_));
-
   const char expected_json[] = R"({
       "kind": "clouddevices#command",
       "deviceId": "device_id",
@@ -71,7 +68,10 @@
       "id": "command_id",
       "creationTimeMs": "1403444174811"
     })";
-  EXPECT_JSON_EQ(expected_json, command_instance);
+
+  EXPECT_CALL(delegate_, OnCommandCreated(MatchDict(expected_json), "foo"))
+      .Times(1);
+  EXPECT_TRUE(ParseNotificationJson(*json, &delegate_, "foo"));
 }
 
 TEST_F(NotificationParserTest, DeviceDeleted) {
@@ -83,7 +83,7 @@
 
   std::string device_id;
   EXPECT_CALL(delegate_, OnDeviceDeleted(_)).WillOnce(SaveArg<0>(&device_id));
-  EXPECT_TRUE(ParseNotificationJson(*json, &delegate_));
+  EXPECT_TRUE(ParseNotificationJson(*json, &delegate_, "foo"));
   EXPECT_EQ("some_device_id", device_id);
 }
 
@@ -106,7 +106,7 @@
     "commandId": "command_id"
   })");
 
-  EXPECT_FALSE(ParseNotificationJson(*json, &delegate_));
+  EXPECT_FALSE(ParseNotificationJson(*json, &delegate_, "bar"));
 }
 
 TEST_F(NotificationParserTest, Failure_NoType) {
@@ -128,7 +128,7 @@
     "commandId": "command_id"
   })");
 
-  EXPECT_FALSE(ParseNotificationJson(*json, &delegate_));
+  EXPECT_FALSE(ParseNotificationJson(*json, &delegate_, "baz"));
 }
 
 TEST_F(NotificationParserTest, IgnoredNotificationType) {
@@ -151,7 +151,7 @@
     "commandId": "command_id"
   })");
 
-  EXPECT_TRUE(ParseNotificationJson(*json, &delegate_));
+  EXPECT_TRUE(ParseNotificationJson(*json, &delegate_, "quux"));
 }
 
 }  // namespace weave
diff --git a/libweave/src/notification/pull_channel.cc b/libweave/src/notification/pull_channel.cc
index b192df6..378351a 100644
--- a/libweave/src/notification/pull_channel.cc
+++ b/libweave/src/notification/pull_channel.cc
@@ -12,12 +12,14 @@
 
 namespace weave {
 
+const char kPullChannelName[] = "pull";
+
 PullChannel::PullChannel(base::TimeDelta pull_interval,
                          provider::TaskRunner* task_runner)
     : pull_interval_{pull_interval}, task_runner_{task_runner} {}
 
 std::string PullChannel::GetName() const {
-  return "pull";
+  return kPullChannelName;
 }
 
 bool PullChannel::IsConnected() const {
@@ -58,7 +60,7 @@
   // Repost before delegate notification to give it a chance to stop channel.
   RePost();
   base::DictionaryValue empty_dict;
-  delegate_->OnCommandCreated(empty_dict);
+  delegate_->OnCommandCreated(empty_dict, GetName());
 }
 
 }  // namespace weave
diff --git a/libweave/src/notification/pull_channel.h b/libweave/src/notification/pull_channel.h
index a48b9f6..7f859e8 100644
--- a/libweave/src/notification/pull_channel.h
+++ b/libweave/src/notification/pull_channel.h
@@ -20,6 +20,8 @@
 class TaskRunner;
 }  // namespace
 
+extern const char kPullChannelName[];
+
 class PullChannel : public NotificationChannel {
  public:
   PullChannel(base::TimeDelta pull_interval, provider::TaskRunner* task_runner);
diff --git a/libweave/src/notification/xmpp_channel.cc b/libweave/src/notification/xmpp_channel.cc
index bca4c3b..ceb45ed 100644
--- a/libweave/src/notification/xmpp_channel.cc
+++ b/libweave/src/notification/xmpp_channel.cc
@@ -279,7 +279,7 @@
   VLOG(2) << "XMPP push notification data: " << json_data;
   auto json_dict = LoadJsonDict(json_data, nullptr);
   if (json_dict && delegate_)
-    ParseNotificationJson(*json_dict, delegate_);
+    ParseNotificationJson(*json_dict, delegate_, GetName());
 }
 
 void XmppChannel::CreateSslSocket() {
diff --git a/libweave/src/privet/security_manager.cc b/libweave/src/privet/security_manager.cc
index 8b1500c..1b4e3c5 100644
--- a/libweave/src/privet/security_manager.cc
+++ b/libweave/src/privet/security_manager.cc
@@ -24,6 +24,10 @@
 #include "src/string_utils.h"
 #include "third_party/chromium/crypto/p224_spake.h"
 
+#if !defined(ARCH_CPU_LITTLE_ENDIAN)
+#error "Big-endian is not supported. See b/25017606"
+#endif
+
 namespace weave {
 namespace privet {