Merge remote-tracking branch 'weave/master' into merge-aosp-master

This merge includes the following commits:

52d006a Support GCC 4.7
7125e8b Enable big-endian build
6f7385f Add gomacc.lock and make files generated by gyp into .gitignore
9e9aca9 Remove dependency on ninja-build
9e78026 Add big-endian defines into base/build/build_config.h
42b0a7b Make sure example command definitions include 'minimalRole'
42d63b9 Fix example command schema to match the expectations of the server
8a05beb Remove object schema type system
c430560 Replace Get* methods returning unique_ptr with reference alternative
3bfb13d Fix GCC 4.7 errors
f21c83a Change XMPP notification tag from 'clouddevices' to 'weave'
2c7740a Remove CommandDefinition class
41429d8 commands: fix unused-function warning
8fb4e62 Added clang-format config
f08caeb Extract privet::AuthManager from privet::SecurityManager
3c2b303 Add fake component/trait property to device draft
b0efd87 Fixed grammar error
88f55d8 Add "component" property to command instance
739e357 Make Split() function work consistently with empty strings
44c1dbe Add ComponentManager class to handle device traits/components
05a979e Update light command definitions
6869ed7 Change StateChangeQueue::NotifyPropertiesUpdated to take const ref
7b588fc Add support for state updates on ComponentManager
88e9d12 Move deprecated APIs on Device object to the end of the interface
e79fa91 Add new component-based APIs to Device interface
a3c5e6d Route commands without path to suitable component
ba98115 Convert ComponentManager into an interface and create a mock
b736c98 Make CommandManager::AddCommand() usable for both cloud and local cases
6b394d1 Add support for legacy state/commandDefs to ComponentManager
d91d625 Switch to use ComponentManager for traits/components
894b6b4 commands: delete obsolete include
321dae6 Allow change of model manifest id
29a11ca Replace IsEqualJson gtest predicate with EXPECT_JSON_EQ
551a82b Add support of Components/Traits to local privet APIs
f969132 Use uint64_t for fingerprints to prevent easy overflows
e3cc230 Move checkForUpdates tests into their own section
78d4f6d Move local device_id into public settings section
6a9d0e2 Fixed name of constant in unit test
a580328 Add config flag to mark that local auth info was changed
1a10871 Remove output error parameter from BuildDeviceResource
7717d93 Support HTTP responses with no body
d6db049 Update example daemons to use the new component/trait APIs
45dc9df Add macaroone implementation from https://weave.googlesource.com/weave/libuweave
47fe6f8 Add macaroone implementation into libweave build
972282c Fix compile error on Brillo
f1fa8be Move build_config.h from chromium/base/build to chromium/base
2033433 Fix a crash when adding a default command handler

Change-Id: Ic0b50041ba7852581a86e5af9118cfffd9cca302
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..685c597
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,5 @@
+---
+Language:        Cpp
+# Usage:  git clang-format -f m/master --style=file
+BasedOnStyle:  Chromium
+...
diff --git a/.gitignore b/.gitignore
index 4011af4..3ee16a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,7 @@
+*.target.mk
 *~
 /out/
 /third_party/include
 /third_party/lib
+gomacc.lock
+Makefile
diff --git a/README b/README
index bf9b70b..c78283b 100644
--- a/README
+++ b/README
@@ -1,6 +1,6 @@
 Overview
 --------
-libWeave is the library to with device side implementation of Weave
+libWeave is the library with device side implementation of Weave
 protocol.
 
 Sources
@@ -71,7 +71,6 @@
   libtool
   gyp
   libexpat1-dev
-  ninja-build
 
 For tests:
 
@@ -90,18 +89,18 @@
 ---------
 Everywhere below Debug can be replaced with Release.
 
-Generate ninja build files:
+Generate build files:
 
   gyp -I libweave_common.gypi --toplevel-dir=. --depth=. \
-      -f ninja libweave_standalone.gyp
+      -f make libweave_standalone.gyp
 
 Build library with tests:
 
-  ninja -C out/Debug
+  make
 
 Build library only:
 
-  ninja -C out/Debug libweave
+  make libweave
 
 Testing
 -------
@@ -130,7 +129,3 @@
   repo upload .
 
 Go to the url from the output of "repo upload" and add reviewers.
-
-Known Issues
-------------
-* No big-endian support. Pairing fails on big-endian hardware.
diff --git a/examples/build.sh b/examples/build.sh
index e4c412d..262c56b 100755
--- a/examples/build.sh
+++ b/examples/build.sh
@@ -8,7 +8,7 @@
 
 cd $ROOT_DIR
 
-gyp -Ilibweave_common.gypi --toplevel-dir=. --depth=. -f ninja $DIR/daemon/examples.gyp
+gyp -Ilibweave_common.gypi --toplevel-dir=. --depth=. -f make $DIR/daemon/examples.gyp
 
 if [ -z "$BUILD_CONFIG" ]; then
    export BUILD_CONFIG=Debug
@@ -20,7 +20,7 @@
 fi
 
 export CORES=`cat /proc/cpuinfo | grep processor | wc -l`
-ninja -j $CORES -C out/${BUILD_CONFIG} $BUILD_TARGET || exit 1
+BUILDTYPE=$BUILD_CONFIG make -j $CORES $BUILD_TARGET || exit 1
 
 if [[ $BUILD_TARGET == *"libweave_testrunner"* ]]; then
   out/${BUILD_CONFIG}/libweave_testrunner --gtest_break_on_failure || exit 1
diff --git a/examples/daemon/common/daemon.h b/examples/daemon/common/daemon.h
index 0e05b88..5a90988 100644
--- a/examples/daemon/common/daemon.h
+++ b/examples/daemon/common/daemon.h
@@ -19,10 +19,11 @@
 class Daemon {
  public:
   struct Options {
-    bool force_bootstrapping_ = false;
-    bool disable_security_ = false;
-    bool disable_privet_ = false;
+    bool force_bootstrapping_{false};
+    bool disable_security_{false};
+    bool disable_privet_{false};
     std::string registration_ticket_;
+    std::string model_id_{"AAAAA"};
 
     static void ShowUsage(const std::string& name) {
       LOG(ERROR) << "\nUsage: " << name << " <option(s)>"
@@ -69,7 +70,7 @@
 
   Daemon(const Options& opts)
       : config_store_{new weave::examples::FileConfigStore(
-            opts.disable_security_)},
+            opts.disable_security_, opts.model_id_)},
         task_runner_{new weave::examples::EventTaskRunner},
         http_client_{new weave::examples::CurlHttpClient(task_runner_.get())},
         network_{new weave::examples::EventNetworkImpl(task_runner_.get())},
diff --git a/examples/daemon/ledflasher/ledflasher.cc b/examples/daemon/ledflasher/ledflasher.cc
index 0019d2c..e63deac 100644
--- a/examples/daemon/ledflasher/ledflasher.cc
+++ b/examples/daemon/ledflasher/ledflasher.cc
@@ -14,6 +14,43 @@
 namespace {
 // Supported LED count on this device
 const size_t kLedCount = 3;
+
+const char kTraits[] = R"({
+  "_ledflasher": {
+    "commands": {
+      "_set": {
+        "minimalRole": "user",
+        "parameters": {
+          "_led": {
+            "type": "integer",
+            "minimum": 1,
+            "maximum": 3
+          },
+          "_on": { "type": "boolean" }
+        }
+      },
+      "_toggle": {
+        "minimalRole": "user",
+        "parameters": {
+          "_led": {
+            "type": "integer",
+            "minimum": 1,
+            "maximum": 3
+          }
+        }
+      }
+    },
+    "state": {
+      "_leds": {
+        "type": "array",
+        "items": { "type": "boolean" }
+      }
+    }
+  }
+})";
+
+const char kComponent[] = "lock";
+
 }  // namespace
 
 // LedFlasherHandler is a complete command handler example that shows
@@ -24,37 +61,18 @@
   void Register(weave::Device* device) {
     device_ = device;
 
-    device->AddStateDefinitionsFromJson(R"({
-      "_ledflasher": {"_leds": {"type": "array", "items": {"type": "boolean"}}}
-    })");
+    device->AddTraitDefinitionsFromJson(kTraits);
+    CHECK(device->AddComponent(kComponent, {"_ledflasher"}, nullptr));
+    UpdateLedState();
 
-    device->SetStatePropertiesFromJson(R"({
-      "_ledflasher":{"_leds": [false, false, false]}
-    })",
-                                       nullptr);
-
-    device->AddCommandDefinitionsFromJson(R"({
-      "_ledflasher": {
-         "_set":{
-           "parameters": {
-             "_led": {"type": "integer", "minimum": 1, "maximum": 3},
-             "_on": {"type": "boolean"}
-           }
-         },
-         "_toggle":{
-           "parameters": {
-             "_led": {"type": "integer", "minimum": 1, "maximum": 3}
-           }
-        }
-      }
-    })");
     device->AddCommandHandler(
-        "_ledflasher._toggle",
+        kComponent, "_ledflasher._toggle",
         base::Bind(&LedFlasherHandler::OnFlasherToggleCommand,
                    weak_ptr_factory_.GetWeakPtr()));
     device->AddCommandHandler(
-        "_ledflasher._set", base::Bind(&LedFlasherHandler::OnFlasherSetCommand,
-                                       weak_ptr_factory_.GetWeakPtr()));
+        kComponent, "_ledflasher._set",
+        base::Bind(&LedFlasherHandler::OnFlasherSetCommand,
+                   weak_ptr_factory_.GetWeakPtr()));
   }
 
  private:
@@ -64,9 +82,10 @@
       return;
     LOG(INFO) << "received command: " << cmd->GetName();
     int32_t led_index = 0;
+    const auto& params = cmd->GetParameters();
     bool cmd_value = false;
-    if (cmd->GetParameters()->GetInteger("_led", &led_index) &&
-        cmd->GetParameters()->GetBoolean("_on", &cmd_value)) {
+    if (params.GetInteger("_led", &led_index) &&
+        params.GetBoolean("_on", &cmd_value)) {
       // Display this command in terminal
       LOG(INFO) << cmd->GetName() << " _led: " << led_index
                 << ", _on: " << (cmd_value ? "true" : "false");
@@ -93,8 +112,9 @@
     if (!cmd)
       return;
     LOG(INFO) << "received command: " << cmd->GetName();
+    const auto& params = cmd->GetParameters();
     int32_t led_index = 0;
-    if (cmd->GetParameters()->GetInteger("_led", &led_index)) {
+    if (params.GetInteger("_led", &led_index)) {
       LOG(INFO) << cmd->GetName() << " _led: " << led_index;
       led_index--;
       led_status_[led_index] = ~led_status_[led_index];
@@ -114,7 +134,7 @@
     for (uint32_t i = 0; i < led_status_.size(); i++)
       list.AppendBoolean(led_status_[i] ? true : false);
 
-    device_->SetStateProperty("_ledflasher._leds", list, nullptr);
+    device_->SetStateProperty(kComponent, "_ledflasher._leds", list, nullptr);
   }
 
   weave::Device* device_{nullptr};
diff --git a/examples/daemon/light/light.cc b/examples/daemon/light/light.cc
index a7eb9b3..611a37d 100644
--- a/examples/daemon/light/light.cc
+++ b/examples/daemon/light/light.cc
@@ -9,6 +9,145 @@
 #include <base/bind.h>
 #include <base/memory/weak_ptr.h>
 
+namespace {
+
+const char kTraits[] = R"({
+  "onOff": {
+    "commands": {
+      "setConfig": {
+        "minimalRole": "user",
+        "parameters": {
+          "state": {
+            "type": "string",
+            "enum": [ "on", "standby" ]
+          }
+        }
+      }
+    },
+    "state": {
+      "state": {
+        "type": "string",
+        "enum": [ "on", "standby" ]
+      }
+    }
+  },
+  "brightness": {
+    "commands": {
+      "setConfig": {
+        "minimalRole": "user",
+        "parameters": {
+          "brightness": {
+            "type": "integer",
+            "minimum": 0,
+            "maximum": 100
+          }
+        }
+      }
+    },
+    "state": { "brightness": { "type": "integer" } }
+  },
+  "colorXY": {
+    "commands": {
+      "setConfig": {
+        "minimalRole": "user",
+        "parameters": {
+          "colorSetting": {
+            "type": "object",
+            "required": [
+              "colorX",
+              "colorY"
+            ],
+            "properties": {
+              "colorX": {
+                "type": "number",
+                "minimum": 0.0,
+                "maximum": 1.0
+              },
+              "colorY": {
+                "type": "number",
+                "minimum": 0.0,
+                "maximum": 1.0
+              }
+            },
+            "additionalProperties": false
+          }
+        }
+      }
+    },
+    "state": {
+      "colorSetting": {
+        "properties": {
+          "colorX": {
+            "type": "number",
+            "minimum": 0.0,
+            "maximum": 1.0
+          },
+          "colorY": {
+            "type": "number",
+            "minimum": 0.0,
+            "maximum": 1.0
+          }
+        }
+      },
+      "colorCapRed": {
+        "properties": {
+          "colorX": {
+            "type": "number",
+            "minimum": 0.0,
+            "maximum": 1.0
+          },
+          "colorY": {
+            "type": "number",
+            "minimum": 0.0,
+            "maximum": 1.0
+          }
+        }
+      },
+      "colorCapGreen": {
+        "properties": {
+          "colorX": {
+            "type": "number",
+            "minimum": 0.0,
+            "maximum": 1.0
+          },
+          "colorY": {
+            "type": "number",
+            "minimum": 0.0,
+            "maximum": 1.0
+          }
+        }
+      },
+      "colorCapBlue": {
+        "properties": {
+          "colorX": {
+            "type": "number",
+            "minimum": 0.0,
+            "maximum": 1.0
+          },
+          "colorY": {
+            "type": "number",
+            "minimum": 0.0,
+            "maximum": 1.0
+          }
+        }
+      }
+    }
+  }
+})";
+
+const char kDefaultState[] = R"({
+  "colorXY": {
+    "colorSetting": {"colorX": 0, "colorY": 0},
+    "colorCapRed":  {"colorX": 0.674, "colorY": 0.322},
+    "colorCapGreen":{"colorX": 0.408, "colorY": 0.517},
+    "colorCapBlue": {"colorX": 0.168, "colorY": 0.041}
+  }
+})";
+
+const char kComponent[] = "light";
+
+}  // anonymous namespace
+
 // LightHandler is a command handler example that shows
 // how to handle commands for a Weave light.
 class LightHandler {
@@ -17,98 +156,20 @@
   void Register(weave::Device* device) {
     device_ = device;
 
-    device->AddStateDefinitionsFromJson(R"({
-      "onOff": {"state": {"type": "string", "enum": ["on", "standby"]}},
-      "brightness": {"brightness": {"type": "integer"}},
-      "colorXY": {
-        "colorSetting": {
-          "properties": {
-            "colorX": {"type": "number", "minimum": 0.0, "maximum": 1.0},
-            "colorY": {"type": "number", "minimum": 0.0, "maximum": 1.0}
-          }
-        },
-        "colorCapRed": {
-          "properties": {
-            "colorX": {"type": "number", "minimum": 0.0, "maximum": 1.0},
-            "colorY": {"type": "number", "minimum": 0.0, "maximum": 1.0}
-          }
-        },
-        "colorCapGreen": {
-          "properties": {
-            "colorX": {"type": "number", "minimum": 0.0, "maximum": 1.0},
-            "colorY": {"type": "number", "minimum": 0.0, "maximum": 1.0}
-          }
-        },
-        "colorCapBlue": {
-          "properties": {
-            "colorX": {"type": "number", "minimum": 0.0, "maximum": 1.0},
-            "colorY": {"type": "number", "minimum": 0.0, "maximum": 1.0}
-          }
-        }
-      }
-    })");
+    device->AddTraitDefinitionsFromJson(kTraits);
+    CHECK(device->AddComponent(kComponent, {"onOff", "brightness", "colorXY"},
+                               nullptr));
+    CHECK(device->SetStatePropertiesFromJson(kComponent, kDefaultState,
+                                             nullptr));
+    UpdateLightState();
 
-    device->SetStatePropertiesFromJson(R"({
-      "onOff":{"state": "standby"},
-      "brightness":{"brightness": 0},
-      "colorXY": {
-        "colorSetting": {"colorX": 0, "colorY": 0},
-        "colorCapRed":  {"colorX": 0.674, "colorY": 0.322},
-        "colorCapGreen":{"colorX": 0.408, "colorY": 0.517},
-        "colorCapBlue": {"colorX": 0.168, "colorY": 0.041}
-      }
-    })",
-                                       nullptr);
-
-    device->AddCommandDefinitionsFromJson(R"({
-      "onOff": {
-         "setConfig":{
-           "parameters": {
-             "state": {"type": "string", "enum": ["on", "standby"]}
-           }
-         }
-       },
-       "brightness": {
-         "setConfig":{
-           "parameters": {
-             "brightness": {
-               "type": "integer",
-               "minimum": 0,
-               "maximum": 100
-             }
-           }
-        }
-      },
-      "_colorXY": {
-        "_setConfig": {
-          "minimalRole": "user",
-          "parameters": {
-            "_colorSetting": {
-              "type": "object",
-              "properties": {
-                "_colorX": {
-                  "type": "number",
-                  "minimum": 0,
-                  "maximum": 1
-                },
-                "_colorY": {
-                  "type": "number",
-                  "minimum": 0,
-                  "maximum": 1
-                }
-              }
-            }
-          }
-        }
-      }
-    })");
-    device->AddCommandHandler("onOff.setConfig",
+    device->AddCommandHandler(kComponent, "onOff.setConfig",
                               base::Bind(&LightHandler::OnOnOffSetConfig,
                                          weak_ptr_factory_.GetWeakPtr()));
-    device->AddCommandHandler("brightness.setConfig",
+    device->AddCommandHandler(kComponent, "brightness.setConfig",
                               base::Bind(&LightHandler::OnBrightnessSetConfig,
                                          weak_ptr_factory_.GetWeakPtr()));
-    device->AddCommandHandler("_colorXY._setConfig",
+    device->AddCommandHandler(kComponent, "colorXY.setConfig",
                               base::Bind(&LightHandler::OnColorXYSetConfig,
                                          weak_ptr_factory_.GetWeakPtr()));
   }
@@ -119,8 +180,9 @@
     if (!cmd)
       return;
     LOG(INFO) << "received command: " << cmd->GetName();
+    const auto& params = cmd->GetParameters();
     int32_t brightness_value = 0;
-    if (cmd->GetParameters()->GetInteger("brightness", &brightness_value)) {
+    if (params.GetInteger("brightness", &brightness_value)) {
       // Display this command in terminal.
       LOG(INFO) << cmd->GetName() << " brightness: " << brightness_value;
 
@@ -142,8 +204,9 @@
     if (!cmd)
       return;
     LOG(INFO) << "received command: " << cmd->GetName();
+    const auto& params = cmd->GetParameters();
     std::string requested_state;
-    if (cmd->GetParameters()->GetString("state", &requested_state)) {
+    if (params.GetString("state", &requested_state)) {
       LOG(INFO) << cmd->GetName() << " state: " << requested_state;
 
       bool new_light_status = requested_state == "on";
@@ -167,18 +230,18 @@
     if (!cmd)
       return;
     LOG(INFO) << "received command: " << cmd->GetName();
-    auto params = cmd->GetParameters();
-    base::DictionaryValue* colorXY = nullptr;
-    if (params->GetDictionary("_colorSetting", &colorXY)) {
+    const auto& params = cmd->GetParameters();
+    const base::DictionaryValue* colorXY = nullptr;
+    if (params.GetDictionary("colorSetting", &colorXY)) {
       bool updateState = false;
       double X = 0.0;
       double Y = 0.0;
-      if (colorXY->GetDouble("_colorX", &X)) {
+      if (colorXY->GetDouble("colorX", &X)) {
         color_X_ = X;
         updateState = true;
       }
 
-      if (colorXY->GetDouble("_colorY", &Y)) {
+      if (colorXY->GetDouble("colorY", &Y)) {
         color_Y_ = Y;
         updateState = true;
       }
@@ -204,16 +267,15 @@
     std::unique_ptr<base::DictionaryValue> colorXY(new base::DictionaryValue());
     colorXY->SetDouble("colorX", color_X_);
     colorXY->SetDouble("colorY", color_Y_);
-    state.Set("colorXY.colorSetting", colorXY.get());
-    device_->SetStateProperties(state, nullptr);
-    colorXY.release();
+    state.Set("colorXY.colorSetting", colorXY.release());
+    device_->SetStateProperties(kComponent, state, nullptr);
   }
 
   weave::Device* device_{nullptr};
 
   // Simulate the state of the light.
-  bool light_status_;
-  int32_t brightness_state_;
+  bool light_status_{false};
+  int32_t brightness_state_{0};
   double color_X_{0.0};
   double color_Y_{0.0};
   base::WeakPtrFactory<LightHandler> weak_ptr_factory_{this};
diff --git a/examples/daemon/lock/lock.cc b/examples/daemon/lock/lock.cc
index 7d941c6..a4bd213 100644
--- a/examples/daemon/lock/lock.cc
+++ b/examples/daemon/lock/lock.cc
@@ -25,6 +25,39 @@
     : EnumToStringMap(lockstate::kLockMapMethod) {}
 }  // namespace weave
 
+namespace {
+
+const char kTraits[] = R"({
+  "lock": {
+    "commands": {
+      "setConfig": {
+        "minimalRole": "user",
+        "parameters": {
+          "lockedState": {
+            "type": "string",
+            "enum": [ "locked", "unlocked" ]
+          }
+        }
+      }
+    },
+    "state": {
+      "lockedState": {
+        "type": "string",
+        "enum": [ "locked", "unlocked", "partiallyLocked" ]
+      },
+      "isLockingSupported": { "type": "boolean" }
+    }
+  }
+})";
+
+const char kDefaultState[] = R"({
+  "lock":{"isLockingSupported": true}
+})";
+
+const char kComponent[] = "lock";
+
+}  // anonymous namespace
+
 // LockHandler is a command handler example that shows
 // how to handle commands for a Weave lock.
 class LockHandler {
@@ -33,33 +66,13 @@
   void Register(weave::Device* device) {
     device_ = device;
 
-    device->AddStateDefinitionsFromJson(R"({
-      "lock": {
-        "lockedState": {
-          "type": "string",
-          "enum": ["locked", "unlocked", "partiallyLocked"],
-        }
-        "isLockingSupported": {"type": "boolean"}}
-    })");
+    device->AddTraitDefinitionsFromJson(kTraits);
+    CHECK(device->AddComponent(kComponent, {"lock"}, nullptr));
+    CHECK(device->SetStatePropertiesFromJson(kComponent, kDefaultState,
+                                             nullptr));
+    UpdateLockState();
 
-    device->SetStatePropertiesFromJson(R"({
-      "lock":{
-        "lockedState": "locked",
-        "isLockingSupported": true
-      }
-    })",
-                                       nullptr);
-
-    device->AddCommandDefinitionsFromJson(R"({
-        "lock": {
-          "setConfig":{
-            "parameters": {
-              "lockedState": {"type": "string", "enum":["locked", "unlocked"]}
-            }
-          }
-        }
-    })");
-    device->AddCommandHandler("lock.setConfig",
+    device->AddCommandHandler(kComponent, "lock.setConfig",
                               base::Bind(&LockHandler::OnLockSetConfig,
                                          weak_ptr_factory_.GetWeakPtr()));
   }
@@ -70,8 +83,9 @@
     if (!cmd)
       return;
     LOG(INFO) << "received command: " << cmd->GetName();
+    const auto& params = cmd->GetParameters();
     std::string requested_state;
-    if (cmd->GetParameters()->GetString("lockedState", &requested_state)) {
+    if (params.GetString("lockedState", &requested_state)) {
       LOG(INFO) << cmd->GetName() << " state: " << requested_state;
 
       weave::lockstate::LockState new_lock_status;
@@ -101,10 +115,9 @@
   }
 
   void UpdateLockState() {
-    base::DictionaryValue state;
     std::string updated_state = weave::EnumToString(lock_state_);
-    state.SetString("lock.lockedState", updated_state);
-    device_->SetStateProperties(state, nullptr);
+    device_->SetStateProperty(kComponent, "lock.lockedState",
+                              base::StringValue{updated_state}, nullptr);
   }
 
   weave::Device* device_{nullptr};
diff --git a/examples/daemon/sample/sample.cc b/examples/daemon/sample/sample.cc
index e065b06..97cef61 100644
--- a/examples/daemon/sample/sample.cc
+++ b/examples/daemon/sample/sample.cc
@@ -10,6 +10,41 @@
 #include <base/bind.h>
 #include <base/memory/weak_ptr.h>
 
+namespace {
+
+const char kTraits[] = R"({
+  "_sample": {
+    "commands": {
+      "_hello": {
+        "minimalRole": "user",
+        "parameters": {
+          "_name": { "type": "string" }
+        }
+      },
+      "_ping": {
+        "minimalRole": "user"
+      },
+      "_countdown": {
+        "minimalRole": "user",
+        "parameters": {
+          "_seconds": {
+            "type": "integer",
+            "minimum": 1,
+            "maximum": 25
+          }
+        }
+      }
+    },
+    "state": {
+      "_ping_count": { "type": "integer" }
+    }
+  }
+})";
+
+const char kComponent[] = "sample";
+
+}  // anonymous namespace
+
 // SampleHandler is a command handler example.
 // It implements the following commands:
 // - _hello: handle a command with an argument and set its results.
@@ -22,46 +57,18 @@
   void Register(weave::Device* device) {
     device_ = device;
 
-    device->AddCommandDefinitionsFromJson(R"({
-      "_sample": {
-        "_hello": {
-          "minimalRole": "user",
-          "parameters": {
-            "_name": {"type": "string"}
-          },
-          "results": { "_reply": {"type": "string"} }
-        },
-        "_ping": {
-          "minimalRole": "user",
-          "results": {}
-        },
-        "_countdown": {
-          "minimalRole": "user",
-          "parameters": {
-            "_seconds": {"type": "integer", "minimum": 1, "maximum": 25}
-          },
-          "progress": { "_seconds_left": {"type": "integer"}},
-          "results": {}
-        }
-      }
-    })");
+    device->AddTraitDefinitionsFromJson(kTraits);
+    CHECK(device->AddComponent(kComponent, {"_sample"}, nullptr));
+    CHECK(device->SetStatePropertiesFromJson(
+        kComponent, R"({"_sample": {"_ping_count": 0}})", nullptr));
 
-    device->AddStateDefinitionsFromJson(R"({
-      "_sample": {"_ping_count": {"type": "integer"}}
-    })");
-
-    device->SetStatePropertiesFromJson(R"({
-      "_sample": {"_ping_count": 0}
-    })",
-                                       nullptr);
-
-    device->AddCommandHandler("_sample._hello",
+    device->AddCommandHandler(kComponent, "_sample._hello",
                               base::Bind(&SampleHandler::OnHelloCommand,
                                          weak_ptr_factory_.GetWeakPtr()));
-    device->AddCommandHandler("_sample._ping",
+    device->AddCommandHandler(kComponent, "_sample._ping",
                               base::Bind(&SampleHandler::OnPingCommand,
                                          weak_ptr_factory_.GetWeakPtr()));
-    device->AddCommandHandler("_sample._countdown",
+    device->AddCommandHandler(kComponent, "_sample._countdown",
                               base::Bind(&SampleHandler::OnCountdownCommand,
                                          weak_ptr_factory_.GetWeakPtr()));
   }
@@ -73,8 +80,9 @@
       return;
     LOG(INFO) << "received command: " << cmd->GetName();
 
+    const auto& params = cmd->GetParameters();
     std::string name;
-    if (!cmd->GetParameters()->GetString("_name", &name)) {
+    if (!params.GetString("_name", &name)) {
       weave::ErrorPtr error;
       weave::Error::AddTo(&error, FROM_HERE, "example",
                           "invalid_parameter_value", "Name is missing");
@@ -94,10 +102,9 @@
       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();
+    device_->SetStateProperty(kComponent, "_sample._ping_count",
+                              base::FundamentalValue{++ping_count_}, nullptr);
+    LOG(INFO) << "New state: " << device_->GetState();
 
     base::DictionaryValue result;
     cmd->Complete(result, nullptr);
@@ -111,8 +118,9 @@
       return;
     LOG(INFO) << "received command: " << cmd->GetName();
 
+    const auto& params = cmd->GetParameters();
     int seconds;
-    if (!cmd->GetParameters()->GetInteger("_seconds", &seconds))
+    if (!params.GetInteger("_seconds", &seconds))
       seconds = 10;
 
     LOG(INFO) << "starting countdown";
@@ -125,8 +133,9 @@
       return;
 
     if (seconds > 0) {
+      const auto& params = cmd->GetParameters();
       std::string todo;
-      cmd->GetParameters()->GetString("_todo", &todo);
+      params.GetString("_todo", &todo);
       LOG(INFO) << "countdown tick: " << seconds << " seconds left";
 
       base::DictionaryValue progress;
diff --git a/examples/daemon/speaker/speaker.cc b/examples/daemon/speaker/speaker.cc
index 178be14..8b3e41b 100644
--- a/examples/daemon/speaker/speaker.cc
+++ b/examples/daemon/speaker/speaker.cc
@@ -9,6 +9,51 @@
 #include <base/bind.h>
 #include <base/memory/weak_ptr.h>
 
+namespace {
+
+const char kTraits[] = R"({
+  "onOff": {
+    "commands": {
+      "setConfig": {
+        "minimalRole": "user",
+        "parameters": {
+          "state": {
+            "type": "string",
+            "enum": [ "on", "standby" ]
+          }
+        }
+      }
+    },
+    "state": {
+      "type": "string",
+      "enum": [ "on", "standby" ]
+    }
+  },
+  "volume": {
+    "commands": {
+      "setConfig": {
+        "minimalRole": "user",
+        "parameters": {
+          "volume": {
+            "type": "integer",
+            "minimum": 0,
+            "maximum": 100
+          },
+          "isMuted": { "type": "boolean" }
+        }
+      }
+    },
+    "state": {
+      "isMuted": { "type": "boolean" },
+      "volume": { "type": "integer" }
+    }
+  }
+})";
+
+const char kComponent[] = "speaker";
+
+}  // anonymous namespace
+
 // SpeakerHandler is a command handler example that shows
 // how to handle commands for a Weave speaker.
 class SpeakerHandler {
@@ -17,48 +62,14 @@
   void Register(weave::Device* device) {
     device_ = device;
 
-    device->AddStateDefinitionsFromJson(R"({
-      "onOff": {"state": {"type": "string", "enum": ["on", "standby"]}},
-      "volume": {
-        "volume": {"type": "integer"},
-        "isMuted": {"type": "boolean"}
-      }
-    })");
+    device->AddTraitDefinitionsFromJson(kTraits);
+    CHECK(device->AddComponent(kComponent, {"onOff", "volume"}, nullptr));
+    UpdateSpeakerState();
 
-    device->SetStatePropertiesFromJson(R"({
-      "onOff":{"state": "standby"},
-      "volume":{
-        "volume": 100,
-        "isMuted": false
-      }
-    })",
-                                       nullptr);
-
-    device->AddCommandDefinitionsFromJson(R"({
-      "onOff": {
-         "setConfig":{
-           "parameters": {
-             "state": {"type": "string", "enum": ["on", "standby"]}
-           }
-         }
-       },
-       "volume": {
-         "setConfig":{
-           "parameters": {
-             "volume": {
-               "type": "integer",
-               "minimum": 0,
-               "maximum": 100
-             },
-             "isMuted": {"type": "boolean"}
-           }
-        }
-      }
-    })");
-    device->AddCommandHandler("onOff.setConfig",
+    device->AddCommandHandler(kComponent, "onOff.setConfig",
                               base::Bind(&SpeakerHandler::OnOnOffSetConfig,
                                          weak_ptr_factory_.GetWeakPtr()));
-    device->AddCommandHandler("volume.setConfig",
+    device->AddCommandHandler(kComponent, "volume.setConfig",
                               base::Bind(&SpeakerHandler::OnVolumeSetConfig,
                                          weak_ptr_factory_.GetWeakPtr()));
   }
@@ -70,9 +81,10 @@
       return;
     LOG(INFO) << "received command: " << cmd->GetName();
 
+    const auto& params = cmd->GetParameters();
     // Handle volume parameter
     int32_t volume_value = 0;
-    if (cmd->GetParameters()->GetInteger("volume", &volume_value)) {
+    if (params.GetInteger("volume", &volume_value)) {
       // Display this command in terminal.
       LOG(INFO) << cmd->GetName() << " volume: " << volume_value;
 
@@ -86,7 +98,7 @@
 
     // Handle isMuted parameter
     bool isMuted_status = false;
-    if (cmd->GetParameters()->GetBoolean("isMuted", &isMuted_status)) {
+    if (params.GetBoolean("isMuted", &isMuted_status)) {
       // Display this command in terminal.
       LOG(INFO) << cmd->GetName() << " is "
                 << (isMuted_status ? "muted" : "not muted");
@@ -108,8 +120,9 @@
     if (!cmd)
       return;
     LOG(INFO) << "received command: " << cmd->GetName();
+    const auto& params = cmd->GetParameters();
     std::string requested_state;
-    if (cmd->GetParameters()->GetString("state", &requested_state)) {
+    if (params.GetString("state", &requested_state)) {
       LOG(INFO) << cmd->GetName() << " state: " << requested_state;
 
       bool new_speaker_status = requested_state == "on";
@@ -128,7 +141,7 @@
     state.SetString("onOff.state", speaker_status_ ? "on" : "standby");
     state.SetBoolean("volume.isMuted", isMuted_status_);
     state.SetInteger("volume.volume", volume_value_);
-    device_->SetStateProperties(state, nullptr);
+    device_->SetStateProperties(kComponent, state, nullptr);
   }
 
   weave::Device* device_{nullptr};
diff --git a/examples/prerequisites.sh b/examples/prerequisites.sh
index 7358b71..1b27806 100755
--- a/examples/prerequisites.sh
+++ b/examples/prerequisites.sh
@@ -20,7 +20,6 @@
   libnl-route-3-dev \
   libssl-dev \
   libtool \
-  ninja-build \
   || exit 1
 
 mkdir -p $ROOT_DIR/third_party/lib $ROOT_DIR/third_party/include 2> /dev/null
diff --git a/examples/provider/avahi_client.h b/examples/provider/avahi_client.h
index 0ca28db..7d9b932 100644
--- a/examples/provider/avahi_client.h
+++ b/examples/provider/avahi_client.h
@@ -34,7 +34,7 @@
   std::unique_ptr<AvahiThreadedPoll, decltype(&avahi_threaded_poll_free)>
       thread_pool_{nullptr, &avahi_threaded_poll_free};
 
-  std::unique_ptr<::AvahiClient, decltype(&avahi_client_free)> client_{
+  std::unique_ptr< ::AvahiClient, decltype(&avahi_client_free)> client_{
       nullptr, &avahi_client_free};
 
   std::unique_ptr<AvahiEntryGroup, decltype(&avahi_entry_group_free)> group_{
diff --git a/examples/provider/curl_http_client.cc b/examples/provider/curl_http_client.cc
index 774c07b..6f0ba64 100644
--- a/examples/provider/curl_http_client.cc
+++ b/examples/provider/curl_http_client.cc
@@ -118,12 +118,6 @@
       response->content_type = header.second;
   }
 
-  if (response->content_type.empty()) {
-    Error::AddTo(&error, FROM_HERE, "curl", "no_content_header",
-                 "Content-Type header is missing");
-    return {nullptr, std::move(error)};
-  }
-
   CHECK_EQ(CURLE_OK, curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE,
                                        &response->status));
 
diff --git a/examples/provider/event_http_client.cc b/examples/provider/event_http_client.cc
index 03da97f..1931547 100644
--- a/examples/provider/event_http_client.cc
+++ b/examples/provider/event_http_client.cc
@@ -91,7 +91,7 @@
                                   const Headers& headers,
                                   const std::string& data,
                                   const SendRequestCallback& callback) {
-  evhttp_cmd_type method_id;
+  evhttp_cmd_type method_id = EVHTTP_REQ_GET;
   CHECK(weave::StringToEnum(weave::EnumToString(method), &method_id));
   EventPtr<evhttp_uri> http_uri{evhttp_uri_parse(url.c_str())};
   CHECK(http_uri);
diff --git a/examples/provider/event_http_server.cc b/examples/provider/event_http_server.cc
index e0ecea6..ae8bbec 100644
--- a/examples/provider/event_http_server.cc
+++ b/examples/provider/event_http_server.cc
@@ -164,14 +164,14 @@
 void HttpServerImpl::AddHttpRequestHandler(
     const std::string& path,
     const RequestHandlerCallback& callback) {
-  handlers_.emplace(path, callback);
+  handlers_.insert(std::make_pair(path, callback));
   evhttp_set_cb(httpd_.get(), path.c_str(), &ProcessRequestCallback, this);
 }
 
 void HttpServerImpl::AddHttpsRequestHandler(
     const std::string& path,
     const RequestHandlerCallback& callback) {
-  handlers_.emplace(path, callback);
+  handlers_.insert(std::make_pair(path, callback));
   evhttp_set_cb(httpsd_.get(), path.c_str(), &ProcessRequestCallback, this);
 }
 
diff --git a/examples/provider/event_task_runner.cc b/examples/provider/event_task_runner.cc
index c07e912..1d94612 100644
--- a/examples/provider/event_task_runner.cc
+++ b/examples/provider/event_task_runner.cc
@@ -34,7 +34,8 @@
   flags |= (what & kClosed) ? EV_CLOSED : 0;
   event* ioevent = event_new(base_.get(), fd, flags, FdEventHandler, this);
   EventPtr<event> ioeventPtr{ioevent};
-  fd_task_map_.emplace(fd, std::make_pair(std::move(ioeventPtr), task));
+  fd_task_map_.insert(
+      std::make_pair(fd, std::make_pair(std::move(ioeventPtr), task)));
   event_add(ioevent, nullptr);
 }
 
diff --git a/examples/provider/file_config_store.cc b/examples/provider/file_config_store.cc
index 8e72792..7d4e31b 100644
--- a/examples/provider/file_config_store.cc
+++ b/examples/provider/file_config_store.cc
@@ -19,8 +19,8 @@
 const char kSettingsPath[] = "/var/lib/weave/weave_settings.json";
 const char kCategory[] = "example";
 
-FileConfigStore::FileConfigStore(bool disable_security)
-    : disable_security_{disable_security} {}
+FileConfigStore::FileConfigStore(bool disable_security, const std::string& model_id)
+    : disable_security_{disable_security}, model_id_{model_id} {}
 
 bool FileConfigStore::LoadDefaults(Settings* settings) {
   char host_name[HOST_NAME_MAX] = {};
@@ -35,7 +35,7 @@
   settings->firmware_version = uname_data.sysname;
   settings->oem_name = "Unknown";
   settings->model_name = "Unknown";
-  settings->model_id = "AAAAA";
+  settings->model_id = model_id_;
   settings->pairing_modes = {PairingType::kEmbeddedCode};
   settings->embedded_code = "0000";
 
diff --git a/examples/provider/file_config_store.h b/examples/provider/file_config_store.h
index e4d16a9..8764cc5 100644
--- a/examples/provider/file_config_store.h
+++ b/examples/provider/file_config_store.h
@@ -16,7 +16,7 @@
 
 class FileConfigStore : public provider::ConfigStore {
  public:
-  explicit FileConfigStore(bool disable_security);
+  explicit FileConfigStore(bool disable_security, const std::string& model_id);
 
   bool LoadDefaults(Settings* settings) override;
   std::string LoadSettings() override;
@@ -24,6 +24,7 @@
 
  private:
   bool disable_security_{false};
+  std::string model_id_{"AAAAA"};
 };
 
 }  // namespace examples
diff --git a/include/weave/command.h b/include/weave/command.h
index 59a9305..91949f6 100644
--- a/include/weave/command.h
+++ b/include/weave/command.h
@@ -33,6 +33,9 @@
   // Returns the full name of the command.
   virtual const std::string& GetName() const = 0;
 
+  // Returns the full path to the component this command is intended for.
+  virtual const std::string& GetComponent() const = 0;
+
   // Returns the command state.
   virtual Command::State GetState() const = 0;
 
@@ -40,13 +43,13 @@
   virtual Command::Origin GetOrigin() const = 0;
 
   // Returns the command parameters.
-  virtual std::unique_ptr<base::DictionaryValue> GetParameters() const = 0;
+  virtual const base::DictionaryValue& GetParameters() const = 0;
 
   // Returns the command progress.
-  virtual std::unique_ptr<base::DictionaryValue> GetProgress() const = 0;
+  virtual const base::DictionaryValue& GetProgress() const = 0;
 
   // Returns the command results.
-  virtual std::unique_ptr<base::DictionaryValue> GetResults() const = 0;
+  virtual const base::DictionaryValue& GetResults() const = 0;
 
   // Returns the command error.
   virtual const Error* GetError() const = 0;
@@ -79,7 +82,7 @@
   virtual bool Cancel(ErrorPtr* error) = 0;
 
  protected:
-  virtual ~Command() = default;
+  virtual ~Command() {}
 };
 
 }  // namespace weave
diff --git a/include/weave/device.h b/include/weave/device.h
index 19012b5..2d7aaff 100644
--- a/include/weave/device.h
+++ b/include/weave/device.h
@@ -8,6 +8,7 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 #include <weave/command.h>
 #include <weave/export.h>
@@ -32,7 +33,7 @@
 
 class Device {
  public:
-  virtual ~Device() = default;
+  virtual ~Device() {}
 
   // Returns reference the current settings.
   virtual const Settings& GetSettings() const = 0;
@@ -45,23 +46,73 @@
   virtual void AddSettingsChangedCallback(
       const SettingsChangedCallback& callback) = 0;
 
-  // Adds provided commands definitions. Can be called multiple times with
-  // condition that definitions do not conflict.
-  // Invalid value is fatal.
-  virtual void AddCommandDefinitionsFromJson(const std::string& json) = 0;
-  virtual void AddCommandDefinitions(const base::DictionaryValue& dict) = 0;
+  // Adds new trait definitions to device.
+  virtual void AddTraitDefinitionsFromJson(const std::string& json) = 0;
+  virtual void AddTraitDefinitions(const base::DictionaryValue& dict) = 0;
+
+  // Returns the full JSON dictionary containing trait definitions.
+  virtual const base::DictionaryValue& GetTraits() const = 0;
+
+  // Adds a new component instance to device. Traits used by this component
+  // must be already defined.
+  virtual bool AddComponent(const std::string& name,
+                            const std::vector<std::string>& traits,
+                            ErrorPtr* error) = 0;
+
+  // Sets callback which is called when new components are added.
+  virtual void AddComponentTreeChangedCallback(
+      const base::Closure& callback) = 0;
+
+  // Returns the full JSON dictionary containing component instances.
+  virtual const base::DictionaryValue& GetComponents() const = 0;
+
+  // Sets value of multiple properties of the state.
+  // It's recommended to call this to initialize component state defined.
+  // Example:
+  //   device->SetStatePropertiesFromJson("myComponent",
+  //                                      "{'base':{'firmwareVersion':'123'}}")
+  // Method completely replaces properties included |json| or |dict|.
+  // Properties of the state not included |json| or |dict| will stay unchanged.
+  virtual bool SetStatePropertiesFromJson(
+      const std::string& component,
+      const std::string& json,
+      ErrorPtr* error) = 0;
+  virtual bool SetStateProperties(
+      const std::string& component,
+      const base::DictionaryValue& dict,
+      ErrorPtr* error) = 0;
+
+  // Returns value of the single property.
+  // |name| is full property name, including trait name. e.g. "base.network".
+  virtual const base::Value* GetStateProperty(
+      const std::string& component,
+      const std::string& name,
+      ErrorPtr* error) const = 0;
+
+  // Sets value of the single property.
+  // |name| is full property name, including trait name. e.g. "base.network".
+  virtual bool SetStateProperty(
+      const std::string& component,
+      const std::string& name,
+      const base::Value& value,
+      ErrorPtr* error) = 0;
 
   // Callback type for AddCommandHandler.
   using CommandHandlerCallback =
       base::Callback<void(const std::weak_ptr<Command>& command)>;
 
   // Sets handler for new commands added to the queue.
+  // |component| is the name of the component for which commands should be
+  // handled.
   // |command_name| is the full command name of the command to handle. e.g.
   // "base.reboot". Each command can have no more than one handler.
-  // Empty |command_name| sets default handler for all unhanded commands.
+  // Empty |component| and |command_name| sets default handler for all unhanded
+  // commands.
   // No new command handlers can be set after default handler was set.
-  virtual void AddCommandHandler(const std::string& command_name,
-                                 const CommandHandlerCallback& callback) = 0;
+  virtual void AddCommandHandler(
+      const std::string& component,
+      const std::string& command_name,
+      const CommandHandlerCallback& callback) = 0;
 
   // Adds a new command to the command queue.
   virtual bool AddCommand(const base::DictionaryValue& command,
@@ -76,38 +127,6 @@
   // Sets callback which is called when stat is changed.
   virtual void AddStateChangedCallback(const base::Closure& callback) = 0;
 
-  // Adds provided state definitions. Can be called multiple times with
-  // condition that definitions do not conflict.
-  // Invalid value is fatal.
-  virtual void AddStateDefinitionsFromJson(const std::string& json) = 0;
-  virtual void AddStateDefinitions(const base::DictionaryValue& dict) = 0;
-
-  // Sets value of multiple properties of the state.
-  // It's recommended to call this to initialize state defined by
-  // AddStateDefinitions.
-  // Example:
-  //   device->SetStatePropertiesFromJson("{'base':{'firmwareVersion':'123'}}")
-  // Method completely replaces properties included |json| or |dict|.
-  // Properties of the state not included |json| or |dict| will stay unchanged.
-  virtual bool SetStatePropertiesFromJson(const std::string& json,
-                                          ErrorPtr* error) = 0;
-  virtual bool SetStateProperties(const base::DictionaryValue& dict,
-                                  ErrorPtr* error) = 0;
-
-  // Returns value of the single property.
-  // |name| is full property name, including package name. e.g. "base.network".
-  virtual std::unique_ptr<base::Value> GetStateProperty(
-      const std::string& name) const = 0;
-
-  // Sets value of the single property.
-  // |name| is full property name, including package name. e.g. "base.network".
-  virtual bool SetStateProperty(const std::string& name,
-                                const base::Value& value,
-                                ErrorPtr* error) = 0;
-
-  // Returns aggregated state properties across all registered packages.
-  virtual std::unique_ptr<base::DictionaryValue> GetState() const = 0;
-
   // Returns current state of GCD connection.
   virtual GcdState GetGcdState() const = 0;
 
@@ -147,6 +166,63 @@
       provider::HttpServer* http_server,
       provider::Wifi* wifi,
       provider::Bluetooth* bluetooth_provider);
+
+  //========================== Deprecated APIs =========================
+
+  // Adds provided commands definitions. Can be called multiple times with
+  // condition that definitions do not conflict.
+  // Invalid value is fatal.
+  LIBWEAVE_DEPRECATED virtual void AddCommandDefinitionsFromJson(
+      const std::string& json) = 0;
+  LIBWEAVE_DEPRECATED virtual void AddCommandDefinitions(
+      const base::DictionaryValue& dict) = 0;
+
+  // Sets handler for new commands added to the queue.
+  // |command_name| is the full command name of the command to handle. e.g.
+  // "base.reboot". Each command can have no more than one handler.
+  // Empty |command_name| sets default handler for all unhanded commands.
+  // No new command handlers can be set after default handler was set.
+  LIBWEAVE_DEPRECATED virtual void AddCommandHandler(
+      const std::string& command_name,
+      const CommandHandlerCallback& callback) = 0;
+
+  // Adds provided state definitions. Can be called multiple times with
+  // condition that definitions do not conflict.
+  // Invalid value is fatal.
+  LIBWEAVE_DEPRECATED virtual void AddStateDefinitionsFromJson(
+      const std::string& json) = 0;
+  LIBWEAVE_DEPRECATED virtual void AddStateDefinitions(
+      const base::DictionaryValue& dict) = 0;
+
+  // Sets value of multiple properties of the state.
+  // It's recommended to call this to initialize state defined by
+  // AddStateDefinitions.
+  // Example:
+  //   device->SetStatePropertiesFromJson("{'base':{'firmwareVersion':'123'}}")
+  // Method completely replaces properties included |json| or |dict|.
+  // Properties of the state not included |json| or |dict| will stay unchanged.
+  LIBWEAVE_DEPRECATED virtual bool SetStatePropertiesFromJson(
+      const std::string& json,
+      ErrorPtr* error) = 0;
+  LIBWEAVE_DEPRECATED virtual bool SetStateProperties(
+      const base::DictionaryValue& dict,
+      ErrorPtr* error) = 0;
+
+  // Returns value of the single property.
+  // |name| is full property name, including package name. e.g. "base.network".
+  LIBWEAVE_DEPRECATED virtual const base::Value* GetStateProperty(
+      const std::string& name) const = 0;
+
+  // Sets value of the single property.
+  // |name| is full property name, including package name. e.g. "base.network".
+  LIBWEAVE_DEPRECATED virtual bool SetStateProperty(
+      const std::string& name,
+      const base::Value& value,
+      ErrorPtr* error) = 0;
+
+  // Returns aggregated state properties across all registered packages.
+  LIBWEAVE_DEPRECATED virtual const base::DictionaryValue& GetState() const = 0;
+
 };
 
 }  // namespace weave
diff --git a/include/weave/export.h b/include/weave/export.h
index 1bc27c5..f698176 100644
--- a/include/weave/export.h
+++ b/include/weave/export.h
@@ -8,4 +8,8 @@
 #define LIBWEAVE_EXPORT __attribute__((__visibility__("default")))
 #define LIBWEAVE_PRIVATE __attribute__((__visibility__("hidden")))
 
+// TODO(avakulenko): Once all the sample clients are migrated to new APIs,
+// mark the old one officially deprecated by uncomment the following attribute.
+#define LIBWEAVE_DEPRECATED // __attribute__((deprecated))
+
 #endif  // LIBWEAVE_INCLUDE_WEAVE_EXPORT_H_
diff --git a/include/weave/provider/bluetooth.h b/include/weave/provider/bluetooth.h
index e8f3b3c..6a47e92 100644
--- a/include/weave/provider/bluetooth.h
+++ b/include/weave/provider/bluetooth.h
@@ -14,7 +14,7 @@
   // TODO(rginda): Add bluetooth interface methods here.
 
  protected:
-  virtual ~Bluetooth() = default;
+  virtual ~Bluetooth() {}
 };
 
 }  // namespace provider
diff --git a/include/weave/provider/config_store.h b/include/weave/provider/config_store.h
index 53c1128..1b7988f 100644
--- a/include/weave/provider/config_store.h
+++ b/include/weave/provider/config_store.h
@@ -75,7 +75,7 @@
   virtual void SaveSettings(const std::string& settings) = 0;
 
  protected:
-  virtual ~ConfigStore() = default;
+  virtual ~ConfigStore() {}
 };
 
 }  // namespace provider
diff --git a/include/weave/provider/dns_service_discovery.h b/include/weave/provider/dns_service_discovery.h
index fa9d50e..37bf84b 100644
--- a/include/weave/provider/dns_service_discovery.h
+++ b/include/weave/provider/dns_service_discovery.h
@@ -91,7 +91,7 @@
   virtual void StopPublishing(const std::string& service_type) = 0;
 
  protected:
-  virtual ~DnsServiceDiscovery() = default;
+  virtual ~DnsServiceDiscovery() {}
 };
 
 }  // namespace provider
diff --git a/include/weave/provider/http_client.h b/include/weave/provider/http_client.h
index deb127a..bf01022 100644
--- a/include/weave/provider/http_client.h
+++ b/include/weave/provider/http_client.h
@@ -78,7 +78,7 @@
     virtual std::string GetContentType() const = 0;
     virtual std::string GetData() const = 0;
 
-    virtual ~Response() = default;
+    virtual ~Response() {}
   };
 
   using Headers = std::vector<std::pair<std::string, std::string>>;
@@ -92,7 +92,7 @@
                            const SendRequestCallback& callback) = 0;
 
  protected:
-  virtual ~HttpClient() = default;
+  virtual ~HttpClient() {}
 };
 
 }  // namespace provider
diff --git a/include/weave/provider/http_server.h b/include/weave/provider/http_server.h
index 622785b..1c28d63 100644
--- a/include/weave/provider/http_server.h
+++ b/include/weave/provider/http_server.h
@@ -109,7 +109,7 @@
  public:
   class Request {
    public:
-    virtual ~Request() = default;
+    virtual ~Request() {}
 
     virtual std::string GetPath() const = 0;
     virtual std::string GetFirstHeader(const std::string& name) const = 0;
@@ -141,7 +141,7 @@
   virtual base::TimeDelta GetRequestTimeout() const = 0;
 
  protected:
-  virtual ~HttpServer() = default;
+  virtual ~HttpServer() {}
 };
 
 }  // namespace provider
diff --git a/include/weave/provider/network.h b/include/weave/provider/network.h
index 651155a..0fb147d 100644
--- a/include/weave/provider/network.h
+++ b/include/weave/provider/network.h
@@ -47,7 +47,7 @@
                              const OpenSslSocketCallback& callback) = 0;
 
  protected:
-  virtual ~Network() = default;
+  virtual ~Network() {}
 };
 
 }  // namespace provider
diff --git a/include/weave/provider/task_runner.h b/include/weave/provider/task_runner.h
index 0804a10..095910b 100644
--- a/include/weave/provider/task_runner.h
+++ b/include/weave/provider/task_runner.h
@@ -28,7 +28,7 @@
                                base::TimeDelta delay) = 0;
 
  protected:
-  virtual ~TaskRunner() = default;
+  virtual ~TaskRunner() {}
 };
 
 }  // namespace provider
diff --git a/include/weave/provider/wifi.h b/include/weave/provider/wifi.h
index 111bf3c..48ac651 100644
--- a/include/weave/provider/wifi.h
+++ b/include/weave/provider/wifi.h
@@ -29,7 +29,7 @@
   virtual void StopAccessPoint() = 0;
 
  protected:
-  virtual ~Wifi() = default;
+  virtual ~Wifi() {}
 };
 
 }  // namespace provider
diff --git a/include/weave/settings.h b/include/weave/settings.h
index e5d21b0..1c8b095 100644
--- a/include/weave/settings.h
+++ b/include/weave/settings.h
@@ -64,6 +64,9 @@
   // Cloud ID of the registered device. Empty if device is not registered.
   std::string cloud_id;
 
+  // Local device id.
+  std::string device_id;
+
   // Internal options to tweak some library functionality. External code should
   // avoid using them.
   bool wifi_auto_setup_enabled{true};
diff --git a/include/weave/stream.h b/include/weave/stream.h
index 19d38a0..14cc7f0 100644
--- a/include/weave/stream.h
+++ b/include/weave/stream.h
@@ -15,7 +15,7 @@
 // Interface for async input streaming.
 class InputStream {
  public:
-  virtual ~InputStream() = default;
+  virtual ~InputStream() {}
 
   // Callback type for Read.
   using ReadCallback = base::Callback<void(size_t size, ErrorPtr error)>;
@@ -31,7 +31,7 @@
 // Interface for async input streaming.
 class OutputStream {
  public:
-  virtual ~OutputStream() = default;
+  virtual ~OutputStream() {}
 
   using WriteCallback = base::Callback<void(ErrorPtr error)>;
 
@@ -47,7 +47,7 @@
 // Interface for async bi-directional streaming.
 class Stream : public InputStream, public OutputStream {
  public:
-  ~Stream() override = default;
+  ~Stream() override {}
 
   // Cancels all pending read or write requests. Canceled operations must not
   // call any callbacks.
diff --git a/include/weave/test/mock_command.h b/include/weave/test/mock_command.h
index 2b1080e..181b9f5 100644
--- a/include/weave/test/mock_command.h
+++ b/include/weave/test/mock_command.h
@@ -22,12 +22,12 @@
 
   MOCK_CONST_METHOD0(GetID, const std::string&());
   MOCK_CONST_METHOD0(GetName, const std::string&());
-  MOCK_CONST_METHOD0(GetCategory, const std::string&());
+  MOCK_CONST_METHOD0(GetComponent, const std::string&());
   MOCK_CONST_METHOD0(GetState, Command::State());
   MOCK_CONST_METHOD0(GetOrigin, Command::Origin());
-  MOCK_CONST_METHOD0(MockGetParameters, const std::string&());
-  MOCK_CONST_METHOD0(MockGetProgress, const std::string&());
-  MOCK_CONST_METHOD0(MockGetResults, const std::string&());
+  MOCK_CONST_METHOD0(GetParameters, const base::DictionaryValue&());
+  MOCK_CONST_METHOD0(GetProgress, const base::DictionaryValue&());
+  MOCK_CONST_METHOD0(GetResults, const base::DictionaryValue&());
   MOCK_CONST_METHOD0(GetError, const Error*());
   MOCK_METHOD2(SetProgress, bool(const base::DictionaryValue&, ErrorPtr*));
   MOCK_METHOD2(Complete, bool(const base::DictionaryValue&, ErrorPtr*));
@@ -35,10 +35,6 @@
   MOCK_METHOD2(SetError, bool(const Error*, ErrorPtr*));
   MOCK_METHOD2(Abort, bool(const Error*, ErrorPtr*));
   MOCK_METHOD1(Cancel, bool(ErrorPtr*));
-
-  std::unique_ptr<base::DictionaryValue> GetParameters() const override;
-  std::unique_ptr<base::DictionaryValue> GetProgress() const override;
-  std::unique_ptr<base::DictionaryValue> GetResults() const override;
 };
 
 }  // namespace test
diff --git a/include/weave/test/mock_device.h b/include/weave/test/mock_device.h
index f751f97..88cc5e0 100644
--- a/include/weave/test/mock_device.h
+++ b/include/weave/test/mock_device.h
@@ -21,26 +21,36 @@
   MOCK_CONST_METHOD0(GetSettings, const Settings&());
   MOCK_METHOD1(AddSettingsChangedCallback,
                void(const SettingsChangedCallback& callback));
-  MOCK_METHOD1(AddCommandDefinitionsFromJson, void(const std::string&));
-  MOCK_METHOD1(AddCommandDefinitions, void(const base::DictionaryValue&));
-  MOCK_METHOD2(AddCommandHandler,
-               void(const std::string&, const CommandHandlerCallback&));
+  MOCK_METHOD1(AddTraitDefinitionsFromJson, void(const std::string& json));
+  MOCK_METHOD1(AddTraitDefinitions, void(const base::DictionaryValue& dict));
+  MOCK_CONST_METHOD0(GetTraits, const base::DictionaryValue&());
+  MOCK_METHOD3(AddComponent, bool(const std::string& name,
+                                  const std::vector<std::string>& traits,
+                                  ErrorPtr* error));
+  MOCK_METHOD1(AddComponentTreeChangedCallback,
+               void(const base::Closure& callback));
+  MOCK_CONST_METHOD0(GetComponents, const base::DictionaryValue&());
+  MOCK_METHOD3(SetStatePropertiesFromJson, bool(const std::string& component,
+                                                const std::string& json,
+                                                ErrorPtr* error));
+  MOCK_METHOD3(SetStateProperties, bool(const std::string& component,
+                                        const base::DictionaryValue& dict,
+                                        ErrorPtr* error));
+  MOCK_CONST_METHOD3(GetStateProperty,
+                     const base::Value*(const std::string& component,
+                                        const std::string& name,
+                                        ErrorPtr* error));
+  MOCK_METHOD4(SetStateProperty, bool(const std::string& component,
+                                      const std::string& name,
+                                      const base::Value& value,
+                                      ErrorPtr* error));
+  MOCK_METHOD3(AddCommandHandler, void(const std::string& component,
+                                       const std::string& command_name,
+                                       const CommandHandlerCallback& callback));
   MOCK_METHOD3(AddCommand,
                bool(const base::DictionaryValue&, std::string*, ErrorPtr*));
   MOCK_METHOD1(FindCommand, Command*(const std::string&));
   MOCK_METHOD1(AddStateChangedCallback, void(const base::Closure& callback));
-  MOCK_METHOD1(AddStateDefinitionsFromJson, void(const std::string&));
-  MOCK_METHOD1(AddStateDefinitions, void(const base::DictionaryValue&));
-  MOCK_METHOD2(SetStatePropertiesFromJson, bool(const std::string&, ErrorPtr*));
-  MOCK_METHOD2(SetStateProperties,
-               bool(const base::DictionaryValue&, ErrorPtr*));
-  MOCK_CONST_METHOD1(MockGetStateProperty,
-                     base::Value*(const std::string& name));
-  MOCK_METHOD3(SetStateProperty,
-               bool(const std::string& name,
-                    const base::Value& value,
-                    ErrorPtr* error));
-  MOCK_CONST_METHOD0(MockGetState, base::DictionaryValue*());
   MOCK_CONST_METHOD0(GetGcdState, GcdState());
   MOCK_METHOD1(AddGcdStateChangedCallback,
                void(const GcdStateChangedCallback& callback));
@@ -51,14 +61,23 @@
                void(const PairingBeginCallback& begin_callback,
                     const PairingEndCallback& end_callback));
 
-  // Gmock 1.7.0 does not work with unuque_ptr as return value.
-  std::unique_ptr<base::Value> GetStateProperty(
-      const std::string& name) const override {
-    return std::unique_ptr<base::Value>(MockGetStateProperty(name));
-  }
-  std::unique_ptr<base::DictionaryValue> GetState() const override {
-    return std::unique_ptr<base::DictionaryValue>(MockGetState());
-  }
+  // Deprecated methods.
+  MOCK_METHOD1(AddCommandDefinitionsFromJson, void(const std::string&));
+  MOCK_METHOD1(AddCommandDefinitions, void(const base::DictionaryValue&));
+  MOCK_METHOD2(AddCommandHandler,
+               void(const std::string&, const CommandHandlerCallback&));
+  MOCK_METHOD1(AddStateDefinitionsFromJson, void(const std::string&));
+  MOCK_METHOD1(AddStateDefinitions, void(const base::DictionaryValue&));
+  MOCK_METHOD2(SetStatePropertiesFromJson, bool(const std::string&, ErrorPtr*));
+  MOCK_METHOD2(SetStateProperties,
+               bool(const base::DictionaryValue&, ErrorPtr*));
+  MOCK_CONST_METHOD1(GetStateProperty,
+                     const base::Value*(const std::string& name));
+  MOCK_METHOD3(SetStateProperty,
+               bool(const std::string& name,
+                    const base::Value& value,
+                    ErrorPtr* error));
+  MOCK_CONST_METHOD0(GetState, const base::DictionaryValue&());
 };
 
 }  // namespace test
diff --git a/libweave.gypi b/libweave.gypi
index c32e99b..b529add 100644
--- a/libweave.gypi
+++ b/libweave.gypi
@@ -7,17 +7,10 @@
       'src/backoff_entry.cc',
       'src/base_api_handler.cc',
       'src/commands/cloud_command_proxy.cc',
-      'src/commands/command_definition.cc',
-      'src/commands/command_dictionary.cc',
       'src/commands/command_instance.cc',
-      'src/commands/command_manager.cc',
       'src/commands/command_queue.cc',
-      'src/commands/object_schema.cc',
-      'src/commands/prop_constraints.cc',
-      'src/commands/prop_types.cc',
-      'src/commands/prop_values.cc',
       'src/commands/schema_constants.cc',
-      'src/commands/schema_utils.cc',
+      'src/component_manager_impl.cc',
       'src/config.cc',
       'src/data_encoding.cc',
       'src/device_manager.cc',
@@ -31,6 +24,7 @@
       'src/notification/xmpp_channel.cc',
       'src/notification/xmpp_iq_stanza_handler.cc',
       'src/notification/xmpp_stream_parser.cc',
+      'src/privet/auth_manager.cc',
       'src/privet/cloud_delegate.cc',
       'src/privet/constants.cc',
       'src/privet/device_delegate.cc',
@@ -44,35 +38,33 @@
       'src/privet/wifi_bootstrap_manager.cc',
       'src/privet/wifi_ssid_generator.cc',
       'src/registration_status.cc',
-      'src/states/error_codes.cc',
       'src/states/state_change_queue.cc',
-      'src/states/state_manager.cc',
-      'src/states/state_package.cc',
       'src/streams.cc',
       'src/string_utils.cc',
       'src/utils.cc',
       'third_party/chromium/crypto/p224.cc',
       'third_party/chromium/crypto/p224_spake.cc',
       'third_party/chromium/crypto/sha2.cc',
+      'third_party/libuweave/src/crypto_hmac.c',
+      'third_party/libuweave/src/crypto_utils.c',
+      'third_party/libuweave/src/macaroon.c',
+      'third_party/libuweave/src/macaroon_caveat.c',
+      'third_party/libuweave/src/macaroon_context.c',
+      'third_party/libuweave/src/macaroon_encoding.c',
       'third_party/modp_b64/modp_b64.cc',
     ],
     'weave_test_sources': [
       'src/test/fake_stream.cc',
       'src/test/fake_task_runner.cc',
-      'src/test/mock_command.cc',
       'src/test/unittest_utils.cc',
     ],
     'weave_unittest_sources': [
       'src/backoff_entry_unittest.cc',
       'src/base_api_handler_unittest.cc',
       'src/commands/cloud_command_proxy_unittest.cc',
-      'src/commands/command_definition_unittest.cc',
-      'src/commands/command_dictionary_unittest.cc',
       'src/commands/command_instance_unittest.cc',
-      'src/commands/command_manager_unittest.cc',
       'src/commands/command_queue_unittest.cc',
-      'src/commands/object_schema_unittest.cc',
-      'src/commands/schema_utils_unittest.cc',
+      'src/component_manager_unittest.cc',
       'src/config_unittest.cc',
       'src/data_encoding_unittest.cc',
       'src/device_registration_info_unittest.cc',
@@ -82,12 +74,11 @@
       'src/notification/xmpp_channel_unittest.cc',
       'src/notification/xmpp_iq_stanza_handler_unittest.cc',
       'src/notification/xmpp_stream_parser_unittest.cc',
+      'src/privet/auth_manager_unittest.cc',
       'src/privet/privet_handler_unittest.cc',
       'src/privet/security_manager_unittest.cc',
       'src/privet/wifi_ssid_generator_unittest.cc',
       'src/states/state_change_queue_unittest.cc',
-      'src/states/state_manager_unittest.cc',
-      'src/states/state_package_unittest.cc',
       'src/streams_unittest.cc',
       'src/string_utils_unittest.cc',
       'src/test/weave_testrunner.cc',
diff --git a/libweave_common.gypi b/libweave_common.gypi
index 73d02fd..e4251d8 100644
--- a/libweave_common.gypi
+++ b/libweave_common.gypi
@@ -17,7 +17,7 @@
           '_DEBUG',
         ],
         'cflags': [
-          '-Og',
+          '-O0  ',
           '-g3',
         ],
       },
@@ -27,6 +27,7 @@
       'include',
       'third_party/chromium',
       'third_party/include',
+      'third_party/libuweave',
       'third_party/modp_b64/modp_b64',
     ],
     'cflags!': ['-fPIE'],
@@ -34,7 +35,6 @@
       '-fno-exceptions',
       '-fPIC',
       '-fvisibility=hidden',
-      '-std=c++11',
       '-Wall',
       '-Werror',
       '-Wextra',
@@ -48,6 +48,16 @@
       '-Wpointer-arith',
       '-Wwrite-strings',
     ],
+    'cflags_cc': [
+      '-std=c++11',
+    ],
+    'cflags_c': [
+      '-std=c99',
+    ],
+    'libraries': [
+      # 'library_dirs' does not work as expected with make files
+      '-Lthird_party/lib',
+    ],
     'library_dirs': ['third_party/lib']
   },
 }
diff --git a/libweave_standalone.gyp b/libweave_standalone.gyp
index fd87f16..d36d208 100644
--- a/libweave_standalone.gyp
+++ b/libweave_standalone.gyp
@@ -8,10 +8,11 @@
   'target_defaults': {
     'libraries': [
       '-lcrypto',
-      '-lgtest',
-      '-lgmock',
       '-lexpat',
+      '-lgmock',
+      '-lgtest',
       '-lpthread',
+      '-lrt',
     ],
   },
   'targets': [
diff --git a/src/backoff_entry.h b/src/backoff_entry.h
index 2df0d8a..002fb8d 100644
--- a/src/backoff_entry.h
+++ b/src/backoff_entry.h
@@ -57,7 +57,7 @@
   // Lifetime of policy must enclose lifetime of BackoffEntry. The
   // pointer must be valid but is not dereferenced during construction.
   explicit BackoffEntry(const Policy* const policy);
-  virtual ~BackoffEntry() = default;
+  virtual ~BackoffEntry() {}
 
   // Inform this item that a request for the network resource it is
   // tracking was made, and whether it failed or succeeded.
diff --git a/src/base_api_handler.cc b/src/base_api_handler.cc
index 3d22a10..6808949 100644
--- a/src/base_api_handler.cc
+++ b/src/base_api_handler.cc
@@ -13,6 +13,8 @@
 namespace weave {
 
 namespace {
+const char kBaseComponent[] = "weave";
+const char kBaseTrait[] = "base";
 const char kBaseStateFirmwareVersion[] = "base.firmwareVersion";
 const char kBaseStateAnonymousAccessRole[] = "base.localAnonymousAccessMaxRole";
 const char kBaseStateDiscoveryEnabled[] = "base.localDiscoveryEnabled";
@@ -22,61 +24,64 @@
 BaseApiHandler::BaseApiHandler(DeviceRegistrationInfo* device_info,
                                Device* device)
     : device_info_{device_info}, device_{device} {
-  device_->AddStateDefinitionsFromJson(R"({
+  device_->AddTraitDefinitionsFromJson(R"({
     "base": {
-      "firmwareVersion": "string",
-      "localDiscoveryEnabled": "boolean",
-      "localAnonymousAccessMaxRole": [ "none", "viewer", "user" ],
-      "localPairingEnabled": "boolean"
+      "commands": {
+        "updateBaseConfiguration": {
+          "minimalRole": "manager",
+          "parameters": {
+            "localAnonymousAccessMaxRole": {
+              "enum": [ "none", "viewer", "user" ],
+              "type": "string"
+            },
+            "localDiscoveryEnabled": {
+              "type": "boolean"
+            },
+            "localPairingEnabled": {
+              "type": "boolean"
+            }
+          }
+        },
+        "updateDeviceInfo": {
+          "minimalRole": "manager",
+          "parameters": {
+            "description": {
+              "type": "string"
+            },
+            "location": {
+              "type": "string"
+            },
+            "name": {
+              "type": "string"
+            }
+          }
+        }
+      },
+      "state": {
+        "firmwareVersion": "string",
+        "localDiscoveryEnabled": "boolean",
+        "localAnonymousAccessMaxRole": [ "none", "viewer", "user" ],
+        "localPairingEnabled": "boolean"
+      }
     }
   })");
+  CHECK(device_->AddComponent(kBaseComponent, {kBaseTrait}, nullptr));
   OnConfigChanged(device_->GetSettings());
 
   const auto& settings = device_info_->GetSettings();
   base::DictionaryValue state;
   state.SetString(kBaseStateFirmwareVersion, settings.firmware_version);
-  CHECK(device_->SetStateProperties(state, nullptr));
-
-  device->AddCommandDefinitionsFromJson(R"({
-    "base": {
-      "updateBaseConfiguration": {
-        "minimalRole": "manager",
-        "parameters": {
-          "localAnonymousAccessMaxRole": {
-            "enum": [ "none", "viewer", "user" ],
-            "type": "string"
-          },
-          "localDiscoveryEnabled": {
-            "type": "boolean"
-          },
-          "localPairingEnabled": {
-            "type": "boolean"
-          }
-        }
-      },
-      "updateDeviceInfo": {
-        "minimalRole": "manager",
-        "parameters": {
-          "description": {
-            "type": "string"
-          },
-          "location": {
-            "type": "string"
-          },
-          "name": {
-            "type": "string"
-          }
-        }
-      }
-    }
-  })");
+  CHECK(device_->SetStateProperty(kBaseComponent, kBaseStateFirmwareVersion,
+                                  base::StringValue{settings.firmware_version},
+                                  nullptr));
 
   device_->AddCommandHandler(
+      kBaseComponent,
       "base.updateBaseConfiguration",
       base::Bind(&BaseApiHandler::UpdateBaseConfiguration,
                  weak_ptr_factory_.GetWeakPtr()));
 
-  device_->AddCommandHandler("base.updateDeviceInfo",
+  device_->AddCommandHandler(kBaseComponent, "base.updateDeviceInfo",
                              base::Bind(&BaseApiHandler::UpdateDeviceInfo,
                                         weak_ptr_factory_.GetWeakPtr()));
 
@@ -99,10 +104,10 @@
   bool discovery_enabled{settings.local_discovery_enabled};
   bool pairing_enabled{settings.local_pairing_enabled};
 
-  auto parameters = command->GetParameters();
-  parameters->GetString("localAnonymousAccessMaxRole", &anonymous_access_role);
-  parameters->GetBoolean("localDiscoveryEnabled", &discovery_enabled);
-  parameters->GetBoolean("localPairingEnabled", &pairing_enabled);
+  const auto& parameters = command->GetParameters();
+  parameters.GetString("localAnonymousAccessMaxRole", &anonymous_access_role);
+  parameters.GetBoolean("localDiscoveryEnabled", &discovery_enabled);
+  parameters.GetBoolean("localPairingEnabled", &pairing_enabled);
 
   AuthScope auth_scope{AuthScope::kNone};
   if (!StringToEnum(anonymous_access_role, &auth_scope)) {
@@ -128,7 +133,7 @@
   state.SetBoolean(kBaseStateDiscoveryEnabled,
                    settings.local_discovery_enabled);
   state.SetBoolean(kBaseStatePairingEnabled, settings.local_pairing_enabled);
-  device_->SetStateProperties(state, nullptr);
+  device_->SetStateProperties(kBaseComponent, state, nullptr);
 }
 
 void BaseApiHandler::UpdateDeviceInfo(const std::weak_ptr<Command>& cmd) {
@@ -144,10 +149,10 @@
   std::string description{settings.description};
   std::string location{settings.location};
 
-  auto parameters = command->GetParameters();
-  parameters->GetString("name", &name);
-  parameters->GetString("description", &description);
-  parameters->GetString("location", &location);
+  const auto& parameters = command->GetParameters();
+  parameters.GetString("name", &name);
+  parameters.GetString("description", &description);
+  parameters.GetString("location", &location);
 
   device_info_->UpdateDeviceInfo(name, description, location);
   command->Complete({}, nullptr);
diff --git a/src/base_api_handler_unittest.cc b/src/base_api_handler_unittest.cc
index df78319..23ef95e 100644
--- a/src/base_api_handler_unittest.cc
+++ b/src/base_api_handler_unittest.cc
@@ -10,13 +10,11 @@
 #include <weave/provider/test/mock_config_store.h>
 #include <weave/provider/test/mock_http_client.h>
 #include <weave/test/mock_device.h>
+#include <weave/test/unittest_utils.h>
 
-#include "src/commands/command_manager.h"
-#include "src/commands/unittest_utils.h"
+#include "src/component_manager_impl.h"
 #include "src/config.h"
 #include "src/device_registration_info.h"
-#include "src/states/mock_state_change_queue_interface.h"
-#include "src/states/state_manager.h"
 
 using testing::_;
 using testing::AnyOf;
@@ -31,35 +29,33 @@
 class BaseApiHandlerTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    EXPECT_CALL(mock_state_change_queue_, MockNotifyPropertiesUpdated(_, _))
-        .WillRepeatedly(Return(true));
-
-    command_manager_ = std::make_shared<CommandManager>();
-
-    state_manager_ = std::make_shared<StateManager>(&mock_state_change_queue_);
-
-    EXPECT_CALL(device_, AddStateDefinitionsFromJson(_))
+    EXPECT_CALL(device_, AddTraitDefinitionsFromJson(_))
         .WillRepeatedly(Invoke([this](const std::string& json) {
-          EXPECT_TRUE(
-              state_manager_->LoadStateDefinitionFromJson(json, nullptr));
+          EXPECT_TRUE(component_manager_.LoadTraits(json, nullptr));
         }));
-    EXPECT_CALL(device_, SetStateProperties(_, _))
-        .WillRepeatedly(
-            Invoke(state_manager_.get(), &StateManager::SetProperties));
-    EXPECT_CALL(device_, AddCommandDefinitionsFromJson(_))
-        .WillRepeatedly(Invoke([this](const std::string& json) {
-          EXPECT_TRUE(command_manager_->LoadCommands(json, nullptr));
+    EXPECT_CALL(device_, SetStateProperties(_, _, _))
+        .WillRepeatedly(Invoke(&component_manager_,
+                               &ComponentManager::SetStateProperties));
+    EXPECT_CALL(device_, SetStateProperty(_, _, _, _))
+        .WillRepeatedly(Invoke(&component_manager_,
+                               &ComponentManager::SetStateProperty));
+    EXPECT_CALL(device_, AddComponent(_, _, _))
+        .WillRepeatedly(Invoke([this](const std::string& name,
+                                      const std::vector<std::string>& traits,
+                                      ErrorPtr* error) {
+          return component_manager_.AddComponent("", name, traits, error);
         }));
 
-    EXPECT_CALL(device_, AddCommandHandler(AnyOf("base.updateBaseConfiguration",
+    EXPECT_CALL(device_, AddCommandHandler(_,
+                                           AnyOf("base.updateBaseConfiguration",
                                                  "base.updateDeviceInfo"),
                                            _))
-        .WillRepeatedly(
-            Invoke(command_manager_.get(), &CommandManager::AddCommandHandler));
+        .WillRepeatedly(Invoke(&component_manager_,
+                               &ComponentManager::AddCommandHandler));
 
     std::unique_ptr<Config> config{new Config{&config_store_}};
     config->Load();
-    dev_reg_.reset(new DeviceRegistrationInfo(command_manager_, state_manager_,
+    dev_reg_.reset(new DeviceRegistrationInfo(&component_manager_,
                                               std::move(config), nullptr,
                                               &http_client_, nullptr));
 
@@ -70,47 +66,44 @@
   }
 
   void AddCommand(const std::string& command) {
-    auto command_instance = CommandInstance::FromJson(
-        test::CreateDictionaryValue(command.c_str()).get(),
-        Command::Origin::kLocal, command_manager_->GetCommandDictionary(),
-        nullptr, nullptr);
-    EXPECT_TRUE(!!command_instance);
-
-    std::string id{base::IntToString(++command_id_)};
-    command_instance->SetID(id);
-    command_manager_->AddCommand(std::move(command_instance));
+    std::string id;
+    auto command_instance = component_manager_.ParseCommandInstance(
+        *test::CreateDictionaryValue(command.c_str()), Command::Origin::kLocal,
+        UserRole::kOwner, &id, nullptr);
+    ASSERT_NE(nullptr, command_instance.get());
+    component_manager_.AddCommand(std::move(command_instance));
     EXPECT_EQ(Command::State::kDone,
-              command_manager_->FindCommand(id)->GetState());
+              component_manager_.FindCommand(id)->GetState());
   }
 
   std::unique_ptr<base::DictionaryValue> GetBaseState() {
-    auto state = state_manager_->GetState();
-    std::set<std::string> result;
-    for (base::DictionaryValue::Iterator it{*state}; !it.IsAtEnd();
-         it.Advance()) {
-      if (it.key() != "base")
-        state->Remove(it.key(), nullptr);
-    }
+    std::unique_ptr<base::DictionaryValue> state;
+    std::string path = component_manager_.FindComponentWithTrait("base");
+    EXPECT_FALSE(path.empty());
+    const auto* component = component_manager_.FindComponent(path, nullptr);
+    CHECK(component);
+    const base::DictionaryValue* base_state = nullptr;
+    if (component->GetDictionary("state.base", &base_state))
+      state.reset(base_state->DeepCopy());
+    else
+      state.reset(new base::DictionaryValue);
     return state;
   }
 
   provider::test::MockConfigStore config_store_;
   StrictMock<provider::test::MockHttpClient> http_client_;
   std::unique_ptr<DeviceRegistrationInfo> dev_reg_;
-  std::shared_ptr<CommandManager> command_manager_;
-  testing::StrictMock<MockStateChangeQueueInterface> mock_state_change_queue_;
-  std::shared_ptr<StateManager> state_manager_;
+  ComponentManagerImpl component_manager_;
   std::unique_ptr<BaseApiHandler> handler_;
   StrictMock<test::MockDevice> device_;
-  int command_id_{0};
 };
 
 TEST_F(BaseApiHandlerTest, Initialization) {
-  auto command_defs =
-      command_manager_->GetCommandDictionary().GetCommandsAsJson(nullptr);
+  const base::DictionaryValue* trait = nullptr;
+  ASSERT_TRUE(component_manager_.GetTraits().GetDictionary("base", &trait));
 
   auto expected = R"({
-    "base": {
+    "commands": {
       "updateBaseConfiguration": {
         "minimalRole": "manager",
         "parameters": {
@@ -140,9 +133,15 @@
           }
         }
       }
-    }
+    },
+   "state": {
+      "firmwareVersion": "string",
+      "localAnonymousAccessMaxRole": [ "none", "viewer", "user" ],
+      "localDiscoveryEnabled": "boolean",
+      "localPairingEnabled": "boolean"
+   }
   })";
-  EXPECT_JSON_EQ(expected, *command_defs);
+  EXPECT_JSON_EQ(expected, *trait);
 }
 
 TEST_F(BaseApiHandlerTest, UpdateBaseConfiguration) {
@@ -150,6 +149,7 @@
 
   AddCommand(R"({
     'name' : 'base.updateBaseConfiguration',
+    'component': 'weave',
     'parameters': {
       'localDiscoveryEnabled': false,
       'localAnonymousAccessMaxRole': 'none',
@@ -161,17 +161,16 @@
   EXPECT_FALSE(settings.local_pairing_enabled);
 
   auto expected = R"({
-    'base': {
-      'firmwareVersion': 'TEST_FIRMWARE',
-      'localAnonymousAccessMaxRole': 'none',
-      'localDiscoveryEnabled': false,
-      'localPairingEnabled': false
-    }
+    'firmwareVersion': 'TEST_FIRMWARE',
+    'localAnonymousAccessMaxRole': 'none',
+    'localDiscoveryEnabled': false,
+    'localPairingEnabled': false
   })";
   EXPECT_JSON_EQ(expected, *GetBaseState());
 
   AddCommand(R"({
     'name' : 'base.updateBaseConfiguration',
+    'component': 'weave',
     'parameters': {
       'localDiscoveryEnabled': true,
       'localAnonymousAccessMaxRole': 'user',
@@ -182,12 +181,10 @@
   EXPECT_TRUE(settings.local_discovery_enabled);
   EXPECT_TRUE(settings.local_pairing_enabled);
   expected = R"({
-    'base': {
-      'firmwareVersion': 'TEST_FIRMWARE',
-      'localAnonymousAccessMaxRole': 'user',
-      'localDiscoveryEnabled': true,
-      'localPairingEnabled': true
-    }
+    'firmwareVersion': 'TEST_FIRMWARE',
+    'localAnonymousAccessMaxRole': 'user',
+    'localDiscoveryEnabled': true,
+    'localPairingEnabled': true
   })";
   EXPECT_JSON_EQ(expected, *GetBaseState());
 
@@ -196,12 +193,10 @@
     change.set_local_anonymous_access_role(AuthScope::kViewer);
   }
   expected = R"({
-    'base': {
-      'firmwareVersion': 'TEST_FIRMWARE',
-      'localAnonymousAccessMaxRole': 'viewer',
-      'localDiscoveryEnabled': true,
-      'localPairingEnabled': true
-    }
+    'firmwareVersion': 'TEST_FIRMWARE',
+    'localAnonymousAccessMaxRole': 'viewer',
+    'localDiscoveryEnabled': true,
+    'localPairingEnabled': true
   })";
   EXPECT_JSON_EQ(expected, *GetBaseState());
 }
@@ -209,6 +204,7 @@
 TEST_F(BaseApiHandlerTest, UpdateDeviceInfo) {
   AddCommand(R"({
     'name' : 'base.updateDeviceInfo',
+    'component': 'weave',
     'parameters': {
       'name': 'testName',
       'description': 'testDescription',
@@ -223,6 +219,7 @@
 
   AddCommand(R"({
     'name' : 'base.updateDeviceInfo',
+    'component': 'weave',
     'parameters': {
       'location': 'newLocation'
     }
diff --git a/src/commands/cloud_command_proxy.cc b/src/commands/cloud_command_proxy.cc
index 9ec3d3e..f8f8d1f 100644
--- a/src/commands/cloud_command_proxy.cc
+++ b/src/commands/cloud_command_proxy.cc
@@ -9,8 +9,6 @@
 #include <weave/provider/task_runner.h>
 
 #include "src/commands/command_instance.h"
-#include "src/commands/prop_constraints.h"
-#include "src/commands/prop_types.h"
 #include "src/commands/schema_constants.h"
 #include "src/utils.h"
 
@@ -19,15 +17,15 @@
 CloudCommandProxy::CloudCommandProxy(
     CommandInstance* command_instance,
     CloudCommandUpdateInterface* cloud_command_updater,
-    StateChangeQueueInterface* state_change_queue,
+    ComponentManager* component_manager,
     std::unique_ptr<BackoffEntry> backoff_entry,
     provider::TaskRunner* task_runner)
     : command_instance_{command_instance},
       cloud_command_updater_{cloud_command_updater},
-      state_change_queue_{state_change_queue},
+      component_manager_{component_manager},
       task_runner_{task_runner},
       cloud_backoff_entry_{std::move(backoff_entry)} {
-  callback_token_ = state_change_queue_->AddOnStateUpdatedCallback(
+  callback_token_ = component_manager_->AddServerStateUpdatedCallback(
       base::Bind(&CloudCommandProxy::OnDeviceStateUpdated,
                  weak_ptr_factory_.GetWeakPtr()));
   observer_.Add(command_instance);
@@ -45,7 +43,7 @@
 void CloudCommandProxy::OnResultsChanged() {
   std::unique_ptr<base::DictionaryValue> patch{new base::DictionaryValue};
   patch->Set(commands::attributes::kCommand_Results,
-             command_instance_->GetResults().release());
+             command_instance_->GetResults().CreateDeepCopy());
   QueueCommandUpdate(std::move(patch));
 }
 
@@ -59,7 +57,7 @@
 void CloudCommandProxy::OnProgressChanged() {
   std::unique_ptr<base::DictionaryValue> patch{new base::DictionaryValue};
   patch->Set(commands::attributes::kCommand_Progress,
-             command_instance_->GetProgress().release());
+             command_instance_->GetProgress().CreateDeepCopy());
   QueueCommandUpdate(std::move(patch));
 }
 
@@ -69,7 +67,7 @@
 
 void CloudCommandProxy::QueueCommandUpdate(
     std::unique_ptr<base::DictionaryValue> patch) {
-  UpdateID id = state_change_queue_->GetLastStateChangeId();
+  ComponentManager::UpdateID id = component_manager_->GetLastStateChangeId();
   if (update_queue_.empty() || update_queue_.back().first != id) {
     // If queue is currently empty or the device state has changed since the
     // last patch request queued, add a new request to the queue.
@@ -160,7 +158,8 @@
   SendCommandUpdate();
 }
 
-void CloudCommandProxy::OnDeviceStateUpdated(UpdateID update_id) {
+void CloudCommandProxy::OnDeviceStateUpdated(
+    ComponentManager::UpdateID update_id) {
   last_state_update_id_ = update_id;
   // Try to send out any queued command updates that could be performed after
   // a device state is updated.
diff --git a/src/commands/cloud_command_proxy.h b/src/commands/cloud_command_proxy.h
index ee6358f..13f4654 100644
--- a/src/commands/cloud_command_proxy.h
+++ b/src/commands/cloud_command_proxy.h
@@ -18,7 +18,7 @@
 #include "src/backoff_entry.h"
 #include "src/commands/cloud_command_update_interface.h"
 #include "src/commands/command_instance.h"
-#include "src/states/state_change_queue_interface.h"
+#include "src/component_manager.h"
 
 namespace weave {
 
@@ -33,7 +33,7 @@
  public:
   CloudCommandProxy(CommandInstance* command_instance,
                     CloudCommandUpdateInterface* cloud_command_updater,
-                    StateChangeQueueInterface* state_change_queue,
+                    ComponentManager* component_manager,
                     std::unique_ptr<BackoffEntry> backoff_entry,
                     provider::TaskRunner* task_runner);
   ~CloudCommandProxy() override = default;
@@ -46,10 +46,8 @@
   void OnStateChanged() override;
 
  private:
-  using UpdateID = StateChangeQueueInterface::UpdateID;
-  using UpdateQueueEntry =
-      std::pair<UpdateID, std::unique_ptr<base::DictionaryValue>>;
-
+  using UpdateQueueEntry = std::pair<ComponentManager::UpdateID,
+                                     std::unique_ptr<base::DictionaryValue>>;
   // Puts a command update data into the update queue, and optionally sends an
   // asynchronous request to GCD server to update the command resource, if there
   // are no pending device status updates.
@@ -68,11 +66,11 @@
   // Callback invoked by the device state change queue to notify of the
   // successful device state update. |update_id| is the ID of the state that
   // has been updated on the server.
-  void OnDeviceStateUpdated(UpdateID update_id);
+  void OnDeviceStateUpdated(ComponentManager::UpdateID update_id);
 
   CommandInstance* command_instance_;
   CloudCommandUpdateInterface* cloud_command_updater_;
-  StateChangeQueueInterface* state_change_queue_;
+  ComponentManager* component_manager_;
   provider::TaskRunner* task_runner_{nullptr};
 
   // Backoff for SendCommandUpdate() method.
@@ -87,11 +85,11 @@
   // Callback token from the state change queue for OnDeviceStateUpdated()
   // callback for ask the device state change queue to call when the state
   // is updated on the server.
-  StateChangeQueueInterface::Token callback_token_;
+  ComponentManager::Token callback_token_;
 
   // Last device state update ID that has been sent out to the server
   // successfully.
-  UpdateID last_state_update_id_{0};
+  ComponentManager::UpdateID last_state_update_id_{0};
 
   ScopedObserver<CommandInstance, CommandInstance::Observer> observer_{this};
 
diff --git a/src/commands/cloud_command_proxy_unittest.cc b/src/commands/cloud_command_proxy_unittest.cc
index fdb22fc..d3a9965 100644
--- a/src/commands/cloud_command_proxy_unittest.cc
+++ b/src/commands/cloud_command_proxy_unittest.cc
@@ -10,11 +10,10 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <weave/provider/test/fake_task_runner.h>
+#include <weave/test/unittest_utils.h>
 
-#include "src/commands/command_dictionary.h"
 #include "src/commands/command_instance.h"
-#include "src/commands/unittest_utils.h"
-#include "src/states/mock_state_change_queue_interface.h"
+#include "src/mock_component_manager.h"
 
 using testing::_;
 using testing::DoAll;
@@ -66,37 +65,16 @@
 class CloudCommandProxyTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    // Set up the test StateChangeQueue.
-    auto callback = [this](
-        const base::Callback<void(StateChangeQueueInterface::UpdateID)>& call) {
+    // Set up the test ComponentManager.
+    auto callback =
+        [this](const base::Callback<void(ComponentManager::UpdateID)>& call) {
       return callbacks_.Add(call).release();
     };
-    EXPECT_CALL(state_change_queue_, MockAddOnStateUpdatedCallback(_))
+    EXPECT_CALL(component_manager_, MockAddServerStateUpdatedCallback(_))
         .WillRepeatedly(Invoke(callback));
-    EXPECT_CALL(state_change_queue_, GetLastStateChangeId())
+    EXPECT_CALL(component_manager_, GetLastStateChangeId())
         .WillRepeatedly(testing::ReturnPointee(&current_state_update_id_));
 
-    // Set up the command schema.
-    auto json = CreateDictionaryValue(R"({
-      'calc': {
-        'add': {
-          'parameters': {
-            'value1': 'integer',
-            'value2': 'integer'
-          },
-          'progress': {
-            'status' : 'string'
-          },
-          'results': {
-            'sum' : 'integer'
-          }
-        }
-      }
-    })");
-    CHECK(json.get());
-    CHECK(command_dictionary_.LoadCommands(*json, nullptr))
-        << "Failed to parse test command dictionary";
-
     CreateCommandInstance();
   }
 
@@ -111,9 +89,8 @@
     })");
     CHECK(command_json.get());
 
-    command_instance_ =
-        CommandInstance::FromJson(command_json.get(), Command::Origin::kCloud,
-                                  command_dictionary_, nullptr, nullptr);
+    command_instance_ = CommandInstance::FromJson(
+        command_json.get(), Command::Origin::kCloud, nullptr, nullptr);
     CHECK(command_instance_.get());
 
     // Backoff - start at 1s and double with each backoff attempt and no jitter.
@@ -124,7 +101,7 @@
 
     // Finally construct the CloudCommandProxy we are going to test here.
     std::unique_ptr<CloudCommandProxy> proxy{new CloudCommandProxy{
-        command_instance_.get(), &cloud_updater_, &state_change_queue_,
+        command_instance_.get(), &cloud_updater_, &component_manager_,
         std::move(backoff), &task_runner_}};
     // CloudCommandProxy::CloudCommandProxy() subscribe itself to weave::Command
     // notifications. When weave::Command is being destroyed it sends
@@ -132,13 +109,12 @@
     proxy.release();
   }
 
-  StateChangeQueueInterface::UpdateID current_state_update_id_{0};
-  base::CallbackList<void(StateChangeQueueInterface::UpdateID)> callbacks_;
+  ComponentManager::UpdateID current_state_update_id_{0};
+  base::CallbackList<void(ComponentManager::UpdateID)> callbacks_;
   testing::StrictMock<MockCloudCommandUpdateInterface> cloud_updater_;
-  testing::StrictMock<MockStateChangeQueueInterface> state_change_queue_;
+  testing::StrictMock<MockComponentManager> component_manager_;
   testing::StrictMock<provider::test::FakeTaskRunner> task_runner_;
   std::queue<base::Closure> task_queue_;
-  CommandDictionary command_dictionary_;
   std::unique_ptr<CommandInstance> command_instance_;
 };
 
diff --git a/src/commands/cloud_command_update_interface.h b/src/commands/cloud_command_update_interface.h
index 9538960..ed3aa7a 100644
--- a/src/commands/cloud_command_update_interface.h
+++ b/src/commands/cloud_command_update_interface.h
@@ -21,7 +21,7 @@
                              const DoneCallback& callback) = 0;
 
  protected:
-  virtual ~CloudCommandUpdateInterface() = default;
+  virtual ~CloudCommandUpdateInterface() {}
 };
 
 }  // namespace weave
diff --git a/src/commands/command_definition.cc b/src/commands/command_definition.cc
deleted file mode 100644
index dbae630..0000000
--- a/src/commands/command_definition.cc
+++ /dev/null
@@ -1,58 +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 "src/commands/command_definition.h"
-
-#include <vector>
-
-#include <weave/error.h>
-#include <weave/enum_to_string.h>
-
-#include "src/commands/schema_constants.h"
-#include "src/string_utils.h"
-
-namespace weave {
-
-namespace {
-
-const EnumToStringMap<UserRole>::Map kMap[] = {
-    {UserRole::kViewer, commands::attributes::kCommand_Role_Viewer},
-    {UserRole::kUser, commands::attributes::kCommand_Role_User},
-    {UserRole::kOwner, commands::attributes::kCommand_Role_Owner},
-    {UserRole::kManager, commands::attributes::kCommand_Role_Manager},
-};
-}
-
-template <>
-LIBWEAVE_EXPORT EnumToStringMap<UserRole>::EnumToStringMap()
-    : EnumToStringMap(kMap) {}
-
-CommandDefinition::CommandDefinition(const base::DictionaryValue& definition,
-                                     UserRole minimal_role)
-    : minimal_role_{minimal_role} {
-  definition_.MergeDictionary(&definition);
-}
-
-std::unique_ptr<CommandDefinition> CommandDefinition::FromJson(
-    const base::DictionaryValue& dict, ErrorPtr* error) {
-  std::unique_ptr<CommandDefinition> definition;
-  // Validate the 'minimalRole' value if present. That's the only thing we
-  // care about so far.
-  std::string value;
-  UserRole minimal_role;
-  if (dict.GetString(commands::attributes::kCommand_Role, &value)) {
-    if (!StringToEnum(value, &minimal_role)) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                          errors::commands::kInvalidPropValue,
-                          "Invalid role: '%s'", value.c_str());
-      return definition;
-    }
-  } else {
-    minimal_role = UserRole::kUser;
-  }
-  definition.reset(new CommandDefinition{dict, minimal_role});
-  return definition;
-}
-
-}  // namespace weave
diff --git a/src/commands/command_definition.h b/src/commands/command_definition.h
deleted file mode 100644
index da02bf5..0000000
--- a/src/commands/command_definition.h
+++ /dev/null
@@ -1,47 +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_SRC_COMMANDS_COMMAND_DEFINITION_H_
-#define LIBWEAVE_SRC_COMMANDS_COMMAND_DEFINITION_H_
-
-#include <memory>
-#include <string>
-
-#include <base/macros.h>
-#include <base/values.h>
-#include <weave/error.h>
-
-namespace weave {
-
-enum class UserRole {
-  kViewer,
-  kUser,
-  kManager,
-  kOwner,
-};
-
-// A simple GCD command definition. This class contains the full object schema
-// describing the command parameter types and constraints.
-class CommandDefinition final {
- public:
-  // Factory method to construct a command definition from a JSON dictionary.
-  static std::unique_ptr<CommandDefinition> FromJson(
-      const base::DictionaryValue& dict, ErrorPtr* error);
-  const base::DictionaryValue& ToJson() const { return definition_; }
-  // Returns the role required to execute command.
-  UserRole GetMinimalRole() const { return minimal_role_; }
-
- private:
-  CommandDefinition(const base::DictionaryValue& definition,
-                    UserRole minimal_role);
-
-  base::DictionaryValue definition_;
-  UserRole minimal_role_;
-
-  DISALLOW_COPY_AND_ASSIGN(CommandDefinition);
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_COMMAND_DEFINITION_H_
diff --git a/src/commands/command_definition_unittest.cc b/src/commands/command_definition_unittest.cc
deleted file mode 100644
index ecd6e54..0000000
--- a/src/commands/command_definition_unittest.cc
+++ /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.
-
-#include "src/commands/command_definition.h"
-
-#include <gtest/gtest.h>
-
-#include "src/commands/unittest_utils.h"
-
-namespace weave {
-
-using test::CreateDictionaryValue;
-
-TEST(CommandDefinition, DefaultRole) {
-  auto params = CreateDictionaryValue(R"({
-    'parameters': {
-      'height': 'integer',
-      'jumpType': ['_withAirFlip', '_withSpin', '_withKick']
-    },
-    'progress': {'progress': 'integer'},
-    'results': {'testResult': 'integer'}
-  })");
-  auto def = CommandDefinition::FromJson(*params, nullptr);
-  EXPECT_EQ(UserRole::kUser, def->GetMinimalRole());
-}
-
-TEST(CommandDefinition, SpecifiedRole) {
-  auto params = CreateDictionaryValue(R"({
-    'parameters': {},
-    'progress': {},
-    'results': {},
-    'minimalRole': 'owner'
-  })");
-  auto def = CommandDefinition::FromJson(*params, nullptr);
-  EXPECT_EQ(UserRole::kOwner, def->GetMinimalRole());
-}
-
-TEST(CommandDefinition, IncorrectRole) {
-  auto params = CreateDictionaryValue(R"({
-    'parameters': {},
-    'progress': {},
-    'results': {},
-    'minimalRole': 'foo'
-  })");
-  ErrorPtr error;
-  auto def = CommandDefinition::FromJson(*params, &error);
-  EXPECT_EQ(nullptr, def.get());
-  EXPECT_EQ("invalid_parameter_value", error->GetCode());
-}
-
-}  // namespace weave
diff --git a/src/commands/command_dictionary.cc b/src/commands/command_dictionary.cc
deleted file mode 100644
index 8fb7f43..0000000
--- a/src/commands/command_dictionary.cc
+++ /dev/null
@@ -1,125 +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 "src/commands/command_dictionary.h"
-
-#include <base/values.h>
-#include <weave/enum_to_string.h>
-
-#include "src/commands/command_definition.h"
-#include "src/commands/schema_constants.h"
-#include "src/string_utils.h"
-
-namespace weave {
-
-bool CommandDictionary::LoadCommands(const base::DictionaryValue& json,
-                                     ErrorPtr* error) {
-  CommandMap new_defs;
-
-  // |json| contains a list of nested objects with the following structure:
-  // {"<pkg_name>": {"<cmd_name>": {"parameters": {object_schema}}, ...}, ...}
-  // Iterate over packages
-  base::DictionaryValue::Iterator package_iter(json);
-  while (!package_iter.IsAtEnd()) {
-    std::string package_name = package_iter.key();
-    const base::DictionaryValue* package_value = nullptr;
-    if (!package_iter.value().GetAsDictionary(&package_value)) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kTypeMismatch,
-                         "Expecting an object for package '%s'",
-                         package_name.c_str());
-      return false;
-    }
-    // Iterate over command definitions within the current package.
-    base::DictionaryValue::Iterator command_iter(*package_value);
-    while (!command_iter.IsAtEnd()) {
-      std::string command_name = command_iter.key();
-      if (command_name.empty()) {
-        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                           errors::commands::kInvalidCommandName,
-                           "Unnamed command encountered in package '%s'",
-                           package_name.c_str());
-        return false;
-      }
-      const base::DictionaryValue* command_def_json = nullptr;
-      if (!command_iter.value().GetAsDictionary(&command_def_json)) {
-        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                           errors::commands::kTypeMismatch,
-                           "Expecting an object for command '%s'",
-                           command_name.c_str());
-        return false;
-      }
-      // Construct the compound command name as "pkg_name.cmd_name".
-      std::string full_command_name = Join(".", package_name, command_name);
-
-      auto command_def = CommandDefinition::FromJson(*command_def_json, error);
-      if (!command_def) {
-        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                           errors::commands::kInvalidMinimalRole,
-                           "Error parsing command '%s'",
-                           full_command_name.c_str());
-        return false;
-      }
-
-      new_defs.emplace(full_command_name, std::move(command_def));
-      command_iter.Advance();
-    }
-    package_iter.Advance();
-  }
-
-  // Verify that newly loaded command definitions do not override existing
-  // definitions in another category. This is unlikely, but we don't want to let
-  // one vendor daemon to define the same commands already handled by another
-  // daemon on the same device.
-  for (const auto& pair : new_defs) {
-    auto iter = definitions_.find(pair.first);
-    CHECK(iter == definitions_.end()) << "Definition for command '"
-                                      << pair.first
-                                      << "' overrides an earlier definition";
-  }
-
-  // Insert new definitions into the global map.
-  for (auto& pair : new_defs)
-    definitions_.emplace(pair.first, std::move(pair.second));
-  return true;
-}
-
-std::unique_ptr<base::DictionaryValue> CommandDictionary::GetCommandsAsJson(
-    ErrorPtr* error) const {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
-  for (const auto& pair : definitions_) {
-    auto parts = SplitAtFirst(pair.first, ".", true);
-    const std::string& package_name = parts.first;
-    const std::string& command_name = parts.second;
-
-    base::DictionaryValue* package = nullptr;
-    if (!dict->GetDictionaryWithoutPathExpansion(package_name, &package)) {
-      // If this is the first time we encounter this package, create a JSON
-      // object for it.
-      package = new base::DictionaryValue;
-      dict->SetWithoutPathExpansion(package_name, package);
-    }
-    package->SetWithoutPathExpansion(command_name,
-                                     pair.second->ToJson().DeepCopy());
-  }
-  return dict;
-}
-
-const CommandDefinition* CommandDictionary::FindCommand(
-    const std::string& command_name) const {
-  auto pair = definitions_.find(command_name);
-  return (pair != definitions_.end()) ? pair->second.get() : nullptr;
-}
-
-CommandDefinition* CommandDictionary::FindCommand(
-    const std::string& command_name) {
-  auto pair = definitions_.find(command_name);
-  return (pair != definitions_.end()) ? pair->second.get() : nullptr;
-}
-
-void CommandDictionary::Clear() {
-  definitions_.clear();
-}
-
-}  // namespace weave
diff --git a/src/commands/command_dictionary.h b/src/commands/command_dictionary.h
deleted file mode 100644
index 03e080a..0000000
--- a/src/commands/command_dictionary.h
+++ /dev/null
@@ -1,67 +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_SRC_COMMANDS_COMMAND_DICTIONARY_H_
-#define LIBWEAVE_SRC_COMMANDS_COMMAND_DICTIONARY_H_
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <base/macros.h>
-#include <weave/error.h>
-
-#include "src/commands/command_definition.h"
-
-namespace base {
-class Value;
-class DictionaryValue;
-}  // namespace base
-
-namespace weave {
-
-// CommandDictionary is a wrapper around a map of command name and the
-// corresponding command definition schema. The command name (the key in
-// the map) is a compound name in a form of "package_name.command_name",
-// where "package_name" is a name of command package such as "base", "printers",
-// and others. So the full command name could be "base.reboot", for example.
-class CommandDictionary final {
- public:
-  CommandDictionary() = default;
-
-  // Loads command definitions from a JSON object. This is done at the daemon
-  // startup and whenever a device daemon decides to update its command list.
-  // |json| is a JSON dictionary that describes the complete commands. Optional
-  // Returns false on failure and |error| provides additional error information
-  // when provided.
-  bool LoadCommands(const base::DictionaryValue& json,
-                    ErrorPtr* error);
-  // Converts all the command definitions to a JSON object for CDD/Device
-  // draft.
-  // Returns empty unique_ptr in case of an error and fills in the additional
-  // error details in |error|.
-  std::unique_ptr<base::DictionaryValue> GetCommandsAsJson(
-      ErrorPtr* error) const;
-  // Returns the number of command definitions in the dictionary.
-  size_t GetSize() const { return definitions_.size(); }
-  // Checks if the dictionary has no command definitions.
-  bool IsEmpty() const { return definitions_.empty(); }
-  // Remove all the command definitions from the dictionary.
-  void Clear();
-  // Finds a definition for the given command.
-  const CommandDefinition* FindCommand(const std::string& command_name) const;
-  CommandDefinition* FindCommand(const std::string& command_name);
-
- private:
-  using CommandMap = std::map<std::string, std::unique_ptr<CommandDefinition>>;
-
-  CommandMap definitions_;  // List of all available command definitions.
-  DISALLOW_COPY_AND_ASSIGN(CommandDictionary);
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_COMMAND_DICTIONARY_H_
diff --git a/src/commands/command_dictionary_unittest.cc b/src/commands/command_dictionary_unittest.cc
deleted file mode 100644
index 5cecd76..0000000
--- a/src/commands/command_dictionary_unittest.cc
+++ /dev/null
@@ -1,182 +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 "src/commands/command_dictionary.h"
-
-#include <gtest/gtest.h>
-
-#include "src/commands/unittest_utils.h"
-
-namespace weave {
-
-using test::CreateDictionaryValue;
-using test::IsEqualValue;
-
-TEST(CommandDictionary, Empty) {
-  CommandDictionary dict;
-  EXPECT_TRUE(dict.IsEmpty());
-  EXPECT_EQ(nullptr, dict.FindCommand("robot.jump"));
-}
-
-TEST(CommandDictionary, LoadCommands) {
-  auto json = CreateDictionaryValue(R"({
-    'robot': {
-      'jump': {
-        'parameters': {
-          'height': 'integer',
-          '_jumpType': ['_withAirFlip', '_withSpin', '_withKick']
-        },
-        'progress': {
-          'progress': 'integer'
-        },
-        'results': {}
-      }
-    }
-  })");
-  CommandDictionary dict;
-  EXPECT_TRUE(dict.LoadCommands(*json, nullptr));
-  EXPECT_EQ(1, dict.GetSize());
-  EXPECT_NE(nullptr, dict.FindCommand("robot.jump"));
-  json = CreateDictionaryValue(R"({
-    'base': {
-      'reboot': {
-        'parameters': {'delay': 'integer'}
-      },
-      'shutdown': {
-      }
-    }
-  })");
-  EXPECT_TRUE(dict.LoadCommands(*json, nullptr));
-  EXPECT_EQ(3, dict.GetSize());
-  EXPECT_NE(nullptr, dict.FindCommand("robot.jump"));
-  EXPECT_NE(nullptr, dict.FindCommand("base.reboot"));
-  EXPECT_NE(nullptr, dict.FindCommand("base.shutdown"));
-  EXPECT_EQ(nullptr, dict.FindCommand("foo.bar"));
-}
-
-TEST(CommandDictionary, LoadCommands_Failures) {
-  CommandDictionary dict;
-  ErrorPtr error;
-
-  // Command definition is not an object.
-  auto json = CreateDictionaryValue("{'robot':{'jump':0}}");
-  EXPECT_FALSE(dict.LoadCommands(*json, &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-
-  // Package definition is not an object.
-  json = CreateDictionaryValue("{'robot':'blah'}");
-  EXPECT_FALSE(dict.LoadCommands(*json, &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-
-  // Empty command name.
-  json = CreateDictionaryValue("{'robot':{'':{'parameters':{},'results':{}}}}");
-  EXPECT_FALSE(dict.LoadCommands(*json, &error));
-  EXPECT_EQ("invalid_command_name", error->GetCode());
-  error.reset();
-}
-
-TEST(CommandDictionaryDeathTest, LoadCommands_Redefine) {
-  // Redefine commands.
-  CommandDictionary dict;
-  ErrorPtr error;
-  auto json = CreateDictionaryValue("{'robot':{'jump':{}}}");
-  dict.LoadCommands(*json, nullptr);
-  ASSERT_DEATH(dict.LoadCommands(*json, &error),
-               ".*Definition for command 'robot.jump' overrides an "
-               "earlier definition");
-}
-
-TEST(CommandDictionary, GetCommandsAsJson) {
-  auto json = CreateDictionaryValue(R"({
-    'base': {
-      'reboot': {
-        'parameters': {'delay': {'minimum': 10}},
-        'results': {}
-      }
-    },
-    'robot': {
-      '_jump': {
-        'parameters': {'_height': 'integer'},
-        'minimalRole': 'user'
-      }
-    }
-  })");
-  CommandDictionary dict;
-  dict.LoadCommands(*json, nullptr);
-
-  json = dict.GetCommandsAsJson(nullptr);
-  ASSERT_NE(nullptr, json.get());
-  auto expected = R"({
-    'base': {
-      'reboot': {
-        'parameters': {'delay': {'minimum': 10}},
-        'results': {}
-      }
-    },
-    'robot': {
-      '_jump': {
-        'parameters': {'_height': 'integer'},
-        'minimalRole': 'user'
-      }
-    }
-  })";
-  EXPECT_JSON_EQ(expected, *json);
-}
-
-TEST(CommandDictionary, LoadWithPermissions) {
-  CommandDictionary base_dict;
-  auto json = CreateDictionaryValue(R"({
-    'base': {
-      'command1': {
-        'parameters': {},
-        'results': {}
-      },
-      'command2': {
-        'minimalRole': 'viewer',
-        'parameters': {},
-        'results': {}
-      },
-      'command3': {
-        'minimalRole': 'user',
-        'parameters': {},
-        'results': {}
-      },
-      'command4': {
-        'minimalRole': 'manager',
-        'parameters': {},
-        'results': {}
-      },
-      'command5': {
-        'minimalRole': 'owner',
-        'parameters': {},
-        'results': {}
-      }
-    }
-  })");
-  EXPECT_TRUE(base_dict.LoadCommands(*json, nullptr));
-
-  auto cmd = base_dict.FindCommand("base.command1");
-  ASSERT_NE(nullptr, cmd);
-  EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole());
-
-  cmd = base_dict.FindCommand("base.command2");
-  ASSERT_NE(nullptr, cmd);
-  EXPECT_EQ(UserRole::kViewer, cmd->GetMinimalRole());
-
-  cmd = base_dict.FindCommand("base.command3");
-  ASSERT_NE(nullptr, cmd);
-  EXPECT_EQ(UserRole::kUser, cmd->GetMinimalRole());
-
-  cmd = base_dict.FindCommand("base.command4");
-  ASSERT_NE(nullptr, cmd);
-  EXPECT_EQ(UserRole::kManager, cmd->GetMinimalRole());
-
-  cmd = base_dict.FindCommand("base.command5");
-  ASSERT_NE(nullptr, cmd);
-  EXPECT_EQ(UserRole::kOwner, cmd->GetMinimalRole());
-}
-
-}  // namespace weave
diff --git a/src/commands/command_instance.cc b/src/commands/command_instance.cc
index aa71b0e..da62887 100644
--- a/src/commands/command_instance.cc
+++ b/src/commands/command_instance.cc
@@ -9,8 +9,6 @@
 #include <weave/error.h>
 #include <weave/export.h>
 
-#include "src/commands/command_definition.h"
-#include "src/commands/command_dictionary.h"
 #include "src/commands/command_queue.h"
 #include "src/commands/schema_constants.h"
 #include "src/json_error_codes.h"
@@ -36,13 +34,6 @@
     {Command::Origin::kCloud, "cloud"},
 };
 
-bool ReportDestroyedError(ErrorPtr* error) {
-  Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
-               errors::commands::kCommandDestroyed,
-               "Command has been destroyed");
-  return false;
-}
-
 bool ReportInvalidStateTransition(ErrorPtr* error,
                                   Command::State from,
                                   Command::State to) {
@@ -65,12 +56,8 @@
 
 CommandInstance::CommandInstance(const std::string& name,
                                  Command::Origin origin,
-                                 const CommandDefinition* command_definition,
                                  const base::DictionaryValue& parameters)
-    : name_{name},
-      origin_{origin},
-      command_definition_{command_definition} {
-  CHECK(command_definition_);
+    : name_{name}, origin_{origin} {
   parameters_.MergeDictionary(&parameters);
 }
 
@@ -86,6 +73,10 @@
   return name_;
 }
 
+const std::string& CommandInstance::GetComponent() const {
+  return component_;
+}
+
 Command::State CommandInstance::GetState() const {
   return state_;
 }
@@ -94,16 +85,16 @@
   return origin_;
 }
 
-std::unique_ptr<base::DictionaryValue> CommandInstance::GetParameters() const {
-  return std::unique_ptr<base::DictionaryValue>(parameters_.DeepCopy());
+const base::DictionaryValue& CommandInstance::GetParameters() const {
+  return parameters_;
 }
 
-std::unique_ptr<base::DictionaryValue> CommandInstance::GetProgress() const {
-  return std::unique_ptr<base::DictionaryValue>(progress_.DeepCopy());
+const base::DictionaryValue& CommandInstance::GetProgress() const {
+  return progress_;
 }
 
-std::unique_ptr<base::DictionaryValue> CommandInstance::GetResults() const {
-  return std::unique_ptr<base::DictionaryValue>(results_.DeepCopy());
+const base::DictionaryValue& CommandInstance::GetResults() const {
+  return results_;
 }
 
 const Error* CommandInstance::GetError() const {
@@ -148,14 +139,12 @@
 namespace {
 
 // Helper method to retrieve command parameters from the command definition
-// object passed in as |json| and corresponding command definition schema
-// specified in |command_def|.
+// object passed in as |json|.
 // On success, returns |true| and the validated parameters and values through
 // |parameters|. Otherwise returns |false| and additional error information in
 // |error|.
 std::unique_ptr<base::DictionaryValue> GetCommandParameters(
     const base::DictionaryValue* json,
-    const CommandDefinition* command_def,
     ErrorPtr* error) {
   // Get the command parameters from 'parameters' property.
   std::unique_ptr<base::DictionaryValue> params;
@@ -183,7 +172,6 @@
 std::unique_ptr<CommandInstance> CommandInstance::FromJson(
     const base::Value* value,
     Command::Origin origin,
-    const CommandDictionary& dictionary,
     std::string* command_id,
     ErrorPtr* error) {
   std::unique_ptr<CommandInstance> instance;
@@ -212,16 +200,8 @@
                  errors::commands::kPropertyMissing, "Command name is missing");
     return instance;
   }
-  // Make sure we know how to handle the command with this name.
-  auto command_def = dictionary.FindCommand(command_name);
-  if (!command_def) {
-    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                       errors::commands::kInvalidCommandName,
-                       "Unknown command received: %s", command_name.c_str());
-    return instance;
-  }
 
-  auto parameters = GetCommandParameters(json, command_def, error);
+  auto parameters = GetCommandParameters(json, error);
   if (!parameters) {
     Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
                        errors::commands::kCommandFailed,
@@ -229,12 +209,16 @@
     return instance;
   }
 
-  instance.reset(
-      new CommandInstance{command_name, origin, command_def, *parameters});
+  instance.reset(new CommandInstance{command_name, origin, *parameters});
 
   if (!command_id->empty())
     instance->SetID(*command_id);
 
+  // Get the component name this command is for.
+  std::string component;
+  if (json->GetString(commands::attributes::kCommand_Component, &component))
+    instance->SetComponent(component);
+
   return instance;
 }
 
diff --git a/src/commands/command_instance.h b/src/commands/command_instance.h
index 30ef907..15f3ac2 100644
--- a/src/commands/command_instance.h
+++ b/src/commands/command_instance.h
@@ -15,16 +15,12 @@
 #include <weave/error.h>
 #include <weave/command.h>
 
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_utils.h"
-
 namespace base {
 class Value;
 }  // namespace base
 
 namespace weave {
 
-class CommandDefinition;
 class CommandDictionary;
 class CommandObserver;
 class CommandQueue;
@@ -40,7 +36,7 @@
     virtual void OnStateChanged() = 0;
 
    protected:
-    virtual ~Observer() = default;
+    virtual ~Observer() {}
   };
 
   // Construct a command instance given the full command |name| which must
@@ -48,18 +44,18 @@
   // their values specified in |parameters|.
   CommandInstance(const std::string& name,
                   Command::Origin origin,
-                  const CommandDefinition* command_definition,
                   const base::DictionaryValue& parameters);
   ~CommandInstance() override;
 
   // Command overrides.
   const std::string& GetID() const override;
   const std::string& GetName() const override;
+  const std::string& GetComponent() const override;
   Command::State GetState() const override;
   Command::Origin GetOrigin() const override;
-  std::unique_ptr<base::DictionaryValue> GetParameters() const override;
-  std::unique_ptr<base::DictionaryValue> GetProgress() const override;
-  std::unique_ptr<base::DictionaryValue> GetResults() const override;
+  const base::DictionaryValue& GetParameters() const override;
+  const base::DictionaryValue& GetProgress() const override;
+  const base::DictionaryValue& GetResults() const override;
   const Error* GetError() const override;
   bool SetProgress(const base::DictionaryValue& progress,
                    ErrorPtr* error) override;
@@ -69,15 +65,9 @@
   bool Abort(const Error* command_error, ErrorPtr* error) override;
   bool Cancel(ErrorPtr* error) override;
 
-  // Returns command definition.
-  const CommandDefinition* GetCommandDefinition() const {
-    return command_definition_;
-  }
-
   // Parses a command instance JSON definition and constructs a CommandInstance
-  // object, checking the JSON |value| against the command definition schema
-  // found in command |dictionary|. On error, returns null unique_ptr and
-  // fills in error details in |error|.
+  // object.
+  // On error, returns null unique_ptr and fills in error details in |error|.
   // |command_id| is the ID of the command returned, as parsed from the |value|.
   // The command ID extracted (if present in the JSON object) even if other
   // parsing/validation error occurs and command instance is not constructed.
@@ -85,7 +75,6 @@
   static std::unique_ptr<CommandInstance> FromJson(
       const base::Value* value,
       Command::Origin origin,
-      const CommandDictionary& dictionary,
       std::string* command_id,
       ErrorPtr* error);
 
@@ -94,6 +83,7 @@
   // Sets the command ID (normally done by CommandQueue when the command
   // instance is added to it).
   void SetID(const std::string& id) { id_ = id; }
+  void SetComponent(const std::string& component) { component_ = component; }
 
   void AddObserver(Observer* observer);
   void RemoveObserver(Observer* observer);
@@ -103,7 +93,6 @@
   void DetachFromQueue() {
     observers_.Clear();
     queue_ = nullptr;
-    command_definition_ = nullptr;
   }
 
  private:
@@ -117,12 +106,12 @@
 
   // Unique command ID within a command queue.
   std::string id_;
-  // Full command name as "<package_name>.<command_name>".
+  // Full command name as "<trait_name>.<command_name>".
   std::string name_;
+  // Full path to the component this command is intended for.
+  std::string component_;
   // The origin of the command, either "local" or "cloud".
   Command::Origin origin_ = Command::Origin::kLocal;
-  // Command definition.
-  const CommandDefinition* command_definition_{nullptr};
   // Command parameters and their values.
   base::DictionaryValue parameters_;
   // Current command execution progress.
diff --git a/src/commands/command_instance_unittest.cc b/src/commands/command_instance_unittest.cc
index cacb86c..8d70b8b 100644
--- a/src/commands/command_instance_unittest.cc
+++ b/src/commands/command_instance_unittest.cc
@@ -5,75 +5,19 @@
 #include "src/commands/command_instance.h"
 
 #include <gtest/gtest.h>
-
-#include "src/commands/command_dictionary.h"
-#include "src/commands/unittest_utils.h"
+#include <weave/test/unittest_utils.h>
 
 namespace weave {
 
 using test::CreateDictionaryValue;
 using test::CreateValue;
 
-namespace {
-
-class CommandInstanceTest : public ::testing::Test {
- protected:
-  void SetUp() override {
-    auto json = CreateDictionaryValue(R"({
-      'base': {
-        'reboot': {
-          'parameters': {},
-          'results': {}
-        }
-      },
-      'robot': {
-        'jump': {
-          'parameters': {
-            'height': {
-              'type': 'integer',
-              'minimum': 0,
-              'maximum': 100
-            },
-            '_jumpType': {
-              'type': 'string',
-              'enum': ['_withAirFlip', '_withSpin', '_withKick']
-            }
-          },
-          'progress': {'progress': 'integer'},
-          'results': {'testResult': 'integer'}
-        },
-        'speak': {
-          'parameters': {
-            'phrase': {
-              'type': 'string',
-              'enum': ['beamMeUpScotty', 'iDontDigOnSwine',
-                       'iPityDaFool', 'dangerWillRobinson']
-            },
-            'volume': {
-              'type': 'integer',
-              'minimum': 0,
-              'maximum': 10
-            }
-          },
-          'results': {'foo': 'integer'}
-        }
-      }
-    })");
-    CHECK(dict_.LoadCommands(*json, nullptr))
-        << "Failed to parse test command dictionary";
-  }
-  CommandDictionary dict_;
-};
-
-}  // anonymous namespace
-
-TEST_F(CommandInstanceTest, Test) {
+TEST(CommandInstanceTest, Test) {
   auto params = CreateDictionaryValue(R"({
     'phrase': 'iPityDaFool',
     'volume': 5
   })");
-  CommandInstance instance{"robot.speak", Command::Origin::kCloud,
-                           dict_.FindCommand("robot.speak"), *params};
+  CommandInstance instance{"robot.speak", Command::Origin::kCloud, *params};
 
   EXPECT_TRUE(
       instance.Complete(*CreateDictionaryValue("{'foo': 239}"), nullptr));
@@ -82,28 +26,23 @@
   EXPECT_EQ("robot.speak", instance.GetName());
   EXPECT_EQ(Command::Origin::kCloud, instance.GetOrigin());
   EXPECT_JSON_EQ("{'phrase': 'iPityDaFool', 'volume': 5}",
-                 *instance.GetParameters());
-  EXPECT_JSON_EQ("{'foo': 239}", *instance.GetResults());
+                 instance.GetParameters());
+  EXPECT_JSON_EQ("{'foo': 239}", instance.GetResults());
 
-  CommandInstance instance2{"base.reboot",
-                            Command::Origin::kLocal,
-                            dict_.FindCommand("base.reboot"),
-                            {}};
+  CommandInstance instance2{"base.reboot", Command::Origin::kLocal, {}};
   EXPECT_EQ(Command::Origin::kLocal, instance2.GetOrigin());
 }
 
-TEST_F(CommandInstanceTest, SetID) {
-  CommandInstance instance{"base.reboot",
-                           Command::Origin::kLocal,
-                           dict_.FindCommand("base.reboot"),
-                           {}};
+TEST(CommandInstanceTest, SetID) {
+  CommandInstance instance{"base.reboot", Command::Origin::kLocal, {}};
   instance.SetID("command_id");
   EXPECT_EQ("command_id", instance.GetID());
 }
 
-TEST_F(CommandInstanceTest, FromJson) {
+TEST(CommandInstanceTest, FromJson) {
   auto json = CreateDictionaryValue(R"({
     'name': 'robot.jump',
+    'component': 'comp1.comp2',
     'id': 'abcd',
     'parameters': {
       'height': 53,
@@ -113,64 +52,56 @@
   })");
   std::string id;
   auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
-                                            dict_, &id, nullptr);
+                                            &id, nullptr);
   EXPECT_EQ("abcd", id);
   EXPECT_EQ("abcd", instance->GetID());
   EXPECT_EQ("robot.jump", instance->GetName());
+  EXPECT_EQ("comp1.comp2", instance->GetComponent());
   EXPECT_JSON_EQ("{'height': 53, '_jumpType': '_withKick'}",
-                 *instance->GetParameters());
+                 instance->GetParameters());
 }
 
-TEST_F(CommandInstanceTest, FromJson_ParamsOmitted) {
+TEST(CommandInstanceTest, FromJson_ParamsOmitted) {
   auto json = CreateDictionaryValue("{'name': 'base.reboot'}");
   auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
-                                            dict_, nullptr, nullptr);
+                                            nullptr, nullptr);
   EXPECT_EQ("base.reboot", instance->GetName());
-  EXPECT_JSON_EQ("{}", *instance->GetParameters());
+  EXPECT_JSON_EQ("{}", instance->GetParameters());
 }
 
-TEST_F(CommandInstanceTest, FromJson_NotObject) {
+TEST(CommandInstanceTest, FromJson_NotObject) {
   auto json = CreateValue("'string'");
   ErrorPtr error;
   auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
-                                            dict_, nullptr, &error);
+                                            nullptr, &error);
   EXPECT_EQ(nullptr, instance.get());
   EXPECT_EQ("json_object_expected", error->GetCode());
 }
 
-TEST_F(CommandInstanceTest, FromJson_NameMissing) {
+TEST(CommandInstanceTest, FromJson_NameMissing) {
   auto json = CreateDictionaryValue("{'param': 'value'}");
   ErrorPtr error;
   auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
-                                            dict_, nullptr, &error);
+                                            nullptr, &error);
   EXPECT_EQ(nullptr, instance.get());
   EXPECT_EQ("parameter_missing", error->GetCode());
 }
 
-TEST_F(CommandInstanceTest, FromJson_UnknownCommand) {
-  auto json = CreateDictionaryValue("{'name': 'robot.scream'}");
-  ErrorPtr error;
-  auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
-                                            dict_, nullptr, &error);
-  EXPECT_EQ(nullptr, instance.get());
-  EXPECT_EQ("invalid_command_name", error->GetCode());
-}
-
-TEST_F(CommandInstanceTest, FromJson_ParamsNotObject) {
+TEST(CommandInstanceTest, FromJson_ParamsNotObject) {
   auto json = CreateDictionaryValue(R"({
     'name': 'robot.speak',
     'parameters': 'hello'
   })");
   ErrorPtr error;
   auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
-                                            dict_, nullptr, &error);
+                                            nullptr, &error);
   EXPECT_EQ(nullptr, instance.get());
   auto inner = error->GetInnerError();
   EXPECT_EQ("json_object_expected", inner->GetCode());
   EXPECT_EQ("command_failed", error->GetCode());
 }
 
-TEST_F(CommandInstanceTest, ToJson) {
+TEST(CommandInstanceTest, ToJson) {
   auto json = CreateDictionaryValue(R"({
     'name': 'robot.jump',
     'parameters': {
@@ -180,7 +111,7 @@
     'results': {}
   })");
   auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
-                                            dict_, nullptr, nullptr);
+                                            nullptr, nullptr);
   EXPECT_TRUE(instance->SetProgress(*CreateDictionaryValue("{'progress': 15}"),
                                     nullptr));
   EXPECT_TRUE(instance->SetProgress(*CreateDictionaryValue("{'progress': 15}"),
@@ -202,13 +133,13 @@
                *json, *converted);
 }
 
-TEST_F(CommandInstanceTest, ToJsonError) {
+TEST(CommandInstanceTest, ToJsonError) {
   auto json = CreateDictionaryValue(R"({
     'name': 'base.reboot',
     'parameters': {}
   })");
   auto instance = CommandInstance::FromJson(json.get(), Command::Origin::kCloud,
-                                            dict_, nullptr, nullptr);
+                                            nullptr, nullptr);
   instance->SetID("testId");
 
   ErrorPtr error;
diff --git a/src/commands/command_manager.cc b/src/commands/command_manager.cc
deleted file mode 100644
index a64c8e5..0000000
--- a/src/commands/command_manager.cc
+++ /dev/null
@@ -1,104 +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 "src/commands/command_manager.h"
-
-#include <base/values.h>
-#include <weave/enum_to_string.h>
-#include <weave/error.h>
-
-#include "src/commands/schema_constants.h"
-#include "src/utils.h"
-
-namespace weave {
-
-CommandManager::CommandManager() {}
-
-CommandManager::~CommandManager() {}
-
-void CommandManager::AddCommandDefChanged(const base::Closure& callback) {
-  on_command_changed_.push_back(callback);
-  callback.Run();
-}
-
-const CommandDictionary& CommandManager::GetCommandDictionary() const {
-  return dictionary_;
-}
-
-bool CommandManager::LoadCommands(const base::DictionaryValue& dict,
-                                  ErrorPtr* error) {
-  bool result = dictionary_.LoadCommands(dict, error);
-  for (const auto& cb : on_command_changed_)
-    cb.Run();
-  return result;
-}
-
-bool CommandManager::LoadCommands(const std::string& json,
-                                  ErrorPtr* error) {
-  std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
-  if (!dict)
-    return false;
-  return LoadCommands(*dict, error);
-}
-
-void CommandManager::AddCommand(
-    std::unique_ptr<CommandInstance> command_instance) {
-  command_queue_.Add(std::move(command_instance));
-}
-
-bool CommandManager::AddCommand(const base::DictionaryValue& command,
-                                std::string* id,
-                                ErrorPtr* error) {
-  return AddCommand(command, UserRole::kOwner, id, error);
-}
-
-bool CommandManager::AddCommand(const base::DictionaryValue& command,
-                                UserRole role,
-                                std::string* id,
-                                ErrorPtr* error) {
-  auto command_instance =
-      CommandInstance::FromJson(&command, Command::Origin::kLocal,
-                                GetCommandDictionary(), nullptr, error);
-  if (!command_instance)
-    return false;
-
-  UserRole minimal_role =
-      command_instance->GetCommandDefinition()->GetMinimalRole();
-  if (role < minimal_role) {
-    Error::AddToPrintf(
-        error, FROM_HERE, errors::commands::kDomain, "access_denied",
-        "User role '%s' less than minimal: '%s'", EnumToString(role).c_str(),
-        EnumToString(minimal_role).c_str());
-    return false;
-  }
-
-  *id = std::to_string(++next_command_id_);
-  command_instance->SetID(*id);
-  AddCommand(std::move(command_instance));
-  return true;
-}
-
-CommandInstance* CommandManager::FindCommand(const std::string& id) {
-  return command_queue_.Find(id);
-}
-
-void CommandManager::AddCommandAddedCallback(
-    const CommandQueue::CommandCallback& callback) {
-  command_queue_.AddCommandAddedCallback(callback);
-}
-
-void CommandManager::AddCommandRemovedCallback(
-    const CommandQueue::CommandCallback& callback) {
-  command_queue_.AddCommandRemovedCallback(callback);
-}
-
-void CommandManager::AddCommandHandler(
-    const std::string& command_name,
-    const Device::CommandHandlerCallback& callback) {
-  CHECK(command_name.empty() || dictionary_.FindCommand(command_name))
-      << "Command undefined: " << command_name;
-  command_queue_.AddCommandHandler(command_name, callback);
-}
-
-}  // namespace weave
diff --git a/src/commands/command_manager.h b/src/commands/command_manager.h
deleted file mode 100644
index 04f49ee..0000000
--- a/src/commands/command_manager.h
+++ /dev/null
@@ -1,79 +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_SRC_COMMANDS_COMMAND_MANAGER_H_
-#define LIBWEAVE_SRC_COMMANDS_COMMAND_MANAGER_H_
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <base/callback.h>
-#include <base/macros.h>
-#include <base/memory/weak_ptr.h>
-
-#include "src/commands/command_dictionary.h"
-#include "src/commands/command_queue.h"
-
-namespace weave {
-
-class CommandInstance;
-
-// CommandManager class that will have a list of all the device command
-// schemas as well as the live command queue of pending command instances
-// dispatched to the device.
-class CommandManager final {
- public:
-  CommandManager();
-
-  ~CommandManager();
-
-  bool AddCommand(const base::DictionaryValue& command,
-                  std::string* id,
-                  ErrorPtr* error);
-  CommandInstance* FindCommand(const std::string& id);
-  void AddCommandAddedCallback(const CommandQueue::CommandCallback& callback);
-  void AddCommandRemovedCallback(const CommandQueue::CommandCallback& callback);
-  void AddCommandHandler(const std::string& command_name,
-                         const Device::CommandHandlerCallback& callback);
-
-  // Sets callback which is called when command definitions is changed.
-  void AddCommandDefChanged(const base::Closure& callback);
-
-  // Returns the command definitions for the device.
-  const CommandDictionary& GetCommandDictionary() const;
-
-  // Same as the overload above, but takes a path to a json file to read
-  // the base command definitions from.
-  bool LoadStandardCommands(const std::string& json, ErrorPtr* error);
-
-  // Loads device command schema.
-  bool LoadCommands(const base::DictionaryValue& dict,
-                    ErrorPtr* error);
-
-  // Same as the overload above, but takes a path to a json file to read
-  // the base command definitions from.
-  bool LoadCommands(const std::string& json,
-                    ErrorPtr* error);
-
-  // Adds a new command to the command queue.
-  void AddCommand(std::unique_ptr<CommandInstance> command_instance);
-
-  bool AddCommand(const base::DictionaryValue& command,
-                  UserRole role,
-                  std::string* id,
-                  ErrorPtr* error);
-
- private:
-  CommandDictionary dictionary_;  // Registered definitions.
-  CommandQueue command_queue_;
-  std::vector<base::Callback<void()>> on_command_changed_;
-  uint32_t next_command_id_{0};
-
-  DISALLOW_COPY_AND_ASSIGN(CommandManager);
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_COMMAND_MANAGER_H_
diff --git a/src/commands/command_manager_unittest.cc b/src/commands/command_manager_unittest.cc
deleted file mode 100644
index eca5175..0000000
--- a/src/commands/command_manager_unittest.cc
+++ /dev/null
@@ -1,94 +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 "src/commands/command_manager.h"
-
-#include <map>
-
-#include <base/json/json_writer.h>
-#include <gtest/gtest.h>
-#include <weave/provider/test/mock_config_store.h>
-
-#include "src/bind_lambda.h"
-#include "src/commands/unittest_utils.h"
-
-using testing::Return;
-
-namespace weave {
-
-using test::CreateDictionaryValue;
-
-namespace {
-
-const char kTestVendorCommands[] = R"({
-  "robot": {
-    "_jump": {
-      "parameters": {"height": "integer"},
-      "results": {}
-    },
-    "_speak": {
-      "parameters": {"phrase": "string"},
-      "results": {}
-    }
-  }
-})";
-
-const char kTestTestCommands[] = R"({
-  "test": {
-    "_yo": {
-      "parameters": {"name": "string"},
-      "results": {}
-    }
-  }
-})";
-
-}  // namespace
-
-TEST(CommandManager, Empty) {
-  CommandManager manager;
-  EXPECT_TRUE(manager.GetCommandDictionary().IsEmpty());
-}
-
-TEST(CommandManager, LoadCommandsDict) {
-  CommandManager manager;
-  auto json = CreateDictionaryValue(kTestVendorCommands);
-  EXPECT_TRUE(manager.LoadCommands(*json, nullptr));
-}
-
-TEST(CommandManager, LoadCommandsJson) {
-  CommandManager manager;
-
-  // Load device-supported commands.
-  auto json_str = R"({
-    "base": {
-      "reboot": {
-        "parameters": {"delay": "integer"},
-        "results": {}
-      }
-    },
-    "robot": {
-      "_jump": {
-        "parameters": {"height": "integer"},
-        "results": {}
-      }
-    }
-  })";
-  EXPECT_TRUE(manager.LoadCommands(json_str, nullptr));
-  EXPECT_EQ(2, manager.GetCommandDictionary().GetSize());
-  EXPECT_NE(nullptr, manager.GetCommandDictionary().FindCommand("base.reboot"));
-  EXPECT_NE(nullptr, manager.GetCommandDictionary().FindCommand("robot._jump"));
-}
-
-TEST(CommandManager, ShouldLoadStandardAndTestDefinitions) {
-  CommandManager manager;
-  ASSERT_TRUE(manager.LoadCommands(kTestVendorCommands, nullptr));
-  ASSERT_TRUE(manager.LoadCommands(kTestTestCommands, nullptr));
-  EXPECT_EQ(3, manager.GetCommandDictionary().GetSize());
-  EXPECT_NE(nullptr, manager.GetCommandDictionary().FindCommand("robot._jump"));
-  EXPECT_NE(nullptr,
-            manager.GetCommandDictionary().FindCommand("robot._speak"));
-  EXPECT_NE(nullptr, manager.GetCommandDictionary().FindCommand("test._yo"));
-}
-
-}  // namespace weave
diff --git a/src/commands/command_queue.cc b/src/commands/command_queue.cc
index e08527b..134dc1c 100644
--- a/src/commands/command_queue.cc
+++ b/src/commands/command_queue.cc
@@ -11,6 +11,11 @@
 
 namespace {
 const int kRemoveCommandDelayMin = 5;
+
+std::string GetCommandHandlerKey(const std::string& component_path,
+                                 const std::string& command_name) {
+  return component_path + ":" + command_name;
+}
 }
 
 void CommandQueue::AddCommandAddedCallback(const CommandCallback& callback) {
@@ -25,6 +30,7 @@
 }
 
 void CommandQueue::AddCommandHandler(
+    const std::string& component_path,
     const std::string& command_name,
     const Device::CommandHandlerCallback& callback) {
   if (!command_name.empty()) {
@@ -33,19 +39,24 @@
 
     for (const auto& command : map_) {
       if (command.second->GetState() == Command::State::kQueued &&
-          command.second->GetName() == command_name) {
+          command.second->GetName() == command_name &&
+          command.second->GetComponent() == component_path) {
         callback.Run(command.second);
       }
     }
 
-    CHECK(command_callbacks_.emplace(command_name, callback).second)
+    std::string key = GetCommandHandlerKey(component_path, command_name);
+    CHECK(command_callbacks_.insert(std::make_pair(key, callback)).second)
         << command_name << " already has handler";
 
   } else {
+    CHECK(component_path.empty())
+        << "Default handler must not be component-specific";
     for (const auto& command : map_) {
+      std::string key = GetCommandHandlerKey(command.second->GetComponent(),
+                                             command.second->GetName());
       if (command.second->GetState() == Command::State::kQueued &&
-          command_callbacks_.find(command.second->GetName()) ==
-              command_callbacks_.end()) {
+          command_callbacks_.find(key) == command_callbacks_.end()) {
         callback.Run(command.second);
       }
     }
@@ -65,7 +76,9 @@
   for (const auto& cb : on_command_added_)
     cb.Run(pair.first->second.get());
 
-  auto it_handler = command_callbacks_.find(pair.first->second->GetName());
+  std::string key = GetCommandHandlerKey(pair.first->second->GetComponent(),
+                                         pair.first->second->GetName());
+  auto it_handler = command_callbacks_.find(key);
 
   if (it_handler != command_callbacks_.end())
     it_handler->second.Run(pair.first->second);
diff --git a/src/commands/command_queue.h b/src/commands/command_queue.h
index 74ed86f..0f0a18b 100644
--- a/src/commands/command_queue.h
+++ b/src/commands/command_queue.h
@@ -34,7 +34,8 @@
   // Adds notifications callback for a command is removed from the queue.
   void AddCommandRemovedCallback(const CommandCallback& callback);
 
-  void AddCommandHandler(const std::string& command_name,
+  void AddCommandHandler(const std::string& component_path,
+                         const std::string& command_name,
                          const Device::CommandHandlerCallback& callback);
 
   // Checks if the command queue is empty.
diff --git a/src/commands/command_queue_unittest.cc b/src/commands/command_queue_unittest.cc
index a9e953e..08e8102 100644
--- a/src/commands/command_queue_unittest.cc
+++ b/src/commands/command_queue_unittest.cc
@@ -12,23 +12,17 @@
 #include <base/memory/weak_ptr.h>
 #include <gtest/gtest.h>
 
-#include "src/commands/command_definition.h"
-#include "src/commands/object_schema.h"
 #include "src/string_utils.h"
 
 namespace weave {
 
 class CommandQueueTest : public testing::Test {
  public:
-  CommandQueueTest() {
-    command_definition_ = CommandDefinition::FromJson({}, nullptr);
-  }
-
   std::unique_ptr<CommandInstance> CreateDummyCommandInstance(
       const std::string& name,
       const std::string& id) {
     std::unique_ptr<CommandInstance> cmd{new CommandInstance{
-        name, Command::Origin::kLocal, command_definition_.get(), {}}};
+        name, Command::Origin::kLocal, {}}};
     cmd->SetID(id);
     return cmd;
   }
@@ -41,9 +35,6 @@
   }
 
   CommandQueue queue_;
-
- private:
-  std::unique_ptr<CommandDefinition> command_definition_;
 };
 
 // Keeps track of commands being added to and removed from the queue_.
@@ -84,14 +75,14 @@
 
 TEST_F(CommandQueueTest, Empty) {
   EXPECT_TRUE(queue_.IsEmpty());
-  EXPECT_EQ(0, queue_.GetCount());
+  EXPECT_EQ(0u, queue_.GetCount());
 }
 
 TEST_F(CommandQueueTest, Add) {
   queue_.Add(CreateDummyCommandInstance("base.reboot", "id1"));
   queue_.Add(CreateDummyCommandInstance("base.reboot", "id2"));
   queue_.Add(CreateDummyCommandInstance("base.reboot", "id3"));
-  EXPECT_EQ(3, queue_.GetCount());
+  EXPECT_EQ(3u, queue_.GetCount());
   EXPECT_FALSE(queue_.IsEmpty());
 }
 
@@ -102,31 +93,31 @@
   queue_.Add(CreateDummyCommandInstance("base.reboot", id2));
   EXPECT_FALSE(queue_.IsEmpty());
   EXPECT_FALSE(Remove("dummy"));
-  EXPECT_EQ(2, queue_.GetCount());
+  EXPECT_EQ(2u, queue_.GetCount());
   EXPECT_TRUE(Remove(id1));
-  EXPECT_EQ(1, queue_.GetCount());
+  EXPECT_EQ(1u, queue_.GetCount());
   EXPECT_FALSE(Remove(id1));
-  EXPECT_EQ(1, queue_.GetCount());
+  EXPECT_EQ(1u, queue_.GetCount());
   EXPECT_TRUE(Remove(id2));
-  EXPECT_EQ(0, queue_.GetCount());
+  EXPECT_EQ(0u, queue_.GetCount());
   EXPECT_FALSE(Remove(id2));
-  EXPECT_EQ(0, queue_.GetCount());
+  EXPECT_EQ(0u, queue_.GetCount());
   EXPECT_TRUE(queue_.IsEmpty());
 }
 
 TEST_F(CommandQueueTest, DelayedRemove) {
   const std::string id1 = "id1";
   queue_.Add(CreateDummyCommandInstance("base.reboot", id1));
-  EXPECT_EQ(1, queue_.GetCount());
+  EXPECT_EQ(1u, queue_.GetCount());
 
   queue_.DelayedRemove(id1);
-  EXPECT_EQ(1, queue_.GetCount());
+  EXPECT_EQ(1u, queue_.GetCount());
 
   Cleanup(base::TimeDelta::FromMinutes(1));
-  EXPECT_EQ(1, queue_.GetCount());
+  EXPECT_EQ(1u, queue_.GetCount());
 
   Cleanup(base::TimeDelta::FromMinutes(15));
-  EXPECT_EQ(0, queue_.GetCount());
+  EXPECT_EQ(0u, queue_.GetCount());
 }
 
 TEST_F(CommandQueueTest, Dispatch) {
diff --git a/src/commands/object_schema.cc b/src/commands/object_schema.cc
deleted file mode 100644
index b70beff..0000000
--- a/src/commands/object_schema.cc
+++ /dev/null
@@ -1,378 +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 "src/commands/object_schema.h"
-
-#include <algorithm>
-#include <limits>
-
-#include <base/logging.h>
-#include <base/values.h>
-#include <weave/enum_to_string.h>
-
-#include "src/commands/prop_types.h"
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_constants.h"
-#include "src/string_utils.h"
-
-namespace weave {
-
-namespace {
-
-// Helper function for to create a PropType based on type string.
-// Generates an error if the string identifies an unknown type.
-std::unique_ptr<PropType> CreatePropType(const std::string& type_name,
-                                         ErrorPtr* error) {
-  auto parts = SplitAtFirst(type_name, ".", false);
-  const std::string& primary_type = parts.first;
-  const std::string& array_type = parts.second;
-
-  std::unique_ptr<PropType> prop;
-  ValueType type;
-  if (PropType::GetTypeFromTypeString(primary_type, &type)) {
-    prop = PropType::Create(type);
-    if (prop && type == ValueType::Array && !array_type.empty()) {
-      auto items_type = CreatePropType(array_type, error);
-      if (items_type) {
-        prop->GetArray()->SetItemType(std::move(items_type));
-      } else {
-        prop.reset();
-        return prop;
-      }
-    }
-  }
-  if (!prop) {
-    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                       errors::commands::kUnknownType, "Unknown type %s",
-                       type_name.c_str());
-  }
-  return prop;
-}
-
-// Generates "no_type_info" error.
-void ErrorInvalidTypeInfo(ErrorPtr* error) {
-  Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
-               errors::commands::kNoTypeInfo,
-               "Unable to determine parameter type");
-}
-
-// Helper function for PropFromJson to handle the case of parameter being
-// defined as a JSON string like this:
-//   "prop":"..."
-std::unique_ptr<PropType> PropFromJsonString(const base::Value& value,
-                                             const PropType* base_schema,
-                                             ErrorPtr* error) {
-  std::string type_name;
-  CHECK(value.GetAsString(&type_name)) << "Unable to get string value";
-  std::unique_ptr<PropType> prop = CreatePropType(type_name, error);
-  base::DictionaryValue empty;
-  if (prop && !prop->FromJson(&empty, base_schema, error))
-    prop.reset();
-
-  return prop;
-}
-
-// Detects a type based on JSON array. Inspects the first element of the array
-// to deduce the PropType from. Returns the string name of the type detected
-// or empty string is type detection failed.
-std::string DetectArrayType(const base::ListValue* list,
-                            const PropType* base_schema,
-                            bool allow_arrays) {
-  std::string type_name;
-  if (base_schema) {
-    type_name = base_schema->GetTypeAsString();
-  } else if (list->GetSize() > 0) {
-    const base::Value* first_element = nullptr;
-    if (list->Get(0, &first_element)) {
-      switch (first_element->GetType()) {
-        case base::Value::TYPE_BOOLEAN:
-          type_name = PropType::GetTypeStringFromType(ValueType::Boolean);
-          break;
-        case base::Value::TYPE_INTEGER:
-          type_name = PropType::GetTypeStringFromType(ValueType::Int);
-          break;
-        case base::Value::TYPE_DOUBLE:
-          type_name = PropType::GetTypeStringFromType(ValueType::Double);
-          break;
-        case base::Value::TYPE_STRING:
-          type_name = PropType::GetTypeStringFromType(ValueType::String);
-          break;
-        case base::Value::TYPE_DICTIONARY:
-          type_name = PropType::GetTypeStringFromType(ValueType::Object);
-          break;
-        case base::Value::TYPE_LIST: {
-          if (allow_arrays) {
-            type_name = PropType::GetTypeStringFromType(ValueType::Array);
-            const base::ListValue* first_element_list = nullptr;
-            if (first_element->GetAsList(&first_element_list)) {
-              // We do not allow arrays of arrays.
-              auto child_type =
-                  DetectArrayType(first_element_list, nullptr, false);
-              if (child_type.empty()) {
-                type_name.clear();
-              } else {
-                type_name += '.' + child_type;
-              }
-            }
-          }
-          break;
-        }
-        default:
-          // The rest are unsupported.
-          break;
-      }
-    }
-  }
-  return type_name;
-}
-
-// Helper function for PropFromJson to handle the case of parameter being
-// defined as a JSON array like this:
-//   "prop":[...]
-std::unique_ptr<PropType> PropFromJsonArray(const base::Value& value,
-                                            const PropType* base_schema,
-                                            ErrorPtr* error) {
-  std::unique_ptr<PropType> prop;
-  const base::ListValue* list = nullptr;
-  CHECK(value.GetAsList(&list)) << "Unable to get array value";
-  std::string type_name = DetectArrayType(list, base_schema, true);
-  if (type_name.empty()) {
-    ErrorInvalidTypeInfo(error);
-    return prop;
-  }
-  base::DictionaryValue array_object;
-  array_object.SetWithoutPathExpansion(commands::attributes::kOneOf_Enum,
-                                       list->DeepCopy());
-  prop = CreatePropType(type_name, error);
-  if (prop && !prop->FromJson(&array_object, base_schema, error))
-    prop.reset();
-
-  return prop;
-}
-
-// Detects a type based on JSON object definition of type. Looks at various
-// members such as minimum/maximum constraints, default and enum values to
-// try to deduce the underlying type of the element. Returns the string name of
-// the type detected or empty string is type detection failed.
-std::string DetectObjectType(const base::DictionaryValue* dict,
-                             const PropType* base_schema) {
-  bool has_min_max = dict->HasKey(commands::attributes::kNumeric_Min) ||
-                     dict->HasKey(commands::attributes::kNumeric_Max);
-
-  // Here we are trying to "detect the type and read in the object based on
-  // the deduced type". Later, we'll verify that this detected type matches
-  // the expectation of the base schema, if applicable, to make sure we are not
-  // changing the expected type. This makes the vendor-side (re)definition of
-  // standard and custom commands behave exactly the same.
-  // The only problem with this approach was the double-vs-int types.
-  // If the type is meant to be a double we want to allow its definition as
-  // "min:0, max:0" instead of just forcing it to be only "min:0.0, max:0.0".
-  // If we have "minimum" or "maximum", and we have a Double schema object,
-  // treat this object as a Double (even if both min and max are integers).
-  if (has_min_max && base_schema && base_schema->GetType() == ValueType::Double)
-    return PropType::GetTypeStringFromType(ValueType::Double);
-
-  // If we have at least one "minimum" or "maximum" that is Double,
-  // it's a Double.
-  const base::Value* value = nullptr;
-  if (dict->Get(commands::attributes::kNumeric_Min, &value) &&
-      value->IsType(base::Value::TYPE_DOUBLE))
-    return PropType::GetTypeStringFromType(ValueType::Double);
-  if (dict->Get(commands::attributes::kNumeric_Max, &value) &&
-      value->IsType(base::Value::TYPE_DOUBLE))
-    return PropType::GetTypeStringFromType(ValueType::Double);
-
-  // If we have "minimum" or "maximum", it's an Integer.
-  if (has_min_max)
-    return PropType::GetTypeStringFromType(ValueType::Int);
-
-  // If we have "minLength" or "maxLength", it's a String.
-  if (dict->HasKey(commands::attributes::kString_MinLength) ||
-      dict->HasKey(commands::attributes::kString_MaxLength))
-    return PropType::GetTypeStringFromType(ValueType::String);
-
-  // If we have "properties", it's an object.
-  if (dict->HasKey(commands::attributes::kObject_Properties))
-    return PropType::GetTypeStringFromType(ValueType::Object);
-
-  // If we have "items", it's an array.
-  if (dict->HasKey(commands::attributes::kItems))
-    return PropType::GetTypeStringFromType(ValueType::Array);
-
-  // If we have "enum", it's an array. Detect type from array elements.
-  const base::ListValue* list = nullptr;
-  if (dict->GetListWithoutPathExpansion(commands::attributes::kOneOf_Enum,
-                                        &list))
-    return DetectArrayType(list, base_schema, true);
-
-  // If we have "default", try to use it for type detection.
-  if (dict->Get(commands::attributes::kDefault, &value)) {
-    if (value->IsType(base::Value::TYPE_DOUBLE))
-      return PropType::GetTypeStringFromType(ValueType::Double);
-    if (value->IsType(base::Value::TYPE_INTEGER))
-      return PropType::GetTypeStringFromType(ValueType::Int);
-    if (value->IsType(base::Value::TYPE_BOOLEAN))
-      return PropType::GetTypeStringFromType(ValueType::Boolean);
-    if (value->IsType(base::Value::TYPE_STRING))
-      return PropType::GetTypeStringFromType(ValueType::String);
-    if (value->IsType(base::Value::TYPE_LIST)) {
-      CHECK(value->GetAsList(&list)) << "List value expected";
-      std::string child_type = DetectArrayType(list, base_schema, false);
-      if (!child_type.empty()) {
-        return PropType::GetTypeStringFromType(ValueType::Array) + '.' +
-               child_type;
-      }
-    }
-  }
-
-  return std::string{};
-}
-
-// Helper function for PropFromJson to handle the case of parameter being
-// defined as a JSON object like this:
-//   "prop":{...}
-std::unique_ptr<PropType> PropFromJsonObject(const base::Value& value,
-                                             const PropType* base_schema,
-                                             ErrorPtr* error) {
-  std::unique_ptr<PropType> prop;
-  const base::DictionaryValue* dict = nullptr;
-  CHECK(value.GetAsDictionary(&dict)) << "Unable to get dictionary value";
-  std::string type_name;
-  if (dict->HasKey(commands::attributes::kType)) {
-    if (!dict->GetString(commands::attributes::kType, &type_name)) {
-      ErrorInvalidTypeInfo(error);
-      return prop;
-    }
-  } else {
-    type_name = DetectObjectType(dict, base_schema);
-  }
-  if (type_name.empty()) {
-    if (!base_schema) {
-      ErrorInvalidTypeInfo(error);
-      return prop;
-    }
-    type_name = base_schema->GetTypeAsString();
-  }
-  prop = CreatePropType(type_name, error);
-  if (prop && !prop->FromJson(dict, base_schema, error))
-    prop.reset();
-
-  return prop;
-}
-
-const EnumToStringMap<base::Value::Type>::Map kMap[] = {
-    {base::Value::TYPE_NULL, "Null"},
-    {base::Value::TYPE_BOOLEAN, "Boolean"},
-    {base::Value::TYPE_INTEGER, "Integer"},
-    {base::Value::TYPE_DOUBLE, "Double"},
-    {base::Value::TYPE_STRING, "String"},
-    {base::Value::TYPE_BINARY, "Binary"},
-    {base::Value::TYPE_DICTIONARY, "Object"},
-    {base::Value::TYPE_LIST, "Array"},
-};
-
-}  // anonymous namespace
-
-template <>
-EnumToStringMap<base::Value::Type>::EnumToStringMap() : EnumToStringMap(kMap) {}
-
-ObjectSchema::ObjectSchema() {}
-ObjectSchema::~ObjectSchema() {}
-
-std::unique_ptr<ObjectSchema> ObjectSchema::Clone() const {
-  std::unique_ptr<ObjectSchema> cloned{new ObjectSchema};
-  for (const auto& pair : properties_) {
-    cloned->properties_.emplace(pair.first, pair.second->Clone());
-  }
-  cloned->extra_properties_allowed_ = extra_properties_allowed_;
-  return cloned;
-}
-
-void ObjectSchema::AddProp(const std::string& name,
-                           std::unique_ptr<PropType> prop) {
-  // Not using emplace() here to make sure we override existing properties.
-  properties_[name] = std::move(prop);
-}
-
-const PropType* ObjectSchema::GetProp(const std::string& name) const {
-  auto p = properties_.find(name);
-  return p != properties_.end() ? p->second.get() : nullptr;
-}
-
-bool ObjectSchema::MarkPropRequired(const std::string& name, ErrorPtr* error) {
-  auto p = properties_.find(name);
-  if (p == properties_.end()) {
-    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                       errors::commands::kUnknownProperty,
-                       "Unknown property '%s'", name.c_str());
-    return false;
-  }
-  p->second->MakeRequired(true);
-  return true;
-}
-
-std::unique_ptr<base::DictionaryValue> ObjectSchema::ToJson(
-    bool full_schema,
-    bool in_command_def) const {
-  std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue);
-  for (const auto& pair : properties_) {
-    auto prop_def = pair.second->ToJson(full_schema, in_command_def);
-    CHECK(prop_def);
-    value->SetWithoutPathExpansion(pair.first, prop_def.release());
-  }
-  return value;
-}
-
-bool ObjectSchema::FromJson(const base::DictionaryValue* value,
-                            const ObjectSchema* object_schema,
-                            ErrorPtr* error) {
-  Properties properties;
-  base::DictionaryValue::Iterator iter(*value);
-  while (!iter.IsAtEnd()) {
-    std::string name = iter.key();
-    const PropType* base_schema =
-        object_schema ? object_schema->GetProp(iter.key()) : nullptr;
-    auto prop_type = PropFromJson(iter.value(), base_schema, error);
-    if (prop_type) {
-      properties.emplace(iter.key(), std::move(prop_type));
-    } else {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kInvalidPropDef,
-                         "Error in definition of property '%s'",
-                         iter.key().c_str());
-      return false;
-    }
-    iter.Advance();
-  }
-  properties_ = std::move(properties);
-  return true;
-}
-
-std::unique_ptr<PropType> ObjectSchema::PropFromJson(
-    const base::Value& value,
-    const PropType* base_schema,
-    ErrorPtr* error) {
-  if (value.IsType(base::Value::TYPE_STRING)) {
-    // A string value is a short-hand object specification and provides
-    // the parameter type.
-    return PropFromJsonString(value, base_schema, error);
-  } else if (value.IsType(base::Value::TYPE_LIST)) {
-    // One of the enumerated types.
-    return PropFromJsonArray(value, base_schema, error);
-  } else if (value.IsType(base::Value::TYPE_DICTIONARY)) {
-    // Full parameter definition.
-    return PropFromJsonObject(value, base_schema, error);
-  }
-  Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                     errors::commands::kUnknownType,
-                     "Unexpected JSON value type: %s",
-                     EnumToString(value.GetType()).c_str());
-  return nullptr;
-}
-
-std::unique_ptr<ObjectSchema> ObjectSchema::Create() {
-  return std::unique_ptr<ObjectSchema>{new ObjectSchema};
-}
-
-}  // namespace weave
diff --git a/src/commands/object_schema.h b/src/commands/object_schema.h
deleted file mode 100644
index 504e1dd..0000000
--- a/src/commands/object_schema.h
+++ /dev/null
@@ -1,95 +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_SRC_COMMANDS_OBJECT_SCHEMA_H_
-#define LIBWEAVE_SRC_COMMANDS_OBJECT_SCHEMA_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include <weave/error.h>
-
-namespace base {
-class Value;
-class DictionaryValue;
-}  // namespace base
-
-namespace weave {
-
-class PropType;
-
-// ObjectSchema is a class representing an object definition in GCD command
-// schema. This could represent a GCD command definition, but also it can be
-// used when defining custom object types for command properties such as
-// output media type (paper) for print command. The schema definition for
-// these type of object description is the same.
-class ObjectSchema final {
- public:
-  // Do not inline the constructor/destructor to allow forward-declared type
-  // PropType to be part of |properties_| member.
-  ObjectSchema();
-  ~ObjectSchema();
-
-  // Properties is a string-to-PropType map representing a list of
-  // properties defined for a command/object. The key is the parameter
-  // name and the value is the parameter type definition object.
-  using Properties = std::map<std::string, std::unique_ptr<PropType>>;
-
-  // Makes a full copy of this object.
-  virtual std::unique_ptr<ObjectSchema> Clone() const;
-
-  // Add a new parameter definition.
-  void AddProp(const std::string& name, std::unique_ptr<PropType> prop);
-
-  // Finds parameter type definition by name. Returns nullptr if not found.
-  const PropType* GetProp(const std::string& name) const;
-
-  // Gets the list of all the properties defined.
-  const Properties& GetProps() const { return properties_; }
-
-  // Marks the property with given name as "required". If |name| specifies
-  // an unknown property, false is returned and |error| is set with detailed
-  // error message for the failure.
-  bool MarkPropRequired(const std::string& name, ErrorPtr* error);
-
-  // Specify whether extra properties are allowed on objects described by
-  // this schema. When validating a value of an object type, we can
-  // make sure that the value has only the properties explicitly defined by
-  // the schema and no other (custom) properties are allowed.
-  // This is to support JSON Schema's "additionalProperties" specification.
-  bool GetExtraPropertiesAllowed() const { return extra_properties_allowed_; }
-  void SetExtraPropertiesAllowed(bool allowed) {
-    extra_properties_allowed_ = allowed;
-  }
-
-  // Saves the object schema to JSON. When |full_schema| is set to true,
-  // then all properties and constraints are saved, otherwise, only
-  // the overridden (not inherited) ones are saved.
-  std::unique_ptr<base::DictionaryValue> ToJson(bool full_schema,
-                                                bool in_command_def) const;
-
-  // Loads the object schema from JSON. If |object_schema| is not nullptr, it is
-  // used as a base schema to inherit omitted properties and constraints from.
-  bool FromJson(const base::DictionaryValue* value,
-                const ObjectSchema* object_schema,
-                ErrorPtr* error);
-
-  // Helper factory method to create a new instance of ObjectSchema object.
-  static std::unique_ptr<ObjectSchema> Create();
-
-  // Helper method to load property type definitions from JSON.
-  static std::unique_ptr<PropType> PropFromJson(const base::Value& value,
-                                                const PropType* base_schema,
-                                                ErrorPtr* error);
-
- private:
-  // Internal parameter type definition map.
-  Properties properties_;
-  bool extra_properties_allowed_{false};
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_OBJECT_SCHEMA_H_
diff --git a/src/commands/object_schema_unittest.cc b/src/commands/object_schema_unittest.cc
deleted file mode 100644
index 6417952..0000000
--- a/src/commands/object_schema_unittest.cc
+++ /dev/null
@@ -1,1697 +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 "src/commands/object_schema.h"
-
-#include <algorithm>
-#include <limits>
-#include <memory>
-#include <vector>
-
-#include <base/json/json_reader.h>
-#include <base/json/json_writer.h>
-#include <base/values.h>
-#include <gtest/gtest.h>
-
-#include "src/commands/prop_constraints.h"
-#include "src/commands/prop_types.h"
-#include "src/commands/schema_constants.h"
-#include "src/commands/unittest_utils.h"
-
-namespace weave {
-
-using test::CreateValue;
-using test::CreateDictionaryValue;
-
-namespace {
-
-template <typename T>
-std::vector<T> GetArrayValues(const ValueVector& arr) {
-  std::vector<T> values;
-  values.reserve(arr.size());
-  for (const auto& prop_value : arr) {
-    const auto& value = static_cast<const TypedValueBase<T>&>(*prop_value);
-    values.push_back(value.GetValue());
-  }
-  return values;
-}
-
-template <typename T>
-std::vector<T> GetOneOfValues(const PropType* prop_type) {
-  auto one_of = static_cast<const ConstraintOneOf*>(
-      prop_type->GetConstraint(ConstraintType::OneOf));
-  if (!one_of)
-    return {};
-
-  return GetArrayValues<T>(one_of->set_.value);
-}
-
-bool ValidateValue(const PropType& type,
-                   const base::Value& value,
-                   ErrorPtr* error) {
-  std::unique_ptr<PropValue> val = type.CreatePropValue(value, error);
-  return val != nullptr;
-}
-
-}  // anonymous namespace
-
-TEST(CommandSchema, IntPropType_Empty) {
-  IntPropType prop;
-  EXPECT_TRUE(prop.GetConstraints().empty());
-  EXPECT_FALSE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-}
-
-TEST(CommandSchema, IntPropType_Types) {
-  IntPropType prop;
-  EXPECT_EQ(nullptr, prop.GetBoolean());
-  EXPECT_EQ(&prop, prop.GetInt());
-  EXPECT_EQ(nullptr, prop.GetDouble());
-  EXPECT_EQ(nullptr, prop.GetString());
-  EXPECT_EQ(nullptr, prop.GetObject());
-  EXPECT_EQ(nullptr, prop.GetArray());
-}
-
-TEST(CommandSchema, IntPropType_ToJson) {
-  IntPropType prop;
-  EXPECT_JSON_EQ("'integer'", *prop.ToJson(false, false));
-  EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false));
-  IntPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minimum':3}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{'minimum':3}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'maximum':-7}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'maximum':-7}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':5}").get(),
-                  &prop, nullptr);
-  EXPECT_JSON_EQ("{'maximum':5,'minimum':0}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'enum':[1,2,3]}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("[1,2,3]", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'default':123}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'default':123}", *param2.ToJson(false, false));
-}
-
-TEST(CommandSchema, IntPropType_FromJson) {
-  IntPropType prop;
-  prop.AddMinMaxConstraint(2, 8);
-  IntPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_FALSE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(2, prop.GetMinValue());
-  EXPECT_EQ(8, prop.GetMaxValue());
-  prop.AddMinMaxConstraint(-2, 30);
-  param2.FromJson(CreateDictionaryValue("{'minimum':7}").get(), &prop, nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(7, param2.GetMinValue());
-  EXPECT_EQ(30, param2.GetMaxValue());
-  param2.FromJson(CreateDictionaryValue("{'maximum':17}").get(), &prop,
-                  nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(-2, param2.GetMinValue());
-  EXPECT_EQ(17, param2.GetMaxValue());
-
-  ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':3}").get(),
-                              &prop, nullptr));
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  ASSERT_NE(nullptr, param2.GetDefaultValue());
-  EXPECT_EQ(3, param2.GetDefaultValue()->GetInt()->GetValue());
-}
-
-TEST(CommandSchema, IntPropType_Validate) {
-  IntPropType prop;
-  prop.AddMinMaxConstraint(2, 4);
-  ErrorPtr error;
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("-1"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("0"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("1"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("2"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("3"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("4"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("5"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("true"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("3.0"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'3'"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-}
-
-TEST(CommandSchema, IntPropType_CreateValue) {
-  IntPropType prop;
-  ErrorPtr error;
-  auto val = prop.CreateValue(base::FundamentalValue{2}, &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_EQ(2, val->GetValue());
-
-  val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, BoolPropType_Empty) {
-  BooleanPropType prop;
-  EXPECT_TRUE(prop.GetConstraints().empty());
-  EXPECT_FALSE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-  EXPECT_FALSE(prop.IsRequired());
-}
-
-TEST(CommandSchema, BoolPropType_Types) {
-  BooleanPropType prop;
-  EXPECT_EQ(nullptr, prop.GetInt());
-  EXPECT_EQ(&prop, prop.GetBoolean());
-  EXPECT_EQ(nullptr, prop.GetDouble());
-  EXPECT_EQ(nullptr, prop.GetString());
-  EXPECT_EQ(nullptr, prop.GetObject());
-  EXPECT_EQ(nullptr, prop.GetArray());
-}
-
-TEST(CommandSchema, BoolPropType_ToJson) {
-  BooleanPropType prop;
-  EXPECT_JSON_EQ("'boolean'", *prop.ToJson(false, false));
-  EXPECT_JSON_EQ("{'type':'boolean'}", *prop.ToJson(true, false));
-  BooleanPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'enum':[true,false]}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("[true,false]", *param2.ToJson(false, false));
-  EXPECT_JSON_EQ("{'enum':[true,false],'type':'boolean'}",
-                 *param2.ToJson(true, false));
-  param2.FromJson(CreateDictionaryValue("{'default':true}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'default':true}", *param2.ToJson(false, false));
-}
-
-TEST(CommandSchema, BoolPropType_FromJson) {
-  BooleanPropType prop;
-  prop.FromJson(CreateDictionaryValue("{'enum':[true]}").get(), &prop, nullptr);
-  BooleanPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_FALSE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(std::vector<bool>{true}, GetOneOfValues<bool>(&prop));
-
-  BooleanPropType prop_base;
-  BooleanPropType param3;
-  ASSERT_TRUE(param3.FromJson(CreateDictionaryValue("{'default':false}").get(),
-                              &prop_base, nullptr));
-  EXPECT_TRUE(param3.HasOverriddenAttributes());
-  ASSERT_NE(nullptr, param3.GetDefaultValue());
-  EXPECT_FALSE(param3.GetDefaultValue()->GetBoolean()->GetValue());
-}
-
-TEST(CommandSchema, BoolPropType_Validate) {
-  BooleanPropType prop;
-  prop.FromJson(CreateDictionaryValue("{'enum':[true]}").get(), &prop, nullptr);
-  ErrorPtr error;
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("false"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("true"), &error));
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("1"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("3.0"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'3'"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-}
-
-TEST(CommandSchema, BoolPropType_CreateValue) {
-  BooleanPropType prop;
-  ErrorPtr error;
-  auto val = prop.CreateValue(base::FundamentalValue{true}, &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(val->GetValue());
-
-  val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, DoublePropType_Empty) {
-  DoublePropType prop;
-  EXPECT_DOUBLE_EQ(std::numeric_limits<double>::lowest(), prop.GetMinValue());
-  EXPECT_DOUBLE_EQ((std::numeric_limits<double>::max)(), prop.GetMaxValue());
-  EXPECT_FALSE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-  EXPECT_FALSE(prop.IsRequired());
-}
-
-TEST(CommandSchema, DoublePropType_Types) {
-  DoublePropType prop;
-  EXPECT_EQ(nullptr, prop.GetInt());
-  EXPECT_EQ(nullptr, prop.GetBoolean());
-  EXPECT_EQ(&prop, prop.GetDouble());
-  EXPECT_EQ(nullptr, prop.GetString());
-  EXPECT_EQ(nullptr, prop.GetObject());
-  EXPECT_EQ(nullptr, prop.GetArray());
-}
-
-TEST(CommandSchema, DoublePropType_ToJson) {
-  DoublePropType prop;
-  EXPECT_JSON_EQ("'number'", *prop.ToJson(false, false));
-  EXPECT_JSON_EQ("{'type':'number'}", *prop.ToJson(true, false));
-  DoublePropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minimum':3}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{'minimum':3.0}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'maximum':-7}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'maximum':-7.0}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':5}").get(),
-                  &prop, nullptr);
-  EXPECT_JSON_EQ("{'maximum':5.0,'minimum':0.0}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'default':12.3}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'default':12.3}", *param2.ToJson(false, false));
-}
-
-TEST(CommandSchema, DoublePropType_FromJson) {
-  DoublePropType prop;
-  prop.AddMinMaxConstraint(2.5, 8.7);
-  DoublePropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_FALSE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_DOUBLE_EQ(2.5, prop.GetMinValue());
-  EXPECT_DOUBLE_EQ(8.7, prop.GetMaxValue());
-  prop.AddMinMaxConstraint(-2.2, 30.4);
-  param2.FromJson(CreateDictionaryValue("{'minimum':7}").get(), &prop, nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_DOUBLE_EQ(7.0, param2.GetMinValue());
-  EXPECT_DOUBLE_EQ(30.4, param2.GetMaxValue());
-  param2.FromJson(CreateDictionaryValue("{'maximum':17.2}").get(), &prop,
-                  nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_DOUBLE_EQ(-2.2, param2.GetMinValue());
-  EXPECT_DOUBLE_EQ(17.2, param2.GetMaxValue());
-  param2.FromJson(CreateDictionaryValue("{'minimum':0,'maximum':6.1}").get(),
-                  &prop, nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_DOUBLE_EQ(0.0, param2.GetMinValue());
-  EXPECT_DOUBLE_EQ(6.1, param2.GetMaxValue());
-
-  ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':-1.234}").get(),
-                              &prop, nullptr));
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  ASSERT_NE(nullptr, param2.GetDefaultValue());
-  EXPECT_DOUBLE_EQ(-1.234, param2.GetDefaultValue()->GetDouble()->GetValue());
-}
-
-TEST(CommandSchema, DoublePropType_Validate) {
-  DoublePropType prop;
-  prop.AddMinMaxConstraint(-1.2, 1.3);
-  ErrorPtr error;
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("-2"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("-1.3"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("-1.2"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("0.0"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("1.3"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("1.31"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("true"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'0.0'"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-}
-
-TEST(CommandSchema, DoublePropType_CreateValue) {
-  DoublePropType prop;
-  ErrorPtr error;
-  auto val = prop.CreateValue(base::FundamentalValue{2.0}, &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_DOUBLE_EQ(2.0, val->GetValue());
-
-  val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, StringPropType_Empty) {
-  StringPropType prop;
-  EXPECT_EQ(0, prop.GetMinLength());
-  EXPECT_EQ((std::numeric_limits<int>::max)(), prop.GetMaxLength());
-  EXPECT_FALSE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-  EXPECT_FALSE(prop.IsRequired());
-}
-
-TEST(CommandSchema, StringPropType_Types) {
-  StringPropType prop;
-  EXPECT_EQ(nullptr, prop.GetInt());
-  EXPECT_EQ(nullptr, prop.GetBoolean());
-  EXPECT_EQ(nullptr, prop.GetDouble());
-  EXPECT_EQ(&prop, prop.GetString());
-  EXPECT_EQ(nullptr, prop.GetObject());
-  EXPECT_EQ(nullptr, prop.GetArray());
-}
-
-TEST(CommandSchema, StringPropType_ToJson) {
-  StringPropType prop;
-  EXPECT_JSON_EQ("'string'", *prop.ToJson(false, false));
-  EXPECT_JSON_EQ("{'type':'string'}", *prop.ToJson(true, false));
-  StringPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minLength':3}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'minLength':3}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'maxLength':7}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'maxLength':7}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'minLength':0,'maxLength':5}").get(),
-                  &prop, nullptr);
-  EXPECT_JSON_EQ("{'maxLength':5,'minLength':0}", *param2.ToJson(false, false));
-  param2.FromJson(CreateDictionaryValue("{'default':'abcd'}").get(), &prop,
-                  nullptr);
-  EXPECT_JSON_EQ("{'default':'abcd'}", *param2.ToJson(false, false));
-}
-
-TEST(CommandSchema, StringPropType_FromJson) {
-  StringPropType prop;
-  prop.AddLengthConstraint(2, 8);
-  StringPropType param2;
-  param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_FALSE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(2, prop.GetMinLength());
-  EXPECT_EQ(8, prop.GetMaxLength());
-  prop.AddLengthConstraint(3, 5);
-  param2.FromJson(CreateDictionaryValue("{'minLength':4}").get(), &prop,
-                  nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(4, param2.GetMinLength());
-  EXPECT_EQ(5, param2.GetMaxLength());
-  param2.FromJson(CreateDictionaryValue("{'maxLength':8}").get(), &prop,
-                  nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(3, param2.GetMinLength());
-  EXPECT_EQ(8, param2.GetMaxLength());
-  param2.FromJson(CreateDictionaryValue("{'minLength':1,'maxLength':7}").get(),
-                  &prop, nullptr);
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  EXPECT_TRUE(param2.IsBasedOnSchema());
-  EXPECT_EQ(1, param2.GetMinLength());
-  EXPECT_EQ(7, param2.GetMaxLength());
-
-  ASSERT_TRUE(param2.FromJson(CreateDictionaryValue("{'default':'foo'}").get(),
-                              &prop, nullptr));
-  EXPECT_TRUE(param2.HasOverriddenAttributes());
-  ASSERT_NE(nullptr, param2.GetDefaultValue());
-  EXPECT_EQ("foo", param2.GetDefaultValue()->GetString()->GetValue());
-}
-
-TEST(CommandSchema, StringPropType_Validate) {
-  StringPropType prop;
-  prop.AddLengthConstraint(1, 3);
-  ErrorPtr error;
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("''"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  prop.AddLengthConstraint(2, 3);
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("''"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'a'"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'ab'"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'abc'"), &error));
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'abcd'"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-
-  prop.FromJson(CreateDictionaryValue("{'enum':['abc','def','xyz!!']}").get(),
-                nullptr, &error);
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'abc'"), &error));
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'def'"), &error));
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("'xyz!!'"), &error));
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("'xyz'"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, StringPropType_CreateValue) {
-  StringPropType prop;
-  ErrorPtr error;
-  auto val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_EQ("blah", val->GetValue());
-
-  val = prop.CreateValue(base::FundamentalValue{4}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, ObjectPropType_Empty) {
-  ObjectPropType prop;
-  EXPECT_TRUE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-  EXPECT_FALSE(prop.IsRequired());
-}
-
-TEST(CommandSchema, ObjectPropType_Types) {
-  ObjectPropType prop;
-  EXPECT_EQ(nullptr, prop.GetInt());
-  EXPECT_EQ(nullptr, prop.GetBoolean());
-  EXPECT_EQ(nullptr, prop.GetDouble());
-  EXPECT_EQ(nullptr, prop.GetString());
-  EXPECT_EQ(&prop, prop.GetObject());
-  EXPECT_EQ(nullptr, prop.GetArray());
-}
-
-TEST(CommandSchema, ObjectPropType_ToJson) {
-  ObjectPropType prop;
-  EXPECT_JSON_EQ("{'additionalProperties':false,'properties':{}}",
-                 *prop.ToJson(false, false));
-  EXPECT_JSON_EQ(
-      "{'additionalProperties':false,'properties':{},'type':'object'}",
-      *prop.ToJson(true, false));
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  ObjectPropType prop2;
-  prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *prop2.ToJson(false, false));
-  EXPECT_TRUE(prop2.IsBasedOnSchema());
-
-  auto schema = ObjectSchema::Create();
-  schema->AddProp("expires", PropType::Create(ValueType::Int));
-  auto pw = PropType::Create(ValueType::String);
-  pw->GetString()->AddLengthConstraint(6, 100);
-  schema->AddProp("password", std::move(pw));
-  prop2.SetObjectSchema(std::move(schema));
-  auto expected = R"({
-    'additionalProperties': false,
-    'properties': {
-      'expires': 'integer',
-      'password': {
-        'maxLength': 100,
-        'minLength': 6
-      }
-    }
-  })";
-  EXPECT_JSON_EQ(expected, *prop2.ToJson(false, false));
-
-  expected = R"({
-    'additionalProperties': false,
-    'properties': {
-      'expires': {
-        'type': 'integer'
-      },
-      'password': {
-        'maxLength': 100,
-        'minLength': 6,
-        'type': 'string'
-      }
-    },
-    'type': 'object'
-  })";
-  EXPECT_JSON_EQ(expected, *prop2.ToJson(true, false));
-
-  ObjectPropType prop3;
-  ASSERT_TRUE(
-      prop3.FromJson(CreateDictionaryValue(
-                         "{'default':{'expires':3,'password':'abracadabra'}}")
-                         .get(),
-                     &prop2, nullptr));
-  expected = R"({
-    'default': {
-      'expires': 3,
-      'password': 'abracadabra'
-    }
-  })";
-  EXPECT_JSON_EQ(expected, *prop3.ToJson(false, false));
-
-  expected = R"({
-    'additionalProperties': false,
-    'default': {
-      'expires': 3,
-      'password': 'abracadabra'
-    },
-    'properties': {
-      'expires': {
-        'type': 'integer'
-      },
-      'password': {
-        'maxLength': 100,
-        'minLength': 6,
-        'type': 'string'
-      }
-    },
-    'type': 'object'
-  })";
-  EXPECT_JSON_EQ(expected, *prop3.ToJson(true, false));
-
-  ObjectPropType prop4;
-  ASSERT_TRUE(prop4.FromJson(
-      CreateDictionaryValue("{'additionalProperties':true,"
-                            "'default':{'expires':3,'password':'abracadabra'}}")
-          .get(),
-      &prop2, nullptr));
-  expected = R"({
-    'additionalProperties': true,
-    'default': {
-      'expires': 3,
-      'password': 'abracadabra'
-    },
-    'properties': {
-      'expires': 'integer',
-      'password': {
-        'maxLength': 100,
-        'minLength': 6
-      }
-    }
-  })";
-  EXPECT_JSON_EQ(expected, *prop4.ToJson(false, false));
-
-  expected = R"({
-    'additionalProperties': true,
-    'default': {
-      'expires': 3,
-      'password': 'abracadabra'
-    },
-    'properties': {
-      'expires': {
-        'type': 'integer'
-      },
-      'password': {
-        'maxLength': 100,
-        'minLength': 6,
-        'type': 'string'
-      }
-    },
-    'type': 'object'
-  })";
-  EXPECT_JSON_EQ(expected, *prop4.ToJson(true, false));
-}
-
-TEST(CommandSchema, ObjectPropType_FromJson) {
-  ObjectPropType base_prop;
-  EXPECT_TRUE(base_prop.FromJson(
-      CreateDictionaryValue("{'properties':{'name':'string','age':'integer'}}")
-          .get(),
-      nullptr, nullptr));
-  auto schema = base_prop.GetObject()->GetObjectSchemaPtr();
-  const PropType* prop = schema->GetProp("name");
-  EXPECT_EQ(ValueType::String, prop->GetType());
-  prop = schema->GetProp("age");
-  EXPECT_EQ(ValueType::Int, prop->GetType());
-
-  ObjectPropType prop2;
-  ASSERT_TRUE(prop2.FromJson(
-      CreateDictionaryValue("{'properties':{'name':'string','age':'integer'},"
-                            "'default':{'name':'Bob','age':33}}")
-          .get(),
-      nullptr, nullptr));
-  ASSERT_NE(nullptr, prop2.GetDefaultValue());
-  const ObjectValue* defval = prop2.GetDefaultValue()->GetObject();
-  ASSERT_NE(nullptr, defval);
-  ValueMap objval = defval->GetValue();
-  EXPECT_EQ("Bob", objval["name"]->GetString()->GetValue());
-  EXPECT_EQ(33, objval["age"]->GetInt()->GetValue());
-}
-
-TEST(CommandSchema, ObjectPropType_Validate) {
-  ObjectPropType prop;
-  prop.FromJson(
-      CreateDictionaryValue("{'properties':{'expires':'integer',"
-                            "'password':{'maxLength':100,'minLength':6}},"
-                            "'required':['expires','password']}")
-          .get(),
-      nullptr, nullptr);
-  ErrorPtr error;
-  EXPECT_TRUE(ValidateValue(
-      prop, *CreateValue("{'expires':10,'password':'abcdef'}"), &error));
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("{'expires':10}"), &error));
-  EXPECT_EQ("parameter_missing", error->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(
-      ValidateValue(prop, *CreateValue("{'password':'abcdef'}"), &error));
-  EXPECT_EQ("parameter_missing", error->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(
-      prop, *CreateValue("{'expires':10,'password':'abcde'}"), &error));
-  EXPECT_EQ("out_of_range", error->GetFirstError()->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("2"), &error));
-  EXPECT_EQ("type_mismatch", error->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(
-      prop, *CreateValue("{'expires':10,'password':'abcdef','retry':true}"),
-      &error));
-  EXPECT_EQ("unexpected_parameter", error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, ObjectPropType_Validate_Enum) {
-  ObjectPropType prop;
-  EXPECT_TRUE(prop.FromJson(
-      CreateDictionaryValue(
-          "{'properties':{'width':'integer','height':'integer'},"
-          "'enum':[{'width':10,'height':20},{'width':100,'height':200}]}")
-          .get(),
-      nullptr, nullptr));
-  ErrorPtr error;
-  EXPECT_TRUE(
-      ValidateValue(prop, *CreateValue("{'height':20,'width':10}"), &error));
-  error.reset();
-
-  EXPECT_TRUE(
-      ValidateValue(prop, *CreateValue("{'height':200,'width':100}"), &error));
-  error.reset();
-
-  EXPECT_FALSE(
-      ValidateValue(prop, *CreateValue("{'height':12,'width':10}"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, ObjectPropType_CreateValue) {
-  ObjectPropType prop;
-  IntPropType int_type;
-  ASSERT_TRUE(prop.FromJson(
-      CreateDictionaryValue(
-          "{'properties':{'width':'integer','height':'integer'},"
-          "'enum':[{'width':10,'height':20},{'width':100,'height':200}]}")
-          .get(),
-      nullptr, nullptr));
-  ValueMap obj{
-      {"width", int_type.CreateValue(base::FundamentalValue{10}, nullptr)},
-      {"height", int_type.CreateValue(base::FundamentalValue{20}, nullptr)},
-  };
-
-  ErrorPtr error;
-  auto val = prop.CreateValue(
-      *CreateDictionaryValue("{'width': 10, 'height': 20}"), &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_EQ(obj, val->GetValue());
-
-  val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, ArrayPropType_Empty) {
-  ArrayPropType prop;
-  EXPECT_FALSE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_EQ(nullptr, prop.GetDefaultValue());
-  EXPECT_EQ(nullptr, prop.GetItemTypePtr());
-  prop.SetItemType(PropType::Create(ValueType::Int));
-  EXPECT_TRUE(prop.HasOverriddenAttributes());
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  EXPECT_NE(nullptr, prop.GetItemTypePtr());
-  EXPECT_FALSE(prop.IsRequired());
-}
-
-TEST(CommandSchema, ArrayPropType_Types) {
-  ArrayPropType prop;
-  EXPECT_EQ(nullptr, prop.GetInt());
-  EXPECT_EQ(nullptr, prop.GetBoolean());
-  EXPECT_EQ(nullptr, prop.GetDouble());
-  EXPECT_EQ(nullptr, prop.GetString());
-  EXPECT_EQ(nullptr, prop.GetObject());
-  EXPECT_EQ(&prop, prop.GetArray());
-}
-
-TEST(CommandSchema, ArrayPropType_ToJson) {
-  ArrayPropType prop;
-  prop.SetItemType(PropType::Create(ValueType::Int));
-  EXPECT_JSON_EQ("{'items':'integer'}", *prop.ToJson(false, false));
-  EXPECT_JSON_EQ("{'items':{'type':'integer'},'type':'array'}",
-                 *prop.ToJson(true, false));
-  EXPECT_FALSE(prop.IsBasedOnSchema());
-  ArrayPropType prop2;
-  prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
-  EXPECT_JSON_EQ("{}", *prop2.ToJson(false, false));
-  EXPECT_TRUE(prop2.IsBasedOnSchema());
-  prop2.FromJson(CreateDictionaryValue("{'default':[1,2,3]}").get(), &prop,
-                 nullptr);
-  EXPECT_JSON_EQ("{'default':[1,2,3]}", *prop2.ToJson(false, false));
-  EXPECT_JSON_EQ(
-      "{'default':[1,2,3],'items':{'type':'integer'},'type':'array'}",
-      *prop2.ToJson(true, false));
-}
-
-TEST(CommandSchema, ArrayPropType_FromJson) {
-  ArrayPropType prop;
-  EXPECT_TRUE(prop.FromJson(CreateDictionaryValue("{'items':'integer'}").get(),
-                            nullptr, nullptr));
-  EXPECT_EQ(ValueType::Int, prop.GetItemTypePtr()->GetType());
-
-  ArrayPropType prop2;
-  ASSERT_TRUE(
-      prop2.FromJson(CreateDictionaryValue(
-                         "{'items':'string','default':['foo', 'bar', 'baz']}")
-                         .get(),
-                     nullptr, nullptr));
-  ASSERT_NE(nullptr, prop2.GetDefaultValue());
-  const ArrayValue* defval = prop2.GetDefaultValue()->GetArray();
-  ASSERT_NE(nullptr, defval);
-  EXPECT_EQ((std::vector<std::string>{"foo", "bar", "baz"}),
-            GetArrayValues<std::string>(defval->GetValue()));
-}
-
-TEST(CommandSchema, ArrayPropType_Validate) {
-  ArrayPropType prop;
-  prop.FromJson(
-      CreateDictionaryValue("{'items':{'minimum':2.3, 'maximum':10.5}}").get(),
-      nullptr, nullptr);
-
-  ErrorPtr error;
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("[3,4,10.5]"), &error));
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2]"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[4, 5, 20]"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, ArrayPropType_Validate_Enum) {
-  ArrayPropType prop;
-  prop.FromJson(
-      CreateDictionaryValue("{'items':'integer', 'enum':[[1], [2,3], [4,5,6]]}")
-          .get(),
-      nullptr, nullptr);
-
-  ErrorPtr error;
-  EXPECT_TRUE(ValidateValue(prop, *CreateValue("[2,3]"), &error));
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2]"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-
-  EXPECT_FALSE(ValidateValue(prop, *CreateValue("[2,3,4]"), &error));
-  EXPECT_EQ("out_of_range", error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, ArrayPropType_CreateValue) {
-  ArrayPropType prop;
-  ASSERT_TRUE(prop.FromJson(
-      CreateDictionaryValue(
-          "{'items':{'properties':{'width':'integer','height':'integer'}}}")
-          .get(),
-      nullptr, nullptr));
-
-  ErrorPtr error;
-  ValueVector arr;
-
-  auto val = prop.CreateValue(base::ListValue{}, &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_EQ(arr, val->GetValue());
-  EXPECT_JSON_EQ("[]", *val->ToJson());
-
-  val = prop.CreateValue(
-      *CreateValue("[{'height':20,'width':10},{'width':17, 'height':18}]"),
-      &error);
-  ASSERT_NE(nullptr, val.get());
-  EXPECT_EQ(nullptr, error.get());
-  EXPECT_JSON_EQ("[{'height':20,'width':10},{'height':18,'width':17}]",
-                 *val->ToJson());
-
-  val = prop.CreateValue(base::StringValue{"blah"}, &error);
-  EXPECT_EQ(nullptr, val.get());
-  ASSERT_NE(nullptr, error.get());
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-}
-
-TEST(CommandSchema, ArrayPropType_NestedArrays_NotSupported) {
-  ArrayPropType prop;
-  ErrorPtr error;
-  EXPECT_FALSE(prop.FromJson(
-      CreateDictionaryValue("{'items':{'items':'integer'}}").get(), nullptr,
-      &error));
-  EXPECT_EQ(errors::commands::kInvalidObjectSchema, error->GetCode());
-  error.reset();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeName) {
-  ObjectSchema schema;
-  const char* schema_str =
-      "{"
-      "'param1':'integer',"
-      "'param2':'number',"
-      "'param3':'string'"
-      "}";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                              nullptr));
-  EXPECT_EQ(ValueType::Int, schema.GetProp("param1")->GetType());
-  EXPECT_EQ(ValueType::Double, schema.GetProp("param2")->GetType());
-  EXPECT_EQ(ValueType::String, schema.GetProp("param3")->GetType());
-  EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString());
-  EXPECT_EQ(nullptr, schema.GetProp("param4"));
-
-  int min_int = (std::numeric_limits<int>::min)();
-  int max_int = (std::numeric_limits<int>::max)();
-  double min_dbl = (std::numeric_limits<double>::lowest)();
-  double max_dbl = (std::numeric_limits<double>::max)();
-  EXPECT_EQ(min_int, schema.GetProp("param1")->GetInt()->GetMinValue());
-  EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue());
-  EXPECT_EQ(min_dbl, schema.GetProp("param2")->GetDouble()->GetMinValue());
-  EXPECT_EQ(max_dbl, schema.GetProp("param2")->GetDouble()->GetMaxValue());
-  EXPECT_EQ(0, schema.GetProp("param3")->GetString()->GetMinLength());
-  EXPECT_EQ(max_int, schema.GetProp("param3")->GetString()->GetMaxLength());
-}
-
-TEST(CommandSchema, ObjectSchema_FromJson_Full_TypeName) {
-  ObjectSchema schema;
-  const char* schema_str =
-      "{"
-      "'param1':{'type':'integer'},"
-      "'param2':{'type':'number'},"
-      "'param3':{'type':'string'},"
-      "'param4':{'type':'array', 'items':'integer'},"
-      "'param5':{'type':'object', 'properties':{'p1':'integer'}}"
-      "}";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                              nullptr));
-  EXPECT_EQ(ValueType::Int, schema.GetProp("param1")->GetType());
-  EXPECT_EQ(ValueType::Double, schema.GetProp("param2")->GetType());
-  EXPECT_EQ(ValueType::String, schema.GetProp("param3")->GetType());
-  EXPECT_EQ(ValueType::Array, schema.GetProp("param4")->GetType());
-  EXPECT_EQ(ValueType::Object, schema.GetProp("param5")->GetType());
-  EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString());
-  EXPECT_EQ("array", schema.GetProp("param4")->GetTypeAsString());
-  EXPECT_EQ("object", schema.GetProp("param5")->GetTypeAsString());
-  EXPECT_EQ(nullptr, schema.GetProp("param77"));
-
-  int min_int = (std::numeric_limits<int>::min)();
-  int max_int = (std::numeric_limits<int>::max)();
-  double min_dbl = (std::numeric_limits<double>::lowest)();
-  double max_dbl = (std::numeric_limits<double>::max)();
-  EXPECT_EQ(min_int, schema.GetProp("param1")->GetInt()->GetMinValue());
-  EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue());
-  EXPECT_EQ(min_dbl, schema.GetProp("param2")->GetDouble()->GetMinValue());
-  EXPECT_EQ(max_dbl, schema.GetProp("param2")->GetDouble()->GetMaxValue());
-  EXPECT_EQ(0, schema.GetProp("param3")->GetString()->GetMinLength());
-  EXPECT_EQ(max_int, schema.GetProp("param3")->GetString()->GetMaxLength());
-}
-
-TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeDeduction_Scalar) {
-  ObjectSchema schema;
-  const char* schema_str =
-      "{"
-      "'param1' :{'minimum':2},"
-      "'param2' :{'maximum':10},"
-      "'param3' :{'maximum':8, 'minimum':2},"
-      "'param4' :{'minimum':2.1},"
-      "'param5' :{'maximum':10.1},"
-      "'param6' :{'maximum':8.1, 'minimum':3.1},"
-      "'param7' :{'maximum':8, 'minimum':3.1},"
-      "'param8' :{'maximum':8.1, 'minimum':3},"
-      "'param9' :{'minLength':2},"
-      "'param10':{'maxLength':10},"
-      "'param11':{'maxLength':8, 'minLength':3},"
-      "'param12':{'default':12},"
-      "'param13':{'default':13.5},"
-      "'param14':{'default':true},"
-      "'param15':{'default':false},"
-      "'param16':{'default':'foobar'},"
-      "'param17':{'default':[1,2,3]},"
-      "'param18':{'items':'number', 'default':[]}"
-      "}";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                              nullptr));
-  EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param2")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param3")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param4")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param6")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param7")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param9")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param10")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param11")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param12")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param13")->GetTypeAsString());
-  EXPECT_EQ("boolean", schema.GetProp("param14")->GetTypeAsString());
-  EXPECT_EQ("boolean", schema.GetProp("param15")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param16")->GetTypeAsString());
-  EXPECT_EQ("array", schema.GetProp("param17")->GetTypeAsString());
-  auto prop17 = schema.GetProp("param17");
-  EXPECT_EQ("integer", prop17->GetArray()->GetItemTypePtr()->GetTypeAsString());
-  EXPECT_EQ("array", schema.GetProp("param18")->GetTypeAsString());
-  auto prop18 = schema.GetProp("param18");
-  EXPECT_EQ("number", prop18->GetArray()->GetItemTypePtr()->GetTypeAsString());
-
-  int min_int = (std::numeric_limits<int>::min)();
-  int max_int = (std::numeric_limits<int>::max)();
-  double min_dbl = (std::numeric_limits<double>::lowest)();
-  double max_dbl = (std::numeric_limits<double>::max)();
-  EXPECT_EQ(2, schema.GetProp("param1")->GetInt()->GetMinValue());
-  EXPECT_EQ(max_int, schema.GetProp("param1")->GetInt()->GetMaxValue());
-  EXPECT_EQ(min_int, schema.GetProp("param2")->GetInt()->GetMinValue());
-  EXPECT_EQ(10, schema.GetProp("param2")->GetInt()->GetMaxValue());
-  EXPECT_EQ(2, schema.GetProp("param3")->GetInt()->GetMinValue());
-  EXPECT_EQ(8, schema.GetProp("param3")->GetInt()->GetMaxValue());
-  EXPECT_DOUBLE_EQ(2.1, schema.GetProp("param4")->GetDouble()->GetMinValue());
-  EXPECT_DOUBLE_EQ(max_dbl,
-                   schema.GetProp("param4")->GetDouble()->GetMaxValue());
-  EXPECT_DOUBLE_EQ(min_dbl,
-                   schema.GetProp("param5")->GetDouble()->GetMinValue());
-  EXPECT_DOUBLE_EQ(10.1, schema.GetProp("param5")->GetDouble()->GetMaxValue());
-  EXPECT_DOUBLE_EQ(3.1, schema.GetProp("param6")->GetDouble()->GetMinValue());
-  EXPECT_DOUBLE_EQ(8.1, schema.GetProp("param6")->GetDouble()->GetMaxValue());
-  EXPECT_DOUBLE_EQ(3.1, schema.GetProp("param7")->GetDouble()->GetMinValue());
-  EXPECT_DOUBLE_EQ(8.0, schema.GetProp("param7")->GetDouble()->GetMaxValue());
-  EXPECT_DOUBLE_EQ(3.0, schema.GetProp("param8")->GetDouble()->GetMinValue());
-  EXPECT_DOUBLE_EQ(8.1, schema.GetProp("param8")->GetDouble()->GetMaxValue());
-  EXPECT_EQ(2, schema.GetProp("param9")->GetString()->GetMinLength());
-  EXPECT_EQ(max_int, schema.GetProp("param9")->GetString()->GetMaxLength());
-  EXPECT_EQ(0, schema.GetProp("param10")->GetString()->GetMinLength());
-  EXPECT_EQ(10, schema.GetProp("param10")->GetString()->GetMaxLength());
-  EXPECT_EQ(3, schema.GetProp("param11")->GetString()->GetMinLength());
-  EXPECT_EQ(8, schema.GetProp("param11")->GetString()->GetMaxLength());
-  const PropValue* val = schema.GetProp("param12")->GetDefaultValue();
-  EXPECT_EQ(12, val->GetInt()->GetValue());
-  val = schema.GetProp("param13")->GetDefaultValue();
-  EXPECT_DOUBLE_EQ(13.5, val->GetDouble()->GetValue());
-  val = schema.GetProp("param14")->GetDefaultValue();
-  EXPECT_TRUE(val->GetBoolean()->GetValue());
-  val = schema.GetProp("param15")->GetDefaultValue();
-  EXPECT_FALSE(val->GetBoolean()->GetValue());
-  val = schema.GetProp("param16")->GetDefaultValue();
-  EXPECT_EQ("foobar", val->GetString()->GetValue());
-  val = schema.GetProp("param17")->GetDefaultValue();
-  EXPECT_EQ((std::vector<int>{1, 2, 3}),
-            GetArrayValues<int>(val->GetArray()->GetValue()));
-  val = schema.GetProp("param18")->GetDefaultValue();
-  EXPECT_TRUE(val->GetArray()->GetValue().empty());
-}
-
-TEST(CommandSchema, ObjectSchema_FromJson_Shorthand_TypeDeduction_Array) {
-  ObjectSchema schema;
-  const char* schema_str =
-      "{"
-      "'param1' :[0,1,2,3],"
-      "'param2' :[0.0,1.1,2.2],"
-      "'param3' :['id1', 'id2'],"
-      "'param4' :{'enum':[1,2,3]},"
-      "'param5' :{'enum':[-1.1,2.2,3]},"
-      "'param6' :{'enum':['id0', 'id1']},"
-      "'param7' :{'type':'integer', 'enum':[1,2,3]},"
-      "'param8' :{'type':'number',  'enum':[1,2,3]},"
-      "'param9' :{'type':'number',  'enum':[]},"
-      "'param10':{'type':'integer', 'enum':[]},"
-      "'param11':[[0,1],[2,3]],"
-      "'param12':[['foo','bar']],"
-      "'param13':{'enum':[['id0', 'id1']]}"
-      "}";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                              nullptr));
-  EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param2")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param3")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param4")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString());
-  EXPECT_EQ("string", schema.GetProp("param6")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param7")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString());
-  EXPECT_EQ("number", schema.GetProp("param9")->GetTypeAsString());
-  EXPECT_EQ("integer", schema.GetProp("param10")->GetTypeAsString());
-
-  auto prop_type11 = schema.GetProp("param11");
-  EXPECT_EQ("array", prop_type11->GetTypeAsString());
-  EXPECT_EQ("integer",
-            prop_type11->GetArray()->GetItemTypePtr()->GetTypeAsString());
-
-  auto prop_type12 = schema.GetProp("param12");
-  EXPECT_EQ("array", prop_type12->GetTypeAsString());
-  EXPECT_EQ("string",
-            prop_type12->GetArray()->GetItemTypePtr()->GetTypeAsString());
-
-  auto prop_type13 = schema.GetProp("param13");
-  EXPECT_EQ("array", prop_type12->GetTypeAsString());
-  EXPECT_EQ("string",
-            prop_type13->GetArray()->GetItemTypePtr()->GetTypeAsString());
-
-  EXPECT_EQ((std::vector<int>{0, 1, 2, 3}),
-            GetOneOfValues<int>(schema.GetProp("param1")));
-  EXPECT_EQ((std::vector<double>{0.0, 1.1, 2.2}),
-            GetOneOfValues<double>(schema.GetProp("param2")));
-  EXPECT_EQ((std::vector<std::string>{"id1", "id2"}),
-            GetOneOfValues<std::string>(schema.GetProp("param3")));
-
-  EXPECT_EQ((std::vector<int>{1, 2, 3}),
-            GetOneOfValues<int>(schema.GetProp("param4")));
-  EXPECT_EQ((std::vector<double>{-1.1, 2.2, 3.0}),
-            GetOneOfValues<double>(schema.GetProp("param5")));
-  EXPECT_EQ((std::vector<std::string>{"id0", "id1"}),
-            GetOneOfValues<std::string>(schema.GetProp("param6")));
-  EXPECT_EQ((std::vector<int>{1, 2, 3}),
-            GetOneOfValues<int>(schema.GetProp("param7")));
-  EXPECT_EQ((std::vector<double>{1.0, 2.0, 3.0}),
-            GetOneOfValues<double>(schema.GetProp("param8")));
-  EXPECT_TRUE(GetOneOfValues<double>(schema.GetProp("param9")).empty());
-  EXPECT_TRUE(GetOneOfValues<int>(schema.GetProp("param10")).empty());
-}
-
-TEST(CommandSchema, ObjectSchema_FromJson_Inheritance) {
-  const char* base_schema_str =
-      "{"
-      "'param0' :{'minimum':1, 'maximum':5},"
-      "'param1' :{'minimum':1, 'maximum':5},"
-      "'param2' :{'minimum':1, 'maximum':5},"
-      "'param3' :{'minimum':1, 'maximum':5},"
-      "'param4' :{'minimum':1, 'maximum':5},"
-      "'param5' :{'minimum':1.1, 'maximum':5.5},"
-      "'param6' :{'minimum':1.1, 'maximum':5.5},"
-      "'param7' :{'minimum':1.1, 'maximum':5.5},"
-      "'param8' :{'minimum':1.1, 'maximum':5.5},"
-      "'param9' :{'minLength':1, 'maxLength':5},"
-      "'param10':{'minLength':1, 'maxLength':5},"
-      "'param11':{'minLength':1, 'maxLength':5},"
-      "'param12':{'minLength':1, 'maxLength':5},"
-      "'param13':[1,2,3],"
-      "'param14':[1,2,3],"
-      "'param15':[1.1,2.2,3.3],"
-      "'param16':[1.1,2.2,3.3],"
-      "'param17':['id1', 'id2'],"
-      "'param18':['id1', 'id2'],"
-      "'param19':{'minimum':1, 'maximum':5},"
-      "'param20':{'default':49},"
-      "'param21':{'default':49},"
-      "'param22':'integer'"
-      "}";
-  ObjectSchema base_schema;
-  EXPECT_TRUE(base_schema.FromJson(CreateDictionaryValue(base_schema_str).get(),
-                                   nullptr, nullptr));
-  const char* schema_str =
-      "{"
-      "'param1' :{},"
-      "'param2' :{'minimum':2},"
-      "'param3' :{'maximum':9},"
-      "'param4' :{'minimum':2, 'maximum':9},"
-      "'param5' :{},"
-      "'param6' :{'minimum':2.2},"
-      "'param7' :{'maximum':9.9},"
-      "'param8' :{'minimum':2.2, 'maximum':9.9},"
-      "'param9' :{},"
-      "'param10':{'minLength':3},"
-      "'param11':{'maxLength':8},"
-      "'param12':{'minLength':3, 'maxLength':8},"
-      "'param13':{},"
-      "'param14':[1,2,3,4],"
-      "'param15':{},"
-      "'param16':[1.1,2.2,3.3,4.4],"
-      "'param17':{},"
-      "'param18':['id1', 'id3'],"
-      "'param19':{},"
-      "'param20':{},"
-      "'param21':{'default':8},"
-      "'param22':{'default':123}"
-      "}";
-  ObjectSchema schema;
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(),
-                              &base_schema, nullptr));
-  EXPECT_EQ(nullptr, schema.GetProp("param0"));
-  EXPECT_NE(nullptr, schema.GetProp("param1"));
-  EXPECT_EQ("integer", schema.GetProp("param1")->GetTypeAsString());
-  EXPECT_EQ(1, schema.GetProp("param1")->GetInt()->GetMinValue());
-  EXPECT_EQ(5, schema.GetProp("param1")->GetInt()->GetMaxValue());
-  EXPECT_EQ("integer", schema.GetProp("param2")->GetTypeAsString());
-  EXPECT_EQ(2, schema.GetProp("param2")->GetInt()->GetMinValue());
-  EXPECT_EQ(5, schema.GetProp("param2")->GetInt()->GetMaxValue());
-  EXPECT_EQ("integer", schema.GetProp("param3")->GetTypeAsString());
-  EXPECT_EQ(1, schema.GetProp("param3")->GetInt()->GetMinValue());
-  EXPECT_EQ(9, schema.GetProp("param3")->GetInt()->GetMaxValue());
-  EXPECT_EQ("integer", schema.GetProp("param4")->GetTypeAsString());
-  EXPECT_EQ(2, schema.GetProp("param4")->GetInt()->GetMinValue());
-  EXPECT_EQ(9, schema.GetProp("param4")->GetInt()->GetMaxValue());
-  EXPECT_EQ("number", schema.GetProp("param5")->GetTypeAsString());
-  EXPECT_EQ(1.1, schema.GetProp("param5")->GetDouble()->GetMinValue());
-  EXPECT_EQ(5.5, schema.GetProp("param5")->GetDouble()->GetMaxValue());
-  EXPECT_EQ("number", schema.GetProp("param6")->GetTypeAsString());
-  EXPECT_EQ(2.2, schema.GetProp("param6")->GetDouble()->GetMinValue());
-  EXPECT_EQ(5.5, schema.GetProp("param6")->GetDouble()->GetMaxValue());
-  EXPECT_EQ("number", schema.GetProp("param7")->GetTypeAsString());
-  EXPECT_EQ(1.1, schema.GetProp("param7")->GetDouble()->GetMinValue());
-  EXPECT_EQ(9.9, schema.GetProp("param7")->GetDouble()->GetMaxValue());
-  EXPECT_EQ("number", schema.GetProp("param8")->GetTypeAsString());
-  EXPECT_EQ(2.2, schema.GetProp("param8")->GetDouble()->GetMinValue());
-  EXPECT_EQ(9.9, schema.GetProp("param8")->GetDouble()->GetMaxValue());
-  EXPECT_EQ("string", schema.GetProp("param9")->GetTypeAsString());
-  EXPECT_EQ(1, schema.GetProp("param9")->GetString()->GetMinLength());
-  EXPECT_EQ(5, schema.GetProp("param9")->GetString()->GetMaxLength());
-  EXPECT_EQ("string", schema.GetProp("param10")->GetTypeAsString());
-  EXPECT_EQ(3, schema.GetProp("param10")->GetString()->GetMinLength());
-  EXPECT_EQ(5, schema.GetProp("param10")->GetString()->GetMaxLength());
-  EXPECT_EQ("string", schema.GetProp("param11")->GetTypeAsString());
-  EXPECT_EQ(1, schema.GetProp("param11")->GetString()->GetMinLength());
-  EXPECT_EQ(8, schema.GetProp("param11")->GetString()->GetMaxLength());
-  EXPECT_EQ("string", schema.GetProp("param12")->GetTypeAsString());
-  EXPECT_EQ(3, schema.GetProp("param12")->GetString()->GetMinLength());
-  EXPECT_EQ(8, schema.GetProp("param12")->GetString()->GetMaxLength());
-  EXPECT_EQ("integer", schema.GetProp("param13")->GetTypeAsString());
-  EXPECT_EQ((std::vector<int>{1, 2, 3}),
-            GetOneOfValues<int>(schema.GetProp("param13")));
-  EXPECT_EQ("integer", schema.GetProp("param14")->GetTypeAsString());
-  EXPECT_EQ((std::vector<int>{1, 2, 3, 4}),
-            GetOneOfValues<int>(schema.GetProp("param14")));
-  EXPECT_EQ("number", schema.GetProp("param15")->GetTypeAsString());
-  EXPECT_EQ((std::vector<double>{1.1, 2.2, 3.3}),
-            GetOneOfValues<double>(schema.GetProp("param15")));
-  EXPECT_EQ("number", schema.GetProp("param16")->GetTypeAsString());
-  EXPECT_EQ((std::vector<double>{1.1, 2.2, 3.3, 4.4}),
-            GetOneOfValues<double>(schema.GetProp("param16")));
-  EXPECT_EQ("string", schema.GetProp("param17")->GetTypeAsString());
-  EXPECT_EQ((std::vector<std::string>{"id1", "id2"}),
-            GetOneOfValues<std::string>(schema.GetProp("param17")));
-  EXPECT_EQ("string", schema.GetProp("param18")->GetTypeAsString());
-  EXPECT_EQ((std::vector<std::string>{"id1", "id3"}),
-            GetOneOfValues<std::string>(schema.GetProp("param18")));
-  EXPECT_EQ("integer", schema.GetProp("param19")->GetTypeAsString());
-  EXPECT_EQ(1, schema.GetProp("param19")->GetInt()->GetMinValue());
-  EXPECT_EQ(5, schema.GetProp("param19")->GetInt()->GetMaxValue());
-  EXPECT_EQ(49,
-            schema.GetProp("param20")->GetDefaultValue()->GetInt()->GetValue());
-  EXPECT_EQ(8,
-            schema.GetProp("param21")->GetDefaultValue()->GetInt()->GetValue());
-  EXPECT_EQ(123,
-            schema.GetProp("param22")->GetDefaultValue()->GetInt()->GetValue());
-}
-
-TEST(CommandSchema, ObjectSchema_UseDefaults) {
-  ObjectPropType prop;
-  const char* schema_str =
-      "{'properties':{"
-      "'param1':{'default':true},"
-      "'param2':{'default':2},"
-      "'param3':{'default':3.3},"
-      "'param4':{'default':'four'},"
-      "'param5':{'default':{'x':5,'y':6},"
-      "'properties':{'x':'integer','y':'integer'}},"
-      "'param6':{'default':[1,2,3]}"
-      "}}";
-  ASSERT_TRUE(
-      prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr));
-
-  // Omit all.
-  auto value =
-      prop.CreatePropValue(*CreateDictionaryValue("{}").get(), nullptr);
-  ASSERT_NE(nullptr, value);
-  ValueMap obj = value->GetObject()->GetValue();
-  EXPECT_TRUE(obj["param1"]->GetBoolean()->GetValue());
-  EXPECT_EQ(2, obj["param2"]->GetInt()->GetValue());
-  EXPECT_DOUBLE_EQ(3.3, obj["param3"]->GetDouble()->GetValue());
-  EXPECT_EQ("four", obj["param4"]->GetString()->GetValue());
-  ValueMap param5 = obj["param5"]->GetObject()->GetValue();
-  EXPECT_EQ(5, param5["x"]->GetInt()->GetValue());
-  EXPECT_EQ(6, param5["y"]->GetInt()->GetValue());
-  ValueVector param6 = obj["param6"]->GetArray()->GetValue();
-  EXPECT_EQ((std::vector<int>{1, 2, 3}), GetArrayValues<int>(param6));
-
-  // Specify some.
-  const char* val_json =
-      "{"
-      "'param1':false,"
-      "'param3':33.3,"
-      "'param5':{'x':-5,'y':-6}"
-      "}";
-  value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
-  ASSERT_NE(nullptr, value);
-  obj = value->GetObject()->GetValue();
-  EXPECT_FALSE(obj["param1"]->GetBoolean()->GetValue());
-  EXPECT_EQ(2, obj["param2"]->GetInt()->GetValue());
-  EXPECT_DOUBLE_EQ(33.3, obj["param3"]->GetDouble()->GetValue());
-  EXPECT_EQ("four", obj["param4"]->GetString()->GetValue());
-  param5 = obj["param5"]->GetObject()->GetValue();
-  EXPECT_EQ(-5, param5["x"]->GetInt()->GetValue());
-  EXPECT_EQ(-6, param5["y"]->GetInt()->GetValue());
-  param6 = obj["param6"]->GetArray()->GetValue();
-  EXPECT_EQ((std::vector<int>{1, 2, 3}), GetArrayValues<int>(param6));
-
-  // Specify all.
-  val_json =
-      "{"
-      "'param1':false,"
-      "'param2':22,"
-      "'param3':333.3,"
-      "'param4':'FOUR',"
-      "'param5':{'x':-55,'y':66},"
-      "'param6':[-1, 0]"
-      "}";
-  value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
-  ASSERT_NE(nullptr, value);
-  obj = value->GetObject()->GetValue();
-  EXPECT_FALSE(obj["param1"]->GetBoolean()->GetValue());
-  EXPECT_EQ(22, obj["param2"]->GetInt()->GetValue());
-  EXPECT_DOUBLE_EQ(333.3, obj["param3"]->GetDouble()->GetValue());
-  EXPECT_EQ("FOUR", obj["param4"]->GetString()->GetValue());
-  param5 = obj["param5"]->GetObject()->GetValue();
-  EXPECT_EQ(-55, param5["x"]->GetInt()->GetValue());
-  EXPECT_EQ(66, param5["y"]->GetInt()->GetValue());
-  param6 = obj["param6"]->GetArray()->GetValue();
-  EXPECT_EQ((std::vector<int>{-1, 0}), GetArrayValues<int>(param6));
-}
-
-TEST(CommandSchema, ObjectSchema_FromJson_BaseSchema_Failures) {
-  ObjectSchema schema;
-  ErrorPtr error;
-  const char* schema_str =
-      "{"
-      "'param1':{}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'type':'foo'}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("unknown_type", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':[]"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'minimum':'foo'}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':[1,2.2]"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'minimum':1, 'enum':[1,2,3]}"  // can't have min/max & enum.
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("unexpected_parameter", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'maximum':1, 'blah':2}"  // 'blah' is unexpected.
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("unexpected_parameter", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'enum':[1,2,3],'default':5}"  // 'default' must be 1, 2, or 3.
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("out_of_range", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':[[1,2.3]]"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':[[1,2],[3,4],['blah']]"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("type_mismatch", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'default':[]}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':[[[1]],[[2]]]"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'enum':[[['foo']]]}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-
-  schema_str =
-      "{"
-      "'param1':{'default':[[1],[2]]}"
-      "}";
-  EXPECT_FALSE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                               &error));
-  EXPECT_EQ("no_type_info", error->GetFirstError()->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, RequiredProperties_Integral) {
-  IntPropType prop;
-
-  prop.MakeRequired(false);
-  EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false));
-  EXPECT_JSON_EQ("{'isRequired':false,'type':'integer'}",
-                 *prop.ToJson(true, true));
-
-  prop.MakeRequired(true);
-  EXPECT_JSON_EQ("{'type':'integer'}", *prop.ToJson(true, false));
-  EXPECT_JSON_EQ("{'isRequired':true,'type':'integer'}",
-                 *prop.ToJson(true, true));
-
-  IntPropType prop2;
-  EXPECT_TRUE(
-      prop2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr));
-  EXPECT_TRUE(prop2.IsRequired());
-
-  EXPECT_TRUE(prop2.FromJson(
-      CreateDictionaryValue("{'isRequired': false}").get(), &prop, nullptr));
-  EXPECT_FALSE(prop2.IsRequired());
-
-  EXPECT_JSON_EQ("{'type':'integer'}", *prop2.ToJson(true, false));
-  EXPECT_JSON_EQ("{'isRequired':false,'type':'integer'}",
-                 *prop2.ToJson(true, true));
-}
-
-TEST(CommandSchema, RequiredProperties_Object) {
-  ObjectPropType obj_type;
-  auto schema = ObjectSchema::Create();
-  auto type = PropType::Create(ValueType::Int);
-  type->MakeRequired(true);
-  schema->AddProp("prop1", std::move(type));
-  type = PropType::Create(ValueType::String);
-  type->MakeRequired(false);
-  schema->AddProp("prop2", std::move(type));
-  type = PropType::Create(ValueType::Boolean);
-  type->MakeRequired(true);
-  schema->AddProp("prop3", std::move(type));
-  type = PropType::Create(ValueType::Array);
-  type->GetArray()->SetItemType(PropType::Create(ValueType::String));
-  schema->AddProp("prop4", std::move(type));
-  auto expected1 = R"({
-    'prop1': 'integer',
-    'prop2': 'string',
-    'prop3': 'boolean',
-    'prop4': {'items': 'string'}
-  })";
-  EXPECT_JSON_EQ(expected1, *schema->ToJson(false, false));
-  auto expected2 = R"({
-    'prop1': {'type':'integer','isRequired': true},
-    'prop2': {'type':'string','isRequired': false},
-    'prop3': {'type':'boolean','isRequired': true},
-    'prop4': {'items': 'string'}
-  })";
-  EXPECT_JSON_EQ(expected2, *schema->ToJson(false, true));
-
-  obj_type.SetObjectSchema(std::move(schema));
-  auto expected3 = R"({
-    'additionalProperties': false,
-    'properties': {
-      'prop1': 'integer',
-      'prop2': 'string',
-      'prop3': 'boolean',
-      'prop4': {'items': 'string'}
-    },
-    'required': ['prop1','prop3']
-  })";
-  EXPECT_JSON_EQ(expected3, *obj_type.ToJson(false, false));
-  EXPECT_JSON_EQ(expected3, *obj_type.ToJson(false, true));
-}
-
-TEST(CommandSchema, RequiredProperties_Schema_FromJson) {
-  ObjectSchema schema;
-  auto schema_str = R"({
-    'prop1': {'type':'integer','isRequired': true},
-    'prop2': {'type':'string','isRequired': false},
-    'prop3': 'boolean'
-  })";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(), nullptr,
-                              nullptr));
-  EXPECT_TRUE(schema.GetProp("prop1")->IsRequired());
-  EXPECT_FALSE(schema.GetProp("prop2")->IsRequired());
-  EXPECT_FALSE(schema.GetProp("prop3")->IsRequired());
-  EXPECT_JSON_EQ(schema_str, *schema.ToJson(false, true));
-}
-
-TEST(CommandSchema, RequiredProperties_Schema_FromJson_Inherit) {
-  ObjectSchema base_schema;
-  auto base_schema_str = R"({
-    'prop1': {'type':'integer','isRequired': true},
-    'prop2': {'type':'integer','isRequired': false},
-    'prop3': {'type':'integer','isRequired': true},
-    'prop4': {'type':'integer','isRequired': false}
-  })";
-  EXPECT_TRUE(base_schema.FromJson(CreateDictionaryValue(base_schema_str).get(),
-                                   nullptr, nullptr));
-  ObjectSchema schema;
-  auto schema_str = R"({
-    'prop1': {'isRequired': false},
-    'prop2': {'isRequired': true},
-    'prop3': {},
-    'prop4': 'integer'
-  })";
-  EXPECT_TRUE(schema.FromJson(CreateDictionaryValue(schema_str).get(),
-                              &base_schema, nullptr));
-  EXPECT_FALSE(schema.GetProp("prop1")->IsRequired());
-  EXPECT_TRUE(schema.GetProp("prop2")->IsRequired());
-  EXPECT_TRUE(schema.GetProp("prop3")->IsRequired());
-  EXPECT_FALSE(schema.GetProp("prop4")->IsRequired());
-  auto expected = R"({
-    'prop1': {'type':'integer','isRequired': false},
-    'prop2': {'type':'integer','isRequired': true},
-    'prop3': {},
-    'prop4': {}
-  })";
-  EXPECT_JSON_EQ(expected, *schema.ToJson(false, true));
-}
-
-TEST(CommandSchema, RequiredProperties_ObjectPropType_FromJson) {
-  ObjectPropType obj_type;
-  auto type_str = R"({
-    'properties': {
-      'prop1': 'integer',
-      'prop2': 'string',
-      'prop3': {'type':'boolean','isRequired':true},
-      'prop4': {'items': 'string','isRequired':false},
-      'prop5': {'type':'number','isRequired':true}
-    },
-    'required': ['prop1','prop3','prop4','prop5']
-  })";
-  EXPECT_TRUE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr,
-                                nullptr));
-  EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop1")->IsRequired());
-  EXPECT_FALSE(obj_type.GetObjectSchemaPtr()->GetProp("prop2")->IsRequired());
-  EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop3")->IsRequired());
-  // 'required' takes precedence over 'isRequired'.
-  EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop4")->IsRequired());
-  EXPECT_TRUE(obj_type.GetObjectSchemaPtr()->GetProp("prop5")->IsRequired());
-}
-
-TEST(CommandSchema, RequiredProperties_Failures) {
-  ObjectPropType obj_type;
-  ErrorPtr error;
-
-  auto type_str = R"({
-    'properties': {
-      'prop1': 'integer',
-      'prop2': 'string'
-    },
-    'required': ['prop1','prop3','prop4']
-  })";
-  EXPECT_FALSE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr,
-                                 &error));
-  EXPECT_EQ(errors::commands::kUnknownProperty, error->GetCode());
-  error.reset();
-
-  type_str = R"({
-    'properties': {
-      'prop1': 'integer',
-      'prop2': 'string'
-    },
-    'required': 'prop1'
-  })";
-  EXPECT_FALSE(obj_type.FromJson(CreateDictionaryValue(type_str).get(), nullptr,
-                                 &error));
-  EXPECT_EQ(errors::commands::kInvalidObjectSchema, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchema, ObjectSchema_UseRequired) {
-  ObjectPropType prop;
-  auto schema_str = R"({
-    'properties':{
-      'param1':'integer',
-      'param2':'integer',
-      'param3':{'default':3},
-      'param4':{'default':4}
-    },
-    'required':['param1','param3']
-  })";
-  ASSERT_TRUE(
-      prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr));
-
-  auto val_json = R"({
-    'param1':10,
-    'param2':20,
-    'param3':30,
-    'param4':40
-  })";
-  auto value =
-      prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
-  ASSERT_NE(nullptr, value);
-  ValueMap obj = value->GetObject()->GetValue();
-  EXPECT_EQ(10, obj["param1"]->GetInt()->GetValue());
-  EXPECT_EQ(20, obj["param2"]->GetInt()->GetValue());
-  EXPECT_EQ(30, obj["param3"]->GetInt()->GetValue());
-  EXPECT_EQ(40, obj["param4"]->GetInt()->GetValue());
-
-  val_json = "{'param1':100}";
-  value = prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), nullptr);
-  ASSERT_NE(nullptr, value);
-  obj = value->GetObject()->GetValue();
-  EXPECT_EQ(3, obj.size());
-
-  EXPECT_EQ(100, obj["param1"]->GetInt()->GetValue());
-  EXPECT_EQ(obj.end(), obj.find("param2"));
-  EXPECT_EQ(3, obj["param3"]->GetInt()->GetValue());
-  EXPECT_EQ(4, obj["param4"]->GetInt()->GetValue());
-}
-
-TEST(CommandSchema, ObjectSchema_UseRequired_Failure) {
-  ObjectPropType prop;
-  auto schema_str = R"({
-    'properties':{
-      'param1':'integer',
-      'param2':'integer',
-      'param3':{'default':3},
-      'param4':{'default':4}
-    },
-    'required':['param1','param3']
-  })";
-  ASSERT_TRUE(
-      prop.FromJson(CreateDictionaryValue(schema_str).get(), nullptr, nullptr));
-
-  auto val_json = "{'param2':20}";
-  ErrorPtr error;
-  auto value =
-      prop.CreatePropValue(*CreateDictionaryValue(val_json).get(), &error);
-  ASSERT_EQ(nullptr, value);
-  EXPECT_EQ(errors::commands::kPropertyMissing, error->GetCode());
-}
-
-}  // namespace weave
diff --git a/src/commands/prop_constraints.cc b/src/commands/prop_constraints.cc
deleted file mode 100644
index b7e9cf6..0000000
--- a/src/commands/prop_constraints.cc
+++ /dev/null
@@ -1,202 +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 "src/commands/prop_constraints.h"
-
-#include <base/json/json_writer.h>
-#include <base/logging.h>
-
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_constants.h"
-#include "src/string_utils.h"
-
-namespace weave {
-
-namespace {
-
-// Helper function to convert a property value to string, which is used for
-// error reporting.
-std::string PropValueToString(const PropValue& value) {
-  std::string result;
-  auto json = value.ToJson();
-  CHECK(json);
-  base::JSONWriter::Write(*json, &result);
-  return result;
-}
-
-}  // anonymous namespace
-
-// Constraint ----------------------------------------------------------------
-Constraint::~Constraint() {}
-
-bool Constraint::ReportErrorLessThan(ErrorPtr* error,
-                                     const std::string& val,
-                                     const std::string& limit) {
-  Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                     errors::commands::kOutOfRange,
-                     "Value %s is out of range. It must not be less than %s",
-                     val.c_str(), limit.c_str());
-  return false;
-}
-
-bool Constraint::ReportErrorGreaterThan(ErrorPtr* error,
-                                        const std::string& val,
-                                        const std::string& limit) {
-  Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                     errors::commands::kOutOfRange,
-                     "Value %s is out of range. It must not be greater than %s",
-                     val.c_str(), limit.c_str());
-  return false;
-}
-
-bool Constraint::ReportErrorNotOneOf(ErrorPtr* error,
-                                     const std::string& val,
-                                     const std::vector<std::string>& values) {
-  Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                     errors::commands::kOutOfRange,
-                     "Value %s is invalid. Expected one of [%s]", val.c_str(),
-                     Join(",", values).c_str());
-  return false;
-}
-
-void Constraint::AddToJsonDict(base::DictionaryValue* dict,
-                               bool overridden_only) const {
-  if (!overridden_only || HasOverriddenAttributes()) {
-    auto value = ToJson();
-    CHECK(value);
-    dict->SetWithoutPathExpansion(GetDictKey(), value.release());
-  }
-}
-
-// ConstraintStringLength -----------------------------------------------------
-ConstraintStringLength::ConstraintStringLength(
-    const InheritableAttribute<int>& limit)
-    : limit_(limit) {}
-ConstraintStringLength::ConstraintStringLength(int limit) : limit_(limit) {}
-
-bool ConstraintStringLength::HasOverriddenAttributes() const {
-  return !limit_.is_inherited;
-}
-
-std::unique_ptr<base::Value> ConstraintStringLength::ToJson() const {
-  return TypedValueToJson(limit_.value);
-}
-
-// ConstraintStringLengthMin --------------------------------------------------
-ConstraintStringLengthMin::ConstraintStringLengthMin(
-    const InheritableAttribute<int>& limit)
-    : ConstraintStringLength(limit) {}
-ConstraintStringLengthMin::ConstraintStringLengthMin(int limit)
-    : ConstraintStringLength(limit) {}
-
-bool ConstraintStringLengthMin::Validate(const PropValue& value,
-                                         ErrorPtr* error) const {
-  CHECK(value.GetString()) << "Expecting a string value for this constraint";
-  const std::string& str = value.GetString()->GetValue();
-  int length = static_cast<int>(str.size());
-  if (length < limit_.value) {
-    if (limit_.value == 1) {
-      Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
-                   errors::commands::kOutOfRange, "String must not be empty");
-    } else {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kOutOfRange,
-                         "String must be at least %d characters long,"
-                         " actual length of string '%s' is %d",
-                         limit_.value, str.c_str(), length);
-    }
-    return false;
-  }
-  return true;
-}
-
-std::unique_ptr<Constraint> ConstraintStringLengthMin::Clone() const {
-  return std::unique_ptr<Constraint>{new ConstraintStringLengthMin{limit_}};
-}
-
-std::unique_ptr<Constraint> ConstraintStringLengthMin::CloneAsInherited()
-    const {
-  return std::unique_ptr<Constraint>{
-      new ConstraintStringLengthMin{limit_.value}};
-}
-
-// ConstraintStringLengthMax --------------------------------------------------
-ConstraintStringLengthMax::ConstraintStringLengthMax(
-    const InheritableAttribute<int>& limit)
-    : ConstraintStringLength(limit) {}
-ConstraintStringLengthMax::ConstraintStringLengthMax(int limit)
-    : ConstraintStringLength(limit) {}
-
-bool ConstraintStringLengthMax::Validate(const PropValue& value,
-                                         ErrorPtr* error) const {
-  CHECK(value.GetString()) << "Expecting a string value for this constraint";
-  const std::string& str = value.GetString()->GetValue();
-  int length = static_cast<int>(str.size());
-  if (length > limit_.value) {
-    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                       errors::commands::kOutOfRange,
-                       "String must be no more than %d character(s) "
-                       "long, actual length of string '%s' is %d",
-                       limit_.value, str.c_str(), length);
-    return false;
-  }
-  return true;
-}
-
-std::unique_ptr<Constraint> ConstraintStringLengthMax::Clone() const {
-  return std::unique_ptr<Constraint>{new ConstraintStringLengthMax{limit_}};
-}
-
-std::unique_ptr<Constraint> ConstraintStringLengthMax::CloneAsInherited()
-    const {
-  return std::unique_ptr<Constraint>{
-      new ConstraintStringLengthMax{limit_.value}};
-}
-
-// ConstraintOneOf --------------------------------------------------
-ConstraintOneOf::ConstraintOneOf(InheritableAttribute<ValueVector> set)
-    : set_(std::move(set)) {}
-ConstraintOneOf::ConstraintOneOf(ValueVector set) : set_(std::move(set)) {}
-
-bool ConstraintOneOf::Validate(const PropValue& value, ErrorPtr* error) const {
-  for (const auto& item : set_.value) {
-    if (value.IsEqual(item.get()))
-      return true;
-  }
-  std::vector<std::string> choice_list;
-  choice_list.reserve(set_.value.size());
-  for (const auto& item : set_.value) {
-    choice_list.push_back(PropValueToString(*item));
-  }
-  return ReportErrorNotOneOf(error, PropValueToString(value), choice_list);
-}
-
-std::unique_ptr<Constraint> ConstraintOneOf::Clone() const {
-  InheritableAttribute<ValueVector> attr;
-  attr.is_inherited = set_.is_inherited;
-  attr.value.reserve(set_.value.size());
-  for (const auto& prop_value : set_.value) {
-    attr.value.push_back(prop_value->Clone());
-  }
-  return std::unique_ptr<Constraint>{new ConstraintOneOf{std::move(attr)}};
-}
-
-std::unique_ptr<Constraint> ConstraintOneOf::CloneAsInherited() const {
-  ValueVector cloned;
-  cloned.reserve(set_.value.size());
-  for (const auto& prop_value : set_.value) {
-    cloned.push_back(prop_value->Clone());
-  }
-  return std::unique_ptr<Constraint>{new ConstraintOneOf{std::move(cloned)}};
-}
-
-std::unique_ptr<base::Value> ConstraintOneOf::ToJson() const {
-  return TypedValueToJson(set_.value);
-}
-
-const char* ConstraintOneOf::GetDictKey() const {
-  return commands::attributes::kOneOf_Enum;
-}
-
-}  // namespace weave
diff --git a/src/commands/prop_constraints.h b/src/commands/prop_constraints.h
deleted file mode 100644
index 53a4d93..0000000
--- a/src/commands/prop_constraints.h
+++ /dev/null
@@ -1,315 +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_SRC_COMMANDS_PROP_CONSTRAINTS_H_
-#define LIBWEAVE_SRC_COMMANDS_PROP_CONSTRAINTS_H_
-
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include <base/macros.h>
-#include <base/values.h>
-#include <weave/error.h>
-
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_constants.h"
-#include "src/commands/schema_utils.h"
-#include "src/string_utils.h"
-
-namespace weave {
-
-enum class ConstraintType { Min, Max, StringLengthMin, StringLengthMax, OneOf };
-
-// Abstract base class for all parameter constraints. Many constraints are
-// type-dependent. Thus, a numeric parameter could have "minimum" and/or
-// "maximum" constraints specified. Some constraints, such as "OneOf" apply to
-// any data type.
-class Constraint {
- public:
-  Constraint() = default;
-  virtual ~Constraint();
-
-  // Gets the constraint type.
-  virtual ConstraintType GetType() const = 0;
-
-  // Checks if any of the constraint properties/attributes are overridden
-  // from their base schema definition. If the constraint is inherited, then
-  // it will not be written to JSON when saving partial schema.
-  virtual bool HasOverriddenAttributes() const = 0;
-
-  // Validates a parameter against the constraint. Returns true if parameter
-  // value satisfies the constraint, otherwise fills the optional |error| with
-  // the details for the failure.
-  virtual bool Validate(const PropValue& value, ErrorPtr* error) const = 0;
-
-  // Makes a full copy of this Constraint instance.
-  virtual std::unique_ptr<Constraint> Clone() const = 0;
-
-  // Makes a copy of the constraint object, marking all the attributes
-  // as inherited from the original definition.
-  virtual std::unique_ptr<Constraint> CloneAsInherited() const = 0;
-
-  // Saves the constraint into the specified JSON |dict| object, representing
-  // the object schema. If |overridden_only| is set to true, then the
-  // inherited constraints will not be added to the schema object.
-  virtual void AddToJsonDict(base::DictionaryValue* dict,
-                             bool overridden_only) const;
-
-  // Saves the value of constraint to JSON value. E.g., if the numeric
-  // constraint was defined as {"minimum":20} this will create a JSON value
-  // of 20. The current design implies that each constraint has one value
-  // only. If this assumption changes, this interface needs to be updated
-  // accordingly.
-  virtual std::unique_ptr<base::Value> ToJson() const = 0;
-
-  // Overloaded by the concrete class implementation, it should return the
-  // JSON object property name to store the constraint's value as.
-  // E.g., if the numeric constraint was defined as {"minimum":20} this
-  // method should return "minimum".
-  virtual const char* GetDictKey() const = 0;
-
- protected:
-  // Static helper methods to format common constraint validation errors.
-  // They fill the |error| object with specific error message.
-  // Since these functions could be used by constraint objects for various
-  // data types, the values used in validation are expected to be
-  // send as strings already.
-  static bool ReportErrorLessThan(ErrorPtr* error,
-                                  const std::string& val,
-                                  const std::string& limit);
-  static bool ReportErrorGreaterThan(ErrorPtr* error,
-                                     const std::string& val,
-                                     const std::string& limit);
-
-  static bool ReportErrorNotOneOf(ErrorPtr* error,
-                                  const std::string& val,
-                                  const std::vector<std::string>& values);
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(Constraint);
-};
-
-// ConstraintMinMaxBase is a base class for numeric Minimum and Maximum
-// constraints.
-template <typename T>
-class ConstraintMinMaxBase : public Constraint {
- public:
-  explicit ConstraintMinMaxBase(const InheritableAttribute<T>& limit)
-      : limit_(limit) {}
-  explicit ConstraintMinMaxBase(const T& limit) : limit_(limit) {}
-
-  // Implementation of Constraint::HasOverriddenAttributes().
-  bool HasOverriddenAttributes() const override { return !limit_.is_inherited; }
-
-  // Implementation of Constraint::ToJson().
-  std::unique_ptr<base::Value> ToJson() const override {
-    return TypedValueToJson(limit_.value);
-  }
-
-  // Stores the upper/lower value limit for maximum/minimum constraint.
-  // |limit_.is_inherited| indicates whether the constraint is inherited
-  // from base schema or overridden.
-  InheritableAttribute<T> limit_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintMinMaxBase);
-};
-
-// Implementation of Minimum value constraint for Integer/Double types.
-template <typename T>
-class ConstraintMin : public ConstraintMinMaxBase<T> {
- public:
-  explicit ConstraintMin(const InheritableAttribute<T>& limit)
-      : ConstraintMinMaxBase<T>(limit) {}
-  explicit ConstraintMin(const T& limit) : ConstraintMinMaxBase<T>(limit) {}
-
-  // Implementation of Constraint::GetType().
-  ConstraintType GetType() const override { return ConstraintType::Min; }
-
-  // Implementation of Constraint::Validate().
-  bool Validate(const PropValue& value, ErrorPtr* error) const override {
-    const T& v = static_cast<const TypedValueBase<T>&>(value).GetValue();
-    if (v < this->limit_.value) {
-      return this->ReportErrorLessThan(error, std::to_string(v),
-                                       std::to_string(this->limit_.value));
-    }
-    return true;
-  }
-
-  // Implementation of Constraint::Clone().
-  std::unique_ptr<Constraint> Clone() const override {
-    return std::unique_ptr<Constraint>{new ConstraintMin{this->limit_}};
-  }
-
-  // Implementation of Constraint::CloneAsInherited().
-  std::unique_ptr<Constraint> CloneAsInherited() const override {
-    return std::unique_ptr<Constraint>{new ConstraintMin{this->limit_.value}};
-  }
-
-  // Implementation of Constraint::GetDictKey().
-  const char* GetDictKey() const override {
-    return commands::attributes::kNumeric_Min;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintMin);
-};
-
-// Implementation of Maximum value constraint for Integer/Double types.
-template <typename T>
-class ConstraintMax : public ConstraintMinMaxBase<T> {
- public:
-  explicit ConstraintMax(const InheritableAttribute<T>& limit)
-      : ConstraintMinMaxBase<T>(limit) {}
-  explicit ConstraintMax(const T& limit) : ConstraintMinMaxBase<T>(limit) {}
-
-  // Implementation of Constraint::GetType().
-  ConstraintType GetType() const override { return ConstraintType::Max; }
-
-  // Implementation of Constraint::Validate().
-  bool Validate(const PropValue& value, ErrorPtr* error) const override {
-    const T& v = static_cast<const TypedValueBase<T>&>(value).GetValue();
-    if (v > this->limit_.value)
-      return this->ReportErrorGreaterThan(error, std::to_string(v),
-                                          std::to_string(this->limit_.value));
-    return true;
-  }
-
-  // Implementation of Constraint::Clone().
-  std::unique_ptr<Constraint> Clone() const override {
-    return std::unique_ptr<Constraint>{new ConstraintMax{this->limit_}};
-  }
-
-  // Implementation of Constraint::CloneAsInherited().
-  std::unique_ptr<Constraint> CloneAsInherited() const override {
-    return std::unique_ptr<Constraint>{new ConstraintMax{this->limit_.value}};
-  }
-
-  // Implementation of Constraint::GetDictKey().
-  const char* GetDictKey() const override {
-    return commands::attributes::kNumeric_Max;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintMax);
-};
-
-// ConstraintStringLength is a base class for Minimum/Maximum string length
-// constraints, similar to ConstraintMinMaxBase of numeric types.
-class ConstraintStringLength : public Constraint {
- public:
-  explicit ConstraintStringLength(const InheritableAttribute<int>& limit);
-  explicit ConstraintStringLength(int limit);
-
-  // Implementation of Constraint::HasOverriddenAttributes().
-  bool HasOverriddenAttributes() const override;
-  // Implementation of Constraint::ToJson().
-  std::unique_ptr<base::Value> ToJson() const override;
-
-  // Stores the upper/lower value limit for string length constraint.
-  // |limit_.is_inherited| indicates whether the constraint is inherited
-  // from base schema or overridden.
-  InheritableAttribute<int> limit_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintStringLength);
-};
-
-// Implementation of Minimum string length constraint.
-class ConstraintStringLengthMin : public ConstraintStringLength {
- public:
-  explicit ConstraintStringLengthMin(const InheritableAttribute<int>& limit);
-  explicit ConstraintStringLengthMin(int limit);
-
-  // Implementation of Constraint::GetType().
-  ConstraintType GetType() const override {
-    return ConstraintType::StringLengthMin;
-  }
-
-  // Implementation of Constraint::Validate().
-  bool Validate(const PropValue& value, ErrorPtr* error) const override;
-
-  // Implementation of Constraint::Clone().
-  std::unique_ptr<Constraint> Clone() const override;
-
-  // Implementation of Constraint::CloneAsInherited().
-  std::unique_ptr<Constraint> CloneAsInherited() const override;
-  // Implementation of Constraint::GetDictKey().
-  const char* GetDictKey() const override {
-    return commands::attributes::kString_MinLength;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintStringLengthMin);
-};
-
-// Implementation of Maximum string length constraint.
-class ConstraintStringLengthMax : public ConstraintStringLength {
- public:
-  explicit ConstraintStringLengthMax(const InheritableAttribute<int>& limit);
-  explicit ConstraintStringLengthMax(int limit);
-
-  // Implementation of Constraint::GetType().
-  ConstraintType GetType() const override {
-    return ConstraintType::StringLengthMax;
-  }
-
-  // Implementation of Constraint::Validate().
-  bool Validate(const PropValue& value, ErrorPtr* error) const override;
-
-  // Implementation of Constraint::Clone().
-  std::unique_ptr<Constraint> Clone() const override;
-
-  // Implementation of Constraint::CloneAsInherited().
-  std::unique_ptr<Constraint> CloneAsInherited() const override;
-
-  // Implementation of Constraint::GetDictKey().
-  const char* GetDictKey() const override {
-    return commands::attributes::kString_MaxLength;
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintStringLengthMax);
-};
-
-// Implementation of OneOf constraint for different data types.
-class ConstraintOneOf : public Constraint {
- public:
-  explicit ConstraintOneOf(InheritableAttribute<ValueVector> set);
-  explicit ConstraintOneOf(ValueVector set);
-
-  // Implementation of Constraint::GetType().
-  ConstraintType GetType() const override { return ConstraintType::OneOf; }
-
-  // Implementation of Constraint::HasOverriddenAttributes().
-  bool HasOverriddenAttributes() const override { return !set_.is_inherited; }
-
-  // Implementation of Constraint::Validate().
-  bool Validate(const PropValue& value, ErrorPtr* error) const override;
-
-  // Implementation of Constraint::Clone().
-  std::unique_ptr<Constraint> Clone() const override;
-
-  // Implementation of Constraint::CloneAsInherited().
-  std::unique_ptr<Constraint> CloneAsInherited() const override;
-
-  // Implementation of Constraint::ToJson().
-  std::unique_ptr<base::Value> ToJson() const override;
-
-  // Implementation of Constraint::GetDictKey().
-  const char* GetDictKey() const override;
-
-  // Stores the list of acceptable values for the parameter.
-  // |set_.is_inherited| indicates whether the constraint is inherited
-  // from base schema or overridden.
-  InheritableAttribute<ValueVector> set_;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ConstraintOneOf);
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_PROP_CONSTRAINTS_H_
diff --git a/src/commands/prop_types.cc b/src/commands/prop_types.cc
deleted file mode 100644
index 88a53bd..0000000
--- a/src/commands/prop_types.cc
+++ /dev/null
@@ -1,653 +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 "src/commands/prop_types.h"
-
-#include <algorithm>
-#include <limits>
-#include <set>
-
-#include <base/json/json_writer.h>
-#include <base/logging.h>
-#include <base/values.h>
-
-#include "src/commands/object_schema.h"
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_constants.h"
-
-namespace weave {
-
-// PropType -------------------------------------------------------------------
-PropType::PropType() {}
-
-PropType::~PropType() {}
-
-std::string PropType::GetTypeAsString() const {
-  return GetTypeStringFromType(GetType());
-}
-
-bool PropType::HasOverriddenAttributes() const {
-  if (default_.value && !default_.is_inherited)
-    return true;
-
-  for (const auto& pair : constraints_) {
-    if (pair.second->HasOverriddenAttributes())
-      return true;
-  }
-  return false;
-}
-
-bool PropType::IsRequired() const {
-  return required_.value;
-}
-
-void PropType::MakeRequired(bool required) {
-  required_.value = required;
-  required_.is_inherited = false;
-}
-
-std::unique_ptr<base::Value> PropType::ToJson(bool full_schema,
-                                              bool in_command_def) const {
-  // Determine if we need to output "isRequired" attribute.
-  const bool include_required = in_command_def && !required_.is_inherited;
-
-  // If we must include "isRequired" attribute, then treat this as "full schema"
-  // request because there could be cases where we have just this attribute and
-  // won't be able to infer the type from the constraints only.
-  if (include_required)
-    full_schema = true;
-
-  if (!full_schema && !HasOverriddenAttributes()) {
-    if (based_on_schema_)
-      return std::unique_ptr<base::Value>(new base::DictionaryValue);
-    return TypedValueToJson(GetTypeAsString());
-  }
-
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
-  if (full_schema) {
-    // If we are asked for full_schema definition, then we need to output every
-    // parameter property, including the "type", and any constraints.
-    // So, we write the "type" only if asked for full schema.
-    // Otherwise we will be able to infer the parameter type based on
-    // the constraints and their types.
-    // That is, the following JSONs could possibly define a parameter:
-    // {'type':'integer'} -> explicit "integer" with no constraints
-    // {'minimum':10} -> no type specified, but since we have "minimum"
-    //                   and 10 is integer, than this is an integer
-    //                   parameter with min constraint.
-    // {'enum':[1,2,3]} -> integer with OneOf constraint.
-    // And so is this: [1,2,3] -> an array of ints assume it's an "int" enum.
-    dict->SetString(commands::attributes::kType, GetTypeAsString());
-  }
-
-  if (!full_schema && constraints_.size() == 1) {
-    // If we are not asked for full schema, and we have only one constraint
-    // which is OneOf, we short-circuit the whole thing and return just
-    // the array [1,2,3] instead of an object with "enum" property like:
-    // {'enum':[1,2,3]}
-    auto p = constraints_.find(ConstraintType::OneOf);
-    if (p != constraints_.end()) {
-      return p->second->ToJson();
-    }
-  }
-
-  for (const auto& pair : constraints_)
-    pair.second->AddToJsonDict(dict.get(), !full_schema);
-
-  if (default_.value && (full_schema || !default_.is_inherited)) {
-    auto def_val = default_.value->ToJson();
-    CHECK(def_val);
-    dict->Set(commands::attributes::kDefault, def_val.release());
-  }
-
-  if (include_required)
-    dict->SetBoolean(commands::attributes::kIsRequired, required_.value);
-  return std::unique_ptr<base::Value>(dict.release());
-}
-
-std::unique_ptr<PropType> PropType::Clone() const {
-  auto cloned = PropType::Create(GetType());
-  cloned->based_on_schema_ = based_on_schema_;
-  for (const auto& pair : constraints_) {
-    cloned->constraints_.emplace(pair.first, pair.second->Clone());
-  }
-  cloned->default_.is_inherited = default_.is_inherited;
-  if (default_.value)
-    cloned->default_.value = default_.value->Clone();
-  cloned->required_ = required_;
-  return cloned;
-}
-
-bool PropType::FromJson(const base::DictionaryValue* value,
-                        const PropType* base_schema,
-                        ErrorPtr* error) {
-  if (base_schema && base_schema->GetType() != GetType()) {
-    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                       errors::commands::kPropTypeChanged,
-                       "Redefining a property of type %s as %s",
-                       base_schema->GetTypeAsString().c_str(),
-                       GetTypeAsString().c_str());
-    return false;
-  }
-  based_on_schema_ = (base_schema != nullptr);
-  constraints_.clear();
-  // Add the well-known object properties first (like "type", "displayName",
-  // "default") to the list of "processed" keys so we do not complain about them
-  // when we check for unknown/unexpected keys below.
-  std::set<std::string> processed_keys{
-      commands::attributes::kType, commands::attributes::kDisplayName,
-      commands::attributes::kDefault, commands::attributes::kIsRequired,
-  };
-  if (!ObjectSchemaFromJson(value, base_schema, &processed_keys, error))
-    return false;
-  if (base_schema) {
-    for (const auto& pair : base_schema->GetConstraints()) {
-      constraints_.emplace(pair.first, pair.second->CloneAsInherited());
-    }
-  }
-  if (!ConstraintsFromJson(value, &processed_keys, error))
-    return false;
-
-  // Now make sure there are no unexpected/unknown keys in the property schema
-  // definition object.
-  base::DictionaryValue::Iterator iter(*value);
-  while (!iter.IsAtEnd()) {
-    std::string key = iter.key();
-    if (processed_keys.find(key) == processed_keys.end()) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kUnknownProperty,
-                         "Unexpected property '%s'", key.c_str());
-      return false;
-    }
-    iter.Advance();
-  }
-
-  // Read the "isRequired" attribute, if specified.
-  bool required = false;
-  if (value->GetBoolean(commands::attributes::kIsRequired, &required)) {
-    required_.value = required;
-    required_.is_inherited = false;
-  } else if (base_schema) {
-    // If we have the base schema, inherit the type's required value from it.
-    if (base_schema->required_.value)
-      required_.value = base_schema->required_.value;
-    required_.is_inherited = true;
-  }
-
-  // Read the default value, if specified.
-  // We need to do this last since the current type definition must be complete,
-  // so we can parse and validate the value of the default.
-  const base::Value* defval = nullptr;  // Owned by value
-  if (value->GetWithoutPathExpansion(commands::attributes::kDefault, &defval)) {
-    std::unique_ptr<PropValue> prop_value = CreatePropValue(*defval, error);
-    if (!prop_value) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kInvalidPropValue,
-                         "Invalid value for property '%s'",
-                         commands::attributes::kDefault);
-      return false;
-    }
-    default_.value = std::move(prop_value);
-    default_.is_inherited = false;
-  } else if (base_schema) {
-    // If we have the base schema, inherit the type's default value from it.
-    // It doesn't matter if the base schema actually has a default value
-    // specified or not. If it doesn't, then the current type definition will
-    // have no default value set either (|default_.value| is a unique_ptr to
-    // PropValue, which can be set to nullptr).
-    if (base_schema->default_.value)
-      default_.value = base_schema->default_.value->Clone();
-    default_.is_inherited = true;
-  }
-  return true;
-}
-
-void PropType::AddConstraint(std::unique_ptr<Constraint> constraint) {
-  constraints_[constraint->GetType()] = std::move(constraint);
-}
-
-void PropType::RemoveConstraint(ConstraintType constraint_type) {
-  constraints_.erase(constraint_type);
-}
-
-void PropType::RemoveAllConstraints() {
-  constraints_.clear();
-}
-
-const Constraint* PropType::GetConstraint(
-    ConstraintType constraint_type) const {
-  auto p = constraints_.find(constraint_type);
-  return p != constraints_.end() ? p->second.get() : nullptr;
-}
-
-Constraint* PropType::GetConstraint(ConstraintType constraint_type) {
-  auto p = constraints_.find(constraint_type);
-  return p != constraints_.end() ? p->second.get() : nullptr;
-}
-
-bool PropType::ValidateConstraints(const PropValue& value,
-                                   ErrorPtr* error) const {
-  for (const auto& pair : constraints_) {
-    if (!pair.second->Validate(value, error))
-      return false;
-  }
-  return true;
-}
-
-const PropType::TypeMap& PropType::GetTypeMap() {
-  static TypeMap map = {
-      {ValueType::Int, "integer"},   {ValueType::Double, "number"},
-      {ValueType::String, "string"}, {ValueType::Boolean, "boolean"},
-      {ValueType::Object, "object"}, {ValueType::Array, "array"},
-  };
-  return map;
-}
-
-std::string PropType::GetTypeStringFromType(ValueType type) {
-  for (const auto& pair : GetTypeMap()) {
-    if (pair.first == type)
-      return pair.second;
-  }
-  LOG(FATAL) << "Type map is missing a type";
-  return std::string();
-}
-
-bool PropType::GetTypeFromTypeString(const std::string& name, ValueType* type) {
-  for (const auto& pair : GetTypeMap()) {
-    if (pair.second == name) {
-      *type = pair.first;
-      return true;
-    }
-  }
-  return false;
-}
-
-std::unique_ptr<PropType> PropType::Create(ValueType type) {
-  PropType* prop = nullptr;
-  switch (type) {
-    case ValueType::Int:
-      prop = new IntPropType;
-      break;
-    case ValueType::Double:
-      prop = new DoublePropType;
-      break;
-    case ValueType::String:
-      prop = new StringPropType;
-      break;
-    case ValueType::Boolean:
-      prop = new BooleanPropType;
-      break;
-    case ValueType::Object:
-      prop = new ObjectPropType;
-      break;
-    case ValueType::Array:
-      prop = new ArrayPropType;
-      break;
-  }
-  return std::unique_ptr<PropType>(prop);
-}
-
-template <typename T>
-static std::unique_ptr<Constraint> LoadOneOfConstraint(
-    const base::DictionaryValue* value,
-    const PropType* prop_type,
-    ErrorPtr* error) {
-  std::unique_ptr<Constraint> constraint;
-  const base::Value* list = nullptr;  // Owned by |value|
-  CHECK(value->Get(commands::attributes::kOneOf_Enum, &list))
-      << "'enum' property missing in JSON dictionary";
-  ValueVector choice_list;
-  ArrayPropType array_type;
-  array_type.SetItemType(prop_type->Clone());
-  if (!TypedValueFromJson(list, &array_type, &choice_list, error))
-    return constraint;
-  InheritableAttribute<ValueVector> val(std::move(choice_list), false);
-  constraint.reset(new ConstraintOneOf{std::move(val)});
-  return constraint;
-}
-
-template <class ConstraintClass, typename T>
-static std::unique_ptr<Constraint> LoadMinMaxConstraint(
-    const char* dict_key,
-    const base::DictionaryValue* value,
-    ErrorPtr* error) {
-  std::unique_ptr<Constraint> constraint;
-  InheritableAttribute<T> limit;
-
-  const base::Value* src_val = nullptr;
-  CHECK(value->Get(dict_key, &src_val)) << "Unable to get min/max constraints";
-  if (!TypedValueFromJson(src_val, nullptr, &limit.value, error))
-    return constraint;
-  limit.is_inherited = false;
-
-  constraint.reset(new ConstraintClass{limit});
-  return constraint;
-}
-
-// PropTypeBase ----------------------------------------------------------------
-
-template <class Derived, class Value, typename T>
-bool PropTypeBase<Derived, Value, T>::ConstraintsFromJson(
-    const base::DictionaryValue* value,
-    std::set<std::string>* processed_keys,
-    ErrorPtr* error) {
-  if (!PropType::ConstraintsFromJson(value, processed_keys, error))
-    return false;
-
-  if (value->HasKey(commands::attributes::kOneOf_Enum)) {
-    auto type = Clone();
-    type->RemoveAllConstraints();
-    auto constraint = LoadOneOfConstraint<T>(value, type.get(), error);
-    if (!constraint)
-      return false;
-    this->AddConstraint(std::move(constraint));
-    this->RemoveConstraint(ConstraintType::Min);
-    this->RemoveConstraint(ConstraintType::Max);
-    processed_keys->insert(commands::attributes::kOneOf_Enum);
-  }
-
-  return true;
-}
-
-// NumericPropTypeBase ---------------------------------------------------------
-
-template <class Derived, class Value, typename T>
-bool NumericPropTypeBase<Derived, Value, T>::ConstraintsFromJson(
-    const base::DictionaryValue* value,
-    std::set<std::string>* processed_keys,
-    ErrorPtr* error) {
-  if (!Base::ConstraintsFromJson(value, processed_keys, error))
-    return false;
-
-  if (processed_keys->find(commands::attributes::kOneOf_Enum) ==
-      processed_keys->end()) {
-    // Process min/max constraints only if "enum" constraint wasn't already
-    // specified.
-    if (value->HasKey(commands::attributes::kNumeric_Min)) {
-      auto constraint = LoadMinMaxConstraint<ConstraintMin<T>, T>(
-          commands::attributes::kNumeric_Min, value, error);
-      if (!constraint)
-        return false;
-      this->AddConstraint(std::move(constraint));
-      this->RemoveConstraint(ConstraintType::OneOf);
-      processed_keys->insert(commands::attributes::kNumeric_Min);
-    }
-    if (value->HasKey(commands::attributes::kNumeric_Max)) {
-      auto constraint = LoadMinMaxConstraint<ConstraintMax<T>, T>(
-          commands::attributes::kNumeric_Max, value, error);
-      if (!constraint)
-        return false;
-      this->AddConstraint(std::move(constraint));
-      this->RemoveConstraint(ConstraintType::OneOf);
-      processed_keys->insert(commands::attributes::kNumeric_Max);
-    }
-  }
-
-  return true;
-}
-
-// StringPropType -------------------------------------------------------------
-
-bool StringPropType::ConstraintsFromJson(const base::DictionaryValue* value,
-                                         std::set<std::string>* processed_keys,
-                                         ErrorPtr* error) {
-  if (!Base::ConstraintsFromJson(value, processed_keys, error))
-    return false;
-
-  if (processed_keys->find(commands::attributes::kOneOf_Enum) ==
-      processed_keys->end()) {
-    // Process min/max constraints only if "enum" constraint wasn't already
-    // specified.
-    if (value->HasKey(commands::attributes::kString_MinLength)) {
-      auto constraint = LoadMinMaxConstraint<ConstraintStringLengthMin, int>(
-          commands::attributes::kString_MinLength, value, error);
-      if (!constraint)
-        return false;
-      AddConstraint(std::move(constraint));
-      RemoveConstraint(ConstraintType::OneOf);
-      processed_keys->insert(commands::attributes::kString_MinLength);
-    }
-    if (value->HasKey(commands::attributes::kString_MaxLength)) {
-      auto constraint = LoadMinMaxConstraint<ConstraintStringLengthMax, int>(
-          commands::attributes::kString_MaxLength, value, error);
-      if (!constraint)
-        return false;
-      AddConstraint(std::move(constraint));
-      RemoveConstraint(ConstraintType::OneOf);
-      processed_keys->insert(commands::attributes::kString_MaxLength);
-    }
-  }
-  return true;
-}
-
-void StringPropType::AddLengthConstraint(int min_len, int max_len) {
-  InheritableAttribute<int> min_attr(min_len, false);
-  InheritableAttribute<int> max_attr(max_len, false);
-  AddConstraint(std::unique_ptr<ConstraintStringLengthMin>{
-      new ConstraintStringLengthMin{min_attr}});
-  AddConstraint(std::unique_ptr<ConstraintStringLengthMax>{
-      new ConstraintStringLengthMax{max_attr}});
-}
-
-int StringPropType::GetMinLength() const {
-  auto slc = static_cast<const ConstraintStringLength*>(
-      GetConstraint(ConstraintType::StringLengthMin));
-  return slc ? slc->limit_.value : 0;
-}
-
-int StringPropType::GetMaxLength() const {
-  auto slc = static_cast<const ConstraintStringLength*>(
-      GetConstraint(ConstraintType::StringLengthMax));
-  return slc ? slc->limit_.value : std::numeric_limits<int>::max();
-}
-
-// ObjectPropType -------------------------------------------------------------
-
-ObjectPropType::ObjectPropType()
-    : object_schema_{ObjectSchema::Create(), false} {}
-
-bool ObjectPropType::HasOverriddenAttributes() const {
-  return PropType::HasOverriddenAttributes() || !object_schema_.is_inherited;
-}
-
-std::unique_ptr<PropType> ObjectPropType::Clone() const {
-  auto cloned = Base::Clone();
-
-  cloned->GetObject()->object_schema_.is_inherited =
-      object_schema_.is_inherited;
-  cloned->GetObject()->object_schema_.value = object_schema_.value->Clone();
-  return cloned;
-}
-
-std::unique_ptr<base::Value> ObjectPropType::ToJson(bool full_schema,
-                                                    bool in_command_def) const {
-  std::unique_ptr<base::Value> value =
-      PropType::ToJson(full_schema, in_command_def);
-  CHECK(value);
-  base::DictionaryValue* dict = nullptr;
-  CHECK(value->GetAsDictionary(&dict)) << "Expecting a JSON object";
-  if (!object_schema_.is_inherited || full_schema) {
-    auto object_schema = object_schema_.value->ToJson(full_schema, false);
-    CHECK(object_schema);
-
-    dict->SetWithoutPathExpansion(commands::attributes::kObject_Properties,
-                                  object_schema.release());
-    dict->SetBooleanWithoutPathExpansion(
-        commands::attributes::kObject_AdditionalProperties,
-        object_schema_.value->GetExtraPropertiesAllowed());
-    std::unique_ptr<base::ListValue> required{new base::ListValue};
-    for (const auto& pair : object_schema_.value->GetProps()) {
-      if (pair.second->IsRequired())
-        required->AppendString(pair.first);
-    }
-    if (required->GetSize() > 0) {
-      dict->Set(commands::attributes::kObject_Required, required.release());
-    }
-  }
-  return value;
-}
-
-bool ObjectPropType::ObjectSchemaFromJson(const base::DictionaryValue* value,
-                                          const PropType* base_schema,
-                                          std::set<std::string>* processed_keys,
-                                          ErrorPtr* error) {
-  if (!Base::ObjectSchemaFromJson(value, base_schema, processed_keys, error))
-    return false;
-
-  using commands::attributes::kObject_Properties;
-  using commands::attributes::kObject_AdditionalProperties;
-
-  const ObjectSchema* base_object_schema = nullptr;
-  if (base_schema)
-    base_object_schema = base_schema->GetObject()->GetObjectSchemaPtr();
-
-  const base::DictionaryValue* props = nullptr;
-  std::unique_ptr<ObjectSchema> object_schema;
-  bool inherited = false;
-  if (value->GetDictionaryWithoutPathExpansion(kObject_Properties, &props)) {
-    processed_keys->insert(kObject_Properties);
-    object_schema.reset(new ObjectSchema);
-    if (!object_schema->FromJson(props, base_object_schema, error)) {
-      Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
-                   errors::commands::kInvalidObjectSchema,
-                   "Error parsing object property schema");
-      return false;
-    }
-  } else if (base_object_schema) {
-    object_schema = base_object_schema->Clone();
-    inherited = true;
-  } else {
-    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                       errors::commands::kInvalidObjectSchema,
-                       "Object type definition must include the "
-                       "object schema ('%s' field not found)",
-                       kObject_Properties);
-    return false;
-  }
-  bool extra_properties_allowed = false;
-  if (value->GetBooleanWithoutPathExpansion(kObject_AdditionalProperties,
-                                            &extra_properties_allowed)) {
-    processed_keys->insert(kObject_AdditionalProperties);
-    object_schema->SetExtraPropertiesAllowed(extra_properties_allowed);
-    inherited = false;
-  }
-  const base::Value* required = nullptr;
-  if (value->Get(commands::attributes::kObject_Required, &required)) {
-    processed_keys->insert(commands::attributes::kObject_Required);
-    const base::ListValue* required_list = nullptr;
-    if (!required->GetAsList(&required_list)) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kInvalidObjectSchema,
-                         "Property '%s' must be an array",
-                         commands::attributes::kObject_Required);
-      return false;
-    }
-    for (const base::Value* value : *required_list) {
-      std::string name;
-      if (!value->GetAsString(&name)) {
-        std::string json_value;
-        CHECK(base::JSONWriter::Write(*value, &json_value));
-        Error::AddToPrintf(
-            error, FROM_HERE, errors::commands::kDomain,
-            errors::commands::kInvalidObjectSchema,
-            "Property '%s' contains invalid element (%s). String expected",
-            commands::attributes::kObject_Required, json_value.c_str());
-        return false;
-      }
-      if (!object_schema->MarkPropRequired(name, error))
-        return false;
-      inherited = false;
-    }
-  }
-  object_schema_.value = std::move(object_schema);
-  object_schema_.is_inherited = inherited;
-
-  return true;
-}
-
-void ObjectPropType::SetObjectSchema(
-    std::unique_ptr<const ObjectSchema> schema) {
-  object_schema_.value = std::move(schema);
-  object_schema_.is_inherited = false;
-}
-
-// ArrayPropType -------------------------------------------------------------
-
-ArrayPropType::ArrayPropType() {}
-
-bool ArrayPropType::HasOverriddenAttributes() const {
-  return PropType::HasOverriddenAttributes() || !item_type_.is_inherited;
-}
-
-std::unique_ptr<PropType> ArrayPropType::Clone() const {
-  auto cloned = Base::Clone();
-
-  cloned->GetArray()->item_type_.is_inherited = item_type_.is_inherited;
-  cloned->GetArray()->item_type_.value = item_type_.value->Clone();
-  return cloned;
-}
-
-std::unique_ptr<base::Value> ArrayPropType::ToJson(bool full_schema,
-                                                   bool in_command_def) const {
-  std::unique_ptr<base::Value> value =
-      PropType::ToJson(full_schema, in_command_def);
-  CHECK(value);
-  if (!item_type_.is_inherited || full_schema) {
-    base::DictionaryValue* dict = nullptr;
-    CHECK(value->GetAsDictionary(&dict)) << "Expecting a JSON object";
-    auto type = item_type_.value->ToJson(full_schema, false);
-    CHECK(type);
-    dict->SetWithoutPathExpansion(commands::attributes::kItems, type.release());
-  }
-  return value;
-}
-
-bool ArrayPropType::ObjectSchemaFromJson(const base::DictionaryValue* value,
-                                         const PropType* base_schema,
-                                         std::set<std::string>* processed_keys,
-                                         ErrorPtr* error) {
-  if (!Base::ObjectSchemaFromJson(value, base_schema, processed_keys, error))
-    return false;
-
-  using commands::attributes::kItems;
-
-  const PropType* base_type = nullptr;
-  if (base_schema)
-    base_type = base_schema->GetArray()->GetItemTypePtr();
-
-  const base::Value* type_value = nullptr;
-  if (value->GetWithoutPathExpansion(kItems, &type_value)) {
-    processed_keys->insert(kItems);
-    auto item_type = ObjectSchema::PropFromJson(*type_value, base_type, error);
-    if (!item_type)
-      return false;
-    if (item_type->GetType() == ValueType::Array) {
-      Error::AddTo(error, FROM_HERE, errors::commands::kDomain,
-                   errors::commands::kInvalidObjectSchema,
-                   "Arrays of arrays are not supported");
-      return false;
-    }
-    SetItemType(std::move(item_type));
-  } else if (!item_type_.value) {
-    if (base_type) {
-      item_type_.value = base_type->Clone();
-      item_type_.is_inherited = true;
-    } else {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kInvalidObjectSchema,
-                         "Array type definition must include the "
-                         "array item type ('%s' field not found)",
-                         kItems);
-      return false;
-    }
-  }
-  return true;
-}
-
-void ArrayPropType::SetItemType(std::unique_ptr<const PropType> item_type) {
-  item_type_.value = std::move(item_type);
-  item_type_.is_inherited = false;
-}
-
-}  // namespace weave
diff --git a/src/commands/prop_types.h b/src/commands/prop_types.h
deleted file mode 100644
index f784eb6..0000000
--- a/src/commands/prop_types.h
+++ /dev/null
@@ -1,352 +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_SRC_COMMANDS_PROP_TYPES_H_
-#define LIBWEAVE_SRC_COMMANDS_PROP_TYPES_H_
-
-#include <limits>
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <weave/error.h>
-
-#include "src/commands/prop_constraints.h"
-#include "src/commands/prop_values.h"
-
-namespace weave {
-
-class IntPropType;
-class DoublePropType;
-class StringPropType;
-class BooleanPropType;
-class ObjectPropType;
-class ArrayPropType;
-
-// PropType is a base class for all parameter type definition objects.
-// Property definitions of a particular type will derive from this class and
-// provide type-specific implementations.
-class PropType {
- public:
-  // ConstraintMap is a type alias for a map containing parameter
-  // constraints. It is implemented as a map for fast look-ups of constraints
-  // of particular type. Also it is expected to have at most one constraint
-  // of each type (e.g. it makes no sense to impose two "minimum" constraints
-  // onto a numeric parameter).
-  using ConstraintMap = std::map<ConstraintType, std::unique_ptr<Constraint>>;
-
-  PropType();
-  virtual ~PropType();
-
-  // Gets the parameter type as an enumeration.
-  virtual ValueType GetType() const = 0;
-  // Gets the parameter type as a string.
-  std::string GetTypeAsString() const;
-  // Returns true if this parameter definition inherits a type
-  // definition from a base object schema.
-  bool IsBasedOnSchema() const { return based_on_schema_; }
-  // Returns a default value specified for the type, or nullptr if no default
-  // is available.
-  const PropValue* GetDefaultValue() const { return default_.value.get(); }
-  // Gets the constraints specified for the parameter, if any.
-  const ConstraintMap& GetConstraints() const { return constraints_; }
-  // Returns true if this value is required. Properties are marked as required
-  // by using "isRequired" attribute or listed in "required" array.
-  bool IsRequired() const;
-  // Sets the required attribute to the value of |required| and marks it as
-  // overridden (not-inherited).
-  void MakeRequired(bool required);
-  // Checks if any of the type attributes were overridden from the base
-  // schema definition. If this type does not inherit from a base schema,
-  // this method returns true.
-  // An attribute could be the value of any of the constraints, default
-  // value of a parameter or any other data that may be specified in
-  // parameter type definition in and can be inherited from the base schema.
-  virtual bool HasOverriddenAttributes() const;
-
-  // Type conversion methods. Used in lieu of RTTI and dynamic_cast<>.
-  virtual IntPropType* GetInt() { return nullptr; }
-  virtual IntPropType const* GetInt() const { return nullptr; }
-  virtual DoublePropType* GetDouble() { return nullptr; }
-  virtual DoublePropType const* GetDouble() const { return nullptr; }
-  virtual StringPropType* GetString() { return nullptr; }
-  virtual StringPropType const* GetString() const { return nullptr; }
-  virtual BooleanPropType* GetBoolean() { return nullptr; }
-  virtual BooleanPropType const* GetBoolean() const { return nullptr; }
-  virtual ObjectPropType* GetObject() { return nullptr; }
-  virtual ObjectPropType const* GetObject() const { return nullptr; }
-  virtual ArrayPropType* GetArray() { return nullptr; }
-  virtual ArrayPropType const* GetArray() const { return nullptr; }
-
-  // Makes a full copy of this type definition.
-  virtual std::unique_ptr<PropType> Clone() const;
-
-  // Creates an instance of associated value object.
-  virtual std::unique_ptr<PropValue> CreatePropValue(const base::Value& value,
-                                                     ErrorPtr* error) const = 0;
-
-  // Saves the parameter type definition as a JSON object.
-  // If |full_schema| is set to true, the full type definition is saved,
-  // otherwise only the overridden properties and attributes from the base
-  // schema is saved. That is, inherited properties and attributes are not
-  // saved.
-  // If it fails, returns "nullptr" and fills in the |error| with additional
-  // error information.
-  // |in_command_def| is set to true if the property type describes a
-  // GCD command parameter, otherwise it is for an object property.
-  // Command definitions handle required parameters differently (using
-  // "isRequired" property as opposed to "required" list for object properties).
-  virtual std::unique_ptr<base::Value> ToJson(bool full_schema,
-                                              bool in_command_def) const;
-  // Parses an JSON parameter type definition. Optional |base_schema| may
-  // specify the base schema type definition this type should be based upon.
-  // If not specified (nullptr), the parameter type is assumed to be a full
-  // definition and any omitted required properties are treated as an error.
-  // Returns true on success, otherwise fills in the |error| with additional
-  // error information.
-  virtual bool FromJson(const base::DictionaryValue* value,
-                        const PropType* base_schema,
-                        ErrorPtr* error);
-  // Helper function to load object schema from JSON.
-  virtual bool ObjectSchemaFromJson(const base::DictionaryValue* value,
-                                    const PropType* base_schema,
-                                    std::set<std::string>* processed_keys,
-                                    ErrorPtr* error) {
-    return true;
-  }
-  // Helper function to load type-specific constraints from JSON.
-  virtual bool ConstraintsFromJson(const base::DictionaryValue* value,
-                                   std::set<std::string>* processed_keys,
-                                   ErrorPtr* error) {
-    return true;
-  }
-
-  // Additional helper static methods to help with converting a type enum
-  // value into a string and back.
-  using TypeMap = std::vector<std::pair<ValueType, std::string>>;
-  // Returns a list of value types and corresponding type names.
-  static const TypeMap& GetTypeMap();
-  // Gets the type name string for the given type.
-  static std::string GetTypeStringFromType(ValueType type);
-  // Finds the type for the given type name. Returns true on success.
-  static bool GetTypeFromTypeString(const std::string& name, ValueType* type);
-
-  // Creates an instance of PropType-derived class for the specified
-  // parameter type.
-  static std::unique_ptr<PropType> Create(ValueType type);
-
-  // Adds a constraint to the type definition.
-  void AddConstraint(std::unique_ptr<Constraint> constraint);
-  // Removes a constraint of given type, if it exists.
-  void RemoveConstraint(ConstraintType constraint_type);
-  // Removes all constraints.
-  void RemoveAllConstraints();
-
-  // Finds a constraint of given type. Returns nullptr if not found.
-  const Constraint* GetConstraint(ConstraintType constraint_type) const;
-  Constraint* GetConstraint(ConstraintType constraint_type);
-
-  // Validates the given value against all the constraints.
-  bool ValidateConstraints(const PropValue& value, ErrorPtr* error) const;
-
- protected:
-  friend class StatePackage;
-  virtual std::unique_ptr<PropValue> CreateDefaultValue() const = 0;
-
-  // Specifies if this parameter definition is derived from a base
-  // object schema.
-  bool based_on_schema_ = false;
-  // A list of constraints specified for the parameter.
-  ConstraintMap constraints_;
-  // The default value specified for the parameter, if any. If the default
-  // value is present, the parameter is treated as optional and the default
-  // value is used if the parameter value is omitted when sending a command.
-  // Otherwise the parameter is treated as required and, if it is omitted,
-  // this is treated as an error.
-  InheritableAttribute<std::unique_ptr<PropValue>> default_;
-  // Specifies whether the parameter/property is required and must be specified
-  // (either directly, or by the default value being provided in the schema).
-  // Non-required parameters can be omitted completely and their values will not
-  // be present in the object instance.
-  InheritableAttribute<bool> required_;
-};
-
-// Base class for all the derived concrete implementations of property
-// type classes. Provides implementations for common methods of PropType base.
-template <class Derived, class Value, typename T>
-class PropTypeBase : public PropType {
- public:
-  // Overrides from PropType.
-  ValueType GetType() const override { return GetValueType<T>(); }
-
-  std::unique_ptr<PropValue> CreatePropValue(const base::Value& value,
-                                             ErrorPtr* error) const override {
-    return CreateValue(value, error);
-  }
-
-  std::unique_ptr<Value> CreateValue(const base::Value& value,
-                                     ErrorPtr* error) const {
-    return Value::CreateFromJson(value, *this, error);
-  }
-
-  bool ConstraintsFromJson(const base::DictionaryValue* value,
-                           std::set<std::string>* processed_keys,
-                           ErrorPtr* error) override;
-
- protected:
-  std::unique_ptr<PropValue> CreateDefaultValue() const override {
-    if (GetDefaultValue())
-      return GetDefaultValue()->Clone();
-    return std::unique_ptr<PropValue>{new Value{*this}};
-  }
-};
-
-// Helper base class for Int and Double parameter types.
-template <class Derived, class Value, typename T>
-class NumericPropTypeBase : public PropTypeBase<Derived, Value, T> {
- public:
-  using Base = PropTypeBase<Derived, Value, T>;
-  bool ConstraintsFromJson(const base::DictionaryValue* value,
-                           std::set<std::string>* processed_keys,
-                           ErrorPtr* error) override;
-
-  // Helper method to set and obtain a min/max constraint values.
-  // Used mostly for unit testing.
-  void AddMinMaxConstraint(T min_value, T max_value) {
-    InheritableAttribute<T> min_attr(min_value, false);
-    InheritableAttribute<T> max_attr(max_value, false);
-    this->AddConstraint(
-        std::unique_ptr<ConstraintMin<T>>{new ConstraintMin<T>{min_attr}});
-    this->AddConstraint(
-        std::unique_ptr<ConstraintMax<T>>{new ConstraintMax<T>{max_attr}});
-  }
-  T GetMinValue() const {
-    auto mmc = static_cast<const ConstraintMin<T>*>(
-        this->GetConstraint(ConstraintType::Min));
-    return mmc ? mmc->limit_.value : std::numeric_limits<T>::lowest();
-  }
-  T GetMaxValue() const {
-    auto mmc = static_cast<const ConstraintMax<T>*>(
-        this->GetConstraint(ConstraintType::Max));
-    return mmc ? mmc->limit_.value : (std::numeric_limits<T>::max)();
-  }
-};
-
-// Property definition of Integer type.
-class IntPropType : public NumericPropTypeBase<IntPropType, IntValue, int> {
- public:
-  // Overrides from the PropType base class.
-  IntPropType* GetInt() override { return this; }
-  IntPropType const* GetInt() const override { return this; }
-};
-
-// Property definition of Number type.
-class DoublePropType
-    : public NumericPropTypeBase<DoublePropType, DoubleValue, double> {
- public:
-  // Overrides from the PropType base class.
-  DoublePropType* GetDouble() override { return this; }
-  DoublePropType const* GetDouble() const override { return this; }
-};
-
-// Property definition of String type.
-class StringPropType
-    : public PropTypeBase<StringPropType, StringValue, std::string> {
- public:
-  using Base = PropTypeBase<StringPropType, StringValue, std::string>;
-  // Overrides from the PropType base class.
-  StringPropType* GetString() override { return this; }
-  StringPropType const* GetString() const override { return this; }
-
-  bool ConstraintsFromJson(const base::DictionaryValue* value,
-                           std::set<std::string>* processed_keys,
-                           ErrorPtr* error) override;
-
-  // Helper methods to add and inspect simple constraints.
-  // Used mostly for unit testing.
-  void AddLengthConstraint(int min_len, int max_len);
-  int GetMinLength() const;
-  int GetMaxLength() const;
-};
-
-// Property definition of Boolean type.
-class BooleanPropType
-    : public PropTypeBase<BooleanPropType, BooleanValue, bool> {
- public:
-  // Overrides from the PropType base class.
-  BooleanPropType* GetBoolean() override { return this; }
-  BooleanPropType const* GetBoolean() const override { return this; }
-};
-
-// Parameter definition of Object type.
-class ObjectPropType
-    : public PropTypeBase<ObjectPropType, ObjectValue, ValueMap> {
- public:
-  using Base = PropTypeBase<ObjectPropType, ObjectValue, ValueMap>;
-  ObjectPropType();
-
-  // Overrides from the ParamType base class.
-  bool HasOverriddenAttributes() const override;
-
-  ObjectPropType* GetObject() override { return this; }
-  ObjectPropType const* GetObject() const override { return this; }
-
-  std::unique_ptr<PropType> Clone() const override;
-
-  std::unique_ptr<base::Value> ToJson(bool full_schema,
-                                      bool in_command_def) const override;
-  bool ObjectSchemaFromJson(const base::DictionaryValue* value,
-                            const PropType* base_schema,
-                            std::set<std::string>* processed_keys,
-                            ErrorPtr* error) override;
-
-  // Returns a schema for Object-type parameter.
-  inline const ObjectSchema* GetObjectSchemaPtr() const {
-    return object_schema_.value.get();
-  }
-  void SetObjectSchema(std::unique_ptr<const ObjectSchema> schema);
-
- private:
-  InheritableAttribute<std::unique_ptr<const ObjectSchema>> object_schema_;
-};
-
-// Parameter definition of Array type.
-class ArrayPropType
-    : public PropTypeBase<ArrayPropType, ArrayValue, ValueVector> {
- public:
-  using Base = PropTypeBase<ArrayPropType, ArrayValue, ValueVector>;
-  ArrayPropType();
-
-  // Overrides from the ParamType base class.
-  bool HasOverriddenAttributes() const override;
-
-  ArrayPropType* GetArray() override { return this; }
-  ArrayPropType const* GetArray() const override { return this; }
-
-  std::unique_ptr<PropType> Clone() const override;
-
-  std::unique_ptr<base::Value> ToJson(bool full_schema,
-                                      bool in_command_def) const override;
-
-  bool ObjectSchemaFromJson(const base::DictionaryValue* value,
-                            const PropType* base_schema,
-                            std::set<std::string>* processed_keys,
-                            ErrorPtr* error) override;
-
-  // Returns a type for Array elements.
-  inline const PropType* GetItemTypePtr() const {
-    return item_type_.value.get();
-  }
-  void SetItemType(std::unique_ptr<const PropType> item_type);
-
- private:
-  InheritableAttribute<std::unique_ptr<const PropType>> item_type_;
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_PROP_TYPES_H_
diff --git a/src/commands/prop_values.cc b/src/commands/prop_values.cc
deleted file mode 100644
index 6f30bee..0000000
--- a/src/commands/prop_values.cc
+++ /dev/null
@@ -1,17 +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 "src/commands/prop_values.h"
-
-#include "src/commands/prop_types.h"
-
-namespace weave {
-
-PropValue::PropValue(const PropType& type) : type_{type.Clone()} {}
-
-PropValue::PropValue(const PropValue& other) : PropValue{*other.type_} {}
-
-PropValue::~PropValue() {}
-
-}  // namespace weave
diff --git a/src/commands/prop_values.h b/src/commands/prop_values.h
deleted file mode 100644
index f0a401e..0000000
--- a/src/commands/prop_values.h
+++ /dev/null
@@ -1,250 +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_SRC_COMMANDS_PROP_VALUES_H_
-#define LIBWEAVE_SRC_COMMANDS_PROP_VALUES_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include <weave/error.h>
-
-#include "src/commands/schema_utils.h"
-
-namespace base {
-class Value;
-class DictionaryValue;
-}  // namespace base
-
-namespace weave {
-
-// Enumeration to indicate supported command parameter types.
-enum class ValueType {
-  Int,
-  Double,
-  String,
-  Boolean,
-  Object,
-  Array,
-};
-
-class PropValue;
-class IntValue;
-class DoubleValue;
-class StringValue;
-class BooleanValue;
-class ObjectValue;
-class ArrayValue;
-
-class PropType;
-
-// Helper methods to get the parameter type enum value for the given
-// native C++ data representation.
-// The generic GetValueType<T>() is undefined, however particular
-// type specializations return the appropriate ValueType.
-template <typename T>
-ValueType GetValueType();  // Undefined.
-template <>
-inline ValueType GetValueType<int>() {
-  return ValueType::Int;
-}
-template <>
-inline ValueType GetValueType<double>() {
-  return ValueType::Double;
-}
-template <>
-inline ValueType GetValueType<std::string>() {
-  return ValueType::String;
-}
-template <>
-inline ValueType GetValueType<bool>() {
-  return ValueType::Boolean;
-}
-template <>
-inline ValueType GetValueType<ValueMap>() {
-  return ValueType::Object;
-}
-template <>
-inline ValueType GetValueType<ValueVector>() {
-  return ValueType::Array;
-}
-
-// The base class for property values.
-// Concrete value classes of various types will be derived from this base.
-// A property value is the actual command parameter value (or a concrete value
-// that can be used in constraints and presets). The PropValue is mostly
-// just parsed content of base::Value when a command is dispatched, however
-// it does have some additional functionality:
-//   - it has a reference to the type definition (PropType) which is used
-//     when validating the value, especially for "object" types.
-//   - it can be compared with another instances of values of the same type.
-//     This is used to validate the values against "enum"/"one of" constraints.
-class PropValue {
- public:
-  // Only CreateDefaultValue should use this constructor.
-  explicit PropValue(const PropType& type);
-  virtual ~PropValue();
-
-  // Gets the type of the value.
-  virtual ValueType GetType() const = 0;
-
-  // Type conversion methods. Used in lieu of RTTI and dynamic_cast<>.
-  virtual IntValue* GetInt() { return nullptr; }
-  virtual IntValue const* GetInt() const { return nullptr; }
-  virtual DoubleValue* GetDouble() { return nullptr; }
-  virtual DoubleValue const* GetDouble() const { return nullptr; }
-  virtual StringValue* GetString() { return nullptr; }
-  virtual StringValue const* GetString() const { return nullptr; }
-  virtual BooleanValue* GetBoolean() { return nullptr; }
-  virtual BooleanValue const* GetBoolean() const { return nullptr; }
-  virtual ObjectValue* GetObject() { return nullptr; }
-  virtual ObjectValue const* GetObject() const { return nullptr; }
-  virtual ArrayValue* GetArray() { return nullptr; }
-  virtual ArrayValue const* GetArray() const { return nullptr; }
-
-  // Makes a full copy of this value class.
-  virtual std::unique_ptr<PropValue> Clone() const = 0;
-
-  // Saves the value as a JSON object. Never fails.
-  virtual std::unique_ptr<base::Value> ToJson() const = 0;
-
-  // Return the type definition of this value.
-  const PropType* GetPropType() const { return type_.get(); }
-  // Compares two values and returns true if they are equal.
-  virtual bool IsEqual(const PropValue* value) const = 0;
-
- protected:
-  // Special out-of-line constructor to help implement PropValue::Clone().
-  // That method needs to clone the underlying type but can't do this in this
-  // header file since PropType is just forward-declared (it needs PropValue
-  // fully defined in its own inner workings).
-  explicit PropValue(const PropValue& other);
-
- private:
-  std::unique_ptr<const PropType> type_;
-};
-
-// A helper template base class for implementing value classes.
-template <typename T>
-class TypedValueBase : public PropValue {
- public:
-  using PropValue::PropValue;
-
-  // Overrides from PropValue base class.
-  ValueType GetType() const override { return GetValueType<T>(); }
-
-  std::unique_ptr<base::Value> ToJson() const override {
-    return TypedValueToJson(value_);
-  }
-
-  bool IsEqual(const PropValue* value) const override {
-    if (GetType() != value->GetType())
-      return false;
-    return CompareValue(GetValue(),
-                        static_cast<const TypedValueBase*>(value)->GetValue());
-  }
-
-  // Helper methods to get and set the C++ representation of the value.
-  const T& GetValue() const { return value_; }
-
- protected:
-  explicit TypedValueBase(const TypedValueBase& other)
-      : PropValue(other), value_(other.value_) {}
-
-  TypedValueBase(const PropType& type, T value)
-      : PropValue(type), value_(value) {}
-
- private:
-  T value_{};  // The value of the parameter in C++ data representation.
-};
-
-// A helper template base class for implementing value classes.
-template <typename Derived, typename T>
-class TypedValueWithClone : public TypedValueBase<T> {
- public:
-  using Base = TypedValueWithClone<Derived, T>;
-
-  // Expose the custom constructor of the base class.
-  using TypedValueBase<T>::TypedValueBase;
-  using PropValue::GetPropType;
-
-  std::unique_ptr<PropValue> Clone() const override {
-    return std::unique_ptr<PropValue>{
-        new Derived{*static_cast<const Derived*>(this)}};
-  }
-
-  static std::unique_ptr<Derived> CreateFromJson(const base::Value& value,
-                                                 const PropType& type,
-                                                 ErrorPtr* error) {
-    T tmp_value;
-    if (!TypedValueFromJson(&value, &type, &tmp_value, error))
-      return nullptr;
-
-    // Only place where invalid value can exist.
-    std::unique_ptr<Derived> result{new Derived{type, tmp_value}};
-    if (!result->GetPropType()->ValidateConstraints(*result, error))
-      return nullptr;
-
-    return result;
-  }
-};
-
-// Value of type Integer.
-class IntValue final : public TypedValueWithClone<IntValue, int> {
- public:
-  using Base::Base;
-  friend class TypedValueWithClone<IntValue, int>;
-  IntValue* GetInt() override { return this; }
-  IntValue const* GetInt() const override { return this; }
-};
-
-// Value of type Number.
-class DoubleValue final : public TypedValueWithClone<DoubleValue, double> {
- public:
-  using Base::Base;
-  friend class TypedValueWithClone<DoubleValue, double>;
-  DoubleValue* GetDouble() override { return this; }
-  DoubleValue const* GetDouble() const override { return this; }
-};
-
-// Value of type String.
-class StringValue final : public TypedValueWithClone<StringValue, std::string> {
- public:
-  using Base::Base;
-  friend class TypedValueWithClone<StringValue, std::string>;
-  StringValue* GetString() override { return this; }
-  StringValue const* GetString() const override { return this; }
-};
-
-// Value of type Boolean.
-class BooleanValue final : public TypedValueWithClone<BooleanValue, bool> {
- public:
-  using Base::Base;
-  friend class TypedValueWithClone<BooleanValue, bool>;
-  BooleanValue* GetBoolean() override { return this; }
-  BooleanValue const* GetBoolean() const override { return this; }
-};
-
-// Value of type Object.
-class ObjectValue final : public TypedValueWithClone<ObjectValue, ValueMap> {
- public:
-  using Base::Base;
-  friend class TypedValueWithClone<ObjectValue, ValueMap>;
-  ObjectValue* GetObject() override { return this; }
-  ObjectValue const* GetObject() const override { return this; }
-};
-
-// Value of type Array.
-class ArrayValue final : public TypedValueWithClone<ArrayValue, ValueVector> {
- public:
-  using Base::Base;
-  friend class TypedValueWithClone<ArrayValue, ValueVector>;
-  ArrayValue* GetArray() override { return this; }
-  ArrayValue const* GetArray() const override { return this; }
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_PROP_VALUES_H_
diff --git a/src/commands/schema_constants.cc b/src/commands/schema_constants.cc
index 7f8431f..34d6db8 100644
--- a/src/commands/schema_constants.cc
+++ b/src/commands/schema_constants.cc
@@ -10,17 +10,9 @@
 namespace commands {
 const char kDomain[] = "command_schema";
 
-const char kOutOfRange[] = "out_of_range";
 const char kTypeMismatch[] = "type_mismatch";
-const char kPropTypeChanged[] = "param_type_changed";
-const char kUnknownType[] = "unknown_type";
-const char kInvalidPropDef[] = "invalid_parameter_definition";
 const char kInvalidPropValue[] = "invalid_parameter_value";
-const char kNoTypeInfo[] = "no_type_info";
 const char kPropertyMissing[] = "parameter_missing";
-const char kUnknownProperty[] = "unexpected_parameter";
-const char kInvalidObjectSchema[] = "invalid_object_schema";
-const char kDuplicateCommandDef[] = "duplicate_command_definition";
 const char kInvalidCommandName[] = "invalid_command_name";
 const char kCommandFailed[] = "command_failed";
 const char kInvalidMinimalRole[] = "invalid_minimal_role";
@@ -32,27 +24,9 @@
 namespace commands {
 namespace attributes {
 
-const char kType[] = "type";
-const char kDisplayName[] = "displayName";
-const char kDefault[] = "default";
-const char kItems[] = "items";
-const char kIsRequired[] = "isRequired";
-
-const char kNumeric_Min[] = "minimum";
-const char kNumeric_Max[] = "maximum";
-
-const char kString_MinLength[] = "minLength";
-const char kString_MaxLength[] = "maxLength";
-
-const char kOneOf_Enum[] = "enum";
-const char kOneOf_Metadata[] = "metadata";
-
-const char kObject_Properties[] = "properties";
-const char kObject_AdditionalProperties[] = "additionalProperties";
-const char kObject_Required[] = "required";
-
 const char kCommand_Id[] = "id";
 const char kCommand_Name[] = "name";
+const char kCommand_Component[] = "component";
 const char kCommand_Parameters[] = "parameters";
 const char kCommand_Progress[] = "progress";
 const char kCommand_Results[] = "results";
diff --git a/src/commands/schema_constants.h b/src/commands/schema_constants.h
index ea59f17..9199480 100644
--- a/src/commands/schema_constants.h
+++ b/src/commands/schema_constants.h
@@ -13,17 +13,9 @@
 extern const char kDomain[];
 
 // Common command definition error codes.
-extern const char kOutOfRange[];
 extern const char kTypeMismatch[];
-extern const char kPropTypeChanged[];
-extern const char kUnknownType[];
-extern const char kInvalidPropDef[];
 extern const char kInvalidPropValue[];
-extern const char kNoTypeInfo[];
 extern const char kPropertyMissing[];
-extern const char kUnknownProperty[];
-extern const char kInvalidObjectSchema[];
-extern const char kDuplicateCommandDef[];
 extern const char kInvalidCommandName[];
 extern const char kCommandFailed[];
 extern const char kInvalidMinimalRole[];
@@ -35,27 +27,9 @@
 namespace commands {
 namespace attributes {
 // Command description JSON schema attributes.
-extern const char kType[];
-extern const char kDisplayName[];
-extern const char kDefault[];
-extern const char kItems[];
-extern const char kIsRequired[];
-
-extern const char kNumeric_Min[];
-extern const char kNumeric_Max[];
-
-extern const char kString_MinLength[];
-extern const char kString_MaxLength[];
-
-extern const char kOneOf_Enum[];
-extern const char kOneOf_Metadata[];
-
-extern const char kObject_Properties[];
-extern const char kObject_AdditionalProperties[];
-extern const char kObject_Required[];
-
 extern const char kCommand_Id[];
 extern const char kCommand_Name[];
+extern const char kCommand_Component[];
 extern const char kCommand_Parameters[];
 extern const char kCommand_Progress[];
 extern const char kCommand_Results[];
diff --git a/src/commands/schema_utils.cc b/src/commands/schema_utils.cc
deleted file mode 100644
index 3c3e949..0000000
--- a/src/commands/schema_utils.cc
+++ /dev/null
@@ -1,266 +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 "src/commands/schema_utils.h"
-
-#include <algorithm>
-#include <set>
-#include <string>
-
-#include <base/json/json_writer.h>
-#include <base/logging.h>
-
-#include "src/commands/object_schema.h"
-#include "src/commands/prop_types.h"
-#include "src/commands/prop_values.h"
-
-namespace weave {
-namespace {
-// Helper function to report "type mismatch" errors when parsing JSON.
-void ReportJsonTypeMismatch(const tracked_objects::Location& location,
-                            const base::Value* value_in,
-                            const std::string& expected_type,
-                            ErrorPtr* error) {
-  std::string value_as_string;
-  base::JSONWriter::Write(*value_in, &value_as_string);
-  Error::AddToPrintf(error, location, errors::commands::kDomain,
-                     errors::commands::kTypeMismatch,
-                     "Unable to convert value %s into %s",
-                     value_as_string.c_str(), expected_type.c_str());
-}
-
-// Template version of ReportJsonTypeMismatch that deduces the type of expected
-// data from the value_out parameter passed to particular overload of
-// TypedValueFromJson() function. Always returns false.
-template <typename T>
-bool ReportUnexpectedJson(const tracked_objects::Location& location,
-                          const base::Value* value_in,
-                          T*,
-                          ErrorPtr* error) {
-  ReportJsonTypeMismatch(location, value_in,
-                         PropType::GetTypeStringFromType(GetValueType<T>()),
-                         error);
-  return false;
-}
-
-bool ErrorMissingProperty(ErrorPtr* error,
-                          const tracked_objects::Location& location,
-                          const char* param_name) {
-  Error::AddToPrintf(error, location, errors::commands::kDomain,
-                     errors::commands::kPropertyMissing,
-                     "Required parameter missing: %s", param_name);
-  return false;
-}
-
-}  // namespace
-
-// Specializations of TypedValueToJson<T>() for supported C++ types.
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(bool value) {
-  return std::unique_ptr<base::FundamentalValue>(
-      new base::FundamentalValue(value));
-}
-
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(int value) {
-  return std::unique_ptr<base::FundamentalValue>(
-      new base::FundamentalValue(value));
-}
-
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(double value) {
-  return std::unique_ptr<base::FundamentalValue>(
-      new base::FundamentalValue(value));
-}
-
-std::unique_ptr<base::StringValue> TypedValueToJson(const std::string& value) {
-  return std::unique_ptr<base::StringValue>(new base::StringValue(value));
-}
-
-std::unique_ptr<base::DictionaryValue> TypedValueToJson(const ValueMap& value) {
-  std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
-  for (const auto& pair : value) {
-    auto prop_value = pair.second->ToJson();
-    CHECK(prop_value);
-    dict->SetWithoutPathExpansion(pair.first, prop_value.release());
-  }
-  return dict;
-}
-
-std::unique_ptr<base::ListValue> TypedValueToJson(const ValueVector& value) {
-  std::unique_ptr<base::ListValue> list(new base::ListValue);
-  for (const auto& item : value) {
-    auto json = item->ToJson();
-    CHECK(json);
-    list->Append(json.release());
-  }
-  return list;
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        bool* value_out,
-                        ErrorPtr* error) {
-  return value_in->GetAsBoolean(value_out) ||
-         ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        int* value_out,
-                        ErrorPtr* error) {
-  return value_in->GetAsInteger(value_out) ||
-         ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        double* value_out,
-                        ErrorPtr* error) {
-  return value_in->GetAsDouble(value_out) ||
-         ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        std::string* value_out,
-                        ErrorPtr* error) {
-  return value_in->GetAsString(value_out) ||
-         ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        ValueMap* value_out,
-                        ErrorPtr* error) {
-  const base::DictionaryValue* dict = nullptr;
-  if (!value_in->GetAsDictionary(&dict))
-    return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-
-  CHECK(type) << "Object definition must be provided";
-  CHECK(ValueType::Object == type->GetType()) << "Type must be Object";
-
-  const ObjectSchema* object_schema = type->GetObject()->GetObjectSchemaPtr();
-  std::set<std::string> keys_processed;
-  value_out->clear();  // Clear possible default values already in |value_out|.
-  for (const auto& pair : object_schema->GetProps()) {
-    const PropValue* def_value = pair.second->GetDefaultValue();
-    if (dict->HasKey(pair.first)) {
-      const base::Value* param_value = nullptr;
-      CHECK(dict->GetWithoutPathExpansion(pair.first, &param_value))
-          << "Unable to get parameter";
-      auto value = pair.second->CreatePropValue(*param_value, error);
-      if (!value) {
-        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                           errors::commands::kInvalidPropValue,
-                           "Invalid value for property '%s'",
-                           pair.first.c_str());
-        return false;
-      }
-      value_out->emplace_hint(value_out->end(), pair.first, std::move(value));
-      keys_processed.insert(pair.first);
-    } else if (def_value) {
-      value_out->emplace_hint(value_out->end(), pair.first, def_value->Clone());
-      keys_processed.insert(pair.first);
-    } else if (pair.second->IsRequired()) {
-      return ErrorMissingProperty(error, FROM_HERE, pair.first.c_str());
-    }
-  }
-
-  // Just for sanity, make sure that we processed all the necessary properties
-  // and there weren't any extra (unknown) ones specified. If so, ignore
-  // them, but log as warnings...
-  base::DictionaryValue::Iterator iter(*dict);
-  while (!iter.IsAtEnd()) {
-    std::string key = iter.key();
-    if (keys_processed.find(key) == keys_processed.end() &&
-        !object_schema->GetExtraPropertiesAllowed()) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kUnknownProperty,
-                         "Unrecognized parameter '%s'", key.c_str());
-      return false;
-    }
-    iter.Advance();
-  }
-
-  // Now go over all property values and validate them.
-  for (const auto& pair : *value_out) {
-    const PropType* prop_type = pair.second->GetPropType();
-    CHECK(prop_type) << "Value property type must be available";
-    if (!prop_type->ValidateConstraints(*pair.second, error)) {
-      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
-                         errors::commands::kInvalidPropValue,
-                         "Invalid value for property '%s'", pair.first.c_str());
-      return false;
-    }
-  }
-  return true;
-}
-
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        ValueVector* value_out,
-                        ErrorPtr* error) {
-  const base::ListValue* list = nullptr;
-  if (!value_in->GetAsList(&list))
-    return ReportUnexpectedJson(FROM_HERE, value_in, value_out, error);
-
-  CHECK(type) << "Array type definition must be provided";
-  CHECK(ValueType::Array == type->GetType()) << "Type must be Array";
-  const PropType* item_type = type->GetArray()->GetItemTypePtr();
-  CHECK(item_type) << "Incomplete array type definition";
-
-  // This value might already contain values from the type defaults.
-  // Clear them first before proceeding.
-  value_out->clear();
-  value_out->reserve(list->GetSize());
-  for (const base::Value* item : *list) {
-    std::unique_ptr<PropValue> prop_value =
-        item_type->CreatePropValue(*item, error);
-    if (!prop_value)
-      return false;
-    value_out->push_back(std::move(prop_value));
-  }
-  return true;
-}
-
-// Compares two sets of key-value pairs from two Objects.
-static bool obj_cmp(const ValueMap::value_type& v1,
-                    const ValueMap::value_type& v2) {
-  return (v1.first == v2.first) && v1.second->IsEqual(v2.second.get());
-}
-
-bool operator==(const ValueMap& obj1, const ValueMap& obj2) {
-  if (obj1.size() != obj2.size())
-    return false;
-
-  auto pair = std::mismatch(obj1.begin(), obj1.end(), obj2.begin(), obj_cmp);
-  return pair == std::make_pair(obj1.end(), obj2.end());
-}
-
-bool operator==(const ValueVector& arr1, const ValueVector& arr2) {
-  if (arr1.size() != arr2.size())
-    return false;
-
-  using Type = const ValueVector::value_type;
-  // Compare two array items.
-  auto arr_cmp = [](const Type& v1, const Type& v2) {
-    return v1->IsEqual(v2.get());
-  };
-  auto pair = std::mismatch(arr1.begin(), arr1.end(), arr2.begin(), arr_cmp);
-  return pair == std::make_pair(arr1.end(), arr2.end());
-}
-
-std::string ToString(const ValueMap& obj) {
-  auto val = TypedValueToJson(obj);
-  std::string str;
-  base::JSONWriter::Write(*val, &str);
-  return str;
-}
-
-std::string ToString(const ValueVector& arr) {
-  auto val = TypedValueToJson(arr);
-  std::string str;
-  base::JSONWriter::Write(*val, &str);
-  return str;
-}
-
-}  // namespace weave
diff --git a/src/commands/schema_utils.h b/src/commands/schema_utils.h
deleted file mode 100644
index 0c1d1b3..0000000
--- a/src/commands/schema_utils.h
+++ /dev/null
@@ -1,136 +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_SRC_COMMANDS_SCHEMA_UTILS_H_
-#define LIBWEAVE_SRC_COMMANDS_SCHEMA_UTILS_H_
-
-#include <cmath>
-#include <limits>
-#include <map>
-#include <memory>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include <base/logging.h>
-#include <base/values.h>
-#include <weave/error.h>
-
-namespace weave {
-
-class PropType;
-class PropValue;
-class ObjectSchema;
-class ObjectValue;
-
-// C++ representation of object values.
-using ValueMap = std::map<std::string, std::shared_ptr<const PropValue>>;
-
-// C++ representation of array of values.
-using ValueVector = std::vector<std::shared_ptr<const PropValue>>;
-
-// Converts an object to string.
-std::string ToString(const ValueMap& obj);
-
-// Converts an array to string.
-std::string ToString(const ValueVector& arr);
-
-// InheritableAttribute class is used for specifying various command parameter
-// attributes that can be inherited from a base (parent) schema.
-// The |value| still specifies the actual attribute values, whether it
-// is inherited or overridden, while |is_inherited| can be used to identify
-// if the attribute was inherited (true) or overridden (false).
-template <typename T>
-class InheritableAttribute final {
- public:
-  InheritableAttribute() = default;
-  explicit InheritableAttribute(T val)
-      : value(std::move(val)), is_inherited(true) {}
-  InheritableAttribute(T val, bool inherited)
-      : value(std::move(val)), is_inherited(inherited) {}
-  T value{};
-  bool is_inherited{true};
-};
-
-// A bunch of helper function to create base::Value for specific C++ classes,
-// including vectors of types. These are used in template classes below
-// to simplify specialization logic.
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(bool value);
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(int value);
-std::unique_ptr<base::FundamentalValue> TypedValueToJson(double value);
-std::unique_ptr<base::StringValue> TypedValueToJson(const std::string& value);
-std::unique_ptr<base::DictionaryValue> TypedValueToJson(const ValueMap& value);
-std::unique_ptr<base::ListValue> TypedValueToJson(const ValueVector& value);
-template <typename T>
-std::unique_ptr<base::ListValue> TypedValueToJson(
-    const std::vector<T>& values) {
-  std::unique_ptr<base::ListValue> list(new base::ListValue);
-  for (const auto& v : values) {
-    auto json = TypedValueToJson(v);
-    CHECK(json);
-    list->Append(json.release());
-  }
-  return list;
-}
-
-// Similarly to TypedValueToJson() function above, the following overloaded
-// helper methods allow to extract specific C++ data types from base::Value.
-// Also used in template classes below to simplify specialization logic.
-// TODO(vitalybuka): Fix this. Interface is misleading. Seeing PropType internal
-// type validation is expected. In reality only ValueMap and ValueVector do
-// validation.
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        bool* value_out,
-                        ErrorPtr* error);
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        int* value_out,
-                        ErrorPtr* error);
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        double* value_out,
-                        ErrorPtr* error);
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        std::string* value_out,
-                        ErrorPtr* error);
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        ValueMap* value_out,
-                        ErrorPtr* error);
-bool TypedValueFromJson(const base::Value* value_in,
-                        const PropType* type,
-                        ValueVector* value_out,
-                        ErrorPtr* error);
-
-bool operator==(const ValueMap& obj1, const ValueMap& obj2);
-bool operator==(const ValueVector& arr1, const ValueVector& arr2);
-
-// CompareValue is a helper function to help with implementing EqualsTo operator
-// for various data types. For most scalar types it is using operator==(),
-// however, for floating point values, rounding errors in binary representation
-// of IEEE floats/doubles can cause straight == comparison to fail for seemingly
-// equivalent values. For these, use approximate comparison with the error
-// margin equal to the epsilon value defined for the corresponding data type.
-// This is used when looking up values for implementation of OneOf constraints
-// which should work reliably for floating points also ("number" type).
-
-// Compare exact types using ==.
-template <typename T>
-inline typename std::enable_if<!std::is_floating_point<T>::value, bool>::type
-CompareValue(const T& v1, const T& v2) {
-  return v1 == v2;
-}
-
-// Compare non-exact types (such as double) using precision margin (epsilon).
-template <typename T>
-inline typename std::enable_if<std::is_floating_point<T>::value, bool>::type
-CompareValue(const T& v1, const T& v2) {
-  return std::abs(v1 - v2) <= std::numeric_limits<T>::epsilon();
-}
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_SCHEMA_UTILS_H_
diff --git a/src/commands/schema_utils_unittest.cc b/src/commands/schema_utils_unittest.cc
deleted file mode 100644
index 1ea4c90..0000000
--- a/src/commands/schema_utils_unittest.cc
+++ /dev/null
@@ -1,212 +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 "src/commands/schema_utils.h"
-
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <base/values.h>
-#include <gtest/gtest.h>
-
-#include "src/commands/object_schema.h"
-#include "src/commands/prop_types.h"
-#include "src/commands/prop_values.h"
-#include "src/commands/schema_constants.h"
-#include "src/commands/unittest_utils.h"
-
-namespace weave {
-
-using test::CreateDictionaryValue;
-using test::CreateValue;
-
-TEST(CommandSchemaUtils, TypedValueToJson_Scalar) {
-  EXPECT_JSON_EQ("true", *TypedValueToJson(true));
-  EXPECT_JSON_EQ("false", *TypedValueToJson(false));
-
-  EXPECT_JSON_EQ("0", *TypedValueToJson(0));
-  EXPECT_JSON_EQ("-10", *TypedValueToJson(-10));
-  EXPECT_JSON_EQ("20", *TypedValueToJson(20));
-
-  EXPECT_JSON_EQ("0.0", *TypedValueToJson(0.0));
-  EXPECT_JSON_EQ("1.2", *TypedValueToJson(1.2));
-
-  EXPECT_JSON_EQ("'abc'", *TypedValueToJson(std::string("abc")));
-
-  std::vector<bool> bool_array{true, false};
-  EXPECT_JSON_EQ("[true,false]", *TypedValueToJson(bool_array));
-
-  std::vector<int> int_array{1, 2, 5};
-  EXPECT_JSON_EQ("[1,2,5]", *TypedValueToJson(int_array));
-
-  std::vector<double> dbl_array{1.1, 2.2};
-  EXPECT_JSON_EQ("[1.1,2.2]", *TypedValueToJson(dbl_array));
-
-  std::vector<std::string> str_array{"a", "bc"};
-  EXPECT_JSON_EQ("['a','bc']", *TypedValueToJson(str_array));
-}
-
-TEST(CommandSchemaUtils, TypedValueToJson_Object) {
-  IntPropType int_type;
-  ValueMap object;
-
-  object.insert(std::make_pair(
-      "width", int_type.CreateValue(base::FundamentalValue{640}, nullptr)));
-  object.insert(std::make_pair(
-      "height", int_type.CreateValue(base::FundamentalValue{480}, nullptr)));
-  EXPECT_JSON_EQ("{'height':480,'width':640}", *TypedValueToJson(object));
-}
-
-TEST(CommandSchemaUtils, TypedValueToJson_Array) {
-  IntPropType int_type;
-  ValueVector arr;
-
-  arr.push_back(int_type.CreateValue(base::FundamentalValue{640}, nullptr));
-  arr.push_back(int_type.CreateValue(base::FundamentalValue{480}, nullptr));
-  EXPECT_JSON_EQ("[640,480]", *TypedValueToJson(arr));
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_Bool) {
-  bool value;
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("true").get(), nullptr, &value, nullptr));
-  EXPECT_TRUE(value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("false").get(), nullptr, &value, nullptr));
-  EXPECT_FALSE(value);
-
-  ErrorPtr error;
-  EXPECT_FALSE(
-      TypedValueFromJson(CreateValue("0").get(), nullptr, &value, &error));
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_Int) {
-  int value;
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("0").get(), nullptr, &value, nullptr));
-  EXPECT_EQ(0, value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("23").get(), nullptr, &value, nullptr));
-  EXPECT_EQ(23, value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("-1234").get(), nullptr, &value, nullptr));
-  EXPECT_EQ(-1234, value);
-
-  ErrorPtr error;
-  EXPECT_FALSE(
-      TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, &error));
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_Double) {
-  double value;
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("0").get(), nullptr, &value, nullptr));
-  EXPECT_DOUBLE_EQ(0.0, value);
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("0.0").get(), nullptr, &value, nullptr));
-  EXPECT_DOUBLE_EQ(0.0, value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("23").get(), nullptr, &value, nullptr));
-  EXPECT_EQ(23.0, value);
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("23.1").get(), nullptr, &value, nullptr));
-  EXPECT_EQ(23.1, value);
-
-  EXPECT_TRUE(TypedValueFromJson(CreateValue("-1.23E+02").get(), nullptr,
-                                 &value, nullptr));
-  EXPECT_EQ(-123.0, value);
-
-  ErrorPtr error;
-  EXPECT_FALSE(
-      TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, &error));
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_String) {
-  std::string value;
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("''").get(), nullptr, &value, nullptr));
-  EXPECT_EQ("", value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("'23'").get(), nullptr, &value, nullptr));
-  EXPECT_EQ("23", value);
-
-  EXPECT_TRUE(
-      TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, nullptr));
-  EXPECT_EQ("abc", value);
-
-  ErrorPtr error;
-  EXPECT_FALSE(
-      TypedValueFromJson(CreateValue("12").get(), nullptr, &value, &error));
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_Object) {
-  ValueMap value;
-  std::unique_ptr<ObjectSchema> schema{new ObjectSchema};
-
-  IntPropType age_prop;
-  age_prop.AddMinMaxConstraint(0, 150);
-  schema->AddProp("age", age_prop.Clone());
-
-  StringPropType name_prop;
-  name_prop.AddLengthConstraint(1, 30);
-  schema->AddProp("name", name_prop.Clone());
-
-  ObjectPropType type;
-  type.SetObjectSchema(std::move(schema));
-  EXPECT_TRUE(TypedValueFromJson(CreateValue("{'age':20,'name':'Bob'}").get(),
-                                 &type, &value, nullptr));
-  ValueMap value2;
-  value2.insert(std::make_pair(
-      "age", age_prop.CreateValue(base::FundamentalValue{20}, nullptr)));
-  value2.insert(std::make_pair(
-      "name", name_prop.CreateValue(base::StringValue("Bob"), nullptr)));
-  EXPECT_EQ(value2, value);
-
-  ErrorPtr error;
-  EXPECT_FALSE(
-      TypedValueFromJson(CreateValue("'abc'").get(), nullptr, &value, &error));
-  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
-  error.reset();
-}
-
-TEST(CommandSchemaUtils, TypedValueFromJson_Array) {
-  ValueVector arr;
-  StringPropType str_type;
-  str_type.AddLengthConstraint(3, 100);
-  ArrayPropType type;
-  type.SetItemType(str_type.Clone());
-
-  EXPECT_TRUE(TypedValueFromJson(CreateValue("['foo', 'bar']").get(), &type,
-                                 &arr, nullptr));
-  ValueVector arr2;
-  arr2.push_back(str_type.CreateValue(base::StringValue{"foo"}, nullptr));
-  arr2.push_back(str_type.CreateValue(base::StringValue{"bar"}, nullptr));
-  EXPECT_EQ(arr2, arr);
-
-  ErrorPtr error;
-  EXPECT_FALSE(TypedValueFromJson(CreateValue("['baz', 'ab']").get(), &type,
-                                  &arr, &error));
-  EXPECT_EQ(errors::commands::kOutOfRange, error->GetCode());
-  error.reset();
-}
-
-}  // namespace weave
diff --git a/src/commands/unittest_utils.h b/src/commands/unittest_utils.h
deleted file mode 100644
index 25392ee..0000000
--- a/src/commands/unittest_utils.h
+++ /dev/null
@@ -1,47 +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_SRC_COMMANDS_UNITTEST_UTILS_H_
-#define LIBWEAVE_SRC_COMMANDS_UNITTEST_UTILS_H_
-
-#include <memory>
-#include <string>
-
-#include <base/values.h>
-#include <gtest/gtest.h>
-#include <weave/test/unittest_utils.h>
-
-#include "src/commands/prop_types.h"
-#include "src/commands/prop_values.h"
-
-namespace weave {
-namespace test {
-
-template <typename T>
-std::unique_ptr<const PropValue> make_prop_value(const base::Value& value) {
-  auto prop_type = PropType::Create(GetValueType<T>());
-  return prop_type->CreatePropValue(value, nullptr);
-}
-
-inline std::unique_ptr<const PropValue> make_int_prop_value(int value) {
-  return make_prop_value<int>(base::FundamentalValue{value});
-}
-
-inline std::unique_ptr<const PropValue> make_double_prop_value(double value) {
-  return make_prop_value<double>(base::FundamentalValue{value});
-}
-
-inline std::unique_ptr<const PropValue> make_bool_prop_value(bool value) {
-  return make_prop_value<bool>(base::FundamentalValue{value});
-}
-
-inline std::unique_ptr<const PropValue> make_string_prop_value(
-    const std::string& value) {
-  return make_prop_value<std::string>(base::StringValue{value});
-}
-
-}  // namespace test
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_COMMANDS_UNITTEST_UTILS_H_
diff --git a/src/component_manager.h b/src/component_manager.h
new file mode 100644
index 0000000..5f16ac4
--- /dev/null
+++ b/src/component_manager.h
@@ -0,0 +1,212 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_SRC_COMPONENT_MANAGER_H_
+#define LIBWEAVE_SRC_COMPONENT_MANAGER_H_
+
+#include <map>
+#include <memory>
+
+#include <base/callback_list.h>
+#include <base/time/clock.h>
+#include <base/values.h>
+#include <weave/error.h>
+
+#include "src/commands/command_queue.h"
+
+namespace weave {
+
+class CommandInstance;
+
+enum class UserRole {
+  kViewer,
+  kUser,
+  kManager,
+  kOwner,
+};
+
+// A simple notification record event to track component state changes.
+// The |timestamp| records the time of the state change.
+// |changed_properties| contains a property set with the new property values
+// which were updated at the time the event was recorded.
+struct ComponentStateChange {
+  ComponentStateChange(base::Time time,
+                       const std::string& path,
+                       std::unique_ptr<base::DictionaryValue> properties)
+      : timestamp{time}, component{path},
+        changed_properties{std::move(properties)} {}
+  base::Time timestamp;
+  std::string component;
+  std::unique_ptr<base::DictionaryValue> changed_properties;
+};
+
+class ComponentManager {
+ public:
+  using UpdateID = uint64_t;
+  using Token =
+      std::unique_ptr<base::CallbackList<void(UpdateID)>::Subscription>;
+  struct StateSnapshot {
+    UpdateID update_id;
+    std::vector<ComponentStateChange> state_changes;
+  };
+
+  ComponentManager() {}
+  virtual ~ComponentManager() {}
+
+  // Loads trait definition schema.
+  virtual bool LoadTraits(const base::DictionaryValue& dict,
+                          ErrorPtr* error) = 0;
+
+  // Same as the overload above, but takes a json string to read the trait
+  // definitions from.
+  virtual bool LoadTraits(const std::string& json, ErrorPtr* error) = 0;
+
+  // Sets callback which is called when new trait definitions are added.
+  virtual void AddTraitDefChangedCallback(const base::Closure& callback) = 0;
+
+  // Adds a new component instance to device.
+  // |path| is a path to the parent component (or empty string if a root-level
+  // component is being added).
+  // |name| is a component name being added.
+  // |traits| is a list of trait names this component supports.
+  virtual bool AddComponent(const std::string& path,
+                            const std::string& name,
+                            const std::vector<std::string>& traits,
+                            ErrorPtr* error) = 0;
+
+  // Adds a new component instance to device, as a part of component array.
+  // |path| is a path to the parent component.
+  // |name| is an array root element inside the child components.
+  // |traits| is a list of trait names this component supports.
+  virtual bool AddComponentArrayItem(const std::string& path,
+                                     const std::string& name,
+                                     const std::vector<std::string>& traits,
+                                     ErrorPtr* error) = 0;
+
+  // Sets callback which is called when new components are added.
+  virtual void AddComponentTreeChangedCallback(
+      const base::Closure& callback) = 0;
+
+  // Adds a new command instance to the command queue. The command specified in
+  // |command_instance| must be fully initialized and have its name, component,
+  // id populated.
+  virtual void AddCommand(
+      std::unique_ptr<CommandInstance> command_instance) = 0;
+
+  // Parses the command definition from a json dictionary. The resulting command
+  // instance is populated with all the required fields and partially validated
+  // against syntax/schema.
+  // The new command ID is returned through optional |id| param.
+  virtual std::unique_ptr<CommandInstance> ParseCommandInstance(
+      const base::DictionaryValue& command,
+      Command::Origin command_origin,
+      UserRole role,
+      std::string* id,
+      ErrorPtr* error) = 0;
+
+  // Find a command instance with the given ID in the command queue.
+  virtual CommandInstance* FindCommand(const std::string& id) = 0;
+
+  // Command queue monitoring callbacks (called when a new command is added to
+  // or removed from the queue).
+  virtual void AddCommandAddedCallback(
+      const CommandQueue::CommandCallback& callback) = 0;
+  virtual void AddCommandRemovedCallback(
+      const CommandQueue::CommandCallback& callback) = 0;
+
+  // Adds a command handler for specific component's command.
+  // |component_path| is a path to target component (e.g. "stove.burners[2]").
+  // |command_name| is a full path of the command, including trait name and
+  // command name (e.g. "burner.setPower").
+  virtual void AddCommandHandler(
+      const std::string& component_path,
+      const std::string& command_name,
+      const Device::CommandHandlerCallback& callback) = 0;
+
+  // Finds a component instance by its full path.
+  virtual const base::DictionaryValue* FindComponent(
+      const std::string& path,
+      ErrorPtr* error) const = 0;
+  // Finds a definition of trait with the given |name|.
+  virtual const base::DictionaryValue* FindTraitDefinition(
+      const std::string& name) const = 0;
+
+  // Finds a command definition, where |command_name| is in the form of
+  // "trait.command".
+  virtual const base::DictionaryValue* FindCommandDefinition(
+      const std::string& command_name) const = 0;
+
+  // Checks the minimum required user role for a given command.
+  virtual bool GetMinimalRole(const std::string& command_name,
+                              UserRole* minimal_role,
+                              ErrorPtr* error) const = 0;
+
+  // Returns the full JSON dictionary containing trait definitions.
+  virtual const base::DictionaryValue& GetTraits() const = 0;
+
+  // Returns the full JSON dictionary containing component instances.
+  virtual const base::DictionaryValue& GetComponents() const = 0;
+
+  // Component state manipulation methods.
+  virtual bool SetStateProperties(const std::string& component_path,
+                                  const base::DictionaryValue& dict,
+                                  ErrorPtr* error) = 0;
+  virtual bool SetStatePropertiesFromJson(const std::string& component_path,
+                                          const std::string& json,
+                                          ErrorPtr* error) = 0;
+  virtual const base::Value* GetStateProperty(const std::string& component_path,
+                                              const std::string& name,
+                                              ErrorPtr* error) const = 0;
+  virtual bool SetStateProperty(const std::string& component_path,
+                                const std::string& name,
+                                const base::Value& value,
+                                ErrorPtr* error) = 0;
+
+  virtual void AddStateChangedCallback(const base::Closure& callback) = 0;
+
+  // Returns the recorded state changes since last time this method was called.
+  virtual StateSnapshot GetAndClearRecordedStateChanges() = 0;
+
+  // Called to notify that the state patch with |id| has been successfully sent
+  // to the server and processed.
+  virtual void NotifyStateUpdatedOnServer(UpdateID id) = 0;
+
+  // Returns an ID of last state change update. Each SetStatePropertyNNN()
+  // invocation increments this value by 1.
+  virtual UpdateID GetLastStateChangeId() const = 0;
+
+  // Subscribes for device state update notifications from cloud server.
+  // The |callback| will be called every time a state patch with given ID is
+  // successfully received and processed by Weave server.
+  // Returns a subscription token. As soon as this token is destroyed, the
+  // respective callback is removed from the callback list.
+  virtual Token AddServerStateUpdatedCallback(
+      const base::Callback<void(UpdateID)>& callback) = 0;
+
+  // Helper method for legacy API to obtain first component that implements
+  // the given trait. This is useful for routing commands that have no component
+  // path specified.
+  // Returns empty string if no components are found.
+  // This method only searches for component on the top level of components
+  // tree. No sub-components are searched.
+  virtual std::string FindComponentWithTrait(
+      const std::string& trait) const = 0;
+
+  // Support for legacy APIs. Setting command and state definitions.
+  // This translates into modifying a trait definition.
+  virtual bool AddLegacyCommandDefinitions(const base::DictionaryValue& dict,
+                                           ErrorPtr* error) = 0;
+  virtual bool AddLegacyStateDefinitions(const base::DictionaryValue& dict,
+                                         ErrorPtr* error) = 0;
+  // Returns device state for legacy APIs.
+  virtual const base::DictionaryValue& GetLegacyState() const = 0;
+  // Returns command definitions for legacy APIs.
+  virtual const base::DictionaryValue& GetLegacyCommandDefinitions() const = 0;
+
+  DISALLOW_COPY_AND_ASSIGN(ComponentManager);
+};
+
+}  // namespace weave
+
+#endif  // LIBWEAVE_SRC_COMPONENT_MANAGER_H_
diff --git a/src/component_manager_impl.cc b/src/component_manager_impl.cc
new file mode 100644
index 0000000..a23f34d
--- /dev/null
+++ b/src/component_manager_impl.cc
@@ -0,0 +1,720 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/component_manager_impl.h"
+
+#include <base/strings/stringprintf.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_util.h>
+
+#include "src/commands/schema_constants.h"
+#include "src/json_error_codes.h"
+#include "src/string_utils.h"
+#include "src/utils.h"
+
+namespace weave {
+
+namespace {
+// Max of 100 state update events should be enough in the queue.
+const size_t kMaxStateChangeQueueSize = 100;
+
+const EnumToStringMap<UserRole>::Map kMap[] = {
+    {UserRole::kViewer, commands::attributes::kCommand_Role_Viewer},
+    {UserRole::kUser, commands::attributes::kCommand_Role_User},
+    {UserRole::kOwner, commands::attributes::kCommand_Role_Owner},
+    {UserRole::kManager, commands::attributes::kCommand_Role_Manager},
+};
+}  // anonymous namespace
+
+template <>
+LIBWEAVE_EXPORT EnumToStringMap<UserRole>::EnumToStringMap()
+    : EnumToStringMap(kMap) {}
+
+ComponentManagerImpl::ComponentManagerImpl() {}
+
+ComponentManagerImpl::ComponentManagerImpl(base::Clock* clock) : clock_{clock} {
+}
+
+ComponentManagerImpl::~ComponentManagerImpl() {}
+
+bool ComponentManagerImpl::AddComponent(const std::string& path,
+                                        const std::string& name,
+                                        const std::vector<std::string>& traits,
+                                        ErrorPtr* error) {
+  base::DictionaryValue* root = &components_;
+  if (!path.empty()) {
+    root = FindComponentGraftNode(path, error);
+    if (!root)
+      return false;
+  }
+  if (root->GetWithoutPathExpansion(name, nullptr)) {
+    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                       errors::commands::kInvalidState,
+                       "Component '%s' already exists at path '%s'",
+                       name.c_str(), path.c_str());
+    return false;
+  }
+
+  // Check to make sure the declared traits are already defined.
+  for (const std::string& trait : traits) {
+    if (!FindTraitDefinition(trait)) {
+      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                         errors::commands::kInvalidPropValue,
+                         "Trait '%s' is undefined", trait.c_str());
+      return false;
+    }
+  }
+  std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue};
+  std::unique_ptr<base::ListValue> traits_list{new base::ListValue};
+  traits_list->AppendStrings(traits);
+  dict->Set("traits", traits_list.release());
+  root->SetWithoutPathExpansion(name, dict.release());
+  for (const auto& cb : on_componet_tree_changed_)
+    cb.Run();
+  return true;
+}
+
+bool ComponentManagerImpl::AddComponentArrayItem(
+    const std::string& path,
+    const std::string& name,
+    const std::vector<std::string>& traits,
+    ErrorPtr* error) {
+  base::DictionaryValue* root = &components_;
+  if (!path.empty()) {
+    root = FindComponentGraftNode(path, error);
+    if (!root)
+      return false;
+  }
+  base::ListValue* array_value = nullptr;
+  if (!root->GetListWithoutPathExpansion(name, &array_value)) {
+    array_value = new base::ListValue;
+    root->SetWithoutPathExpansion(name, array_value);
+  }
+  std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue};
+  std::unique_ptr<base::ListValue> traits_list{new base::ListValue};
+  traits_list->AppendStrings(traits);
+  dict->Set("traits", traits_list.release());
+  array_value->Append(dict.release());
+  for (const auto& cb : on_componet_tree_changed_)
+    cb.Run();
+  return true;
+}
+
+void ComponentManagerImpl::AddComponentTreeChangedCallback(
+    const base::Closure& callback) {
+  on_componet_tree_changed_.push_back(callback);
+  callback.Run();
+}
+
+bool ComponentManagerImpl::LoadTraits(const base::DictionaryValue& dict,
+                                      ErrorPtr* error) {
+  bool modified = false;
+  bool result = true;
+  // Check if any of the new traits are already defined. If so, make sure the
+  // definition is exactly the same, or else this is an error.
+  for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+    if (it.value().GetType() != base::Value::TYPE_DICTIONARY) {
+      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                         errors::commands::kTypeMismatch,
+                         "Trait '%s' must be an object", it.key().c_str());
+      result = false;
+      break;
+    }
+    const base::DictionaryValue* existing_def = nullptr;
+    if (traits_.GetDictionary(it.key(), &existing_def)) {
+      if (!existing_def->Equals(&it.value())) {
+        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                           errors::commands::kTypeMismatch,
+                           "Trait '%s' cannot be redefined",
+                           it.key().c_str());
+        result = false;
+        break;
+      }
+    } else {
+      traits_.Set(it.key(), it.value().DeepCopy());
+      modified = true;
+    }
+  }
+
+  if (modified) {
+    for (const auto& cb : on_trait_changed_)
+      cb.Run();
+  }
+  return result;
+}
+
+bool ComponentManagerImpl::LoadTraits(const std::string& json,
+                                      ErrorPtr* error) {
+  std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
+  if (!dict)
+    return false;
+  return LoadTraits(*dict, error);
+}
+
+void ComponentManagerImpl::AddTraitDefChangedCallback(
+    const base::Closure& callback) {
+  on_trait_changed_.push_back(callback);
+  callback.Run();
+}
+
+void ComponentManagerImpl::AddCommand(
+    std::unique_ptr<CommandInstance> command_instance) {
+  command_queue_.Add(std::move(command_instance));
+}
+
+std::unique_ptr<CommandInstance> ComponentManagerImpl::ParseCommandInstance(
+    const base::DictionaryValue& command,
+    Command::Origin command_origin,
+    UserRole role,
+    std::string* id,
+    ErrorPtr* error) {
+  std::string command_id;
+  auto command_instance = CommandInstance::FromJson(&command, command_origin,
+                                                    &command_id, error);
+  // If we fail to validate the command definition, but there was a command ID
+  // specified there, return it to the caller when requested. This will be
+  // used to abort cloud commands.
+  if (id)
+    *id = command_id;
+
+  if (!command_instance)
+    return nullptr;
+
+  UserRole minimal_role;
+  if (!GetMinimalRole(command_instance->GetName(), &minimal_role, error))
+    return nullptr;
+
+  if (role < minimal_role) {
+    Error::AddToPrintf(
+        error, FROM_HERE, errors::commands::kDomain, "access_denied",
+        "User role '%s' less than minimal: '%s'", EnumToString(role).c_str(),
+        EnumToString(minimal_role).c_str());
+    return nullptr;
+  }
+
+  std::string component_path = command_instance->GetComponent();
+  if (component_path.empty()) {
+    // Find the component to which to route this command. Get the trait name
+    // from the command name and find the first component that has this trait.
+    auto trait_name = SplitAtFirst(command_instance->GetName(), ".", true).first;
+    component_path = FindComponentWithTrait(trait_name);
+    if (component_path.empty()) {
+      Error::AddToPrintf(
+          error, FROM_HERE, errors::commands::kDomain, "unrouted_command",
+          "Unable route command '%s' because there is no component supporting"
+          "trait '%s'", command_instance->GetName().c_str(),
+          trait_name.c_str());
+      return nullptr;
+    }
+    command_instance->SetComponent(component_path);
+  }
+
+  const base::DictionaryValue* component = FindComponent(component_path, error);
+  if (!component)
+    return nullptr;
+
+  // Check that the command's trait is supported by the given component.
+  auto pair = SplitAtFirst(command_instance->GetName(), ".", true);
+
+  bool trait_supported = false;
+  const base::ListValue* supported_traits = nullptr;
+  if (component->GetList("traits", &supported_traits)) {
+    for (const base::Value* value : *supported_traits) {
+      std::string trait;
+      CHECK(value->GetAsString(&trait));
+      if (trait == pair.first) {
+        trait_supported = true;
+        break;
+      }
+    }
+  }
+
+  if (!trait_supported) {
+    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                       "trait_not_supported",
+                       "Component '%s' doesn't support trait '%s'",
+                       component_path.c_str(), pair.first.c_str());
+    return nullptr;
+  }
+
+  if (command_id.empty()) {
+    command_id = std::to_string(++next_command_id_);
+    command_instance->SetID(command_id);
+    if (id)
+      *id = command_id;
+  }
+
+  return command_instance;
+}
+
+CommandInstance* ComponentManagerImpl::FindCommand(const std::string& id) {
+  return command_queue_.Find(id);
+}
+
+void ComponentManagerImpl::AddCommandAddedCallback(
+    const CommandQueue::CommandCallback& callback) {
+  command_queue_.AddCommandAddedCallback(callback);
+}
+
+void ComponentManagerImpl::AddCommandRemovedCallback(
+    const CommandQueue::CommandCallback& callback) {
+  command_queue_.AddCommandRemovedCallback(callback);
+}
+
+void ComponentManagerImpl::AddCommandHandler(
+    const std::string& component_path,
+    const std::string& command_name,
+    const Device::CommandHandlerCallback& callback) {
+  // If both component_path and command_name are empty, we are adding the
+  // default handler for all commands.
+  if (!component_path.empty() || !command_name.empty()) {
+    CHECK(FindCommandDefinition(command_name))
+        << "Command undefined: " << command_name;
+  }
+  command_queue_.AddCommandHandler(component_path, command_name, callback);
+}
+
+const base::DictionaryValue* ComponentManagerImpl::FindComponent(
+    const std::string& path, ErrorPtr* error) const {
+  return FindComponentAt(&components_, path, error);
+}
+
+const base::DictionaryValue* ComponentManagerImpl::FindTraitDefinition(
+    const std::string& name) const {
+  const base::DictionaryValue* trait = nullptr;
+  traits_.GetDictionaryWithoutPathExpansion(name, &trait);
+  return trait;
+}
+
+const base::DictionaryValue* ComponentManagerImpl::FindCommandDefinition(
+    const std::string& command_name) const {
+  const base::DictionaryValue* definition = nullptr;
+  std::vector<std::string> components = Split(command_name, ".", true, false);
+  // Make sure the |command_name| came in form of trait_name.command_name.
+  if (components.size() != 2)
+    return definition;
+  std::string key = base::StringPrintf("%s.commands.%s", components[0].c_str(),
+                                       components[1].c_str());
+  traits_.GetDictionary(key, &definition);
+  return definition;
+}
+
+bool ComponentManagerImpl::GetMinimalRole(const std::string& command_name,
+                                          UserRole* minimal_role,
+                                          ErrorPtr* error) const {
+  const base::DictionaryValue* command = FindCommandDefinition(command_name);
+  if (!command) {
+    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                       errors::commands::kInvalidCommandName,
+                       "Command definition for '%s' not found",
+                       command_name.c_str());
+    return false;
+  }
+  std::string value;
+  // The JSON definition has been pre-validated already in LoadCommands, so
+  // just using CHECKs here.
+  CHECK(command->GetString(commands::attributes::kCommand_Role, &value));
+  CHECK(StringToEnum(value, minimal_role));
+  return true;
+}
+
+void ComponentManagerImpl::AddStateChangedCallback(
+    const base::Closure& callback) {
+  on_state_changed_.push_back(callback);
+  callback.Run();  // Force to read current state.
+}
+
+bool ComponentManagerImpl::SetStateProperties(const std::string& component_path,
+                                              const base::DictionaryValue& dict,
+                                              ErrorPtr* error) {
+  base::DictionaryValue* component =
+      FindMutableComponent(component_path, error);
+  if (!component)
+    return false;
+
+  base::DictionaryValue* state = nullptr;
+  if (!component->GetDictionary("state", &state)) {
+    state = new base::DictionaryValue;
+    component->Set("state", state);
+  }
+  state->MergeDictionary(&dict);
+  last_state_change_id_++;
+  auto& queue = state_change_queues_[component_path];
+  if (!queue)
+    queue.reset(new StateChangeQueue{kMaxStateChangeQueueSize});
+  base::Time timestamp = clock_ ? clock_->Now() : base::Time::Now();
+  queue->NotifyPropertiesUpdated(timestamp, dict);
+  for (const auto& cb : on_state_changed_)
+    cb.Run();
+  return true;
+}
+
+bool ComponentManagerImpl::SetStatePropertiesFromJson(
+    const std::string& component_path,
+    const std::string& json,
+    ErrorPtr* error) {
+  std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
+  return dict && SetStateProperties(component_path, *dict, error);
+}
+
+const base::Value* ComponentManagerImpl::GetStateProperty(
+    const std::string& component_path,
+    const std::string& name,
+    ErrorPtr* error) const {
+  const base::DictionaryValue* component = FindComponent(component_path, error);
+  if (!component)
+    return nullptr;
+  auto pair = SplitAtFirst(name, ".", true);
+  if (pair.first.empty()) {
+    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                       errors::commands::kPropertyMissing,
+                       "Empty state package in '%s'", name.c_str());
+    return nullptr;
+  }
+  if (pair.second.empty()) {
+    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                       errors::commands::kPropertyMissing,
+                       "State property name not specified in '%s'",
+                       name.c_str());
+    return nullptr;
+  }
+  std::string key = base::StringPrintf("state.%s", name.c_str());
+  const base::Value* value = nullptr;
+  if (!component->Get(key, &value)) {
+    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                       errors::commands::kPropertyMissing,
+                       "State property '%s' not found in component '%s'",
+                       name.c_str(), component_path.c_str());
+  }
+  return value;
+}
+
+bool ComponentManagerImpl::SetStateProperty(const std::string& component_path,
+                                            const std::string& name,
+                                            const base::Value& value,
+                                            ErrorPtr* error) {
+  base::DictionaryValue dict;
+  auto pair = SplitAtFirst(name, ".", true);
+  if (pair.first.empty()) {
+    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                       errors::commands::kPropertyMissing,
+                       "Empty state package in '%s'", name.c_str());
+    return false;
+  }
+  if (pair.second.empty()) {
+    Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                       errors::commands::kPropertyMissing,
+                       "State property name not specified in '%s'",
+                       name.c_str());
+    return false;
+  }
+  dict.Set(name, value.DeepCopy());
+  return SetStateProperties(component_path, dict, error);
+}
+
+ComponentManager::StateSnapshot
+ComponentManagerImpl::GetAndClearRecordedStateChanges() {
+  StateSnapshot snapshot;
+  snapshot.update_id = GetLastStateChangeId();
+  for (auto& pair : state_change_queues_) {
+    auto changes = pair.second->GetAndClearRecordedStateChanges();
+    auto component = pair.first;
+    auto conv = [component](weave::StateChange& change) {
+      return ComponentStateChange{change.timestamp, component,
+                                  std::move(change.changed_properties)};
+    };
+    std::transform(changes.begin(), changes.end(),
+                   std::back_inserter(snapshot.state_changes), conv);
+  }
+
+  // Sort events by the timestamp.
+  auto pred = [](const ComponentStateChange& lhs,
+                 const ComponentStateChange& rhs) {
+    return lhs.timestamp < rhs.timestamp;
+  };
+  std::sort(snapshot.state_changes.begin(), snapshot.state_changes.end(), pred);
+  state_change_queues_.clear();
+  return snapshot;
+}
+
+void ComponentManagerImpl::NotifyStateUpdatedOnServer(UpdateID id) {
+  on_server_state_updated_.Notify(id);
+}
+
+ComponentManager::Token ComponentManagerImpl::AddServerStateUpdatedCallback(
+    const base::Callback<void(UpdateID)>& callback) {
+  if (state_change_queues_.empty())
+    callback.Run(GetLastStateChangeId());
+  return Token{on_server_state_updated_.Add(callback).release()};
+}
+
+std::string ComponentManagerImpl::FindComponentWithTrait(
+    const std::string& trait) const {
+  for (base::DictionaryValue::Iterator it(components_); !it.IsAtEnd();
+       it.Advance()) {
+    const base::ListValue* supported_traits = nullptr;
+    const base::DictionaryValue* component = nullptr;
+    CHECK(it.value().GetAsDictionary(&component));
+    if (component->GetList("traits", &supported_traits)) {
+      for (const base::Value* value : *supported_traits) {
+        std::string supported_trait;
+        CHECK(value->GetAsString(&supported_trait));
+        if (trait == supported_trait)
+          return it.key();
+      }
+    }
+  }
+  return std::string{};
+}
+
+bool ComponentManagerImpl::AddLegacyCommandDefinitions(
+    const base::DictionaryValue& dict,
+    ErrorPtr* error) {
+  bool result = true;
+  bool modified = false;
+  for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+    const base::DictionaryValue* command_dict = nullptr;
+    if (!it.value().GetAsDictionary(&command_dict)) {
+      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                         errors::commands::kTypeMismatch,
+                         "Package '%s' must be an object", it.key().c_str());
+      result = false;
+      continue;
+    }
+    AddTraitToLegacyComponent(it.key());
+    for (base::DictionaryValue::Iterator it_def(*command_dict);
+         !it_def.IsAtEnd(); it_def.Advance()) {
+      std::string key = base::StringPrintf("%s.commands.%s", it.key().c_str(),
+                                           it_def.key().c_str());
+      if (traits_.GetDictionary(key, nullptr)) {
+        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                           errors::commands::kInvalidPropValue,
+                           "Redefining command '%s.%s'",
+                           it.key().c_str(), it_def.key().c_str());
+        result = false;
+        continue;
+      }
+      traits_.Set(key, it_def.value().DeepCopy());
+      modified = true;
+    }
+  }
+
+  if (modified) {
+    for (const auto& cb : on_trait_changed_)
+      cb.Run();
+  }
+  return result;
+}
+
+bool ComponentManagerImpl::AddLegacyStateDefinitions(
+    const base::DictionaryValue& dict,
+    ErrorPtr* error) {
+  bool result = true;
+  bool modified = false;
+  for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+    const base::DictionaryValue* state_dict = nullptr;
+    if (!it.value().GetAsDictionary(&state_dict)) {
+      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                         errors::commands::kTypeMismatch,
+                         "Package '%s' must be an object", it.key().c_str());
+      result = false;
+      continue;
+    }
+    AddTraitToLegacyComponent(it.key());
+    for (base::DictionaryValue::Iterator it_def(*state_dict); !it_def.IsAtEnd();
+         it_def.Advance()) {
+      std::string key = base::StringPrintf("%s.state.%s", it.key().c_str(),
+                                           it_def.key().c_str());
+      if (traits_.GetDictionary(key, nullptr)) {
+        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                           errors::commands::kInvalidPropValue,
+                           "Redefining state property '%s.%s'",
+                           it.key().c_str(), it_def.key().c_str());
+        result = false;
+        continue;
+      }
+      traits_.Set(key, it_def.value().DeepCopy());
+      modified = true;
+    }
+  }
+
+  if (modified) {
+    for (const auto& cb : on_trait_changed_)
+      cb.Run();
+  }
+  return result;
+}
+
+const base::DictionaryValue& ComponentManagerImpl::GetLegacyState() const {
+  legacy_state_.Clear();
+  // Build state from components.
+  for (base::DictionaryValue::Iterator it(components_); !it.IsAtEnd();
+       it.Advance()) {
+    const base::DictionaryValue* component_dict = nullptr;
+    const base::DictionaryValue* component_state = nullptr;
+    if (it.value().GetAsDictionary(&component_dict) &&
+        component_dict->GetDictionary("state", &component_state)) {
+      legacy_state_.MergeDictionary(component_state);
+    }
+  }
+  return legacy_state_;
+}
+
+const base::DictionaryValue&
+ComponentManagerImpl::GetLegacyCommandDefinitions() const {
+  legacy_command_defs_.Clear();
+  // Build commandDefs from traits.
+  for (base::DictionaryValue::Iterator it(traits_); !it.IsAtEnd();
+       it.Advance()) {
+    const base::DictionaryValue* trait_dict = nullptr;
+    const base::DictionaryValue* trait_commands = nullptr;
+    if (it.value().GetAsDictionary(&trait_dict) &&
+        trait_dict->GetDictionary("commands", &trait_commands)) {
+      base::DictionaryValue dict;
+      dict.Set(it.key(), trait_commands->DeepCopy());
+      legacy_command_defs_.MergeDictionary(&dict);
+    }
+  }
+  return legacy_command_defs_;
+}
+
+void ComponentManagerImpl::AddTraitToLegacyComponent(const std::string& trait) {
+  // First check if we already have a component supporting this trait.
+  if (!FindComponentWithTrait(trait).empty())
+    return;
+
+  // If not, add this trait to the first component available.
+  base::DictionaryValue* component = nullptr;
+  base::DictionaryValue::Iterator it(components_);
+  if (it.IsAtEnd()) {
+    // No components at all. Create a new one with dummy name.
+    // This normally wouldn't happen since libweave creates its own component
+    // at startup.
+    component = new base::DictionaryValue;
+    components_.Set("__weave__", component);
+  } else {
+    CHECK(components_.GetDictionary(it.key(), &component));
+  }
+  base::ListValue* traits = nullptr;
+  if (!component->GetList("traits", &traits)) {
+    traits = new base::ListValue;
+    component->Set("traits", traits);
+  }
+  traits->AppendString(trait);
+}
+
+base::DictionaryValue* ComponentManagerImpl::FindComponentGraftNode(
+    const std::string& path, ErrorPtr* error) {
+  base::DictionaryValue* root = nullptr;
+  base::DictionaryValue* component = FindMutableComponent(path, error);
+  if (component && !component->GetDictionary("components", &root)) {
+    root = new base::DictionaryValue;
+    component->Set("components", root);
+  }
+  return root;
+}
+
+base::DictionaryValue* ComponentManagerImpl::FindMutableComponent(
+    const std::string& path,
+    ErrorPtr* error) {
+  return const_cast<base::DictionaryValue*>(
+      FindComponentAt(&components_, path, error));
+}
+
+const base::DictionaryValue* ComponentManagerImpl::FindComponentAt(
+    const base::DictionaryValue* root,
+    const std::string& path,
+    ErrorPtr* error) {
+  auto parts = Split(path, ".", true, false);
+  std::string root_path;
+  for (size_t i = 0; i < parts.size(); i++) {
+    auto element = SplitAtFirst(parts[i], "[", true);
+    int array_index = -1;
+    if (element.first.empty()) {
+      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                         errors::commands::kPropertyMissing,
+                         "Empty path element at '%s'", root_path.c_str());
+      return nullptr;
+    }
+    if (!element.second.empty()) {
+      if (element.second.back() != ']') {
+        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                           errors::commands::kPropertyMissing,
+                           "Invalid array element syntax '%s'",
+                           parts[i].c_str());
+        return nullptr;
+      }
+      element.second.pop_back();
+      std::string index_str;
+      base::TrimWhitespaceASCII(element.second, base::TrimPositions::TRIM_ALL,
+                                &index_str);
+      if (!base::StringToInt(index_str, &array_index) || array_index < 0) {
+        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                           errors::commands::kInvalidPropValue,
+                           "Invalid array index '%s'", element.second.c_str());
+        return nullptr;
+      }
+    }
+
+    if (!root_path.empty()) {
+      // We have processed at least one item in the path before, so now |root|
+      // points to the actual parent component. We need the root to point to
+      // the 'components' element containing child sub-components instead.
+      if (!root->GetDictionary("components", &root)) {
+        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                           errors::commands::kPropertyMissing,
+                           "Component '%s' does not exist at '%s'",
+                           element.first.c_str(), root_path.c_str());
+        return nullptr;
+      }
+    }
+
+    const base::Value* value = nullptr;
+    if (!root->GetWithoutPathExpansion(element.first, &value)) {
+      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                         errors::commands::kPropertyMissing,
+                         "Component '%s' does not exist at '%s'",
+                         element.first.c_str(), root_path.c_str());
+      return nullptr;
+    }
+
+    if (value->GetType() == base::Value::TYPE_LIST && array_index < 0) {
+      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                         errors::commands::kTypeMismatch,
+                         "Element '%s.%s' is an array",
+                         root_path.c_str(), element.first.c_str());
+      return nullptr;
+    }
+    if (value->GetType() == base::Value::TYPE_DICTIONARY && array_index >= 0) {
+      Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                         errors::commands::kTypeMismatch,
+                         "Element '%s.%s' is not an array",
+                         root_path.c_str(), element.first.c_str());
+      return nullptr;
+    }
+
+    if (value->GetType() == base::Value::TYPE_DICTIONARY) {
+      CHECK(value->GetAsDictionary(&root));
+    } else {
+      const base::ListValue* component_array = nullptr;
+      CHECK(value->GetAsList(&component_array));
+      const base::Value* component_value = nullptr;
+      if (!component_array->Get(array_index, &component_value) ||
+          !component_value->GetAsDictionary(&root)) {
+        Error::AddToPrintf(error, FROM_HERE, errors::commands::kDomain,
+                           errors::commands::kPropertyMissing,
+                           "Element '%s.%s' does not contain item #%d",
+                           root_path.c_str(), element.first.c_str(),
+                           array_index);
+        return nullptr;
+      }
+    }
+    if (!root_path.empty())
+      root_path += '.';
+    root_path += parts[i];
+  }
+  return root;
+}
+
+}  // namespace weave
diff --git a/src/component_manager_impl.h b/src/component_manager_impl.h
new file mode 100644
index 0000000..c59c6d9
--- /dev/null
+++ b/src/component_manager_impl.h
@@ -0,0 +1,217 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_SRC_COMPONENT_MANAGER_IMPL_H_
+#define LIBWEAVE_SRC_COMPONENT_MANAGER_IMPL_H_
+
+#include "src/commands/command_queue.h"
+#include "src/component_manager.h"
+#include "src/states/state_change_queue.h"
+
+namespace weave {
+
+class ComponentManagerImpl final : public ComponentManager {
+ public:
+  ComponentManagerImpl();
+  explicit ComponentManagerImpl(base::Clock* clock);
+  ~ComponentManagerImpl() override;
+
+  // Loads trait definition schema.
+  bool LoadTraits(const base::DictionaryValue& dict, ErrorPtr* error) override;
+
+  // Same as the overload above, but takes a json string to read the trait
+  // definitions from.
+  bool LoadTraits(const std::string& json, ErrorPtr* error) override;
+
+  // Sets callback which is called when new trait definitions are added.
+  void AddTraitDefChangedCallback(const base::Closure& callback) override;
+
+  // Adds a new component instance to device.
+  // |path| is a path to the parent component (or empty string if a root-level
+  // component is being added).
+  // |name| is a component name being added.
+  // |traits| is a list of trait names this component supports.
+  bool AddComponent(const std::string& path,
+                    const std::string& name,
+                    const std::vector<std::string>& traits,
+                    ErrorPtr* error) override;
+
+  // Adds a new component instance to device, as a part of component array.
+  // |path| is a path to the parent component.
+  // |name| is an array root element inside the child components.
+  // |traits| is a list of trait names this component supports.
+  bool AddComponentArrayItem(const std::string& path,
+                             const std::string& name,
+                             const std::vector<std::string>& traits,
+                             ErrorPtr* error) override;
+
+  // Sets callback which is called when new components are added.
+  void AddComponentTreeChangedCallback(const base::Closure& callback) override;
+
+  // Adds a new command instance to the command queue. The command specified in
+  // |command_instance| must be fully initialized and have its name, component,
+  // id populated.
+  void AddCommand(
+      std::unique_ptr<CommandInstance> command_instance) override;
+
+  // Parses the command definition from a json dictionary. The resulting command
+  // instance is populated with all the required fields and partially validated
+  // against syntax/schema.
+  // The new command ID is returned through optional |id| param.
+  std::unique_ptr<CommandInstance> ParseCommandInstance(
+      const base::DictionaryValue& command,
+      Command::Origin command_origin,
+      UserRole role,
+      std::string* id,
+      ErrorPtr* error) override;
+
+  // Find a command instance with the given ID in the command queue.
+  CommandInstance* FindCommand(const std::string& id) override;
+
+  // Command queue monitoring callbacks (called when a new command is added to
+  // or removed from the queue).
+  void AddCommandAddedCallback(
+      const CommandQueue::CommandCallback& callback) override;
+  void AddCommandRemovedCallback(
+      const CommandQueue::CommandCallback& callback) override;
+
+  // Adds a command handler for specific component's command.
+  // |component_path| is a path to target component (e.g. "stove.burners[2]").
+  // |command_name| is a full path of the command, including trait name and
+  // command name (e.g. "burner.setPower").
+  void AddCommandHandler(
+      const std::string& component_path,
+      const std::string& command_name,
+      const Device::CommandHandlerCallback& callback) override;
+
+  // Finds a component instance by its full path.
+  const base::DictionaryValue* FindComponent(const std::string& path,
+                                             ErrorPtr* error) const override;
+  // Finds a definition of trait with the given |name|.
+  const base::DictionaryValue* FindTraitDefinition(
+      const std::string& name) const override;
+
+  // Finds a command definition, where |command_name| is in the form of
+  // "trait.command".
+  const base::DictionaryValue* FindCommandDefinition(
+      const std::string& command_name) const override;
+
+  // Checks the minimum required user role for a given command.
+  bool GetMinimalRole(const std::string& command_name,
+                      UserRole* minimal_role,
+                      ErrorPtr* error) const override;
+
+  // Returns the full JSON dictionary containing trait definitions.
+  const base::DictionaryValue& GetTraits() const override { return traits_; }
+
+  // Returns the full JSON dictionary containing component instances.
+  const base::DictionaryValue& GetComponents() const override {
+    return components_;
+  }
+
+  // Component state manipulation methods.
+  bool SetStateProperties(const std::string& component_path,
+                          const base::DictionaryValue& dict,
+                          ErrorPtr* error) override;
+  bool SetStatePropertiesFromJson(const std::string& component_path,
+                                  const std::string& json,
+                                  ErrorPtr* error) override;
+  const base::Value* GetStateProperty(const std::string& component_path,
+                                      const std::string& name,
+                                      ErrorPtr* error) const override;
+  bool SetStateProperty(const std::string& component_path,
+                        const std::string& name,
+                        const base::Value& value,
+                        ErrorPtr* error) override;
+
+  void AddStateChangedCallback(const base::Closure& callback) override;
+
+  // Returns the recorded state changes since last time this method was called.
+  StateSnapshot GetAndClearRecordedStateChanges() override;
+
+  // Called to notify that the state patch with |id| has been successfully sent
+  // to the server and processed.
+  void NotifyStateUpdatedOnServer(UpdateID id) override;
+
+  // Returns an ID of last state change update. Each SetStatePropertyNNN()
+  // invocation increments this value by 1.
+  UpdateID GetLastStateChangeId() const override {
+    return last_state_change_id_;
+  }
+
+  // Subscribes for device state update notifications from cloud server.
+  // The |callback| will be called every time a state patch with given ID is
+  // successfully received and processed by Weave server.
+  // Returns a subscription token. As soon as this token is destroyed, the
+  // respective callback is removed from the callback list.
+  Token AddServerStateUpdatedCallback(
+      const base::Callback<void(UpdateID)>& callback) override;
+
+  // Helper method for legacy API to obtain first component that implements
+  // the given trait. This is useful for routing commands that have no component
+  // path specified.
+  // Returns empty string if no components are found.
+  // This method only searches for component on the top level of components
+  // tree. No sub-components are searched.
+  std::string FindComponentWithTrait(const std::string& trait) const override;
+
+  // Support for legacy APIs. Setting command and state definitions.
+  // This translates into modifying a trait definition.
+  bool AddLegacyCommandDefinitions(const base::DictionaryValue& dict,
+                                   ErrorPtr* error) override;
+  bool AddLegacyStateDefinitions(const base::DictionaryValue& dict,
+                                 ErrorPtr* error) override;
+  // Returns device state for legacy APIs.
+  const base::DictionaryValue& GetLegacyState() const override;
+  // Returns command definitions for legacy APIs.
+  const base::DictionaryValue& GetLegacyCommandDefinitions() const override;
+
+ private:
+  // A helper method to find a JSON element of component at |path| to add new
+  // sub-components to.
+  base::DictionaryValue* FindComponentGraftNode(const std::string& path,
+                                                ErrorPtr* error);
+  base::DictionaryValue* FindMutableComponent(const std::string& path,
+                                              ErrorPtr* error);
+
+  // Legacy API support: Helper function to support state/command definitions.
+  // Adds the given trait to at least one component.
+  // Searches for available components and if none of them already supports this
+  // trait, it adds it to the first available component.
+  void AddTraitToLegacyComponent(const std::string& trait);
+
+  // Helper method to find a sub-component given a root node and a relative path
+  // from the root to the target component.
+  static const base::DictionaryValue* FindComponentAt(
+      const base::DictionaryValue* root,
+      const std::string& path,
+      ErrorPtr* error);
+
+  base::Clock* clock_{nullptr};
+  // An ID of last state change update. Each NotifyPropertiesUpdated()
+  // invocation increments this value by 1.
+  UpdateID last_state_change_id_{0};
+  // Callback list for state change queue event sinks.
+  // This member must be defined before |command_queue_|.
+  base::CallbackList<void(UpdateID)> on_server_state_updated_;
+
+  base::DictionaryValue traits_;  // Trait definitions.
+  base::DictionaryValue components_;  // Component instances.
+  CommandQueue command_queue_;  // Command queue containing command instances.
+  std::vector<base::Closure> on_trait_changed_;
+  std::vector<base::Closure> on_componet_tree_changed_;
+  std::vector<base::Closure> on_state_changed_;
+  uint32_t next_command_id_{0};
+  std::map<std::string, std::unique_ptr<StateChangeQueue>> state_change_queues_;
+
+  // Legacy API support.
+  mutable base::DictionaryValue legacy_state_;  // Device state.
+  mutable base::DictionaryValue legacy_command_defs_;  // Command definitions.
+
+  DISALLOW_COPY_AND_ASSIGN(ComponentManagerImpl);
+};
+
+}  // namespace weave
+
+#endif  // LIBWEAVE_SRC_COMPONENT_MANAGER_IMPL_H_
diff --git a/src/component_manager_unittest.cc b/src/component_manager_unittest.cc
new file mode 100644
index 0000000..902ee15
--- /dev/null
+++ b/src/component_manager_unittest.cc
@@ -0,0 +1,1456 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/component_manager_impl.h"
+
+#include <map>
+
+#include <gtest/gtest.h>
+#include <weave/test/unittest_utils.h>
+
+#include "src/bind_lambda.h"
+#include "src/commands/schema_constants.h"
+#include "src/mock_component_manager.h"
+
+namespace weave {
+
+using test::CreateDictionaryValue;
+
+namespace {
+
+bool HasTrait(const base::DictionaryValue& comp, const std::string& trait) {
+  const base::ListValue* list = nullptr;
+  if (!comp.GetList("traits", &list))
+    return false;
+  for (const base::Value* item : *list) {
+    std::string value;
+    if (item->GetAsString(&value) && value == trait)
+      return true;
+  }
+  return false;
+}
+
+// Creates sample trait/component trees:
+// {
+//   "traits": {
+//     "t1": {},
+//     "t2": {},
+//     "t3": {},
+//     "t4": {},
+//     "t5": {},
+//     "t6": {},
+//   },
+//   "components": {
+//     "comp1": {
+//       "traits": [ "t1" ],
+//       "components": {
+//         "comp2": [
+//           { "traits": [ "t2" ] },
+//           {
+//             "traits": [ "t3" ],
+//             "components": {
+//               "comp3": {
+//                 "traits": [ "t4" ],
+//                 "components": {
+//                   "comp4": {
+//                     "traits": [ "t5", "t6" ]
+//                   }
+//                 }
+//               }
+//             }
+//           }
+//         ],
+//       }
+//     }
+//   }
+// }
+void CreateTestComponentTree(ComponentManager* manager) {
+  const char kTraits[] = R"({"t1":{},"t2":{},"t3":{},"t4":{},"t5":{},"t6":{}})";
+  auto json = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager->LoadTraits(*json, nullptr));
+  EXPECT_TRUE(manager->AddComponent("", "comp1", {"t1"}, nullptr));
+  EXPECT_TRUE(manager->AddComponentArrayItem("comp1", "comp2", {"t2"},
+                                             nullptr));
+  EXPECT_TRUE(manager->AddComponentArrayItem("comp1", "comp2", {"t3"},
+                                             nullptr));
+  EXPECT_TRUE(manager->AddComponent("comp1.comp2[1]", "comp3", {"t4"},
+                                    nullptr));
+  EXPECT_TRUE(manager->AddComponent("comp1.comp2[1].comp3", "comp4",
+                                    {"t5", "t6"}, nullptr));
+}
+
+// Test clock class to record predefined time intervals.
+// Implementation from base/test/simple_test_clock.{h|cc}
+class SimpleTestClock : public base::Clock {
+ public:
+  base::Time Now() override { return now_; }
+
+  // Advances the clock by |delta|.
+  void Advance(base::TimeDelta delta) { now_ += delta; }
+
+  // Sets the clock to the given time.
+  void SetNow(base::Time now) { now_ = now; }
+
+ private:
+  base::Time now_;
+};
+
+}  // anonymous namespace
+
+TEST(ComponentManager, Empty) {
+  ComponentManagerImpl manager;
+  EXPECT_TRUE(manager.GetTraits().empty());
+  EXPECT_TRUE(manager.GetComponents().empty());
+}
+
+TEST(ComponentManager, LoadTraits) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": {
+          "minimalRole": "user",
+          "parameters": {"height": {"type": "integer"}}
+        }
+      },
+      "state": {
+        "property1": {"type": "boolean"}
+      }
+    },
+    "trait2": {
+      "state": {
+        "property2": {"type": "string"}
+      }
+    }
+  })";
+  auto json = CreateDictionaryValue(kTraits);
+  EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
+  EXPECT_JSON_EQ(kTraits, manager.GetTraits());
+  EXPECT_TRUE(manager.GetComponents().empty());
+}
+
+TEST(ComponentManager, LoadTraitsDuplicateIdentical) {
+  ComponentManagerImpl manager;
+  const char kTraits1[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": {
+          "minimalRole": "user",
+          "parameters": {"height": {"type": "integer"}}
+        }
+      },
+      "state": {
+        "property1": {"type": "boolean"}
+      }
+    },
+    "trait2": {
+      "state": {
+        "property2": {"type": "string"}
+      }
+    }
+  })";
+  auto json = CreateDictionaryValue(kTraits1);
+  EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
+  const char kTraits2[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": {
+          "minimalRole": "user",
+          "parameters": {"height": {"type": "integer"}}
+        }
+      },
+      "state": {
+        "property1": {"type": "boolean"}
+      }
+    },
+    "trait3": {
+      "state": {
+        "property3": {"type": "string"}
+      }
+    }
+  })";
+  json = CreateDictionaryValue(kTraits2);
+  EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
+  const char kExpected[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": {
+          "minimalRole": "user",
+          "parameters": {"height": {"type": "integer"}}
+        }
+      },
+      "state": {
+        "property1": {"type": "boolean"}
+      }
+    },
+    "trait2": {
+      "state": {
+        "property2": {"type": "string"}
+      }
+    },
+    "trait3": {
+      "state": {
+        "property3": {"type": "string"}
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected, manager.GetTraits());
+}
+
+TEST(ComponentManager, LoadTraitsDuplicateOverride) {
+  ComponentManagerImpl manager;
+  const char kTraits1[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": {
+          "minimalRole": "user",
+          "parameters": {"height": {"type": "integer"}}
+        }
+      },
+      "state": {
+        "property1": {"type": "boolean"}
+      }
+    },
+    "trait2": {
+      "state": {
+        "property2": {"type": "string"}
+      }
+    }
+  })";
+  auto json = CreateDictionaryValue(kTraits1);
+  EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
+  const char kTraits2[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": {
+          "minimalRole": "user",
+          "parameters": {"height": {"type": "string"}}
+        }
+      },
+      "state": {
+        "property1": {"type": "boolean"}
+      }
+    },
+    "trait3": {
+      "state": {
+        "property3": {"type": "string"}
+      }
+    }
+  })";
+  json = CreateDictionaryValue(kTraits2);
+  EXPECT_FALSE(manager.LoadTraits(*json, nullptr));
+}
+
+TEST(ComponentManager, AddTraitDefChangedCallback) {
+  ComponentManagerImpl manager;
+  int count = 0;
+  int count2 = 0;
+  manager.AddTraitDefChangedCallback(base::Bind([&count]() { count++; }));
+  manager.AddTraitDefChangedCallback(base::Bind([&count2]() { count2++; }));
+  EXPECT_EQ(1, count);
+  EXPECT_EQ(1, count2);
+  // New definitions.
+  const char kTraits1[] = R"({
+    "trait1": {
+      "state": {
+        "property1": {"type": "boolean"}
+      }
+    },
+    "trait2": {
+      "state": {
+        "property2": {"type": "string"}
+      }
+    }
+  })";
+  auto json = CreateDictionaryValue(kTraits1);
+  EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
+  EXPECT_EQ(2, count);
+  // Duplicate definition, shouldn't call the callback.
+  const char kTraits2[] = R"({
+    "trait1": {
+      "state": {
+        "property1": {"type": "boolean"}
+      }
+    }
+  })";
+  json = CreateDictionaryValue(kTraits2);
+  EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
+  EXPECT_EQ(2, count);
+  // New definition, should call the callback now.
+  const char kTraits3[] = R"({
+    "trait3": {
+      "state": {
+        "property3": {"type": "string"}
+      }
+    }
+  })";
+  json = CreateDictionaryValue(kTraits3);
+  EXPECT_TRUE(manager.LoadTraits(*json, nullptr));
+  EXPECT_EQ(3, count);
+  // Wrong definition, shouldn't call the callback.
+  const char kTraits4[] = R"({
+    "trait4": "foo"
+  })";
+  json = CreateDictionaryValue(kTraits4);
+  EXPECT_FALSE(manager.LoadTraits(*json, nullptr));
+  EXPECT_EQ(3, count);
+  // Make sure both callbacks were called the same number of times.
+  EXPECT_EQ(count2, count);
+}
+
+TEST(ComponentManager, LoadTraitsNotAnObject) {
+  ComponentManagerImpl manager;
+  const char kTraits1[] = R"({"trait1": 0})";
+  auto json = CreateDictionaryValue(kTraits1);
+  ErrorPtr error;
+  EXPECT_FALSE(manager.LoadTraits(*json, &error));
+  EXPECT_EQ(errors::commands::kTypeMismatch, error->GetCode());
+}
+
+TEST(ComponentManager, FindTraitDefinition) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": {
+          "minimalRole": "user",
+          "parameters": {"height": {"type": "integer"}}
+        }
+      },
+      "state": {
+        "property1": {"type": "boolean"}
+      }
+    },
+    "trait2": {
+      "state": {
+        "property2": {"type": "string"}
+      }
+    }
+  })";
+  auto json = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
+
+  const base::DictionaryValue* trait = manager.FindTraitDefinition("trait1");
+  ASSERT_NE(nullptr, trait);
+  const char kExpected1[] = R"({
+    "commands": {
+      "command1": {
+        "minimalRole": "user",
+        "parameters": {"height": {"type": "integer"}}
+      }
+    },
+    "state": {
+      "property1": {"type": "boolean"}
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected1, *trait);
+
+  trait = manager.FindTraitDefinition("trait2");
+  ASSERT_NE(nullptr, trait);
+  const char kExpected2[] = R"({
+    "state": {
+      "property2": {"type": "string"}
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected2, *trait);
+
+  EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait3"));
+}
+
+TEST(ComponentManager, FindCommandDefinition) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": {
+          "minimalRole": "user",
+          "parameters": {"height": {"type": "integer"}}
+        }
+      }
+    },
+    "trait2": {
+      "commands": {
+        "command1": {
+          "minimalRole": "manager"
+        },
+        "command2": {
+          "minimalRole": "owner"
+        }
+      }
+    }
+  })";
+  auto json = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
+
+  const auto* cmd_def = manager.FindCommandDefinition("trait1.command1");
+  ASSERT_NE(nullptr, cmd_def);
+  const char kExpected1[] = R"({
+    "minimalRole": "user",
+    "parameters": {"height": {"type": "integer"}}
+  })";
+  EXPECT_JSON_EQ(kExpected1, *cmd_def);
+
+  cmd_def = manager.FindCommandDefinition("trait2.command1");
+  ASSERT_NE(nullptr, cmd_def);
+  const char kExpected2[] = R"({
+    "minimalRole": "manager"
+  })";
+  EXPECT_JSON_EQ(kExpected2, *cmd_def);
+
+  cmd_def = manager.FindCommandDefinition("trait2.command2");
+  ASSERT_NE(nullptr, cmd_def);
+  const char kExpected3[] = R"({
+    "minimalRole": "owner"
+  })";
+  EXPECT_JSON_EQ(kExpected3, *cmd_def);
+
+  EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait1.command2"));
+  EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait3.command1"));
+  EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait"));
+  EXPECT_EQ(nullptr, manager.FindTraitDefinition("trait1.command1.parameters"));
+}
+
+TEST(ComponentManager, GetMinimalRole) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": { "minimalRole": "user" },
+        "command2": { "minimalRole": "viewer" }
+      }
+    },
+    "trait2": {
+      "commands": {
+        "command1": { "minimalRole": "manager" },
+        "command2": { "minimalRole": "owner" }
+      }
+    }
+  })";
+  auto json = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
+
+  UserRole role;
+  ASSERT_TRUE(manager.GetMinimalRole("trait1.command1", &role, nullptr));
+  EXPECT_EQ(UserRole::kUser, role);
+
+  ASSERT_TRUE(manager.GetMinimalRole("trait1.command2", &role, nullptr));
+  EXPECT_EQ(UserRole::kViewer, role);
+
+  ASSERT_TRUE(manager.GetMinimalRole("trait2.command1", &role, nullptr));
+  EXPECT_EQ(UserRole::kManager, role);
+
+  ASSERT_TRUE(manager.GetMinimalRole("trait2.command2", &role, nullptr));
+  EXPECT_EQ(UserRole::kOwner, role);
+
+  EXPECT_FALSE(manager.GetMinimalRole("trait1.command3", &role, nullptr));
+}
+
+TEST(ComponentManager, AddComponent) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({"trait1": {}, "trait2": {}, "trait3": {}})";
+  auto json = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
+  EXPECT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
+  EXPECT_TRUE(manager.AddComponent("", "comp2", {"trait3"}, nullptr));
+  const char kExpected[] = R"({
+    "comp1": {
+      "traits": ["trait1", "trait2"]
+    },
+    "comp2": {
+      "traits": ["trait3"]
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected, manager.GetComponents());
+
+  // 'trait4' is undefined, so can't add a component referring to it.
+  EXPECT_FALSE(manager.AddComponent("", "comp3", {"trait4"}, nullptr));
+}
+
+TEST(ComponentManager, AddSubComponent) {
+  ComponentManagerImpl manager;
+  EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
+  EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr));
+  EXPECT_TRUE(manager.AddComponent("comp1", "comp3", {}, nullptr));
+  EXPECT_TRUE(manager.AddComponent("comp1.comp2", "comp4", {}, nullptr));
+  const char kExpected[] = R"({
+    "comp1": {
+      "traits": [],
+      "components": {
+        "comp2": {
+          "traits": [],
+          "components": {
+            "comp4": {
+              "traits": []
+            }
+          }
+        },
+        "comp3": {
+          "traits": []
+        }
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected, manager.GetComponents());
+}
+
+TEST(ComponentManager, AddComponentArrayItem) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({"foo": {}, "bar": {}})";
+  auto json = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*json, nullptr));
+
+  EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
+  EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp2", {"foo"},
+                                            nullptr));
+  EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp2", {"bar"},
+                                            nullptr));
+  EXPECT_TRUE(manager.AddComponent("comp1.comp2[1]", "comp3", {}, nullptr));
+  EXPECT_TRUE(manager.AddComponent("comp1.comp2[1].comp3", "comp4", {},
+                                   nullptr));
+  const char kExpected[] = R"({
+    "comp1": {
+      "traits": [],
+      "components": {
+        "comp2": [
+          {
+            "traits": ["foo"]
+          },
+          {
+            "traits": ["bar"],
+            "components": {
+              "comp3": {
+                "traits": [],
+                "components": {
+                  "comp4": {
+                    "traits": []
+                  }
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected, manager.GetComponents());
+}
+
+TEST(ComponentManager, AddComponentExist) {
+  ComponentManagerImpl manager;
+  EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
+  EXPECT_FALSE(manager.AddComponent("", "comp1", {}, nullptr));
+  EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr));
+  EXPECT_FALSE(manager.AddComponent("comp1", "comp2", {}, nullptr));
+}
+
+TEST(ComponentManager, AddComponentDoesNotExist) {
+  ComponentManagerImpl manager;
+  EXPECT_FALSE(manager.AddComponent("comp1", "comp2", {}, nullptr));
+}
+
+TEST(ComponentManager, AddComponentTreeChangedCallback) {
+  ComponentManagerImpl manager;
+  int count = 0;
+  int count2 = 0;
+  manager.AddComponentTreeChangedCallback(base::Bind([&count]() { count++; }));
+  manager.AddComponentTreeChangedCallback(
+      base::Bind([&count2]() { count2++; }));
+  EXPECT_EQ(1, count);
+  EXPECT_EQ(1, count2);
+  EXPECT_TRUE(manager.AddComponent("", "comp1", {}, nullptr));
+  EXPECT_EQ(2, count);
+  EXPECT_TRUE(manager.AddComponent("comp1", "comp2", {}, nullptr));
+  EXPECT_EQ(3, count);
+  EXPECT_TRUE(manager.AddComponent("comp1.comp2", "comp4", {}, nullptr));
+  EXPECT_EQ(4, count);
+  EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp3", {}, nullptr));
+  EXPECT_EQ(5, count);
+  EXPECT_TRUE(manager.AddComponentArrayItem("comp1", "comp3", {}, nullptr));
+  EXPECT_EQ(6, count);
+  // Make sure both callbacks were called the same number of times.
+  EXPECT_EQ(count2, count);
+}
+
+TEST(ComponentManager, FindComponent) {
+  ComponentManagerImpl manager;
+  CreateTestComponentTree(&manager);
+
+  const base::DictionaryValue* comp = manager.FindComponent("comp1", nullptr);
+  ASSERT_NE(nullptr, comp);
+  EXPECT_TRUE(HasTrait(*comp, "t1"));
+
+  comp = manager.FindComponent("comp1.comp2[0]", nullptr);
+  ASSERT_NE(nullptr, comp);
+  EXPECT_TRUE(HasTrait(*comp, "t2"));
+
+  comp = manager.FindComponent("comp1.comp2[1]", nullptr);
+  ASSERT_NE(nullptr, comp);
+  EXPECT_TRUE(HasTrait(*comp, "t3"));
+
+  comp = manager.FindComponent("comp1.comp2[1].comp3", nullptr);
+  ASSERT_NE(nullptr, comp);
+  EXPECT_TRUE(HasTrait(*comp, "t4"));
+
+  comp = manager.FindComponent("comp1.comp2[1].comp3.comp4", nullptr);
+  ASSERT_NE(nullptr, comp);
+  EXPECT_TRUE(HasTrait(*comp, "t5"));
+
+  // Some whitespaces don't hurt.
+  comp = manager.FindComponent(" comp1 . comp2 [  \t 1 ] .   comp3.comp4 ",
+                               nullptr);
+  EXPECT_NE(nullptr, comp);
+
+  // Now check some failure cases.
+  ErrorPtr error;
+  EXPECT_EQ(nullptr, manager.FindComponent("", &error));
+  EXPECT_NE(nullptr, error.get());
+  // 'comp2' doesn't exist:
+  EXPECT_EQ(nullptr, manager.FindComponent("comp2", nullptr));
+  // 'comp1.comp2' is an array, not a component:
+  EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2", nullptr));
+  // 'comp1.comp2[3]' doesn't exist:
+  EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[3]", nullptr));
+  // Empty component names:
+  EXPECT_EQ(nullptr, manager.FindComponent(".comp2[1]", nullptr));
+  EXPECT_EQ(nullptr, manager.FindComponent("comp1.[1]", nullptr));
+  // Invalid array indices:
+  EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[s]", nullptr));
+  EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[-2]", nullptr));
+  EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[1e1]", nullptr));
+  EXPECT_EQ(nullptr, manager.FindComponent("comp1.comp2[1", nullptr));
+}
+
+TEST(ComponentManager, ParseCommandInstance) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": { "minimalRole": "user" },
+        "command2": { "minimalRole": "viewer" }
+      }
+    },
+    "trait2": {
+      "commands": {
+        "command1": { "minimalRole": "manager" },
+        "command2": { "minimalRole": "owner" }
+      }
+    },
+    "trait3": {
+      "commands": {
+        "command1": { "minimalRole": "manager" },
+        "command2": { "minimalRole": "owner" }
+      }
+    }
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait2"}, nullptr));
+
+  std::string id;
+  const char kCommand1[] = R"({
+    "name": "trait1.command1",
+    "id": "1234-12345",
+    "component": "comp1",
+    "parameters": {}
+  })";
+  auto command1 = CreateDictionaryValue(kCommand1);
+  EXPECT_NE(nullptr,
+            manager.ParseCommandInstance(*command1, Command::Origin::kLocal,
+                                         UserRole::kUser, &id, nullptr).get());
+  EXPECT_EQ("1234-12345", id);
+  // Not enough access rights
+  EXPECT_EQ(nullptr,
+            manager.ParseCommandInstance(*command1, Command::Origin::kLocal,
+                                         UserRole::kViewer, &id, nullptr).get());
+
+  const char kCommand2[] = R"({
+    "name": "trait1.command3",
+    "component": "comp1",
+    "parameters": {}
+  })";
+  auto command2 = CreateDictionaryValue(kCommand2);
+  // trait1.command3 doesn't exist
+  EXPECT_EQ(nullptr,
+            manager.ParseCommandInstance(*command2, Command::Origin::kLocal,
+                                         UserRole::kOwner, &id, nullptr).get());
+  EXPECT_TRUE(id.empty());
+
+  const char kCommand3[] = R"({
+    "name": "trait2.command1",
+    "component": "comp1",
+    "parameters": {}
+  })";
+  auto command3 = CreateDictionaryValue(kCommand3);
+  // Component comp1 doesn't have trait2.
+  EXPECT_EQ(nullptr,
+            manager.ParseCommandInstance(*command3, Command::Origin::kLocal,
+                                         UserRole::kOwner, &id, nullptr).get());
+
+  // No component specified, find the suitable component
+  const char kCommand4[] = R"({
+    "name": "trait1.command1",
+    "parameters": {}
+  })";
+  auto command4 = CreateDictionaryValue(kCommand4);
+  auto command_instance = manager.ParseCommandInstance(
+      *command4, Command::Origin::kLocal, UserRole::kOwner, &id, nullptr);
+  EXPECT_NE(nullptr, command_instance.get());
+  EXPECT_EQ("comp1", command_instance->GetComponent());
+
+  const char kCommand5[] = R"({
+    "name": "trait2.command1",
+    "parameters": {}
+  })";
+  auto command5 = CreateDictionaryValue(kCommand5);
+  command_instance = manager.ParseCommandInstance(
+      *command5, Command::Origin::kLocal, UserRole::kOwner, &id, nullptr);
+  EXPECT_NE(nullptr, command_instance.get());
+  EXPECT_EQ("comp2", command_instance->GetComponent());
+
+  // Cannot route the command, no component with 'trait3'.
+  const char kCommand6[] = R"({
+    "name": "trait3.command1",
+    "parameters": {}
+  })";
+  auto command6 = CreateDictionaryValue(kCommand6);
+  EXPECT_EQ(nullptr,
+            manager.ParseCommandInstance(*command6, Command::Origin::kLocal,
+                                         UserRole::kOwner, &id, nullptr).get());
+}
+
+TEST(ComponentManager, AddCommand) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": { "minimalRole": "user" }
+      }
+    }
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
+
+  std::string id;
+  const char kCommand[] = R"({
+    "name": "trait1.command1",
+    "id": "1234-12345",
+    "component": "comp1",
+    "parameters": {}
+  })";
+  auto command = CreateDictionaryValue(kCommand);
+  auto command_instance = manager.ParseCommandInstance(
+      *command, Command::Origin::kLocal, UserRole::kUser, &id, nullptr);
+  ASSERT_NE(nullptr, command_instance.get());
+  manager.AddCommand(std::move(command_instance));
+  const auto* queued_command = manager.FindCommand(id);
+  ASSERT_NE(nullptr, queued_command);
+  EXPECT_EQ("trait1.command1", queued_command->GetName());
+}
+
+TEST(ComponentManager, AddCommandHandler) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": { "minimalRole": "user" }
+      }
+    },
+    "trait2": {
+      "commands": {
+        "command2": { "minimalRole": "user" }
+      }
+    }
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr));
+
+  std::string last_tags;
+  auto handler = [&last_tags](int tag, const std::weak_ptr<Command>& command) {
+    if (!last_tags.empty())
+      last_tags += ',';
+    last_tags += std::to_string(tag);
+  };
+
+  manager.AddCommandHandler("comp1", "trait1.command1", base::Bind(handler, 1));
+  manager.AddCommandHandler("comp2", "trait1.command1", base::Bind(handler, 2));
+  manager.AddCommandHandler("comp2", "trait2.command2", base::Bind(handler, 3));
+  EXPECT_TRUE(last_tags.empty());
+
+  const char kCommand1[] = R"({
+    "name": "trait1.command1",
+    "component": "comp1"
+  })";
+  auto command1 = CreateDictionaryValue(kCommand1);
+  auto command_instance = manager.ParseCommandInstance(
+      *command1, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
+  ASSERT_NE(nullptr, command_instance.get());
+  manager.AddCommand(std::move(command_instance));
+  EXPECT_EQ("1", last_tags);
+  last_tags.clear();
+
+  const char kCommand2[] = R"({
+    "name": "trait1.command1",
+    "component": "comp2"
+  })";
+  auto command2 = CreateDictionaryValue(kCommand2);
+  command_instance = manager.ParseCommandInstance(
+      *command2, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
+  ASSERT_NE(nullptr, command_instance.get());
+  manager.AddCommand(std::move(command_instance));
+  EXPECT_EQ("2", last_tags);
+  last_tags.clear();
+
+  const char kCommand3[] = R"({
+    "name": "trait2.command2",
+    "component": "comp2",
+    "parameters": {}
+  })";
+  auto command3 = CreateDictionaryValue(kCommand3);
+  command_instance = manager.ParseCommandInstance(
+      *command3, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
+  ASSERT_NE(nullptr, command_instance.get());
+  manager.AddCommand(std::move(command_instance));
+  EXPECT_EQ("3", last_tags);
+  last_tags.clear();
+}
+
+TEST(ComponentManager, AddDefaultCommandHandler) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({
+    "trait1": {
+      "commands": {
+        "command1": { "minimalRole": "user" }
+      }
+    },
+    "trait2": {
+      "commands": {
+        "command2": { "minimalRole": "user" }
+      }
+    }
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp", {"trait1", "trait2"}, nullptr));
+
+  int count = 0;
+  auto handler = [&count](int tag, const std::weak_ptr<Command>& command) {
+    count++;
+  };
+
+  manager.AddCommandHandler("", "", base::Bind(handler, 1));
+  EXPECT_EQ(0, count);
+
+  const char kCommand1[] = R"({
+    "name": "trait1.command1",
+    "component": "comp"
+  })";
+  auto command1 = CreateDictionaryValue(kCommand1);
+  auto command_instance = manager.ParseCommandInstance(
+      *command1, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
+  ASSERT_NE(nullptr, command_instance.get());
+  manager.AddCommand(std::move(command_instance));
+  EXPECT_EQ(1, count);
+
+  const char kCommand2[] = R"({
+    "name": "trait2.command2",
+    "component": "comp"
+  })";
+  auto command2 = CreateDictionaryValue(kCommand2);
+  command_instance = manager.ParseCommandInstance(
+      *command2, Command::Origin::kCloud, UserRole::kUser, nullptr, nullptr);
+  ASSERT_NE(nullptr, command_instance.get());
+  manager.AddCommand(std::move(command_instance));
+  EXPECT_EQ(2, count);
+}
+
+TEST(ComponentManager, SetStateProperties) {
+  ComponentManagerImpl manager;
+  CreateTestComponentTree(&manager);
+
+  const char kState1[] = R"({"t1": {"p1": 0, "p2": "foo"}})";
+  auto state1 = CreateDictionaryValue(kState1);
+  ASSERT_TRUE(manager.SetStateProperties("comp1", *state1, nullptr));
+  const char kExpected1[] = R"({
+    "comp1": {
+      "traits": [ "t1" ],
+      "state": {"t1": {"p1": 0, "p2": "foo"}},
+      "components": {
+        "comp2": [
+          {
+            "traits": [ "t2" ]
+          },
+          {
+            "traits": [ "t3" ],
+            "components": {
+              "comp3": {
+                "traits": [ "t4" ],
+                "components": {
+                  "comp4": {
+                    "traits": [ "t5", "t6" ]
+                  }
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
+
+  const char kState2[] = R"({"t1": {"p1": {"bar": "baz"}}})";
+  auto state2 = CreateDictionaryValue(kState2);
+  ASSERT_TRUE(manager.SetStateProperties("comp1", *state2, nullptr));
+
+  const char kExpected2[] = R"({
+    "comp1": {
+      "traits": [ "t1" ],
+      "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
+      "components": {
+        "comp2": [
+          {
+            "traits": [ "t2" ]
+          },
+          {
+            "traits": [ "t3" ],
+            "components": {
+              "comp3": {
+                "traits": [ "t4" ],
+                "components": {
+                  "comp4": {
+                    "traits": [ "t5", "t6" ]
+                  }
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
+
+  const char kState3[] = R"({"t5": {"p1": 1}})";
+  auto state3 = CreateDictionaryValue(kState3);
+  ASSERT_TRUE(manager.SetStateProperties("comp1.comp2[1].comp3.comp4", *state3,
+                                         nullptr));
+
+  const char kExpected3[] = R"({
+    "comp1": {
+      "traits": [ "t1" ],
+      "state": {"t1": {"p1": {"bar": "baz"}, "p2": "foo"}},
+      "components": {
+        "comp2": [
+          {
+            "traits": [ "t2" ]
+          },
+          {
+            "traits": [ "t3" ],
+            "components": {
+              "comp3": {
+                "traits": [ "t4" ],
+                "components": {
+                  "comp4": {
+                    "traits": [ "t5", "t6" ],
+                    "state": { "t5": { "p1": 1 } }
+                  }
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected3, manager.GetComponents());
+}
+
+TEST(ComponentManager, SetStatePropertiesFromJson) {
+  ComponentManagerImpl manager;
+  CreateTestComponentTree(&manager);
+
+  ASSERT_TRUE(manager.SetStatePropertiesFromJson(
+      "comp1.comp2[1].comp3.comp4", R"({"t5": {"p1": 3}, "t6": {"p2": 5}})",
+      nullptr));
+
+  const char kExpected[] = R"({
+    "comp1": {
+      "traits": [ "t1" ],
+      "components": {
+        "comp2": [
+          {
+            "traits": [ "t2" ]
+          },
+          {
+            "traits": [ "t3" ],
+            "components": {
+              "comp3": {
+                "traits": [ "t4" ],
+                "components": {
+                  "comp4": {
+                    "traits": [ "t5", "t6" ],
+                    "state": {
+                      "t5": { "p1": 3 },
+                      "t6": { "p2": 5 }
+                    }
+                  }
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected, manager.GetComponents());
+}
+
+TEST(ComponentManager, SetGetStateProperty) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({
+    "trait1": {
+      "state": {
+        "prop1": { "type": "string" },
+        "prop2": { "type": "integer" }
+      }
+    },
+    "trait2": {
+      "state": {
+        "prop3": { "type": "string" },
+        "prop4": { "type": "string" }
+      }
+    }
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
+
+  base::StringValue p1("foo");
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
+
+  const char kExpected1[] = R"({
+    "comp1": {
+      "traits": [ "trait1", "trait2" ],
+      "state": {
+        "trait1": { "prop1": "foo" }
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected1, manager.GetComponents());
+
+  base::FundamentalValue p2(2);
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait2.prop3", p2, nullptr));
+
+  const char kExpected2[] = R"({
+    "comp1": {
+      "traits": [ "trait1", "trait2" ],
+      "state": {
+        "trait1": { "prop1": "foo" },
+        "trait2": { "prop3": 2 }
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected2, manager.GetComponents());
+  // Just the package name without property:
+  EXPECT_FALSE(manager.SetStateProperty("comp1", "trait2", p2, nullptr));
+
+  const base::Value* value = manager.GetStateProperty("comp1", "trait1.prop1",
+                                                      nullptr);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(p1.Equals(value));
+  value = manager.GetStateProperty("comp1", "trait2.prop3", nullptr);
+  ASSERT_NE(nullptr, value);
+  EXPECT_TRUE(p2.Equals(value));
+
+  // Non-existing property:
+  EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2.p", nullptr));
+  // Non-existing component
+  EXPECT_EQ(nullptr, manager.GetStateProperty("comp2", "trait.prop", nullptr));
+  // Just the package name without property:
+  EXPECT_EQ(nullptr, manager.GetStateProperty("comp1", "trait2", nullptr));
+}
+
+TEST(ComponentManager, AddStateChangedCallback) {
+  SimpleTestClock clock;
+  ComponentManagerImpl manager{&clock};
+  const char kTraits[] = R"({
+    "trait1": {
+      "state": {
+        "prop1": { "type": "string" },
+        "prop2": { "type": "string" }
+      }
+    }
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
+
+  int count = 0;
+  int count2 = 0;
+  manager.AddStateChangedCallback(base::Bind([&count]() { count++; }));
+  manager.AddStateChangedCallback(base::Bind([&count2]() { count2++; }));
+  EXPECT_EQ(1, count);
+  EXPECT_EQ(1, count2);
+  EXPECT_EQ(0, manager.GetLastStateChangeId());
+
+  base::StringValue p1("foo");
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", p1, nullptr));
+  EXPECT_EQ(2, count);
+  EXPECT_EQ(2, count2);
+  EXPECT_EQ(1, manager.GetLastStateChangeId());
+
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", p1, nullptr));
+  EXPECT_EQ(3, count);
+  EXPECT_EQ(3, count2);
+  EXPECT_EQ(2, manager.GetLastStateChangeId());
+
+  // Fail - no component.
+  ASSERT_FALSE(manager.SetStateProperty("comp2", "trait1.prop2", p1, nullptr));
+  EXPECT_EQ(3, count);
+  EXPECT_EQ(3, count2);
+  EXPECT_EQ(2, manager.GetLastStateChangeId());
+}
+
+TEST(ComponentManager, ComponentStateUpdates) {
+  SimpleTestClock clock;
+  ComponentManagerImpl manager{&clock};
+  const char kTraits[] = R"({
+    "trait1": {
+      "state": {
+        "prop1": { "type": "string" },
+        "prop2": { "type": "string" }
+      }
+    },
+    "trait2": {
+      "state": {
+        "prop3": { "type": "string" },
+        "prop4": { "type": "string" }
+      }
+    }
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait1", "trait2"}, nullptr));
+
+  std::vector<ComponentManager::UpdateID> updates1;
+  auto callback1 = [&updates1](ComponentManager::UpdateID id) {
+    updates1.push_back(id);
+  };
+  // State change queue is empty, callback should be called immediately.
+  auto token1 = manager.AddServerStateUpdatedCallback(base::Bind(callback1));
+  ASSERT_EQ(1u, updates1.size());
+  EXPECT_EQ(manager.GetLastStateChangeId(), updates1.front());
+  updates1.clear();
+
+  base::StringValue foo("foo");
+  base::Time time1 = base::Time::Now();
+  clock.SetNow(time1);
+  // These three updates should be grouped into two separate state change queue
+  // items, since they all happen at the same time, but for two different
+  // components.
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", foo, nullptr));
+  ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", foo, nullptr));
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", foo, nullptr));
+
+  std::vector<ComponentManager::UpdateID> updates2;
+  auto callback2 = [&updates2](ComponentManager::UpdateID id) {
+    updates2.push_back(id);
+  };
+  // State change queue is not empty, so callback will be called later.
+  auto token2 = manager.AddServerStateUpdatedCallback(base::Bind(callback2));
+  EXPECT_TRUE(updates2.empty());
+
+  base::StringValue bar("bar");
+  base::Time time2 = time1 + base::TimeDelta::FromSeconds(1);
+  clock.SetNow(time2);
+  // Two more update events (as above) but at |time2|.
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop1", bar, nullptr));
+  ASSERT_TRUE(manager.SetStateProperty("comp2", "trait2.prop3", bar, nullptr));
+  ASSERT_TRUE(manager.SetStateProperty("comp1", "trait1.prop2", bar, nullptr));
+
+  auto snapshot = manager.GetAndClearRecordedStateChanges();
+  EXPECT_EQ(manager.GetLastStateChangeId(), snapshot.update_id);
+  ASSERT_EQ(4u, snapshot.state_changes.size());
+
+  EXPECT_EQ("comp1", snapshot.state_changes[0].component);
+  EXPECT_EQ(time1, snapshot.state_changes[0].timestamp);
+  EXPECT_JSON_EQ(R"({"trait1":{"prop1":"foo","prop2":"foo"}})",
+                 *snapshot.state_changes[0].changed_properties);
+
+  EXPECT_EQ("comp2", snapshot.state_changes[1].component);
+  EXPECT_EQ(time1, snapshot.state_changes[1].timestamp);
+  EXPECT_JSON_EQ(R"({"trait2":{"prop3":"foo"}})",
+                 *snapshot.state_changes[1].changed_properties);
+
+  EXPECT_EQ("comp1", snapshot.state_changes[2].component);
+  EXPECT_EQ(time2, snapshot.state_changes[2].timestamp);
+  EXPECT_JSON_EQ(R"({"trait1":{"prop1":"bar","prop2":"bar"}})",
+                 *snapshot.state_changes[2].changed_properties);
+
+  EXPECT_EQ("comp2", snapshot.state_changes[3].component);
+  EXPECT_EQ(time2, snapshot.state_changes[3].timestamp);
+  EXPECT_JSON_EQ(R"({"trait2":{"prop3":"bar"}})",
+                 *snapshot.state_changes[3].changed_properties);
+
+  // Make sure previous GetAndClearRecordedStateChanges() clears the queue.
+  auto snapshot2 = manager.GetAndClearRecordedStateChanges();
+  EXPECT_EQ(manager.GetLastStateChangeId(), snapshot2.update_id);
+  EXPECT_TRUE(snapshot2.state_changes.empty());
+
+  // Now indicate that we have update the changes on the server.
+  manager.NotifyStateUpdatedOnServer(snapshot.update_id);
+  ASSERT_EQ(1u, updates1.size());
+  EXPECT_EQ(snapshot.update_id, updates1.front());
+  ASSERT_EQ(1u, updates2.size());
+  EXPECT_EQ(snapshot.update_id, updates2.front());
+}
+
+TEST(ComponentManager, FindComponentWithTrait) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({
+    "trait1": {},
+    "trait2": {},
+    "trait3": {}
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1", "trait2"}, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait3"}, nullptr));
+
+  EXPECT_EQ("comp1", manager.FindComponentWithTrait("trait1"));
+  EXPECT_EQ("comp1", manager.FindComponentWithTrait("trait2"));
+  EXPECT_EQ("comp2", manager.FindComponentWithTrait("trait3"));
+  EXPECT_EQ("", manager.FindComponentWithTrait("trait4"));
+}
+
+TEST(ComponentManager, AddLegacyCommandAndStateDefinitions) {
+  ComponentManagerImpl manager;
+  const char kCommandDefs1[] = R"({
+    "package1": {
+      "command1": {
+        "minimalRole": "user",
+        "parameters": {"height": {"type": "integer"}}
+      },
+      "command2": {
+        "minimalRole": "owner",
+        "parameters": {}
+      }
+    },
+    "package2": {
+      "command1": { "minimalRole": "user" },
+      "command2": { "minimalRole": "owner" }
+    }
+  })";
+  auto json = CreateDictionaryValue(kCommandDefs1);
+  EXPECT_TRUE(manager.AddLegacyCommandDefinitions(*json, nullptr));
+  const char kExpected1[] = R"({
+    "package1": {
+      "commands": {
+        "command1": {
+          "minimalRole": "user",
+          "parameters": {"height": {"type": "integer"}}
+        },
+        "command2": {
+          "minimalRole": "owner",
+          "parameters": {}
+        }
+      }
+    },
+    "package2": {
+      "commands": {
+        "command1": { "minimalRole": "user" },
+        "command2": { "minimalRole": "owner" }
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected1, manager.GetTraits());
+  const char kExpectedComponents1[] = R"({
+    "__weave__": { "traits": ["package1", "package2"] }
+  })";
+  EXPECT_JSON_EQ(kExpectedComponents1, manager.GetComponents());
+
+  const char kCommandDefs2[] = R"({
+    "package2": {
+      "command3": { "minimalRole": "user" }
+    },
+    "package3": {
+      "command1": { "minimalRole": "user" },
+      "command2": { "minimalRole": "owner" }
+    }
+  })";
+  json = CreateDictionaryValue(kCommandDefs2);
+  EXPECT_TRUE(manager.AddLegacyCommandDefinitions(*json, nullptr));
+  const char kExpected2[] = R"({
+    "package1": {
+      "commands": {
+        "command1": {
+          "minimalRole": "user",
+          "parameters": {"height": {"type": "integer"}}
+        },
+        "command2": {
+          "minimalRole": "owner",
+          "parameters": {}
+        }
+      }
+    },
+    "package2": {
+      "commands": {
+        "command1": { "minimalRole": "user" },
+        "command2": { "minimalRole": "owner" },
+        "command3": { "minimalRole": "user" }
+      }
+    },
+    "package3": {
+      "commands": {
+        "command1": { "minimalRole": "user" },
+        "command2": { "minimalRole": "owner" }
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected2, manager.GetTraits());
+  const char kExpectedComponents2[] = R"({
+    "__weave__": { "traits": ["package1", "package2", "package3"] }
+  })";
+  EXPECT_JSON_EQ(kExpectedComponents2, manager.GetComponents());
+
+  // Redefining existing commands.
+  EXPECT_FALSE(manager.AddLegacyCommandDefinitions(*json, nullptr));
+
+  const char kStateDefs1[] = R"({
+    "package1": {
+      "prop1": { "type": "string" },
+      "prop2": { "type": "string" }
+    },
+    "package4": {
+      "prop3": { "type": "string" },
+      "prop4": { "type": "string" }
+    }
+  })";
+  json = CreateDictionaryValue(kStateDefs1);
+  EXPECT_TRUE(manager.AddLegacyStateDefinitions(*json, nullptr));
+  const char kExpectedComponents3[] = R"({
+    "__weave__": { "traits": ["package1", "package2", "package3", "package4"] }
+  })";
+  EXPECT_JSON_EQ(kExpectedComponents3, manager.GetComponents());
+
+  const char kExpected3[] = R"({
+    "package1": {
+      "commands": {
+        "command1": {
+          "minimalRole": "user",
+          "parameters": {"height": {"type": "integer"}}
+        },
+        "command2": {
+          "minimalRole": "owner",
+          "parameters": {}
+        }
+      },
+      "state": {
+        "prop1": { "type": "string" },
+        "prop2": { "type": "string" }
+      }
+    },
+    "package2": {
+      "commands": {
+        "command1": { "minimalRole": "user" },
+        "command2": { "minimalRole": "owner" },
+        "command3": { "minimalRole": "user" }
+      }
+    },
+    "package3": {
+      "commands": {
+        "command1": { "minimalRole": "user" },
+        "command2": { "minimalRole": "owner" }
+      }
+    },
+    "package4": {
+      "state": {
+        "prop3": { "type": "string" },
+        "prop4": { "type": "string" }
+      }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected3, manager.GetTraits());
+  const char kExpectedComponents4[] = R"({
+    "__weave__": { "traits": ["package1", "package2", "package3", "package4"] }
+  })";
+  EXPECT_JSON_EQ(kExpectedComponents4, manager.GetComponents());
+
+  // Redefining existing commands.
+  EXPECT_FALSE(manager.AddLegacyStateDefinitions(*json, nullptr));
+
+  const char kExpected4[] = R"({
+    "package1": {
+      "command1": {
+        "minimalRole": "user",
+        "parameters": {"height": {"type": "integer"}}
+      },
+      "command2": {
+        "minimalRole": "owner",
+        "parameters": {}
+      }
+    },
+    "package2": {
+      "command1": { "minimalRole": "user" },
+      "command2": { "minimalRole": "owner" },
+      "command3": { "minimalRole": "user" }
+    },
+    "package3": {
+      "command1": { "minimalRole": "user" },
+      "command2": { "minimalRole": "owner" }
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected4, manager.GetLegacyCommandDefinitions());
+}
+
+TEST(ComponentManager, GetLegacyState) {
+  ComponentManagerImpl manager;
+  const char kTraits[] = R"({
+    "trait1": {
+      "state": {
+        "prop1": { "type": "string" },
+        "prop2": { "type": "string" }
+      }
+    },
+    "trait2": {
+      "state": {
+        "prop3": { "type": "string" },
+        "prop4": { "type": "string" }
+      }
+    }
+  })";
+  auto traits = CreateDictionaryValue(kTraits);
+  ASSERT_TRUE(manager.LoadTraits(*traits, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp1", {"trait1"}, nullptr));
+  ASSERT_TRUE(manager.AddComponent("", "comp2", {"trait2"}, nullptr));
+
+  ASSERT_TRUE(manager.SetStatePropertiesFromJson(
+      "comp1", R"({"trait1": {"prop1": "foo", "prop2": "bar"}})", nullptr));
+  ASSERT_TRUE(manager.SetStatePropertiesFromJson(
+      "comp2", R"({"trait2": {"prop3": "baz", "prop4": "quux"}})", nullptr));
+
+  const char kExpected[] = R"({
+    "trait1": {
+      "prop1": "foo",
+      "prop2": "bar"
+    },
+    "trait2": {
+      "prop3": "baz",
+      "prop4": "quux"
+    }
+  })";
+  EXPECT_JSON_EQ(kExpected, manager.GetLegacyState());
+}
+
+TEST(ComponentManager, TestMockComponentManager) {
+  // Check that all the virtual methods are mocked out.
+  MockComponentManager mock;
+}
+
+}  // namespace weave
diff --git a/src/config.cc b/src/config.cc
index 52f5ea1..f0ec963 100644
--- a/src/config.cc
+++ b/src/config.cc
@@ -15,6 +15,7 @@
 #include <base/values.h>
 #include <weave/enum_to_string.h>
 
+#include "src/data_encoding.h"
 #include "src/privet/privet_types.h"
 #include "src/string_utils.h"
 
@@ -41,6 +42,7 @@
 const char kRobotAccount[] = "robot_account";
 const char kLastConfiguredSsid[] = "last_configured_ssid";
 const char kSecret[] = "secret";
+const char kLocalAuthInfoChanged[] = "local_auth_info_changed";
 
 }  // namespace config_keys
 
@@ -65,7 +67,7 @@
   result.oauth_url = "https://accounts.google.com/o/oauth2/";
   result.service_url = kWeaveUrl;
   result.local_anonymous_access_role = AuthScope::kViewer;
-  result.pairing_modes.emplace(PairingType::kPinCode);
+  result.pairing_modes.insert(PairingType::kPinCode);
   result.device_id = base::GenerateGUID();
   return result;
 }
@@ -119,6 +121,7 @@
   CHECK(settings_.robot_account.empty());
   CHECK(settings_.last_configured_ssid.empty());
   CHECK(settings_.secret.empty());
+  CHECK(settings_.local_auth_info_changed);
 
   change.LoadState();
 }
@@ -207,8 +210,12 @@
   if (dict->GetString(config_keys::kLastConfiguredSsid, &tmp))
     set_last_configured_ssid(tmp);
 
-  if (dict->GetString(config_keys::kSecret, &tmp))
-    set_secret(tmp);
+  std::vector<uint8_t> secret;
+  if (dict->GetString(config_keys::kSecret, &tmp) && Base64Decode(tmp, &secret))
+    set_secret(secret);
+
+  if (dict->GetBoolean(config_keys::kLocalAuthInfoChanged, &tmp_bool))
+    set_local_auth_info_changed(tmp_bool);
 }
 
 void Config::Save() {
@@ -229,7 +236,9 @@
   dict.SetString(config_keys::kRobotAccount, settings_.robot_account);
   dict.SetString(config_keys::kLastConfiguredSsid,
                  settings_.last_configured_ssid);
-  dict.SetString(config_keys::kSecret, settings_.secret);
+  dict.SetString(config_keys::kSecret, Base64Encode(settings_.secret));
+  dict.SetBoolean(config_keys::kLocalAuthInfoChanged,
+                  settings_.local_auth_info_changed);
   dict.SetString(config_keys::kName, settings_.name);
   dict.SetString(config_keys::kDescription, settings_.description);
   dict.SetString(config_keys::kLocation, settings_.location);
diff --git a/src/config.h b/src/config.h
index f4c3465..fc2568f 100644
--- a/src/config.h
+++ b/src/config.h
@@ -24,11 +24,11 @@
 class Config final {
  public:
   struct Settings : public weave::Settings {
-    std::string device_id;
     std::string refresh_token;
     std::string robot_account;
     std::string last_configured_ssid;
-    std::string secret;
+    std::vector<uint8_t> secret;
+    bool local_auth_info_changed{true};
   };
 
   using OnChangedCallback = base::Callback<void(const weave::Settings&)>;
@@ -89,7 +89,12 @@
     void set_last_configured_ssid(const std::string& ssid) {
       settings_->last_configured_ssid = ssid;
     }
-    void set_secret(const std::string& secret) { settings_->secret = secret; }
+    void set_secret(const std::vector<uint8_t>& secret) {
+      settings_->secret = secret;
+    }
+    void set_local_auth_info_changed(bool local_auth_info_changed) {
+      settings_->local_auth_info_changed = local_auth_info_changed;
+    }
 
     void Commit();
 
diff --git a/src/config_unittest.cc b/src/config_unittest.cc
index 10ed07e..8c99131 100644
--- a/src/config_unittest.cc
+++ b/src/config_unittest.cc
@@ -10,8 +10,9 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <weave/provider/test/mock_config_store.h>
+#include <weave/test/unittest_utils.h>
 
-#include "src/commands/unittest_utils.h"
+#include "src/data_encoding.h"
 
 using testing::_;
 using testing::Invoke;
@@ -75,7 +76,8 @@
   EXPECT_EQ("", GetSettings().refresh_token);
   EXPECT_EQ("", GetSettings().robot_account);
   EXPECT_EQ("", GetSettings().last_configured_ssid);
-  EXPECT_EQ("", GetSettings().secret);
+  EXPECT_EQ(std::vector<uint8_t>(), GetSettings().secret);
+  EXPECT_TRUE(GetSettings().local_auth_info_changed);
 }
 
 TEST_F(ConfigTest, LoadStateV0) {
@@ -115,6 +117,7 @@
     "device_id": "state_device_id",
     "last_configured_ssid": "state_last_configured_ssid",
     "local_anonymous_access_role": "user",
+    "local_auth_info_changed": false,
     "local_discovery_enabled": false,
     "local_pairing_enabled": false,
     "location": "state_location",
@@ -122,7 +125,7 @@
     "oauth_url": "state_oauth_url",
     "refresh_token": "state_refresh_token",
     "robot_account": "state_robot_account",
-    "secret": "state_secret",
+    "secret": "c3RhdGVfc2VjcmV0",
     "service_url": "state_service_url"
   })";
   EXPECT_CALL(config_store_, LoadSettings()).WillOnce(Return(state));
@@ -157,7 +160,8 @@
   EXPECT_EQ("state_refresh_token", GetSettings().refresh_token);
   EXPECT_EQ("state_robot_account", GetSettings().robot_account);
   EXPECT_EQ("state_last_configured_ssid", GetSettings().last_configured_ssid);
-  EXPECT_EQ("state_secret", GetSettings().secret);
+  EXPECT_EQ("c3RhdGVfc2VjcmV0", Base64Encode(GetSettings().secret));
+  EXPECT_FALSE(GetSettings().local_auth_info_changed);
 }
 
 TEST_F(ConfigTest, Setters) {
@@ -223,8 +227,12 @@
   change.set_last_configured_ssid("set_last_configured_ssid");
   EXPECT_EQ("set_last_configured_ssid", GetSettings().last_configured_ssid);
 
-  change.set_secret("set_secret");
-  EXPECT_EQ("set_secret", GetSettings().secret);
+  const std::vector<uint8_t> secret{1, 2, 3, 4, 5};
+  change.set_secret(secret);
+  EXPECT_EQ(secret, GetSettings().secret);
+
+  change.set_local_auth_info_changed(false);
+  EXPECT_FALSE(GetSettings().local_auth_info_changed);
 
   EXPECT_CALL(*this, OnConfigChanged(_)).Times(1);
 
@@ -240,6 +248,7 @@
           'device_id': 'set_device_id',
           'last_configured_ssid': 'set_last_configured_ssid',
           'local_anonymous_access_role': 'user',
+          'local_auth_info_changed': false,
           'local_discovery_enabled': true,
           'local_pairing_enabled': true,
           'location': 'set_location',
@@ -247,7 +256,7 @@
           'oauth_url': 'set_oauth_url',
           'refresh_token': 'set_token',
           'robot_account': 'set_account',
-          'secret': 'set_secret',
+          'secret': 'AQIDBAU=',
           'service_url': 'set_service_url'
         })";
         EXPECT_JSON_EQ(expected, *test::CreateValue(json));
diff --git a/src/data_encoding_unittest.cc b/src/data_encoding_unittest.cc
index 7e3bfe4..53dc534 100644
--- a/src/data_encoding_unittest.cc
+++ b/src/data_encoding_unittest.cc
@@ -30,7 +30,7 @@
   EXPECT_EQ("q=test&path=%2Fusr%2Fbin&%23=%25", encoded);
 
   auto params = WebParamsDecode(encoded);
-  EXPECT_EQ(3, params.size());
+  EXPECT_EQ(3u, params.size());
   EXPECT_EQ("q", params[0].first);
   EXPECT_EQ("test", params[0].second);
   EXPECT_EQ("path", params[1].first);
diff --git a/src/device_manager.cc b/src/device_manager.cc
index c0b8e9a..7a9021f 100644
--- a/src/device_manager.cc
+++ b/src/device_manager.cc
@@ -9,22 +9,16 @@
 #include <base/bind.h>
 
 #include "src/base_api_handler.h"
-#include "src/commands/command_manager.h"
+#include "src/commands/schema_constants.h"
+#include "src/component_manager_impl.h"
 #include "src/config.h"
 #include "src/device_registration_info.h"
 #include "src/privet/privet_manager.h"
-#include "src/states/state_change_queue.h"
-#include "src/states/state_manager.h"
+#include "src/string_utils.h"
+#include "src/utils.h"
 
 namespace weave {
 
-namespace {
-
-// Max of 100 state update events should be enough in the queue.
-const size_t kMaxStateChangeQueueSize = 100;
-
-}  // namespace
-
 DeviceManager::DeviceManager(provider::ConfigStore* config_store,
                              provider::TaskRunner* task_runner,
                              provider::HttpClient* http_client,
@@ -33,16 +27,14 @@
                              provider::HttpServer* http_server,
                              provider::Wifi* wifi,
                              provider::Bluetooth* bluetooth) {
-  command_manager_ = std::make_shared<CommandManager>();
-  state_change_queue_.reset(new StateChangeQueue(kMaxStateChangeQueueSize));
-  state_manager_ = std::make_shared<StateManager>(state_change_queue_.get());
+  component_manager_.reset(new ComponentManagerImpl);
 
   std::unique_ptr<Config> config{new Config{config_store}};
   config->Load();
 
   device_info_.reset(new DeviceRegistrationInfo(
-      command_manager_, state_manager_, std::move(config), task_runner,
-      http_client, network));
+      component_manager_.get(), std::move(config), task_runner, http_client,
+      network));
   base_api_handler_.reset(new BaseApiHandler{device_info_.get(), this});
 
   device_info_->Start();
@@ -77,7 +69,7 @@
                                 provider::Bluetooth* bluetooth) {
   privet_.reset(new privet::Manager{task_runner});
   privet_->Start(network, dns_sd, http_server, wifi, device_info_.get(),
-                 command_manager_.get(), state_manager_.get());
+                 component_manager_.get());
 }
 
 GcdState DeviceManager::GetGcdState() const {
@@ -89,64 +81,168 @@
   device_info_->AddGcdStateChangedCallback(callback);
 }
 
+void DeviceManager::AddTraitDefinitionsFromJson(const std::string& json) {
+  CHECK(component_manager_->LoadTraits(json, nullptr));
+}
+
+void DeviceManager::AddTraitDefinitions(const base::DictionaryValue& dict) {
+  CHECK(component_manager_->LoadTraits(dict, nullptr));
+}
+
+const base::DictionaryValue& DeviceManager::GetTraits() const {
+  return component_manager_->GetTraits();
+}
+
+bool DeviceManager::AddComponent(const std::string& name,
+                                 const std::vector<std::string>& traits,
+                                 ErrorPtr* error) {
+  return component_manager_->AddComponent("", name, traits, error);
+}
+
+void DeviceManager::AddComponentTreeChangedCallback(
+    const base::Closure& callback) {
+  component_manager_->AddComponentTreeChangedCallback(callback);
+}
+
+const base::DictionaryValue& DeviceManager::GetComponents() const {
+  return component_manager_->GetComponents();
+}
+
+bool DeviceManager::SetStatePropertiesFromJson(const std::string& component,
+                                               const std::string& json,
+                                               ErrorPtr* error) {
+  return component_manager_->SetStatePropertiesFromJson(component, json, error);
+}
+
+bool DeviceManager::SetStateProperties(const std::string& component,
+                                       const base::DictionaryValue& dict,
+                                       ErrorPtr* error) {
+  return component_manager_->SetStateProperties(component, dict, error);
+}
+
+const base::Value* DeviceManager::GetStateProperty(
+    const std::string& component,
+    const std::string& name,
+    ErrorPtr* error) const {
+  return component_manager_->GetStateProperty(component, name, error);
+}
+
+bool DeviceManager::SetStateProperty(const std::string& component,
+                                     const std::string& name,
+                                     const base::Value& value,
+                                     ErrorPtr* error) {
+  return component_manager_->SetStateProperty(component, name, value, error);
+}
+
+void DeviceManager::AddCommandHandler(const std::string& component,
+                                      const std::string& command_name,
+                                      const CommandHandlerCallback& callback) {
+  component_manager_->AddCommandHandler(component, command_name, callback);
+}
+
 void DeviceManager::AddCommandDefinitionsFromJson(const std::string& json) {
-  CHECK(command_manager_->LoadCommands(json, nullptr));
+  auto dict = LoadJsonDict(json, nullptr);
+  CHECK(dict);
+  AddCommandDefinitions(*dict);
 }
 
 void DeviceManager::AddCommandDefinitions(const base::DictionaryValue& dict) {
-  CHECK(command_manager_->LoadCommands(dict, nullptr));
+  CHECK(component_manager_->AddLegacyCommandDefinitions(dict, nullptr));
 }
 
 bool DeviceManager::AddCommand(const base::DictionaryValue& command,
                                std::string* id,
                                ErrorPtr* error) {
-  return command_manager_->AddCommand(command, id, error);
+  auto command_instance =
+      component_manager_->ParseCommandInstance(command, Command::Origin::kLocal,
+                                               UserRole::kOwner, id, error);
+  if (!command_instance)
+    return false;
+  component_manager_->AddCommand(std::move(command_instance));
+  return true;
 }
 
 Command* DeviceManager::FindCommand(const std::string& id) {
-  return command_manager_->FindCommand(id);
+  return component_manager_->FindCommand(id);
 }
 
 void DeviceManager::AddCommandHandler(const std::string& command_name,
                                       const CommandHandlerCallback& callback) {
-  return command_manager_->AddCommandHandler(command_name, callback);
+  if (command_name.empty())
+    return component_manager_->AddCommandHandler("", "", callback);
+
+  auto trait = SplitAtFirst(command_name, ".", true).first;
+  std::string component = component_manager_->FindComponentWithTrait(trait);
+  CHECK(!component.empty());
+  component_manager_->AddCommandHandler(component, command_name, callback);
 }
 
 void DeviceManager::AddStateChangedCallback(const base::Closure& callback) {
-  state_manager_->AddChangedCallback(callback);
+  component_manager_->AddStateChangedCallback(callback);
 }
 
 void DeviceManager::AddStateDefinitionsFromJson(const std::string& json) {
-  CHECK(state_manager_->LoadStateDefinitionFromJson(json, nullptr));
+  auto dict = LoadJsonDict(json, nullptr);
+  CHECK(dict);
+  AddStateDefinitions(*dict);
 }
 
 void DeviceManager::AddStateDefinitions(const base::DictionaryValue& dict) {
-  CHECK(state_manager_->LoadStateDefinition(dict, nullptr));
+  CHECK(component_manager_->AddLegacyStateDefinitions(dict, nullptr));
 }
 
 bool DeviceManager::SetStatePropertiesFromJson(const std::string& json,
                                                ErrorPtr* error) {
-  return state_manager_->SetPropertiesFromJson(json, error);
+  auto dict = LoadJsonDict(json, error);
+  return dict && SetStateProperties(*dict, error);
 }
 
 bool DeviceManager::SetStateProperties(const base::DictionaryValue& dict,
                                        ErrorPtr* error) {
-  return state_manager_->SetProperties(dict, error);
+  for (base::DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+    std::string component =
+        component_manager_->FindComponentWithTrait(it.key());
+    if (component.empty()) {
+      Error::AddToPrintf(
+        error, FROM_HERE, errors::commands::kDomain, "unrouted_state",
+        "Unable to set property value because there is no component supporting "
+        "trait '%s'", it.key().c_str());
+      return false;
+    }
+    base::DictionaryValue trait_state;
+    trait_state.Set(it.key(), it.value().DeepCopy());
+    if (!component_manager_->SetStateProperties(component, trait_state, error))
+      return false;
+  }
+  return true;
 }
 
-std::unique_ptr<base::Value> DeviceManager::GetStateProperty(
+const base::Value* DeviceManager::GetStateProperty(
     const std::string& name) const {
-  return state_manager_->GetProperty(name);
+  auto trait = SplitAtFirst(name, ".", true).first;
+  std::string component = component_manager_->FindComponentWithTrait(trait);
+  if (component.empty())
+    return nullptr;
+  return component_manager_->GetStateProperty(component, name, nullptr);
 }
 
 bool DeviceManager::SetStateProperty(const std::string& name,
                                      const base::Value& value,
                                      ErrorPtr* error) {
-  return state_manager_->SetProperty(name, value, error);
+  auto trait = SplitAtFirst(name, ".", true).first;
+  std::string component = component_manager_->FindComponentWithTrait(trait);
+  if (component.empty()) {
+    Error::AddToPrintf(
+      error, FROM_HERE, errors::commands::kDomain, "unrouted_state",
+      "Unable set value of state property '%s' because there is no component "
+      "supporting trait '%s'", name.c_str(), trait.c_str());
+    return false;
+  }
+  return component_manager_->SetStateProperty(component, name, value, error);
 }
 
-std::unique_ptr<base::DictionaryValue> DeviceManager::GetState() const {
-  return state_manager_->GetState();
+const base::DictionaryValue& DeviceManager::GetState() const {
+  return component_manager_->GetLegacyState();
 }
 
 void DeviceManager::Register(const std::string& ticket_id,
diff --git a/src/device_manager.h b/src/device_manager.h
index ccf8778..3b042eb 100644
--- a/src/device_manager.h
+++ b/src/device_manager.h
@@ -12,10 +12,8 @@
 
 class BaseApiHandler;
 class Config;
-class CommandManager;
+class ComponentManager;
 class DeviceRegistrationInfo;
-class StateChangeQueue;
-class StateManager;
 
 namespace privet {
 class Manager;
@@ -37,27 +35,35 @@
   const Settings& GetSettings() const override;
   void AddSettingsChangedCallback(
       const SettingsChangedCallback& callback) override;
-  void AddCommandDefinitionsFromJson(const std::string& json) override;
-  void AddCommandDefinitions(const base::DictionaryValue& dict) override;
+  void AddTraitDefinitionsFromJson(const std::string& json) override;
+  void AddTraitDefinitions(const base::DictionaryValue& dict) override;
+  const base::DictionaryValue& GetTraits() const override;
+  bool AddComponent(const std::string& name,
+                    const std::vector<std::string>& traits,
+                    ErrorPtr* error) override;
+  void AddComponentTreeChangedCallback(const base::Closure& callback) override;
+  const base::DictionaryValue& GetComponents() const override;
+  bool SetStatePropertiesFromJson(const std::string& component,
+                                  const std::string& json,
+                                  ErrorPtr* error) override;
+  bool SetStateProperties(const std::string& component,
+                          const base::DictionaryValue& dict,
+                          ErrorPtr* error) override;
+  const base::Value* GetStateProperty(const std::string& component,
+                                      const std::string& name,
+                                      ErrorPtr* error) const override;
+  bool SetStateProperty(const std::string& component,
+                        const std::string& name,
+                        const base::Value& value,
+                        ErrorPtr* error) override;
+  void AddCommandHandler(const std::string& component,
+                         const std::string& command_name,
+                         const CommandHandlerCallback& callback) override;
   bool AddCommand(const base::DictionaryValue& command,
                   std::string* id,
                   ErrorPtr* error) override;
   Command* FindCommand(const std::string& id) override;
-  void AddCommandHandler(const std::string& command_name,
-                         const CommandHandlerCallback& callback) override;
   void AddStateChangedCallback(const base::Closure& callback) override;
-  void AddStateDefinitionsFromJson(const std::string& json) override;
-  void AddStateDefinitions(const base::DictionaryValue& dict) override;
-  bool SetStatePropertiesFromJson(const std::string& json,
-                                  ErrorPtr* error) override;
-  bool SetStateProperties(const base::DictionaryValue& dict,
-                          ErrorPtr* error) override;
-  std::unique_ptr<base::Value> GetStateProperty(
-      const std::string& name) const override;
-  bool SetStateProperty(const std::string& name,
-                        const base::Value& value,
-                        ErrorPtr* error) override;
-  std::unique_ptr<base::DictionaryValue> GetState() const override;
   void Register(const std::string& ticket_id,
                 const DoneCallback& callback) override;
   GcdState GetGcdState() const override;
@@ -67,6 +73,22 @@
       const PairingBeginCallback& begin_callback,
       const PairingEndCallback& end_callback) override;
 
+  void AddCommandDefinitionsFromJson(const std::string& json) override;
+  void AddCommandDefinitions(const base::DictionaryValue& dict) override;
+  void AddCommandHandler(const std::string& command_name,
+                         const CommandHandlerCallback& callback) override;
+  void AddStateDefinitionsFromJson(const std::string& json) override;
+  void AddStateDefinitions(const base::DictionaryValue& dict) override;
+  bool SetStatePropertiesFromJson(const std::string& json,
+                                  ErrorPtr* error) override;
+  bool SetStateProperties(const base::DictionaryValue& dict,
+                          ErrorPtr* error) override;
+  const base::Value* GetStateProperty(const std::string& name) const override;
+  bool SetStateProperty(const std::string& name,
+                        const base::Value& value,
+                        ErrorPtr* error) override;
+  const base::DictionaryValue& GetState() const override;
+
   Config* GetConfig();
 
  private:
@@ -77,9 +99,7 @@
                    provider::Wifi* wifi,
                    provider::Bluetooth* bluetooth);
 
-  std::shared_ptr<CommandManager> command_manager_;
-  std::unique_ptr<StateChangeQueue> state_change_queue_;
-  std::shared_ptr<StateManager> state_manager_;
+  std::unique_ptr<ComponentManager> component_manager_;
   std::unique_ptr<DeviceRegistrationInfo> device_info_;
   std::unique_ptr<BaseApiHandler> base_api_handler_;
   std::unique_ptr<privet::Manager> privet_;
diff --git a/src/device_registration_info.cc b/src/device_registration_info.cc
index 5c8625a..110ba81 100644
--- a/src/device_registration_info.cc
+++ b/src/device_registration_info.cc
@@ -14,6 +14,7 @@
 #include <base/json/json_reader.h>
 #include <base/json/json_writer.h>
 #include <base/strings/string_number_conversions.h>
+#include <base/strings/stringprintf.h>
 #include <base/values.h>
 #include <weave/provider/http_client.h>
 #include <weave/provider/network.h>
@@ -21,14 +22,11 @@
 
 #include "src/bind_lambda.h"
 #include "src/commands/cloud_command_proxy.h"
-#include "src/commands/command_definition.h"
-#include "src/commands/command_manager.h"
 #include "src/commands/schema_constants.h"
 #include "src/data_encoding.h"
 #include "src/http_constants.h"
 #include "src/json_error_codes.h"
 #include "src/notification/xmpp_channel.h"
-#include "src/states/state_manager.h"
 #include "src/string_utils.h"
 #include "src/utils.h"
 
@@ -239,16 +237,14 @@
 }  // anonymous namespace
 
 DeviceRegistrationInfo::DeviceRegistrationInfo(
-    const std::shared_ptr<CommandManager>& command_manager,
-    const std::shared_ptr<StateManager>& state_manager,
+    ComponentManager* component_manager,
     std::unique_ptr<Config> config,
     provider::TaskRunner* task_runner,
     provider::HttpClient* http_client,
     provider::Network* network)
     : http_client_{http_client},
       task_runner_{task_runner},
-      command_manager_{command_manager},
-      state_manager_{state_manager},
+      component_manager_{component_manager},
       config_{std::move(config)},
       network_{network} {
   cloud_backoff_policy_.reset(new BackoffEntry::Policy{});
@@ -267,10 +263,13 @@
   gcd_state_ =
       revoked ? GcdState::kInvalidCredentials : GcdState::kUnconfigured;
 
-  command_manager_->AddCommandDefChanged(
-      base::Bind(&DeviceRegistrationInfo::OnCommandDefsChanged,
+  component_manager_->AddTraitDefChangedCallback(
+      base::Bind(&DeviceRegistrationInfo::OnTraitDefsChanged,
                  weak_factory_.GetWeakPtr()));
-  state_manager_->AddChangedCallback(base::Bind(
+  component_manager_->AddComponentTreeChangedCallback(base::Bind(
+      &DeviceRegistrationInfo::OnComponentTreeChanged,
+      weak_factory_.GetWeakPtr()));
+  component_manager_->AddStateChangedCallback(base::Bind(
       &DeviceRegistrationInfo::OnStateChanged, weak_factory_.GetWeakPtr()));
 }
 
@@ -478,16 +477,7 @@
 }
 
 std::unique_ptr<base::DictionaryValue>
-DeviceRegistrationInfo::BuildDeviceResource(ErrorPtr* error) {
-  // Limit only to commands that are visible to the cloud.
-  auto commands =
-      command_manager_->GetCommandDictionary().GetCommandsAsJson(error);
-  if (!commands)
-    return nullptr;
-
-  std::unique_ptr<base::DictionaryValue> state = state_manager_->GetState();
-  CHECK(state);
-
+DeviceRegistrationInfo::BuildDeviceResource() const {
   std::unique_ptr<base::DictionaryValue> resource{new base::DictionaryValue};
   if (!GetSettings().cloud_id.empty())
     resource->SetString("id", GetSettings().cloud_id);
@@ -506,8 +496,11 @@
     channel->SetString("supportedType", "pull");
   }
   resource->Set("channel", channel.release());
-  resource->Set("commandDefs", commands.release());
-  resource->Set("state", state.release());
+  resource->Set("commandDefs",
+                component_manager_->GetLegacyCommandDefinitions().DeepCopy());
+  resource->Set("state", component_manager_->GetLegacyState().DeepCopy());
+  resource->Set("traits", component_manager_->GetTraits().DeepCopy());
+  resource->Set("components", component_manager_->GetComponents().DeepCopy());
 
   return resource;
 }
@@ -528,11 +521,8 @@
 
 void DeviceRegistrationInfo::RegisterDevice(const std::string& ticket_id,
                                             const DoneCallback& callback) {
-  ErrorPtr error;
-  std::unique_ptr<base::DictionaryValue> device_draft =
-      BuildDeviceResource(&error);
-  if (!device_draft)
-    return RegisterDeviceError(callback, std::move(error));
+  std::unique_ptr<base::DictionaryValue> device_draft = BuildDeviceResource();
+  CHECK(device_draft);
 
   base::DictionaryValue req_json;
   req_json.SetString("id", ticket_id);
@@ -722,6 +712,12 @@
     return;
   }
 
+  if (data->allow_response_without_content &&
+      response->GetContentType().empty()) {
+    cloud_backoff_entry_->InformOfRequest(true);
+    return data->callback.Run({}, nullptr);
+  }
+
   auto json_resp = ParseJsonResponse(*response, &error);
   if (!json_resp) {
     cloud_backoff_entry_->InformOfRequest(true);
@@ -904,12 +900,9 @@
   queued_resource_update_callbacks_.clear();
 
   VLOG(1) << "Updating GCD server with CDD...";
-  ErrorPtr error;
   std::unique_ptr<base::DictionaryValue> device_resource =
-      BuildDeviceResource(&error);
-  if (!device_resource) {
-    return OnUpdateDeviceResourceError(std::move(error));
-  }
+      BuildDeviceResource();
+  CHECK(device_resource);
 
   std::string url = GetDeviceURL(
       {}, {{"lastUpdateTimeMs", last_device_resource_updated_timestamp_}});
@@ -1080,9 +1073,8 @@
     const base::DictionaryValue& command) {
   std::string command_id;
   ErrorPtr error;
-  auto command_instance = CommandInstance::FromJson(
-      &command, Command::Origin::kCloud,
-      command_manager_->GetCommandDictionary(), &command_id, &error);
+  auto command_instance = component_manager_->ParseCommandInstance(
+      command, Command::Origin::kCloud, UserRole::kOwner, &command_id, &error);
   if (!command_instance) {
     LOG(WARNING) << "Failed to parse a command instance: " << command;
     if (!command_id.empty())
@@ -1091,19 +1083,19 @@
   }
 
   // TODO(antonm): Properly process cancellation of commands.
-  if (!command_manager_->FindCommand(command_instance->GetID())) {
+  if (!component_manager_->FindCommand(command_instance->GetID())) {
     LOG(INFO) << "New command '" << command_instance->GetName()
               << "' arrived, ID: " << command_instance->GetID();
     std::unique_ptr<BackoffEntry> backoff_entry{
         new BackoffEntry{cloud_backoff_policy_.get()}};
     std::unique_ptr<CloudCommandProxy> cloud_proxy{new CloudCommandProxy{
-        command_instance.get(), this, state_manager_->GetStateChangeQueue(),
+        command_instance.get(), this, component_manager_,
         std::move(backoff_entry), task_runner_}};
     // CloudCommandProxy::CloudCommandProxy() subscribe itself to Command
     // notifications. When Command is being destroyed it sends
     // ::OnCommandDestroyed() and CloudCommandProxy deletes itself.
     cloud_proxy.release();
-    command_manager_->AddCommand(std::move(command_instance));
+    component_manager_->AddCommand(std::move(command_instance));
   }
 }
 
@@ -1112,18 +1104,18 @@
   if (device_state_update_pending_)
     return;
 
-  StateChangeQueueInterface::UpdateID update_id = 0;
-  std::vector<StateChange> state_changes;
-  std::tie(update_id, state_changes) =
-      state_manager_->GetAndClearRecordedStateChanges();
-  if (state_changes.empty())
+  auto snapshot = component_manager_->GetAndClearRecordedStateChanges();
+  if (snapshot.state_changes.empty())
     return;
 
   std::unique_ptr<base::ListValue> patches{new base::ListValue};
-  for (auto& state_change : state_changes) {
+  for (auto& state_change : snapshot.state_changes) {
     std::unique_ptr<base::DictionaryValue> patch{new base::DictionaryValue};
     patch->SetString("timeMs",
                      std::to_string(state_change.timestamp.ToJavaTime()));
+    // TODO(avakulenko): Uncomment this once server supports "component"
+    // attribute on a state patch object.
+    // patch->SetString("component", state_change.component);
     patch->Set("patch", state_change.changed_properties.release());
     patches->Append(patch.release());
   }
@@ -1136,11 +1128,11 @@
   device_state_update_pending_ = true;
   DoCloudRequest(HttpClient::Method::kPost, GetDeviceURL("patchState"), &body,
                  base::Bind(&DeviceRegistrationInfo::OnPublishStateDone,
-                            AsWeakPtr(), update_id));
+                            AsWeakPtr(), snapshot.update_id));
 }
 
 void DeviceRegistrationInfo::OnPublishStateDone(
-    StateChangeQueueInterface::UpdateID update_id,
+    ComponentManager::UpdateID update_id,
     const base::DictionaryValue& reply,
     ErrorPtr error) {
   device_state_update_pending_ = false;
@@ -1148,7 +1140,7 @@
     LOG(ERROR) << "Permanent failure while trying to update device state";
     return;
   }
-  state_manager_->NotifyStateUpdatedOnServer(update_id);
+  component_manager_->NotifyStateUpdatedOnServer(update_id);
   // See if there were more pending state updates since the previous request
   // had been sent out.
   PublishStateUpdates();
@@ -1162,7 +1154,7 @@
     cb.Run(gcd_state_);
 }
 
-void DeviceRegistrationInfo::OnCommandDefsChanged() {
+void DeviceRegistrationInfo::OnTraitDefsChanged() {
   VLOG(1) << "CommandDefinitionChanged notification received";
   if (!HaveRegistrationCredentials() || !connected_to_cloud_)
     return;
@@ -1179,6 +1171,14 @@
   PublishStateUpdates();
 }
 
+void DeviceRegistrationInfo::OnComponentTreeChanged() {
+  VLOG(1) << "ComponentTreeChanged notification received";
+  if (!HaveRegistrationCredentials() || !connected_to_cloud_)
+    return;
+
+  UpdateDeviceResource(base::Bind(&IgnoreCloudError));
+}
+
 void DeviceRegistrationInfo::OnConnected(const std::string& channel_name) {
   LOG(INFO) << "Notification channel successfully established over "
             << channel_name;
diff --git a/src/device_registration_info.h b/src/device_registration_info.h
index 1399b37..bacab48 100644
--- a/src/device_registration_info.h
+++ b/src/device_registration_info.h
@@ -21,13 +21,12 @@
 
 #include "src/backoff_entry.h"
 #include "src/commands/cloud_command_update_interface.h"
-#include "src/commands/command_manager.h"
+#include "src/component_manager.h"
 #include "src/config.h"
 #include "src/data_encoding.h"
 #include "src/notification/notification_channel.h"
 #include "src/notification/notification_delegate.h"
 #include "src/notification/pull_channel.h"
-#include "src/states/state_change_queue_interface.h"
 
 namespace base {
 class DictionaryValue;
@@ -54,12 +53,12 @@
       base::Callback<void(const base::DictionaryValue& response,
                           ErrorPtr error)>;
 
-  DeviceRegistrationInfo(const std::shared_ptr<CommandManager>& command_manager,
-                         const std::shared_ptr<StateManager>& state_manager,
-                         std::unique_ptr<Config> config,
-                         provider::TaskRunner* task_runner,
-                         provider::HttpClient* http_client,
-                         provider::Network* network);
+  DeviceRegistrationInfo(
+      ComponentManager* component_manager,
+      std::unique_ptr<Config> config,
+      provider::TaskRunner* task_runner,
+      provider::HttpClient* http_client,
+      provider::Network* network);
 
   ~DeviceRegistrationInfo() override;
 
@@ -179,6 +178,8 @@
     provider::HttpClient::Method method;
     std::string url;
     std::string body;
+    // Workaround for inconsistent APIs which returns no body.
+    bool allow_response_without_content = false;
     CloudRequestDoneCallback callback;
   };
   void SendCloudRequest(const std::shared_ptr<const CloudRequestData>& data);
@@ -236,7 +237,7 @@
   void FetchAndPublishCommands(const std::string& reason);
 
   void PublishStateUpdates();
-  void OnPublishStateDone(StateChangeQueueInterface::UpdateID update_id,
+  void OnPublishStateDone(ComponentManager::UpdateID update_id,
                           const base::DictionaryValue& reply,
                           ErrorPtr error);
   void OnPublishStateError(ErrorPtr error);
@@ -248,13 +249,14 @@
   // Builds Cloud API devices collection REST resource which matches
   // current state of the device including command definitions
   // for all supported commands and current device state.
-  std::unique_ptr<base::DictionaryValue> BuildDeviceResource(ErrorPtr* error);
+  std::unique_ptr<base::DictionaryValue> BuildDeviceResource() const;
 
   void SetGcdState(GcdState new_state);
   void SetDeviceId(const std::string& cloud_id);
 
   // Callback called when command definitions are changed to re-publish new CDD.
-  void OnCommandDefsChanged();
+  void OnTraitDefsChanged();
+  void OnComponentTreeChanged();
   void OnStateChanged();
 
   // Overrides from NotificationDelegate.
@@ -299,10 +301,8 @@
   provider::HttpClient* http_client_{nullptr};
 
   provider::TaskRunner* task_runner_{nullptr};
-  // Global command manager.
-  std::shared_ptr<CommandManager> command_manager_;
-  // Device state manager.
-  std::shared_ptr<StateManager> state_manager_;
+  // Global component manager.
+  ComponentManager* component_manager_{nullptr};
 
   std::unique_ptr<Config> config_;
 
diff --git a/src/device_registration_info_unittest.cc b/src/device_registration_info_unittest.cc
index 9b53872..72405dc 100644
--- a/src/device_registration_info_unittest.cc
+++ b/src/device_registration_info_unittest.cc
@@ -11,13 +11,11 @@
 #include <weave/provider/test/fake_task_runner.h>
 #include <weave/provider/test/mock_config_store.h>
 #include <weave/provider/test/mock_http_client.h>
+#include <weave/test/unittest_utils.h>
 
 #include "src/bind_lambda.h"
-#include "src/commands/command_manager.h"
-#include "src/commands/unittest_utils.h"
+#include "src/component_manager_impl.h"
 #include "src/http_constants.h"
-#include "src/states/mock_state_change_queue_interface.h"
-#include "src/states/state_manager.h"
 
 using testing::_;
 using testing::AtLeast;
@@ -50,7 +48,7 @@
     "123543821385-sfjkjshdkjhfk234sdfsdfkskd"
     "fkjh7f.apps.googleusercontent.com";
 const char kClientSecret[] = "5sdGdGlfolGlrFKfdFlgP6FG";
-const char kDeviceId[] = "4a7ea2d1-b331-1e1f-b206-e863c7635196";
+const char kCloudId[] = "4a7ea2d1-b331-1e1f-b206-e863c7635196";
 const char kClaimTicketId[] = "RTcUE";
 const char kAccessToken[] =
     "ya29.1.AADtN_V-dLUM-sVZ0qVjG9Dxm5NgdS9J"
@@ -115,17 +113,9 @@
 class DeviceRegistrationInfoTest : public ::testing::Test {
  protected:
   void SetUp() override {
-    EXPECT_CALL(mock_state_change_queue_, GetLastStateChangeId())
-        .WillRepeatedly(Return(0));
-    EXPECT_CALL(mock_state_change_queue_, MockAddOnStateUpdatedCallback(_))
-        .WillRepeatedly(Return(nullptr));
-
-    command_manager_ = std::make_shared<CommandManager>();
-    state_manager_ = std::make_shared<StateManager>(&mock_state_change_queue_);
-
     std::unique_ptr<Config> config{new Config{&config_store_}};
     config_ = config.get();
-    dev_reg_.reset(new DeviceRegistrationInfo{command_manager_, state_manager_,
+    dev_reg_.reset(new DeviceRegistrationInfo{&component_manager_,
                                               std::move(config), &task_runner_,
                                               &http_client_, nullptr});
 
@@ -156,7 +146,7 @@
   void ReloadSettings() {
     base::DictionaryValue dict;
     dict.SetString("refresh_token", test_data::kRefreshToken);
-    dict.SetString("cloud_id", test_data::kDeviceId);
+    dict.SetString("cloud_id", test_data::kCloudId);
     dict.SetString("robot_account", test_data::kRobotAccountEmail);
     std::string json_string;
     base::JSONWriter::WriteWithOptions(
@@ -196,9 +186,7 @@
   base::DictionaryValue data_;
   Config* config_{nullptr};
   std::unique_ptr<DeviceRegistrationInfo> dev_reg_;
-  std::shared_ptr<CommandManager> command_manager_;
-  StrictMock<MockStateChangeQueueInterface> mock_state_change_queue_;
-  std::shared_ptr<StateManager> state_manager_;
+  ComponentManagerImpl component_manager_;
 };
 
 TEST_F(DeviceRegistrationInfoTest, GetServiceURL) {
@@ -316,7 +304,7 @@
   EXPECT_FALSE(RefreshAccessToken(&error));
   EXPECT_TRUE(error->HasError(kErrorDomainOAuth2, "invalid_grant"));
   EXPECT_EQ(GcdState::kInvalidCredentials, GetGcdState());
-  EXPECT_EQ(test_data::kDeviceId, dev_reg_->GetSettings().cloud_id);
+  EXPECT_EQ(test_data::kCloudId, dev_reg_->GetSettings().cloud_id);
 }
 
 TEST_F(DeviceRegistrationInfoTest, GetDeviceInfo) {
@@ -333,7 +321,7 @@
             base::DictionaryValue json;
             json.SetString("channel.supportedType", "xmpp");
             json.SetString("deviceKind", "vendor");
-            json.SetString("id", test_data::kDeviceId);
+            json.SetString("id", test_data::kCloudId);
             json.SetString("kind", "weave#device");
             callback.Run(ReplyWithJson(200, json), nullptr);
           })));
@@ -344,7 +332,7 @@
     EXPECT_FALSE(error);
     std::string id;
     EXPECT_TRUE(info.GetString("id", &id));
-    EXPECT_EQ(test_data::kDeviceId, id);
+    EXPECT_EQ(test_data::kCloudId, id);
     succeeded = true;
   };
   dev_reg_->GetDeviceInfo(base::Bind(callback));
@@ -352,21 +340,33 @@
 }
 
 TEST_F(DeviceRegistrationInfoTest, RegisterDevice) {
-  auto json_cmds = CreateDictionaryValue(R"({
+  auto json_traits = CreateDictionaryValue(R"({
     'base': {
-      'reboot': {
-        'parameters': {'delay': {'minimum': 10, 'type': 'integer'}},
-        'minimalRole': 'user'
+      'commands': {
+        'reboot': {
+          'parameters': {'delay': {'minimum': 10, 'type': 'integer'}},
+          'minimalRole': 'user'
+        }
+      },
+      'state': {
+        'firmwareVersion': {'type': 'string'}
       }
     },
     'robot': {
-      '_jump': {
-        'parameters': {'_height': {'type': 'integer'}},
-        'minimalRole': 'user'
+      'commands': {
+        '_jump': {
+          'parameters': {'_height': {'type': 'integer'}},
+          'minimalRole': 'user'
+        }
       }
     }
   })");
-  EXPECT_TRUE(command_manager_->LoadCommands(*json_cmds, nullptr));
+  EXPECT_TRUE(component_manager_.LoadTraits(*json_traits, nullptr));
+  EXPECT_TRUE(component_manager_.AddComponent("", "comp", {"base", "robot"},
+                                              nullptr));
+  base::StringValue ver{"1.0"};
+  EXPECT_TRUE(component_manager_.SetStateProperty(
+      "comp", "base.firmwareVersion", ver, nullptr));
 
   std::string ticket_url = dev_reg_->GetServiceURL("registrationTickets/") +
                            test_data::kClaimTicketId;
@@ -395,12 +395,9 @@
         EXPECT_EQ("AAAAA", value);
         EXPECT_TRUE(json->GetString("deviceDraft.name", &value));
         EXPECT_EQ("Coffee Pot", value);
-        base::DictionaryValue* commandDefs = nullptr;
-        EXPECT_TRUE(
-            json->GetDictionary("deviceDraft.commandDefs", &commandDefs));
-        EXPECT_FALSE(commandDefs->empty());
-
-        auto expected = R"({
+        base::DictionaryValue* dict = nullptr;
+        EXPECT_TRUE(json->GetDictionary("deviceDraft.commandDefs", &dict));
+        auto expectedCommandDefs = R"({
             'base': {
               'reboot': {
                 'parameters': {
@@ -423,7 +420,50 @@
               }
             }
           })";
-        EXPECT_JSON_EQ(expected, *commandDefs);
+        EXPECT_JSON_EQ(expectedCommandDefs, *dict);
+
+        EXPECT_TRUE(json->GetDictionary("deviceDraft.state", &dict));
+        auto expectedState = R"({
+            'base': {
+              'firmwareVersion': '1.0'
+            }
+          })";
+        EXPECT_JSON_EQ(expectedState, *dict);
+
+        EXPECT_TRUE(json->GetDictionary("deviceDraft.traits", &dict));
+        auto expectedTraits = R"({
+            'base': {
+              'commands': {
+                'reboot': {
+                  'parameters': {'delay': {'minimum': 10, 'type': 'integer'}},
+                  'minimalRole': 'user'
+                }
+              },
+              'state': {
+                'firmwareVersion': {'type': 'string'}
+              }
+            },
+            'robot': {
+              'commands': {
+                '_jump': {
+                  'parameters': {'_height': {'type': 'integer'}},
+                  'minimalRole': 'user'
+                }
+              }
+            }
+          })";
+        EXPECT_JSON_EQ(expectedTraits, *dict);
+
+        EXPECT_TRUE(json->GetDictionary("deviceDraft.components", &dict));
+        auto expectedComponents = R"({
+            'comp': {
+              'traits': ['base', 'robot'],
+              'state': {
+                'base': { 'firmwareVersion': '1.0' }
+              }
+            }
+          })";
+        EXPECT_JSON_EQ(expectedComponents, *dict);
 
         base::DictionaryValue json_resp;
         json_resp.SetString("id", test_data::kClaimTicketId);
@@ -432,7 +472,7 @@
         base::DictionaryValue* device_draft = nullptr;
         EXPECT_TRUE(json->GetDictionary("deviceDraft", &device_draft));
         device_draft = device_draft->DeepCopy();
-        device_draft->SetString("id", test_data::kDeviceId);
+        device_draft->SetString("id", test_data::kCloudId);
         device_draft->SetString("kind", "weave#device");
         json_resp.Set("deviceDraft", device_draft);
 
@@ -450,7 +490,7 @@
             json.SetString("kind", "weave#registrationTicket");
             json.SetString("oauthClientId", test_data::kClientId);
             json.SetString("userEmail", "user@email.com");
-            json.SetString("deviceDraft.id", test_data::kDeviceId);
+            json.SetString("deviceDraft.id", test_data::kCloudId);
             json.SetString("deviceDraft.kind", "weave#device");
             json.SetString("deviceDraft.channel.supportedType", "xmpp");
             json.SetString("robotAccountEmail", test_data::kRobotAccountEmail);
@@ -491,7 +531,7 @@
         EXPECT_EQ(GcdState::kConnecting, GetGcdState());
 
         // Validate the device info saved to storage...
-        EXPECT_EQ(test_data::kDeviceId, dev_reg_->GetSettings().cloud_id);
+        EXPECT_EQ(test_data::kCloudId, dev_reg_->GetSettings().cloud_id);
         EXPECT_EQ(test_data::kRefreshToken,
                   dev_reg_->GetSettings().refresh_token);
         EXPECT_EQ(test_data::kRobotAccountEmail,
@@ -519,22 +559,27 @@
     ReloadSettings();
     SetAccessToken();
 
-    auto json_cmds = CreateDictionaryValue(R"({
+    auto json_traits = CreateDictionaryValue(R"({
       'robot': {
-        '_jump': {
-          'parameters': {'_height': 'integer'},
-          'progress': {'progress': 'integer'},
-          'results': {'status': 'string'},
-          'minimalRole': 'user'
+        'commands': {
+          '_jump': {
+            'parameters': {'_height': 'integer'},
+            'progress': {'progress': 'integer'},
+            'results': {'status': 'string'},
+            'minimalRole': 'user'
+          }
         }
       }
     })");
-    EXPECT_TRUE(command_manager_->LoadCommands(*json_cmds, nullptr));
+    EXPECT_TRUE(component_manager_.LoadTraits(*json_traits, nullptr));
+    EXPECT_TRUE(component_manager_.AddComponent("", "comp", {"robot"},
+                                                nullptr));
 
     command_url_ = dev_reg_->GetServiceURL("commands/1234");
 
     auto commands_json = CreateValue(R"([{
       'name':'robot._jump',
+      'component': 'comp',
       'id':'1234',
       'parameters': {'_height': 100},
       'minimalRole': 'user'
@@ -543,7 +588,7 @@
     const base::ListValue* command_list = nullptr;
     ASSERT_TRUE(commands_json->GetAsList(&command_list));
     PublishCommands(*command_list);
-    command_ = command_manager_->FindCommand("1234");
+    command_ = component_manager_.FindCommand("1234");
     ASSERT_NE(nullptr, command_);
   }
 
diff --git a/src/mock_component_manager.h b/src/mock_component_manager.h
new file mode 100644
index 0000000..e30b8b0
--- /dev/null
+++ b/src/mock_component_manager.h
@@ -0,0 +1,117 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_SRC_MOCK_COMPONENT_MANAGER_H_
+#define LIBWEAVE_SRC_MOCK_COMPONENT_MANAGER_H_
+
+#include "src/component_manager.h"
+
+#include <gmock/gmock.h>
+
+namespace weave {
+
+class MockComponentManager : public ComponentManager {
+ public:
+  ~MockComponentManager() override {}
+  MOCK_METHOD2(LoadTraits, bool(const base::DictionaryValue& dict,
+                                ErrorPtr* error));
+  MOCK_METHOD2(LoadTraits, bool(const std::string& json, ErrorPtr* error));
+  MOCK_METHOD1(AddTraitDefChangedCallback, void(const base::Closure& callback));
+  MOCK_METHOD4(AddComponent, bool(const std::string& path,
+                                  const std::string& name,
+                                  const std::vector<std::string>& traits,
+                                  ErrorPtr* error));
+  MOCK_METHOD4(AddComponentArrayItem,
+               bool(const std::string& path,
+                    const std::string& name,
+                    const std::vector<std::string>& traits,
+                    ErrorPtr* error));
+  MOCK_METHOD1(AddComponentTreeChangedCallback,
+               void(const base::Closure& callback));
+  MOCK_METHOD1(MockAddCommand, void(CommandInstance* command_instance));
+  MOCK_METHOD5(MockParseCommandInstance,
+               CommandInstance*(const base::DictionaryValue& command,
+                                Command::Origin command_origin,
+                                UserRole role,
+                                std::string* id,
+                                ErrorPtr* error));
+  MOCK_METHOD1(FindCommand, CommandInstance*(const std::string& id));
+  MOCK_METHOD1(AddCommandAddedCallback,
+               void(const CommandQueue::CommandCallback& callback));
+  MOCK_METHOD1(AddCommandRemovedCallback,
+               void(const CommandQueue::CommandCallback& callback));
+  MOCK_METHOD3(AddCommandHandler,
+               void(const std::string& component_path,
+                    const std::string& command_name,
+                    const Device::CommandHandlerCallback& callback));
+  MOCK_CONST_METHOD2(FindComponent,
+                     const base::DictionaryValue*(const std::string& path,
+                                                  ErrorPtr* error));
+  MOCK_CONST_METHOD1(FindTraitDefinition,
+                     const base::DictionaryValue*(const std::string& name));
+  MOCK_CONST_METHOD1(
+      FindCommandDefinition,
+      const base::DictionaryValue*(const std::string& command_name));
+  MOCK_CONST_METHOD3(GetMinimalRole, bool(const std::string& command_name,
+                                          UserRole* minimal_role,
+                                          ErrorPtr* error));
+  MOCK_CONST_METHOD0(GetTraits, const base::DictionaryValue&());
+  MOCK_CONST_METHOD0(GetComponents, const base::DictionaryValue&());
+  MOCK_METHOD3(SetStateProperties, bool(const std::string& component_path,
+                                        const base::DictionaryValue& dict,
+                                        ErrorPtr* error));
+  MOCK_METHOD3(SetStatePropertiesFromJson,
+               bool(const std::string& component_path,
+                    const std::string& json,
+                    ErrorPtr* error));
+  MOCK_CONST_METHOD3(GetStateProperty,
+                     const base::Value*(const std::string& component_path,
+                                        const std::string& name,
+                                        ErrorPtr* error));
+  MOCK_METHOD4(SetStateProperty, bool(const std::string& component_path,
+                                      const std::string& name,
+                                      const base::Value& value,
+                                      ErrorPtr* error));
+  MOCK_METHOD1(AddStateChangedCallback, void(const base::Closure& callback));
+  MOCK_METHOD0(MockGetAndClearRecordedStateChanges, StateSnapshot&());
+  MOCK_METHOD1(NotifyStateUpdatedOnServer, void(UpdateID id));
+  MOCK_CONST_METHOD0(GetLastStateChangeId, UpdateID());
+  MOCK_METHOD1(MockAddServerStateUpdatedCallback,
+               base::CallbackList<void(UpdateID)>::Subscription*(
+                  const base::Callback<void(UpdateID)>& callback));
+  MOCK_CONST_METHOD1(FindComponentWithTrait,
+                     std::string(const std::string& trait));
+  MOCK_METHOD2(AddLegacyCommandDefinitions,
+               bool(const base::DictionaryValue& dict, ErrorPtr* error));
+  MOCK_METHOD2(AddLegacyStateDefinitions,
+               bool(const base::DictionaryValue& dict, ErrorPtr* error));
+  MOCK_CONST_METHOD0(GetLegacyState, const base::DictionaryValue&());
+  MOCK_CONST_METHOD0(GetLegacyCommandDefinitions,
+                     const base::DictionaryValue&());
+
+ private:
+  void AddCommand(std::unique_ptr<CommandInstance> command_instance) override {
+    MockAddCommand(command_instance.get());
+  }
+  std::unique_ptr<CommandInstance> ParseCommandInstance(
+      const base::DictionaryValue& command,
+      Command::Origin command_origin,
+      UserRole role,
+      std::string* id,
+      ErrorPtr* error) {
+    return std::unique_ptr<CommandInstance>{MockParseCommandInstance(
+        command, command_origin, role, id, error)};
+  }
+  StateSnapshot GetAndClearRecordedStateChanges() override {
+    return std::move(MockGetAndClearRecordedStateChanges());
+  }
+  Token AddServerStateUpdatedCallback(
+      const base::Callback<void(UpdateID)>& callback) override {
+    return Token{MockAddServerStateUpdatedCallback(callback)};
+  }
+};
+
+}  // namespace weave
+
+#endif  // LIBWEAVE_SRC_COMPONENT_MANAGER_H_
diff --git a/src/notification/notification_channel.h b/src/notification/notification_channel.h
index 5fb7993..ef152a8 100644
--- a/src/notification/notification_channel.h
+++ b/src/notification/notification_channel.h
@@ -17,7 +17,7 @@
 
 class NotificationChannel {
  public:
-  virtual ~NotificationChannel() = default;
+  virtual ~NotificationChannel() {}
 
   virtual std::string GetName() const = 0;
   virtual bool IsConnected() const = 0;
diff --git a/src/notification/notification_delegate.h b/src/notification/notification_delegate.h
index 719d76d..263c2f3 100644
--- a/src/notification/notification_delegate.h
+++ b/src/notification/notification_delegate.h
@@ -24,7 +24,7 @@
   virtual void OnDeviceDeleted(const std::string& cloud_id) = 0;
 
  protected:
-  virtual ~NotificationDelegate() = default;
+  virtual ~NotificationDelegate() {}
 };
 
 }  // namespace weave
diff --git a/src/notification/notification_parser.cc b/src/notification/notification_parser.cc
index 0a27f1c..d7c4f48 100644
--- a/src/notification/notification_parser.cc
+++ b/src/notification/notification_parser.cc
@@ -46,9 +46,9 @@
 
   std::string kind;
   if (!notification.GetString("kind", &kind) ||
-      kind != "clouddevices#notification") {
+      kind != "weave#notification") {
     LOG(WARNING) << "Push notification should have 'kind' property set to "
-                    "clouddevices#notification";
+                    "weave#notification";
     return false;
   }
 
diff --git a/src/notification/notification_parser_unittest.cc b/src/notification/notification_parser_unittest.cc
index d0b43fe..c69f9dd 100644
--- a/src/notification/notification_parser_unittest.cc
+++ b/src/notification/notification_parser_unittest.cc
@@ -6,8 +6,7 @@
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-
-#include "src/commands/unittest_utils.h"
+#include <weave/test/unittest_utils.h>
 
 using testing::SaveArg;
 using testing::Invoke;
@@ -38,11 +37,11 @@
 
 TEST_F(NotificationParserTest, CommandCreated) {
   auto json = CreateDictionaryValue(R"({
-    "kind": "clouddevices#notification",
+    "kind": "weave#notification",
     "type": "COMMAND_CREATED",
     "deviceId": "device_id",
     "command": {
-      "kind": "clouddevices#command",
+      "kind": "weave#command",
       "deviceId": "device_id",
       "state": "queued",
       "name": "storage.list",
@@ -57,7 +56,7 @@
   })");
 
   const char expected_json[] = R"({
-      "kind": "clouddevices#command",
+      "kind": "weave#command",
       "deviceId": "device_id",
       "state": "queued",
       "name": "storage.list",
@@ -76,7 +75,7 @@
 
 TEST_F(NotificationParserTest, DeviceDeleted) {
   auto json = CreateDictionaryValue(R"({
-    "kind":"clouddevices#notification",
+    "kind":"weave#notification",
     "type":"DEVICE_DELETED",
     "deviceId":"some_device_id"
   })");
@@ -92,7 +91,7 @@
     "type": "COMMAND_CREATED",
     "deviceId": "device_id",
     "command": {
-      "kind": "clouddevices#command",
+      "kind": "weave#command",
       "deviceId": "device_id",
       "state": "queued",
       "name": "storage.list",
@@ -111,10 +110,10 @@
 
 TEST_F(NotificationParserTest, Failure_NoType) {
   auto json = CreateDictionaryValue(R"({
-    "kind": "clouddevices#notification",
+    "kind": "weave#notification",
     "deviceId": "device_id",
     "command": {
-      "kind": "clouddevices#command",
+      "kind": "weave#command",
       "deviceId": "device_id",
       "state": "queued",
       "name": "storage.list",
@@ -133,11 +132,11 @@
 
 TEST_F(NotificationParserTest, IgnoredNotificationType) {
   auto json = CreateDictionaryValue(R"({
-    "kind": "clouddevices#notification",
+    "kind": "weave#notification",
     "type": "COMMAND_EXPIRED",
     "deviceId": "device_id",
     "command": {
-      "kind": "clouddevices#command",
+      "kind": "weave#command",
       "deviceId": "device_id",
       "state": "queued",
       "name": "storage.list",
diff --git a/src/notification/xmpp_channel.h b/src/notification/xmpp_channel.h
index e6185d9..50e84d2 100644
--- a/src/notification/xmpp_channel.h
+++ b/src/notification/xmpp_channel.h
@@ -33,7 +33,7 @@
   virtual void SendMessage(const std::string& message) = 0;
 
  protected:
-  virtual ~XmppChannelInterface() = default;
+  virtual ~XmppChannelInterface() {}
 };
 
 class XmppChannel : public NotificationChannel,
diff --git a/src/notification/xmpp_iq_stanza_handler.cc b/src/notification/xmpp_iq_stanza_handler.cc
index 8753030..9ac4223 100644
--- a/src/notification/xmpp_iq_stanza_handler.cc
+++ b/src/notification/xmpp_iq_stanza_handler.cc
@@ -72,7 +72,7 @@
     const ResponseCallback& response_callback,
     const TimeoutCallback& timeout_callback) {
   // Remember the response callback to call later.
-  requests_.emplace(++last_request_id_, response_callback);
+  requests_.insert(std::make_pair(++last_request_id_, response_callback));
   // Schedule a time-out callback for this request.
   if (timeout < base::TimeDelta::Max()) {
     task_runner_->PostDelayedTask(
diff --git a/src/notification/xmpp_stream_parser.cc b/src/notification/xmpp_stream_parser.cc
index de3b8f1..9efaeb5 100644
--- a/src/notification/xmpp_stream_parser.cc
+++ b/src/notification/xmpp_stream_parser.cc
@@ -36,7 +36,7 @@
   std::map<std::string, std::string> attributes;
   if (attr != nullptr) {
     for (size_t n = 0; attr[n] != nullptr && attr[n + 1] != nullptr; n += 2) {
-      attributes.emplace(attr[n], attr[n + 1]);
+      attributes.insert(std::make_pair(attr[n], attr[n + 1]));
     }
   }
   self->OnOpenElement(element, std::move(attributes));
diff --git a/src/notification/xmpp_stream_parser.h b/src/notification/xmpp_stream_parser.h
index 41faaff..b8f5723 100644
--- a/src/notification/xmpp_stream_parser.h
+++ b/src/notification/xmpp_stream_parser.h
@@ -49,7 +49,7 @@
     virtual void OnStanza(std::unique_ptr<XmlNode> stanza) = 0;
 
    protected:
-    virtual ~Delegate() = default;
+    virtual ~Delegate() {}
   };
 
   explicit XmppStreamParser(Delegate* delegate);
diff --git a/src/privet/auth_manager.cc b/src/privet/auth_manager.cc
new file mode 100644
index 0000000..0864242
--- /dev/null
+++ b/src/privet/auth_manager.cc
@@ -0,0 +1,88 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/privet/auth_manager.h"
+
+#include <base/rand_util.h>
+#include <base/strings/string_number_conversions.h>
+
+#include "src/data_encoding.h"
+#include "src/privet/openssl_utils.h"
+#include "src/string_utils.h"
+
+namespace weave {
+namespace privet {
+
+namespace {
+
+const char kTokenDelimeter[] = ":";
+
+// Returns "scope:id:time".
+std::string CreateTokenData(const UserInfo& user_info, const base::Time& time) {
+  return base::IntToString(static_cast<int>(user_info.scope())) +
+         kTokenDelimeter + base::Uint64ToString(user_info.user_id()) +
+         kTokenDelimeter + base::Int64ToString(time.ToTimeT());
+}
+
+// Splits string of "scope:id:time" format.
+UserInfo SplitTokenData(const std::string& token, base::Time* time) {
+  const UserInfo kNone;
+  auto parts = Split(token, kTokenDelimeter, false, false);
+  if (parts.size() != 3)
+    return kNone;
+  int scope = 0;
+  if (!base::StringToInt(parts[0], &scope) ||
+      scope < static_cast<int>(AuthScope::kNone) ||
+      scope > static_cast<int>(AuthScope::kOwner)) {
+    return kNone;
+  }
+
+  uint64_t id{0};
+  if (!base::StringToUint64(parts[1], &id))
+    return kNone;
+
+  int64_t timestamp{0};
+  if (!base::StringToInt64(parts[2], &timestamp))
+    return kNone;
+  *time = base::Time::FromTimeT(timestamp);
+  return UserInfo{static_cast<AuthScope>(scope), id};
+}
+
+}  // namespace
+
+AuthManager::AuthManager(const std::vector<uint8_t>& secret,
+                         const std::vector<uint8_t>& certificate_fingerprint)
+    : secret_{secret}, certificate_fingerprint_{certificate_fingerprint} {
+  if (secret_.size() != kSha256OutputSize) {
+    secret_.resize(kSha256OutputSize);
+    base::RandBytes(secret_.data(), secret_.size());
+  }
+}
+
+AuthManager::~AuthManager() {}
+
+// Returns "[hmac]scope:id:time".
+std::vector<uint8_t> AuthManager::CreateAccessToken(const UserInfo& user_info,
+                                                    const base::Time& time) {
+  std::string data_str{CreateTokenData(user_info, time)};
+  std::vector<uint8_t> data{data_str.begin(), data_str.end()};
+  std::vector<uint8_t> hash{HmacSha256(secret_, data)};
+  hash.insert(hash.end(), data.begin(), data.end());
+  return hash;
+}
+
+// Parses "base64([hmac]scope:id:time)".
+UserInfo AuthManager::ParseAccessToken(const std::vector<uint8_t>& token,
+                                       base::Time* time) const {
+  if (token.size() <= kSha256OutputSize)
+    return UserInfo{};
+  std::vector<uint8_t> hash(token.begin(), token.begin() + kSha256OutputSize);
+  std::vector<uint8_t> data(token.begin() + kSha256OutputSize, token.end());
+  if (hash != HmacSha256(secret_, data))
+    return UserInfo{};
+  return SplitTokenData(std::string(data.begin(), data.end()), time);
+}
+
+}  // namespace privet
+}  // namespace weave
diff --git a/src/privet/auth_manager.h b/src/privet/auth_manager.h
new file mode 100644
index 0000000..607b820
--- /dev/null
+++ b/src/privet/auth_manager.h
@@ -0,0 +1,45 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBWEAVE_SRC_PRIVET_AUTH_MANAGER_H_
+#define LIBWEAVE_SRC_PRIVET_AUTH_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#include <base/time/time.h>
+#include <weave/error.h>
+
+#include "src/privet/privet_types.h"
+
+namespace weave {
+namespace privet {
+
+class AuthManager {
+ public:
+  AuthManager(const std::vector<uint8_t>& secret,
+              const std::vector<uint8_t>& certificate_fingerprint);
+  ~AuthManager();
+
+  std::vector<uint8_t> CreateAccessToken(const UserInfo& user_info,
+                                         const base::Time& time);
+  UserInfo ParseAccessToken(const std::vector<uint8_t>& token,
+                            base::Time* time) const;
+
+  const std::vector<uint8_t>& GetSecret() const { return secret_; }
+  const std::vector<uint8_t>& GetCertificateFingerprint() const {
+    return certificate_fingerprint_;
+  }
+
+ private:
+  std::vector<uint8_t> secret_;
+  std::vector<uint8_t> certificate_fingerprint_;
+
+  DISALLOW_COPY_AND_ASSIGN(AuthManager);
+};
+
+}  // namespace privet
+}  // namespace weave
+
+#endif  // LIBWEAVE_SRC_PRIVET_AUTH_MANAGER_H_
diff --git a/src/privet/auth_manager_unittest.cc b/src/privet/auth_manager_unittest.cc
new file mode 100644
index 0000000..6b9ae29
--- /dev/null
+++ b/src/privet/auth_manager_unittest.cc
@@ -0,0 +1,86 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/privet/auth_manager.h"
+
+#include <gtest/gtest.h>
+#include <weave/settings.h>
+
+namespace weave {
+namespace privet {
+
+class AuthManagerTest : public testing::Test {
+ public:
+  void SetUp() override {}
+
+ protected:
+  const base::Time time_ = base::Time::FromTimeT(1410000000);
+  AuthManager auth_{{}, {}};
+};
+
+TEST_F(AuthManagerTest, RandomSecret) {
+  EXPECT_GE(auth_.GetSecret().size(), 32u);
+}
+
+TEST_F(AuthManagerTest, DifferentSecret) {
+  AuthManager auth{{}, {}};
+  EXPECT_NE(auth_.GetSecret(), auth.GetSecret());
+}
+
+TEST_F(AuthManagerTest, Constructor) {
+  std::vector<uint8_t> secret;
+  std::vector<uint8_t> fingerpint;
+  for (uint8_t i = 0; i < 32; ++i) {
+    secret.push_back(i);
+    fingerpint.push_back(i + 100);
+  }
+
+  AuthManager auth{secret, fingerpint};
+  EXPECT_EQ(secret, auth.GetSecret());
+  EXPECT_EQ(fingerpint, auth.GetCertificateFingerprint());
+}
+
+TEST_F(AuthManagerTest, CreateSameToken) {
+  EXPECT_EQ(auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555}, time_),
+            auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, 555}, time_));
+}
+
+TEST_F(AuthManagerTest, CreateTokenDifferentScope) {
+  EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kViewer, 456}, time_),
+            auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456}, time_));
+}
+
+TEST_F(AuthManagerTest, CreateTokenDifferentUser) {
+  EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 456}, time_),
+            auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 789}, time_));
+}
+
+TEST_F(AuthManagerTest, CreateTokenDifferentTime) {
+  EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567}, time_),
+            auth_.CreateAccessToken(UserInfo{AuthScope::kOwner, 567},
+                                    base::Time::FromTimeT(1400000000)));
+}
+
+TEST_F(AuthManagerTest, CreateTokenDifferentInstance) {
+  EXPECT_NE(auth_.CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_),
+            AuthManager({}, {})
+                .CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_));
+}
+
+TEST_F(AuthManagerTest, ParseAccessToken) {
+  // Multiple attempts with random secrets.
+  for (size_t i = 0; i < 1000; ++i) {
+    AuthManager auth{{}, {}};
+
+    auto token = auth.CreateAccessToken(UserInfo{AuthScope::kUser, 5}, time_);
+    base::Time time2;
+    EXPECT_EQ(AuthScope::kUser, auth.ParseAccessToken(token, &time2).scope());
+    EXPECT_EQ(5u, auth.ParseAccessToken(token, &time2).user_id());
+    // Token timestamp resolution is one second.
+    EXPECT_GE(1, std::abs((time_ - time2).InSeconds()));
+  }
+}
+
+}  // namespace privet
+}  // namespace weave
diff --git a/src/privet/cloud_delegate.cc b/src/privet/cloud_delegate.cc
index 32ebf48..170688b 100644
--- a/src/privet/cloud_delegate.cc
+++ b/src/privet/cloud_delegate.cc
@@ -15,11 +15,10 @@
 #include <weave/provider/task_runner.h>
 
 #include "src/backoff_entry.h"
-#include "src/commands/command_manager.h"
+#include "src/component_manager.h"
 #include "src/config.h"
 #include "src/device_registration_info.h"
 #include "src/privet/constants.h"
-#include "src/states/state_manager.h"
 
 namespace weave {
 namespace privet {
@@ -42,26 +41,28 @@
  public:
   CloudDelegateImpl(provider::TaskRunner* task_runner,
                     DeviceRegistrationInfo* device,
-                    CommandManager* command_manager,
-                    StateManager* state_manager)
+                    ComponentManager* component_manager)
       : task_runner_{task_runner},
         device_{device},
-        command_manager_{command_manager},
-        state_manager_{state_manager} {
+        component_manager_{component_manager} {
     device_->GetMutableConfig()->AddOnChangedCallback(base::Bind(
         &CloudDelegateImpl::OnConfigChanged, weak_factory_.GetWeakPtr()));
     device_->AddGcdStateChangedCallback(base::Bind(
         &CloudDelegateImpl::OnRegistrationChanged, weak_factory_.GetWeakPtr()));
 
-    command_manager_->AddCommandDefChanged(base::Bind(
-        &CloudDelegateImpl::OnCommandDefChanged, weak_factory_.GetWeakPtr()));
-    command_manager_->AddCommandAddedCallback(base::Bind(
+    component_manager_->AddTraitDefChangedCallback(base::Bind(
+        &CloudDelegateImpl::NotifyOnTraitDefsChanged,
+        weak_factory_.GetWeakPtr()));
+    component_manager_->AddCommandAddedCallback(base::Bind(
         &CloudDelegateImpl::OnCommandAdded, weak_factory_.GetWeakPtr()));
-    command_manager_->AddCommandRemovedCallback(base::Bind(
+    component_manager_->AddCommandRemovedCallback(base::Bind(
         &CloudDelegateImpl::OnCommandRemoved, weak_factory_.GetWeakPtr()));
-
-    state_manager_->AddChangedCallback(base::Bind(
-        &CloudDelegateImpl::OnStateChanged, weak_factory_.GetWeakPtr()));
+    component_manager_->AddStateChangedCallback(base::Bind(
+        &CloudDelegateImpl::NotifyOnStateChanged,
+        weak_factory_.GetWeakPtr()));
+    component_manager_->AddComponentTreeChangedCallback(base::Bind(
+        &CloudDelegateImpl::NotifyOnComponentTreeChanged,
+        weak_factory_.GetWeakPtr()));
   }
 
   ~CloudDelegateImpl() override = default;
@@ -139,10 +140,20 @@
                : "";
   }
 
-  const base::DictionaryValue& GetState() const override { return state_; }
+  const base::DictionaryValue& GetLegacyCommandDef() const override {
+    return component_manager_->GetLegacyCommandDefinitions();
+  }
 
-  const base::DictionaryValue& GetCommandDef() const override {
-    return command_defs_;
+  const base::DictionaryValue& GetLegacyState() const override {
+    return component_manager_->GetLegacyState();
+  }
+
+  const base::DictionaryValue& GetComponents() const override {
+    return component_manager_->GetComponents();
+  }
+
+  const base::DictionaryValue& GetTraits() const override {
+    return component_manager_->GetTraits();
   }
 
   void AddCommand(const base::DictionaryValue& command,
@@ -162,11 +173,13 @@
     }
 
     std::string id;
-    if (!command_manager_->AddCommand(command, role, &id, &error))
+    auto command_instance = component_manager_->ParseCommandInstance(
+        command, Command::Origin::kLocal, role, &id, &error);
+    if (!command_instance)
       return callback.Run({}, std::move(error));
-
+    component_manager_->AddCommand(std::move(command_instance));
     command_owners_[id] = user_info.user_id();
-    callback.Run(*command_manager_->FindCommand(id)->ToJson(), nullptr);
+    callback.Run(*component_manager_->FindCommand(id)->ToJson(), nullptr);
   }
 
   void GetCommand(const std::string& id,
@@ -200,7 +213,7 @@
     for (const auto& it : command_owners_) {
       if (CanAccessCommand(it.second, user_info, nullptr)) {
         list_value.Append(
-            command_manager_->FindCommand(it.first)->ToJson().release());
+            component_manager_->FindCommand(it.first)->ToJson().release());
       }
     }
 
@@ -213,7 +226,7 @@
  private:
   void OnCommandAdded(Command* command) {
     // Set to 0 for any new unknown command.
-    command_owners_.emplace(command->GetID(), 0);
+    command_owners_.insert(std::make_pair(command->GetID(), 0));
   }
 
   void OnCommandRemoved(Command* command) {
@@ -241,23 +254,6 @@
     NotifyOnDeviceInfoChanged();
   }
 
-  void OnStateChanged() {
-    state_.Clear();
-    auto state = state_manager_->GetState();
-    CHECK(state);
-    state_.MergeDictionary(state.get());
-    NotifyOnStateChanged();
-  }
-
-  void OnCommandDefChanged() {
-    command_defs_.Clear();
-    auto commands =
-      command_manager_->GetCommandDictionary().GetCommandsAsJson(nullptr);
-    CHECK(commands);
-    command_defs_.MergeDictionary(commands.get());
-    NotifyOnCommandDefsChanged();
-  }
-
   void OnRegisterSuccess(const std::string& cloud_id) {
     VLOG(1) << "Device registered: " << cloud_id;
     setup_state_ = SetupState(SetupState::kSuccess);
@@ -306,7 +302,7 @@
         return nullptr;
     }
 
-    auto command = command_manager_->FindCommand(command_id);
+    auto command = component_manager_->FindCommand(command_id);
     if (!command)
       return ReturnNotFound(command_id, error);
 
@@ -331,8 +327,7 @@
 
   provider::TaskRunner* task_runner_{nullptr};
   DeviceRegistrationInfo* device_{nullptr};
-  CommandManager* command_manager_{nullptr};
-  StateManager* state_manager_{nullptr};
+  ComponentManager* component_manager_{nullptr};
 
   // Primary state of GCD.
   ConnectionState connection_state_{ConnectionState::kDisabled};
@@ -340,12 +335,6 @@
   // State of the current or last setup.
   SetupState setup_state_{SetupState::kNone};
 
-  // Current device state.
-  base::DictionaryValue state_;
-
-  // Current commands definitions.
-  base::DictionaryValue command_defs_;
-
   // Map of command IDs to user IDs.
   std::map<std::string, uint64_t> command_owners_;
 
@@ -369,18 +358,21 @@
 std::unique_ptr<CloudDelegate> CloudDelegate::CreateDefault(
     provider::TaskRunner* task_runner,
     DeviceRegistrationInfo* device,
-    CommandManager* command_manager,
-    StateManager* state_manager) {
+    ComponentManager* component_manager) {
   return std::unique_ptr<CloudDelegateImpl>{new CloudDelegateImpl{
-      task_runner, device, command_manager, state_manager}};
+      task_runner, device, component_manager}};
 }
 
 void CloudDelegate::NotifyOnDeviceInfoChanged() {
   FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceInfoChanged());
 }
 
-void CloudDelegate::NotifyOnCommandDefsChanged() {
-  FOR_EACH_OBSERVER(Observer, observer_list_, OnCommandDefsChanged());
+void CloudDelegate::NotifyOnTraitDefsChanged() {
+  FOR_EACH_OBSERVER(Observer, observer_list_, OnTraitDefsChanged());
+}
+
+void CloudDelegate::NotifyOnComponentTreeChanged() {
+  FOR_EACH_OBSERVER(Observer, observer_list_, OnComponentTreeChanged());
 }
 
 void CloudDelegate::NotifyOnStateChanged() {
diff --git a/src/privet/cloud_delegate.h b/src/privet/cloud_delegate.h
index 8763fbe..e80c39d 100644
--- a/src/privet/cloud_delegate.h
+++ b/src/privet/cloud_delegate.h
@@ -22,9 +22,8 @@
 
 namespace weave {
 
-class CommandManager;
+class ComponentManager;
 class DeviceRegistrationInfo;
-class StateManager;
 
 namespace provider {
 class TaskRunner;
@@ -45,11 +44,12 @@
 
   class Observer {
    public:
-    virtual ~Observer() = default;
+    virtual ~Observer() {}
 
     virtual void OnDeviceInfoChanged() {}
-    virtual void OnCommandDefsChanged() {}
+    virtual void OnTraitDefsChanged() {}
     virtual void OnStateChanged() {}
+    virtual void OnComponentTreeChanged() {}
   };
 
   // Returns the ID of the device.
@@ -95,11 +95,17 @@
   // Returns cloud id if the registered device or empty string if unregistered.
   virtual std::string GetCloudId() const = 0;
 
-  // Returns dictionary with device state.
-  virtual const base::DictionaryValue& GetState() const = 0;
+  // Returns dictionary with device state (for legacy APIs).
+  virtual const base::DictionaryValue& GetLegacyState() const = 0;
 
-  // Returns dictionary with commands definitions.
-  virtual const base::DictionaryValue& GetCommandDef() const = 0;
+  // Returns dictionary with commands definitions (for legacy APIs).
+  virtual const base::DictionaryValue& GetLegacyCommandDef() const = 0;
+
+  // Returns dictionary with component tree.
+  virtual const base::DictionaryValue& GetComponents() const = 0;
+
+  // Returns dictionary with trait definitions.
+  virtual const base::DictionaryValue& GetTraits() const = 0;
 
   // Adds command created from the given JSON representation.
   virtual void AddCommand(const base::DictionaryValue& command,
@@ -126,15 +132,15 @@
   }
 
   void NotifyOnDeviceInfoChanged();
-  void NotifyOnCommandDefsChanged();
+  void NotifyOnTraitDefsChanged();
   void NotifyOnStateChanged();
+  void NotifyOnComponentTreeChanged();
 
   // Create default instance.
   static std::unique_ptr<CloudDelegate> CreateDefault(
       provider::TaskRunner* task_runner,
       DeviceRegistrationInfo* device,
-      CommandManager* command_manager,
-      StateManager* state_manager);
+      ComponentManager* component_manager);
 
  private:
   base::ObserverList<Observer> observer_list_;
diff --git a/src/privet/mock_delegates.h b/src/privet/mock_delegates.h
index 2186f2f..0cfa4ed 100644
--- a/src/privet/mock_delegates.h
+++ b/src/privet/mock_delegates.h
@@ -153,8 +153,10 @@
   MOCK_CONST_METHOD0(GetSetupState, const SetupState&());
   MOCK_METHOD3(Setup, bool(const std::string&, const std::string&, ErrorPtr*));
   MOCK_CONST_METHOD0(GetCloudId, std::string());
-  MOCK_CONST_METHOD0(GetState, const base::DictionaryValue&());
-  MOCK_CONST_METHOD0(GetCommandDef, const base::DictionaryValue&());
+  MOCK_CONST_METHOD0(GetLegacyState, const base::DictionaryValue&());
+  MOCK_CONST_METHOD0(GetLegacyCommandDef, const base::DictionaryValue&());
+  MOCK_CONST_METHOD0(GetComponents, const base::DictionaryValue&());
+  MOCK_CONST_METHOD0(GetTraits, const base::DictionaryValue&());
   MOCK_METHOD3(AddCommand,
                void(const base::DictionaryValue&,
                     const UserInfo&,
@@ -185,8 +187,11 @@
     EXPECT_CALL(*this, GetSetupState()).WillRepeatedly(ReturnRef(setup_state_));
     EXPECT_CALL(*this, GetCloudId()).WillRepeatedly(Return("TestCloudId"));
     test_dict_.Set("test", new base::DictionaryValue);
-    EXPECT_CALL(*this, GetCommandDef()).WillRepeatedly(ReturnRef(test_dict_));
-    EXPECT_CALL(*this, GetState()).WillRepeatedly(ReturnRef(test_dict_));
+    EXPECT_CALL(*this, GetLegacyState()).WillRepeatedly(ReturnRef(test_dict_));
+    EXPECT_CALL(*this,
+                GetLegacyCommandDef()).WillRepeatedly(ReturnRef(test_dict_));
+    EXPECT_CALL(*this, GetTraits()).WillRepeatedly(ReturnRef(test_dict_));
+    EXPECT_CALL(*this, GetComponents()).WillRepeatedly(ReturnRef(test_dict_));
   }
 
   ConnectionState connection_state_{ConnectionState::kOnline};
diff --git a/src/privet/openssl_utils.cc b/src/privet/openssl_utils.cc
index 2a98fa8..f7bee9b 100644
--- a/src/privet/openssl_utils.cc
+++ b/src/privet/openssl_utils.cc
@@ -6,21 +6,25 @@
 
 #include <algorithm>
 
-#include <openssl/evp.h>
-#include <openssl/hmac.h>
-
 #include <base/logging.h>
 
+extern "C" {
+#include "third_party/libuweave/src/crypto_hmac.h"
+}
+
 namespace weave {
 namespace privet {
 
 std::vector<uint8_t> HmacSha256(const std::vector<uint8_t>& key,
                                 const std::vector<uint8_t>& data) {
   std::vector<uint8_t> mac(kSha256OutputSize);
-  uint32_t len = 0;
-  CHECK(HMAC(EVP_sha256(), key.data(), key.size(), data.data(), data.size(),
-             mac.data(), &len));
-  CHECK_EQ(len, kSha256OutputSize);
+  uint8_t hmac_state[uw_crypto_hmac_required_buffer_size_()];
+  CHECK_EQ(0u, uw_crypto_hmac_init_(hmac_state, sizeof(hmac_state), key.data(),
+                                    key.size()));
+  CHECK(uw_crypto_hmac_update_(hmac_state, sizeof(hmac_state), data.data(),
+                               data.size()));
+  CHECK(uw_crypto_hmac_final_(hmac_state, sizeof(hmac_state), mac.data(),
+                              mac.size()));
   return mac;
 }
 
diff --git a/src/privet/privet_handler.cc b/src/privet/privet_handler.cc
index 157c982..0c9887f 100644
--- a/src/privet/privet_handler.cc
+++ b/src/privet/privet_handler.cc
@@ -103,10 +103,14 @@
 const char kFingerprintKey[] = "fingerprint";
 const char kStateKey[] = "state";
 const char kCommandsKey[] = "commands";
+const char kTraitsKey[] = "traits";
+const char kComponentsKey[] = "components";
 const char kCommandsIdKey[] = "id";
 
 const char kStateFingerprintKey[] = "stateFingerprint";
 const char kCommandsFingerprintKey[] = "commandsFingerprint";
+const char kTraitsFingerprintKey[] = "traitsFingerprint";
+const char kComponentsFingerprintKey[] = "componentsFingerprint";
 const char kWaitTimeoutKey[] = "waitTimeout";
 
 const char kInvalidParamValueFormat[] = "Invalid parameter: '%s'='%s'";
@@ -371,6 +375,10 @@
                    &PrivetHandler::HandleCommandsList, AuthScope::kViewer);
   AddSecureHandler("/privet/v3/checkForUpdates",
                    &PrivetHandler::HandleCheckForUpdates, AuthScope::kViewer);
+  AddSecureHandler("/privet/v3/traits", &PrivetHandler::HandleTraits,
+                   AuthScope::kViewer);
+  AddSecureHandler("/privet/v3/components", &PrivetHandler::HandleComponents,
+                   AuthScope::kViewer);
 }
 
 PrivetHandler::~PrivetHandler() {
@@ -378,10 +386,10 @@
     ReplyToUpdateRequest(req.callback);
 }
 
-void PrivetHandler::OnCommandDefsChanged() {
-  ++command_defs_fingerprint_;
+void PrivetHandler::OnTraitDefsChanged() {
+  ++traits_fingerprint_;
   auto pred = [this](const UpdateRequestParameters& params) {
-    return params.command_defs_fingerprint < 0;
+    return params.traits_fingerprint == 0;
   };
   auto last =
       std::partition(update_requests_.begin(), update_requests_.end(), pred);
@@ -391,9 +399,23 @@
 }
 
 void PrivetHandler::OnStateChanged() {
+  // State updates also change the component tree, so update both fingerprints.
   ++state_fingerprint_;
+  ++components_fingerprint_;
   auto pred = [this](const UpdateRequestParameters& params) {
-    return params.state_fingerprint < 0;
+    return params.state_fingerprint == 0 && params.components_fingerprint == 0;
+  };
+  auto last =
+      std::partition(update_requests_.begin(), update_requests_.end(), pred);
+  for (auto p = last; p != update_requests_.end(); ++p)
+    ReplyToUpdateRequest(p->callback);
+  update_requests_.erase(last, update_requests_.end());
+}
+
+void PrivetHandler::OnComponentTreeChanged() {
+  ++components_fingerprint_;
+  auto pred = [this](const UpdateRequestParameters& params) {
+    return params.components_fingerprint == 0;
   };
   auto last =
       std::partition(update_requests_.begin(), update_requests_.end(), pred);
@@ -469,7 +491,7 @@
   params.handler = handler;
   params.scope = scope;
   params.https_only = false;
-  CHECK(handlers_.emplace(path, params).second);
+  CHECK(handlers_.insert(std::make_pair(path, params)).second);
 }
 
 void PrivetHandler::AddSecureHandler(const std::string& path,
@@ -479,7 +501,7 @@
   params.handler = handler;
   params.scope = scope;
   params.https_only = true;
-  CHECK(handlers_.emplace(path, params).second);
+  CHECK(handlers_.insert(std::make_pair(path, params)).second);
 }
 
 void PrivetHandler::HandleInfo(const base::DictionaryValue&,
@@ -762,20 +784,40 @@
                                 const UserInfo& user_info,
                                 const RequestCallback& callback) {
   base::DictionaryValue output;
-  base::DictionaryValue* defs = cloud_->GetState().DeepCopy();
-  output.Set(kStateKey, defs);
+  output.Set(kStateKey, cloud_->GetLegacyState().DeepCopy());
   output.SetString(kFingerprintKey, std::to_string(state_fingerprint_));
 
   callback.Run(http::kOk, output);
 }
 
+void PrivetHandler::HandleTraits(const base::DictionaryValue& input,
+                                 const UserInfo& user_info,
+                                 const RequestCallback& callback) {
+  base::DictionaryValue output;
+  output.Set(kTraitsKey, cloud_->GetTraits().DeepCopy());
+  output.SetString(kFingerprintKey, std::to_string(traits_fingerprint_));
+
+  callback.Run(http::kOk, output);
+}
+
+void PrivetHandler::HandleComponents(const base::DictionaryValue& input,
+                                     const UserInfo& user_info,
+                                     const RequestCallback& callback) {
+  base::DictionaryValue output;
+  output.Set(kComponentsKey, cloud_->GetComponents().DeepCopy());
+  output.SetString(kFingerprintKey, std::to_string(components_fingerprint_));
+
+  callback.Run(http::kOk, output);
+}
+
 void PrivetHandler::HandleCommandDefs(const base::DictionaryValue& input,
                                       const UserInfo& user_info,
                                       const RequestCallback& callback) {
   base::DictionaryValue output;
-  base::DictionaryValue* defs = cloud_->GetCommandDef().DeepCopy();
-  output.Set(kCommandsKey, defs);
-  output.SetString(kFingerprintKey, std::to_string(command_defs_fingerprint_));
+  output.Set(kCommandsKey, cloud_->GetLegacyCommandDef().DeepCopy());
+  // Use traits fingerprint since right now we treat traits and command defs
+  // as being equivalent.
+  output.SetString(kFingerprintKey, std::to_string(traits_fingerprint_));
 
   callback.Run(http::kOk, output);
 }
@@ -847,12 +889,18 @@
 
   std::string state_fingerprint;
   std::string commands_fingerprint;
+  std::string traits_fingerprint;
+  std::string components_fingerprint;
   input.GetString(kStateFingerprintKey, &state_fingerprint);
   input.GetString(kCommandsFingerprintKey, &commands_fingerprint);
+  input.GetString(kTraitsFingerprintKey, &traits_fingerprint);
+  input.GetString(kComponentsFingerprintKey, &components_fingerprint);
   const bool ignore_state = state_fingerprint.empty();
   const bool ignore_commands = commands_fingerprint.empty();
-  // If both fingerprints are missing, nothing to wait for, return immediately.
-  if (ignore_state && ignore_commands)
+  const bool ignore_traits = traits_fingerprint.empty();
+  const bool ignore_components = components_fingerprint.empty();
+  // If all fingerprints are missing, nothing to wait for, return immediately.
+  if (ignore_state && ignore_commands && ignore_traits && ignore_components)
     return ReplyToUpdateRequest(callback);
   // If the current state fingerprint is different from the requested one,
   // return new fingerprints.
@@ -860,17 +908,32 @@
     return ReplyToUpdateRequest(callback);
   // If the current commands fingerprint is different from the requested one,
   // return new fingerprints.
+  // NOTE: We are using traits fingerprint for command fingerprint as well.
   if (!ignore_commands &&
-      commands_fingerprint != std::to_string(command_defs_fingerprint_)) {
+      commands_fingerprint != std::to_string(traits_fingerprint_)) {
+    return ReplyToUpdateRequest(callback);
+  }
+  // If the current traits fingerprint is different from the requested one,
+  // return new fingerprints.
+  if (!ignore_traits &&
+      traits_fingerprint != std::to_string(traits_fingerprint_)) {
+    return ReplyToUpdateRequest(callback);
+  }
+  // If the current components fingerprint is different from the requested one,
+  // return new fingerprints.
+  if (!ignore_components &&
+      components_fingerprint != std::to_string(components_fingerprint_)) {
     return ReplyToUpdateRequest(callback);
   }
 
   UpdateRequestParameters params;
   params.request_id = ++last_update_request_id_;
   params.callback = callback;
-  params.command_defs_fingerprint =
-      ignore_commands ? -1 : command_defs_fingerprint_;
-  params.state_fingerprint = ignore_state ? -1 : state_fingerprint_;
+  params.traits_fingerprint =
+      (ignore_traits && ignore_commands) ? 0 : traits_fingerprint_;
+  params.state_fingerprint = ignore_state ? 0 : state_fingerprint_;
+  params.components_fingerprint =
+      ignore_components ? 0 : components_fingerprint_;
   update_requests_.push_back(params);
   if (timeout != base::TimeDelta::Max()) {
     device_->PostDelayedTask(
@@ -886,7 +949,11 @@
   base::DictionaryValue output;
   output.SetString(kStateFingerprintKey, std::to_string(state_fingerprint_));
   output.SetString(kCommandsFingerprintKey,
-                   std::to_string(command_defs_fingerprint_));
+                   std::to_string(traits_fingerprint_));
+  output.SetString(kTraitsFingerprintKey,
+                   std::to_string(traits_fingerprint_));
+  output.SetString(kComponentsFingerprintKey,
+                   std::to_string(components_fingerprint_));
   callback.Run(http::kOk, output);
 }
 
diff --git a/src/privet/privet_handler.h b/src/privet/privet_handler.h
index fb9cc94..fc024d1 100644
--- a/src/privet/privet_handler.h
+++ b/src/privet/privet_handler.h
@@ -45,8 +45,9 @@
                 WifiDelegate* wifi);
   ~PrivetHandler() override;
 
-  void OnCommandDefsChanged() override;
+  void OnTraitDefsChanged() override;
   void OnStateChanged() override;
+  void OnComponentTreeChanged() override;
 
   std::vector<std::string> GetHttpPaths() const;
   std::vector<std::string> GetHttpsPaths() const;
@@ -118,15 +119,21 @@
   void HandleCheckForUpdates(const base::DictionaryValue& input,
                              const UserInfo& user_info,
                              const RequestCallback& callback);
+  void HandleTraits(const base::DictionaryValue& input,
+                    const UserInfo& user_info,
+                    const RequestCallback& callback);
+  void HandleComponents(const base::DictionaryValue& input,
+                        const UserInfo& user_info,
+                        const RequestCallback& callback);
 
   void ReplyWithSetupStatus(const RequestCallback& callback) const;
   void ReplyToUpdateRequest(const RequestCallback& callback) const;
   void OnUpdateRequestTimeout(int update_request_id);
 
-  CloudDelegate* cloud_ = nullptr;
-  DeviceDelegate* device_ = nullptr;
-  SecurityDelegate* security_ = nullptr;
-  WifiDelegate* wifi_ = nullptr;
+  CloudDelegate* cloud_{nullptr};
+  DeviceDelegate* device_{nullptr};
+  SecurityDelegate* security_{nullptr};
+  WifiDelegate* wifi_{nullptr};
 
   struct HandlerParameters {
     ApiHandler handler;
@@ -137,16 +144,18 @@
 
   struct UpdateRequestParameters {
     RequestCallback callback;
-    int request_id = 0;
-    int state_fingerprint = -1;
-    int command_defs_fingerprint = -1;
+    int request_id{0};
+    uint64_t state_fingerprint{0};
+    uint64_t traits_fingerprint{0};
+    uint64_t components_fingerprint{0};
   };
   std::vector<UpdateRequestParameters> update_requests_;
   int last_update_request_id_{0};
 
   uint64_t last_user_id_{0};
-  int state_fingerprint_{0};
-  int command_defs_fingerprint_{0};
+  uint64_t state_fingerprint_{1};
+  uint64_t traits_fingerprint_{1};
+  uint64_t components_fingerprint_{1};
   ScopedObserver<CloudDelegate, CloudDelegate::Observer> cloud_observer_{this};
 
   base::WeakPtrFactory<PrivetHandler> weak_ptr_factory_{this};
diff --git a/src/privet/privet_handler_unittest.cc b/src/privet/privet_handler_unittest.cc
index c212e54..13c1999 100644
--- a/src/privet/privet_handler_unittest.cc
+++ b/src/privet/privet_handler_unittest.cc
@@ -15,6 +15,7 @@
 #include <base/values.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <weave/test/unittest_utils.h>
 
 #include "src/privet/constants.h"
 #include "src/privet/mock_delegates.h"
@@ -48,10 +49,6 @@
     dictionary->MergeDictionary(dictionary_ptr);
 }
 
-bool IsEqualValue(const base::Value& val1, const base::Value& val2) {
-  return val1.Equals(&val2);
-}
-
 struct CodeWithReason {
   CodeWithReason(int code_in, const std::string& reason_in)
       : code(code_in), reason(reason_in) {}
@@ -72,49 +69,19 @@
          reason == expected.reason;
 }
 
-bool IsEqualDictionary(const base::DictionaryValue& dictionary1,
-                       const base::DictionaryValue& dictionary2) {
-  base::DictionaryValue::Iterator it1(dictionary1);
-  base::DictionaryValue::Iterator it2(dictionary2);
-  for (; !it1.IsAtEnd() && !it2.IsAtEnd(); it1.Advance(), it2.Advance()) {
-    // Output mismatched keys.
-    EXPECT_EQ(it1.key(), it2.key());
-    if (it1.key() != it2.key())
-      return false;
-
-    if (it1.key() == "error") {
-      std::string code1;
-      std::string code2;
-      const char kCodeKey[] = "error.code";
-      if (!dictionary1.GetString(kCodeKey, &code1) ||
-          !dictionary2.GetString(kCodeKey, &code2) || code1 != code2) {
-        return false;
-      }
-      continue;
-    }
-
-    const base::DictionaryValue* d1{nullptr};
-    const base::DictionaryValue* d2{nullptr};
-    if (it1.value().GetAsDictionary(&d1) && it2.value().GetAsDictionary(&d2)) {
-      if (!IsEqualDictionary(*d1, *d2))
-        return false;
-      continue;
-    }
-
-    // Output mismatched values.
-    EXPECT_PRED2(IsEqualValue, it1.value(), it2.value());
-    if (!IsEqualValue(it1.value(), it2.value()))
-      return false;
-  }
-
-  return it1.IsAtEnd() && it2.IsAtEnd();
-}
-
-bool IsEqualJson(const std::string& test_json,
-                 const base::DictionaryValue& dictionary) {
-  base::DictionaryValue dictionary2;
-  LoadTestJson(test_json, &dictionary2);
-  return IsEqualDictionary(dictionary2, dictionary);
+// Some error sections in response JSON objects contained debugging information
+// which is of no interest for this test. So, remove the debug info from the JSON
+// before running validation logic on it.
+std::unique_ptr<base::DictionaryValue> StripDebugErrorDetails(
+    const std::string& path_to_error_object,
+    const base::DictionaryValue& value) {
+  std::unique_ptr<base::DictionaryValue> result{value.DeepCopy()};
+  base::DictionaryValue* error_dict = nullptr;
+  EXPECT_TRUE(result->GetDictionary(path_to_error_object, &error_dict));
+  scoped_ptr<base::Value> dummy;
+  error_dict->RemovePath("error.debugInfo", &dummy);
+  error_dict->RemovePath("error.message", &dummy);
+  return result;
 }
 
 }  // namespace
@@ -275,7 +242,7 @@
     },
     'uptime': 3600
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected, HandleRequest("/privet/info", "{}"));
+  EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/info", "{}"));
 }
 
 TEST_F(PrivetHandlerTest, Info) {
@@ -336,7 +303,7 @@
     },
     'uptime': 3600
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected, HandleRequest("/privet/info", "{}"));
+  EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/info", "{}"));
 }
 
 TEST_F(PrivetHandlerTest, PairingStartInvalidParams) {
@@ -350,16 +317,14 @@
 }
 
 TEST_F(PrivetHandlerTest, PairingStart) {
-  EXPECT_PRED2(
-      IsEqualJson,
+  EXPECT_JSON_EQ(
       "{'deviceCommitment': 'testCommitment', 'sessionId': 'testSession'}",
       HandleRequest("/privet/v3/pairing/start",
                     "{'pairing': 'embeddedCode', 'crypto': 'p224_spake2'}"));
 }
 
 TEST_F(PrivetHandlerTest, PairingConfirm) {
-  EXPECT_PRED2(
-      IsEqualJson,
+  EXPECT_JSON_EQ(
       "{'certFingerprint':'testFingerprint','certSignature':'testSignature'}",
       HandleRequest(
           "/privet/v3/pairing/confirm",
@@ -367,9 +332,9 @@
 }
 
 TEST_F(PrivetHandlerTest, PairingCancel) {
-  EXPECT_PRED2(IsEqualJson, "{}",
-               HandleRequest("/privet/v3/pairing/cancel",
-                             "{'sessionId': 'testSession'}"));
+  EXPECT_JSON_EQ("{}",
+                 HandleRequest("/privet/v3/pairing/cancel",
+                               "{'sessionId': 'testSession'}"));
 }
 
 TEST_F(PrivetHandlerTest, AuthErrorNoType) {
@@ -419,9 +384,9 @@
     'scope': 'user',
     'tokenType': 'Privet'
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/auth",
-                             "{'mode':'anonymous','requestedScope':'auto'}"));
+  EXPECT_JSON_EQ(kExpected,
+                 HandleRequest("/privet/v3/auth",
+                               "{'mode':'anonymous','requestedScope':'auto'}"));
 }
 
 TEST_F(PrivetHandlerTest, AuthPairing) {
@@ -440,11 +405,10 @@
     'scope': 'owner',
     'tokenType': 'Privet'
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/auth", kInput));
+  EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/auth", kInput));
 }
 
-class PrivetHandlerSetupTest : public PrivetHandlerTest {
+class PrivetHandlerTestWithAuth : public PrivetHandlerTest {
  public:
   void SetUp() override {
     PrivetHandlerTest::SetUp();
@@ -455,10 +419,11 @@
   }
 };
 
+class PrivetHandlerSetupTest : public PrivetHandlerTestWithAuth {};
+
 TEST_F(PrivetHandlerSetupTest, StatusEmpty) {
   SetNoWifiAndGcd();
-  EXPECT_PRED2(IsEqualJson, "{}",
-               HandleRequest("/privet/v3/setup/status", "{}"));
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/setup/status", "{}"));
 }
 
 TEST_F(PrivetHandlerSetupTest, StatusWifi) {
@@ -470,8 +435,7 @@
         'status': 'success'
      }
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/setup/status", "{}"));
+  EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/status", "{}"));
 }
 
 TEST_F(PrivetHandlerSetupTest, StatusWifiError) {
@@ -487,8 +451,10 @@
         }
      }
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/setup/status", "{}"));
+  EXPECT_JSON_EQ(kExpected,
+                 *StripDebugErrorDetails("wifi",
+                                         HandleRequest(
+                                            "/privet/v3/setup/status", "{}")));
 }
 
 TEST_F(PrivetHandlerSetupTest, StatusGcd) {
@@ -500,8 +466,7 @@
         'status': 'success'
      }
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/setup/status", "{}"));
+  EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/status", "{}"));
 }
 
 TEST_F(PrivetHandlerSetupTest, StatusGcdError) {
@@ -517,8 +482,10 @@
         }
      }
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/setup/status", "{}"));
+  EXPECT_JSON_EQ(kExpected,
+                 *StripDebugErrorDetails("gcd",
+                                         HandleRequest(
+                                            "/privet/v3/setup/status", "{}")));
 }
 
 TEST_F(PrivetHandlerSetupTest, SetupNameDescriptionLocation) {
@@ -530,8 +497,7 @@
     'description': 'testDescription',
     'location': 'testLocation'
   })";
-  EXPECT_PRED2(IsEqualJson, "{}",
-               HandleRequest("/privet/v3/setup/start", kInput));
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/setup/start", kInput));
 }
 
 TEST_F(PrivetHandlerSetupTest, InvalidParams) {
@@ -581,8 +547,7 @@
   wifi_.setup_state_ = SetupState{SetupState::kInProgress};
   EXPECT_CALL(wifi_, ConfigureCredentials("testSsid", "testPass", _))
       .WillOnce(Return(true));
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/setup/start", kInput));
+  EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/start", kInput));
 }
 
 TEST_F(PrivetHandlerSetupTest, GcdSetupUnavailable) {
@@ -622,28 +587,53 @@
   cloud_.setup_state_ = SetupState{SetupState::kInProgress};
   EXPECT_CALL(cloud_, Setup("testTicket", "testUser", _))
       .WillOnce(Return(true));
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/setup/start", kInput));
+  EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/setup/start", kInput));
 }
 
 TEST_F(PrivetHandlerSetupTest, State) {
-  EXPECT_PRED2(IsEqualJson, "{'state': {'test': {}}, 'fingerprint': '0'}",
-               HandleRequest("/privet/v3/state", "{}"));
+  EXPECT_JSON_EQ("{'state': {'test': {}}, 'fingerprint': '1'}",
+                 HandleRequest("/privet/v3/state", "{}"));
 
   cloud_.NotifyOnStateChanged();
 
-  EXPECT_PRED2(IsEqualJson, "{'state': {'test': {}}, 'fingerprint': '1'}",
-               HandleRequest("/privet/v3/state", "{}"));
+  EXPECT_JSON_EQ("{'state': {'test': {}}, 'fingerprint': '2'}",
+                 HandleRequest("/privet/v3/state", "{}"));
 }
 
 TEST_F(PrivetHandlerSetupTest, CommandsDefs) {
-  EXPECT_PRED2(IsEqualJson, "{'commands': {'test':{}}, 'fingerprint': '0'}",
-               HandleRequest("/privet/v3/commandDefs", "{}"));
+  EXPECT_JSON_EQ("{'commands': {'test':{}}, 'fingerprint': '1'}",
+                 HandleRequest("/privet/v3/commandDefs", "{}"));
 
-  cloud_.NotifyOnCommandDefsChanged();
+  cloud_.NotifyOnTraitDefsChanged();
 
-  EXPECT_PRED2(IsEqualJson, "{'commands': {'test':{}}, 'fingerprint': '1'}",
-               HandleRequest("/privet/v3/commandDefs", "{}"));
+  EXPECT_JSON_EQ("{'commands': {'test':{}}, 'fingerprint': '2'}",
+                 HandleRequest("/privet/v3/commandDefs", "{}"));
+}
+
+TEST_F(PrivetHandlerSetupTest, Traits) {
+  EXPECT_JSON_EQ("{'traits': {'test': {}}, 'fingerprint': '1'}",
+                 HandleRequest("/privet/v3/traits", "{}"));
+
+  cloud_.NotifyOnTraitDefsChanged();
+
+  EXPECT_JSON_EQ("{'traits': {'test': {}}, 'fingerprint': '2'}",
+                 HandleRequest("/privet/v3/traits", "{}"));
+}
+
+TEST_F(PrivetHandlerSetupTest, Components) {
+  EXPECT_JSON_EQ("{'components': {'test': {}}, 'fingerprint': '1'}",
+                 HandleRequest("/privet/v3/components", "{}"));
+
+  cloud_.NotifyOnComponentTreeChanged();
+
+  EXPECT_JSON_EQ("{'components': {'test': {}}, 'fingerprint': '2'}",
+                 HandleRequest("/privet/v3/components", "{}"));
+
+  // State change will also change the components fingerprint.
+  cloud_.NotifyOnStateChanged();
+
+  EXPECT_JSON_EQ("{'components': {'test': {}}, 'fingerprint': '3'}",
+                 HandleRequest("/privet/v3/components", "{}"));
 }
 
 TEST_F(PrivetHandlerSetupTest, CommandsExecute) {
@@ -657,8 +647,8 @@
             callback.Run(command, nullptr);
           })));
 
-  EXPECT_PRED2(IsEqualJson, "{'name':'test', 'id':'5'}",
-               HandleRequest("/privet/v3/commands/execute", kInput));
+  EXPECT_JSON_EQ("{'name':'test', 'id':'5'}",
+                 HandleRequest("/privet/v3/commands/execute", kInput));
 }
 
 TEST_F(PrivetHandlerSetupTest, CommandsStatus) {
@@ -672,8 +662,8 @@
             callback.Run(command, nullptr);
           })));
 
-  EXPECT_PRED2(IsEqualJson, "{'name':'test', 'id':'5'}",
-               HandleRequest("/privet/v3/commands/status", kInput));
+  EXPECT_JSON_EQ("{'name':'test', 'id':'5'}",
+                 HandleRequest("/privet/v3/commands/status", kInput));
 
   ErrorPtr error;
   Error::AddTo(&error, FROM_HERE, errors::kDomain, "notFound", "");
@@ -697,8 +687,8 @@
             callback.Run(command, nullptr);
           })));
 
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/commands/cancel", "{'id': '8'}"));
+  EXPECT_JSON_EQ(kExpected,
+                 HandleRequest("/privet/v3/commands/cancel", "{'id': '8'}"));
 
   ErrorPtr error;
   Error::AddTo(&error, FROM_HERE, errors::kDomain, "notFound", "");
@@ -728,242 +718,323 @@
             callback.Run(commands, nullptr);
           })));
 
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/commands/list", "{}"));
+  EXPECT_JSON_EQ(kExpected, HandleRequest("/privet/v3/commands/list", "{}"));
 }
 
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_NoInput) {
+class PrivetHandlerCheckForUpdatesTest : public PrivetHandlerTestWithAuth {};
+
+TEST_F(PrivetHandlerCheckForUpdatesTest, NoInput) {
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
-  cloud_.NotifyOnCommandDefsChanged();
+  cloud_.NotifyOnTraitDefsChanged();
+  cloud_.NotifyOnComponentTreeChanged();
   cloud_.NotifyOnStateChanged();
   const char kInput[] = "{}";
   const char kExpected[] = R"({
-   'commandsFingerprint': '1',
-   'stateFingerprint': '1'
+   'commandsFingerprint': '2',
+   'stateFingerprint': '2',
+   'traitsFingerprint': '2',
+   'componentsFingerprint': '3'
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
+  EXPECT_JSON_EQ(kExpected,
+                 HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(1, GetResponseCount());
 }
 
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_AlreadyChanged) {
+TEST_F(PrivetHandlerCheckForUpdatesTest, AlreadyChanged) {
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
-  cloud_.NotifyOnCommandDefsChanged();
+  cloud_.NotifyOnTraitDefsChanged();
+  cloud_.NotifyOnComponentTreeChanged();
   cloud_.NotifyOnStateChanged();
   const char kInput[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0'
-  })";
-  const char kExpected[] = R"({
    'commandsFingerprint': '1',
-   'stateFingerprint': '1'
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1'
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
+  const char kExpected[] = R"({
+   'commandsFingerprint': '2',
+   'stateFingerprint': '2',
+   'traitsFingerprint': '2',
+   'componentsFingerprint': '3'
+  })";
+  EXPECT_JSON_EQ(kExpected,
+                 HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(1, GetResponseCount());
 }
 
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_LongPollCommands) {
+TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollCommands) {
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0'
-  })";
-  EXPECT_PRED2(IsEqualJson, "{}",
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
-  EXPECT_EQ(0, GetResponseCount());
-  cloud_.NotifyOnCommandDefsChanged();
-  EXPECT_EQ(1, GetResponseCount());
-  const char kExpected[] = R"({
    'commandsFingerprint': '1',
-   'stateFingerprint': '0'
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1'
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected, GetResponse());
-}
-
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_LongPollState) {
-  EXPECT_CALL(device_, GetHttpRequestTimeout())
-      .WillOnce(Return(base::TimeDelta::Max()));
-  const char kInput[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0'
-  })";
-  EXPECT_PRED2(IsEqualJson, "{}",
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
-  cloud_.NotifyOnStateChanged();
+  cloud_.NotifyOnTraitDefsChanged();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '1'
+   'commandsFingerprint': '2',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '2',
+   'componentsFingerprint': '1'
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected, GetResponse());
+  EXPECT_JSON_EQ(kExpected, GetResponse());
 }
 
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_LongPollIgnoreCommands) {
+TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollTraits) {
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'stateFingerprint': '0'
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1'
   })";
-  EXPECT_PRED2(IsEqualJson, "{}",
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
-  cloud_.NotifyOnCommandDefsChanged();
+  cloud_.NotifyOnTraitDefsChanged();
+  EXPECT_EQ(1, GetResponseCount());
+  const char kExpected[] = R"({
+   'commandsFingerprint': '2',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '2',
+   'componentsFingerprint': '1'
+  })";
+  EXPECT_JSON_EQ(kExpected, GetResponse());
+}
+
+TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollState) {
+  EXPECT_CALL(device_, GetHttpRequestTimeout())
+      .WillOnce(Return(base::TimeDelta::Max()));
+  const char kInput[] = R"({
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1'
+  })";
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
   cloud_.NotifyOnStateChanged();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
    'commandsFingerprint': '1',
-   'stateFingerprint': '1'
+   'stateFingerprint': '2',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '2'
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected, GetResponse());
+  EXPECT_JSON_EQ(kExpected, GetResponse());
 }
 
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_LongPollIgnoreState) {
+TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollComponents) {
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '0'
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1'
   })";
-  EXPECT_PRED2(IsEqualJson, "{}",
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
-  cloud_.NotifyOnStateChanged();
-  EXPECT_EQ(0, GetResponseCount());
-  cloud_.NotifyOnCommandDefsChanged();
+  cloud_.NotifyOnComponentTreeChanged();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
    'commandsFingerprint': '1',
-   'stateFingerprint': '1'
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '2'
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected, GetResponse());
+  EXPECT_JSON_EQ(kExpected, GetResponse());
 }
 
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_InstantTimeout) {
+TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollIgnoreTraits) {
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0',
+   'stateFingerprint': '1',
+   'componentsFingerprint': '1'
+  })";
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
+  EXPECT_EQ(0, GetResponseCount());
+  cloud_.NotifyOnTraitDefsChanged();
+  EXPECT_EQ(0, GetResponseCount());
+  cloud_.NotifyOnComponentTreeChanged();
+  EXPECT_EQ(1, GetResponseCount());
+  const char kExpected[] = R"({
+   'commandsFingerprint': '2',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '2',
+   'componentsFingerprint': '2'
+  })";
+  EXPECT_JSON_EQ(kExpected, GetResponse());
+}
+
+TEST_F(PrivetHandlerCheckForUpdatesTest, LongPollIgnoreState) {
+  EXPECT_CALL(device_, GetHttpRequestTimeout())
+      .WillOnce(Return(base::TimeDelta::Max()));
+  const char kInput[] = R"({
+   'commandsFingerprint': '1',
+   'traitsFingerprint': '1'
+  })";
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
+  EXPECT_EQ(0, GetResponseCount());
+  cloud_.NotifyOnStateChanged();
+  EXPECT_EQ(0, GetResponseCount());
+  cloud_.NotifyOnComponentTreeChanged();
+  EXPECT_EQ(0, GetResponseCount());
+  cloud_.NotifyOnTraitDefsChanged();
+  EXPECT_EQ(1, GetResponseCount());
+  const char kExpected[] = R"({
+   'commandsFingerprint': '2',
+   'stateFingerprint': '2',
+   'traitsFingerprint': '2',
+   'componentsFingerprint': '3'
+  })";
+  EXPECT_JSON_EQ(kExpected, GetResponse());
+}
+
+TEST_F(PrivetHandlerCheckForUpdatesTest, InstantTimeout) {
+  EXPECT_CALL(device_, GetHttpRequestTimeout())
+      .WillOnce(Return(base::TimeDelta::Max()));
+  const char kInput[] = R"({
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1',
    'waitTimeout': 0
   })";
   const char kExpected[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0'
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1'
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected,
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
+  EXPECT_JSON_EQ(kExpected,
+                 HandleRequest("/privet/v3/checkForUpdates", kInput));
 }
 
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_UserTimeout) {
+TEST_F(PrivetHandlerCheckForUpdatesTest, UserTimeout) {
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::Max()));
   const char kInput[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0',
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1',
    'waitTimeout': 3
   })";
   base::Closure callback;
   EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(3)))
       .WillOnce(SaveArg<1>(&callback));
-  EXPECT_PRED2(IsEqualJson, "{}",
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
   callback.Run();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0'
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1'
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected, GetResponse());
+  EXPECT_JSON_EQ(kExpected, GetResponse());
 }
 
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_ServerTimeout) {
+TEST_F(PrivetHandlerCheckForUpdatesTest, ServerTimeout) {
   EXPECT_CALL(device_, GetHttpRequestTimeout())
       .WillOnce(Return(base::TimeDelta::FromMinutes(1)));
   const char kInput[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0'
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1'
   })";
   base::Closure callback;
   EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(50)))
       .WillOnce(SaveArg<1>(&callback));
-  EXPECT_PRED2(IsEqualJson, "{}",
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(0, GetResponseCount());
   callback.Run();
   EXPECT_EQ(1, GetResponseCount());
   const char kExpected[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0'
-  })";
-  EXPECT_PRED2(IsEqualJson, kExpected, GetResponse());
-}
-
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_VeryShortServerTimeout) {
-  EXPECT_CALL(device_, GetHttpRequestTimeout())
-      .WillOnce(Return(base::TimeDelta::FromSeconds(5)));
-  const char kInput[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0'
-  })";
-  EXPECT_PRED2(IsEqualJson, kInput,
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
-  EXPECT_EQ(1, GetResponseCount());
-}
-
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_ServerAndUserTimeout) {
-  EXPECT_CALL(device_, GetHttpRequestTimeout())
-      .WillOnce(Return(base::TimeDelta::FromMinutes(1)));
-  const char kInput[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0',
-   'waitTimeout': 10
-  })";
-  base::Closure callback;
-  EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(10)))
-      .WillOnce(SaveArg<1>(&callback));
-  EXPECT_PRED2(IsEqualJson, "{}",
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
-  EXPECT_EQ(0, GetResponseCount());
-  callback.Run();
-  EXPECT_EQ(1, GetResponseCount());
-  const char kExpected[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0'
-  })";
-  EXPECT_PRED2(IsEqualJson, kExpected, GetResponse());
-}
-
-TEST_F(PrivetHandlerSetupTest, CheckForUpdates_ChangeBeforeTimeout) {
-  EXPECT_CALL(device_, GetHttpRequestTimeout())
-      .WillOnce(Return(base::TimeDelta::Max()));
-  const char kInput[] = R"({
-   'commandsFingerprint': '0',
-   'stateFingerprint': '0',
-   'waitTimeout': 10
-  })";
-  base::Closure callback;
-  EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(10)))
-      .WillOnce(SaveArg<1>(&callback));
-  EXPECT_PRED2(IsEqualJson, "{}",
-               HandleRequest("/privet/v3/checkForUpdates", kInput));
-  EXPECT_EQ(0, GetResponseCount());
-  cloud_.NotifyOnCommandDefsChanged();
-  EXPECT_EQ(1, GetResponseCount());
-  const char kExpected[] = R"({
    'commandsFingerprint': '1',
-   'stateFingerprint': '0'
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1'
   })";
-  EXPECT_PRED2(IsEqualJson, kExpected, GetResponse());
-  callback.Run();
+  EXPECT_JSON_EQ(kExpected, GetResponse());
+}
+
+TEST_F(PrivetHandlerCheckForUpdatesTest, VeryShortServerTimeout) {
+  EXPECT_CALL(device_, GetHttpRequestTimeout())
+      .WillOnce(Return(base::TimeDelta::FromSeconds(5)));
+  const char kInput[] = R"({
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1'
+  })";
+  EXPECT_JSON_EQ(kInput, HandleRequest("/privet/v3/checkForUpdates", kInput));
   EXPECT_EQ(1, GetResponseCount());
 }
 
+TEST_F(PrivetHandlerCheckForUpdatesTest, ServerAndUserTimeout) {
+  EXPECT_CALL(device_, GetHttpRequestTimeout())
+      .WillOnce(Return(base::TimeDelta::FromMinutes(1)));
+  const char kInput[] = R"({
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1',
+   'waitTimeout': 10
+  })";
+  base::Closure callback;
+  EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(10)))
+      .WillOnce(SaveArg<1>(&callback));
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
+  EXPECT_EQ(0, GetResponseCount());
+  callback.Run();
+  EXPECT_EQ(1, GetResponseCount());
+  const char kExpected[] = R"({
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1'
+  })";
+  EXPECT_JSON_EQ(kExpected, GetResponse());
+}
+
+TEST_F(PrivetHandlerCheckForUpdatesTest, ChangeBeforeTimeout) {
+  EXPECT_CALL(device_, GetHttpRequestTimeout())
+      .WillOnce(Return(base::TimeDelta::Max()));
+  const char kInput[] = R"({
+   'commandsFingerprint': '1',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '1',
+   'componentsFingerprint': '1',
+   'waitTimeout': 10
+  })";
+  base::Closure callback;
+  EXPECT_CALL(device_, PostDelayedTask(_, _, base::TimeDelta::FromSeconds(10)))
+      .WillOnce(SaveArg<1>(&callback));
+  EXPECT_JSON_EQ("{}", HandleRequest("/privet/v3/checkForUpdates", kInput));
+  EXPECT_EQ(0, GetResponseCount());
+  cloud_.NotifyOnTraitDefsChanged();
+  EXPECT_EQ(1, GetResponseCount());
+  const char kExpected[] = R"({
+   'commandsFingerprint': '2',
+   'stateFingerprint': '1',
+   'traitsFingerprint': '2',
+   'componentsFingerprint': '1'
+  })";
+  EXPECT_JSON_EQ(kExpected, GetResponse());
+  callback.Run();
+  EXPECT_EQ(1, GetResponseCount());
+}
 
 }  // namespace privet
 }  // namespace weave
diff --git a/src/privet/privet_manager.cc b/src/privet/privet_manager.cc
index edba589..a308eec 100644
--- a/src/privet/privet_manager.cc
+++ b/src/privet/privet_manager.cc
@@ -18,8 +18,10 @@
 #include <weave/provider/network.h>
 
 #include "src/bind_lambda.h"
+#include "src/component_manager.h"
 #include "src/device_registration_info.h"
 #include "src/http_constants.h"
+#include "src/privet/auth_manager.h"
 #include "src/privet/cloud_delegate.h"
 #include "src/privet/constants.h"
 #include "src/privet/device_delegate.h"
@@ -46,21 +48,21 @@
                     HttpServer* http_server,
                     Wifi* wifi,
                     DeviceRegistrationInfo* device,
-                    CommandManager* command_manager,
-                    StateManager* state_manager) {
+                    ComponentManager* component_manager) {
   disable_security_ = device->GetSettings().disable_security;
 
   device_ = DeviceDelegate::CreateDefault(
       task_runner_, http_server->GetHttpPort(), http_server->GetHttpsPort(),
       http_server->GetRequestTimeout());
-  cloud_ = CloudDelegate::CreateDefault(task_runner_, device, command_manager,
-                                        state_manager);
+  cloud_ = CloudDelegate::CreateDefault(task_runner_, device,
+                                        component_manager);
   cloud_observer_.Add(cloud_.get());
+
+  auth_.reset(new AuthManager(device->GetSettings().secret,
+                              http_server->GetHttpsCertificateFingerprint()));
   security_.reset(new SecurityManager(
-      device->GetSettings().secret, device->GetSettings().pairing_modes,
+      auth_.get(), device->GetSettings().pairing_modes,
       device->GetSettings().embedded_code, disable_security_, task_runner_));
-  security_->SetCertificateFingerprint(
-      http_server->GetHttpsCertificateFingerprint());
   if (device->GetSettings().secret.empty()) {
     // TODO(vitalybuka): Post all Config::Transaction to avoid following.
     task_runner_->PostDelayedTask(
@@ -174,7 +176,7 @@
 
 void Manager::SaveDeviceSecret(Config* config) {
   Config::Transaction transaction(config);
-  transaction.set_secret(security_->GetSecret());
+  transaction.set_secret(auth_->GetSecret());
 }
 
 }  // namespace privet
diff --git a/src/privet/privet_manager.h b/src/privet/privet_manager.h
index 3174ca0..1342584 100644
--- a/src/privet/privet_manager.h
+++ b/src/privet/privet_manager.h
@@ -27,11 +27,10 @@
 
 namespace weave {
 
-class CommandManager;
+class ComponentManager;
 class DeviceRegistrationInfo;
 class DnsServiceDiscovery;
 class Network;
-class StateManager;
 
 namespace privet {
 
@@ -52,8 +51,7 @@
              provider::HttpServer* http_server,
              provider::Wifi* wifi,
              DeviceRegistrationInfo* device,
-             CommandManager* command_manager,
-             StateManager* state_manager);
+             ComponentManager* component_manager);
 
   std::string GetCurrentlyConnectedSsid() const;
 
@@ -86,6 +84,7 @@
   provider::TaskRunner* task_runner_{nullptr};
   std::unique_ptr<CloudDelegate> cloud_;
   std::unique_ptr<DeviceDelegate> device_;
+  std::unique_ptr<AuthManager> auth_;
   std::unique_ptr<SecurityManager> security_;
   std::unique_ptr<WifiBootstrapManager> wifi_bootstrap_manager_;
   std::unique_ptr<Publisher> publisher_;
diff --git a/src/privet/security_delegate.h b/src/privet/security_delegate.h
index 1d28ba3..40f297f 100644
--- a/src/privet/security_delegate.h
+++ b/src/privet/security_delegate.h
@@ -19,7 +19,7 @@
 // Interface to provide Security related logic for |PrivetHandler|.
 class SecurityDelegate {
  public:
-  virtual ~SecurityDelegate() = default;
+  virtual ~SecurityDelegate() {}
 
   // Creates access token for the given scope, user id and |time|.
   virtual std::string CreateAccessToken(const UserInfo& user_info,
diff --git a/src/privet/security_manager.cc b/src/privet/security_manager.cc
index 1b4e3c5..a838dae 100644
--- a/src/privet/security_manager.cc
+++ b/src/privet/security_manager.cc
@@ -19,15 +19,12 @@
 #include <weave/provider/task_runner.h>
 
 #include "src/data_encoding.h"
+#include "src/privet/auth_manager.h"
 #include "src/privet/constants.h"
 #include "src/privet/openssl_utils.h"
 #include "src/string_utils.h"
 #include "third_party/chromium/crypto/p224_spake.h"
 
-#if !defined(ARCH_CPU_LITTLE_ENDIAN)
-#error "Big-endian is not supported. See b/25017606"
-#endif
-
 namespace weave {
 namespace privet {
 
@@ -122,19 +119,17 @@
 
 }  // namespace
 
-SecurityManager::SecurityManager(const std::string& secret,
+SecurityManager::SecurityManager(AuthManager* auth_manager,
                                  const std::set<PairingType>& pairing_modes,
                                  const std::string& embedded_code,
                                  bool disable_security,
                                  provider::TaskRunner* task_runner)
-    : is_security_disabled_(disable_security),
+    : auth_manager_{auth_manager},
+      is_security_disabled_(disable_security),
       pairing_modes_(pairing_modes),
       embedded_code_(embedded_code),
       task_runner_{task_runner} {
-  if (!Base64Decode(secret, &secret_) || secret_.size() != kSha256OutputSize) {
-    secret_.resize(kSha256OutputSize);
-    base::RandBytes(secret_.data(), secret_.size());
-  }
+  CHECK(auth_manager_);
   CHECK_EQ(embedded_code_.empty(),
            std::find(pairing_modes_.begin(), pairing_modes_.end(),
                      PairingType::kEmbeddedCode) == pairing_modes_.end());
@@ -148,25 +143,17 @@
 // Returns "base64([hmac]scope:id:time)".
 std::string SecurityManager::CreateAccessToken(const UserInfo& user_info,
                                                const base::Time& time) {
-  std::string data_str{CreateTokenData(user_info, time)};
-  std::vector<uint8_t> data{data_str.begin(), data_str.end()};
-  std::vector<uint8_t> hash{HmacSha256(secret_, data)};
-  hash.insert(hash.end(), data.begin(), data.end());
-  return Base64Encode(hash);
+  return Base64Encode(auth_manager_->CreateAccessToken(user_info, time));
 }
 
 // Parses "base64([hmac]scope:id:time)".
 UserInfo SecurityManager::ParseAccessToken(const std::string& token,
                                            base::Time* time) const {
   std::vector<uint8_t> decoded;
-  if (!Base64Decode(token, &decoded) || decoded.size() <= kSha256OutputSize) {
+  if (!Base64Decode(token, &decoded))
     return UserInfo{};
-  }
-  std::vector<uint8_t> data(decoded.begin() + kSha256OutputSize, decoded.end());
-  decoded.resize(kSha256OutputSize);
-  if (decoded != HmacSha256(secret_, data))
-    return UserInfo{};
-  return SplitTokenData(std::string(data.begin(), data.end()), time);
+
+  return auth_manager_->ParseAccessToken(decoded, time);
 }
 
 std::set<PairingType> SecurityManager::GetPairingTypes() const {
@@ -258,7 +245,7 @@
   } while (confirmed_sessions_.find(session) != confirmed_sessions_.end() ||
            pending_sessions_.find(session) != pending_sessions_.end());
   std::string commitment = spake->GetMessage();
-  pending_sessions_.emplace(session, std::move(spake));
+  pending_sessions_.insert(std::make_pair(session, std::move(spake)));
 
   task_runner_->PostDelayedTask(
       FROM_HERE,
@@ -291,7 +278,6 @@
                        session_id.c_str());
     return false;
   }
-  CHECK(!certificate_fingerprint_.empty());
 
   std::vector<uint8_t> commitment;
   if (!Base64Decode(client_commitment, &commitment)) {
@@ -313,11 +299,14 @@
   const std::string& key = session->second->GetKey();
   VLOG(3) << "KEY " << base::HexEncode(key.data(), key.size());
 
-  *fingerprint = Base64Encode(certificate_fingerprint_);
+  const auto& certificate_fingerprint =
+      auth_manager_->GetCertificateFingerprint();
+  *fingerprint = Base64Encode(certificate_fingerprint);
   std::vector<uint8_t> cert_hmac = HmacSha256(
-      std::vector<uint8_t>(key.begin(), key.end()), certificate_fingerprint_);
+      std::vector<uint8_t>(key.begin(), key.end()), certificate_fingerprint);
   *signature = Base64Encode(cert_hmac);
-  confirmed_sessions_.emplace(session->first, std::move(session->second));
+  confirmed_sessions_.insert(
+      std::make_pair(session->first, std::move(session->second)));
   task_runner_->PostDelayedTask(
       FROM_HERE,
       base::Bind(base::IgnoreResult(&SecurityManager::CloseConfirmedSession),
@@ -351,10 +340,6 @@
   on_end_ = on_end;
 }
 
-std::string SecurityManager::GetSecret() const {
-  return Base64Encode(secret_);
-}
-
 bool SecurityManager::CheckIfPairingAllowed(ErrorPtr* error) {
   if (is_security_disabled_)
     return true;
diff --git a/src/privet/security_manager.h b/src/privet/security_manager.h
index c99201b..26a42d4 100644
--- a/src/privet/security_manager.h
+++ b/src/privet/security_manager.h
@@ -30,6 +30,8 @@
 
 namespace privet {
 
+class AuthManager;
+
 class SecurityManager : public SecurityDelegate {
  public:
   using PairingStartListener =
@@ -41,7 +43,7 @@
 
   class KeyExchanger {
    public:
-    virtual ~KeyExchanger() = default;
+    virtual ~KeyExchanger() {}
 
     virtual const std::string& GetMessage() = 0;
     virtual bool ProcessMessage(const std::string& message,
@@ -49,7 +51,7 @@
     virtual const std::string& GetKey() const = 0;
   };
 
-  SecurityManager(const std::string& secret,
+  SecurityManager(AuthManager* auth_manager,
                   const std::set<PairingType>& pairing_modes,
                   const std::string& embedded_code,
                   bool disable_security,
@@ -82,12 +84,6 @@
   void RegisterPairingListeners(const PairingStartListener& on_start,
                                 const PairingEndListener& on_end);
 
-  void SetCertificateFingerprint(const std::vector<uint8_t>& fingerprint) {
-    certificate_fingerprint_ = fingerprint;
-  }
-
-  std::string GetSecret() const;
-
  private:
   FRIEND_TEST_ALL_PREFIXES(SecurityManagerTest, ThrottlePairing);
   // Allows limited number of new sessions without successful authorization.
@@ -95,6 +91,8 @@
   bool ClosePendingSession(const std::string& session_id);
   bool CloseConfirmedSession(const std::string& session_id);
 
+  AuthManager* auth_manager_{nullptr};
+
   // If true allows unencrypted pairing and accepts any access code.
   bool is_security_disabled_{false};
   std::set<PairingType> pairing_modes_;
@@ -105,8 +103,6 @@
   std::map<std::string, std::unique_ptr<KeyExchanger>> confirmed_sessions_;
   mutable int pairing_attemts_{0};
   mutable base::Time block_pairing_until_;
-  std::vector<uint8_t> secret_;
-  std::vector<uint8_t> certificate_fingerprint_;
   PairingStartListener on_start_;
   PairingEndListener on_end_;
 
diff --git a/src/privet/security_manager_unittest.cc b/src/privet/security_manager_unittest.cc
index 7229710..6236d78 100644
--- a/src/privet/security_manager_unittest.cc
+++ b/src/privet/security_manager_unittest.cc
@@ -22,6 +22,7 @@
 #include <weave/provider/test/fake_task_runner.h>
 
 #include "src/data_encoding.h"
+#include "src/privet/auth_manager.h"
 #include "src/privet/openssl_utils.h"
 #include "third_party/chromium/crypto/p224_spake.h"
 
@@ -55,14 +56,6 @@
 }  // namespace
 
 class SecurityManagerTest : public testing::Test {
- public:
-  void SetUp() override {
-    std::vector<uint8_t> fingerprint;
-    fingerprint.resize(32);
-    base::RandBytes(fingerprint.data(), fingerprint.size());
-    security_.SetCertificateFingerprint(fingerprint);
-  }
-
  protected:
   void PairAndAuthenticate(std::string* fingerprint, std::string* signature) {
     std::string session_id;
@@ -101,29 +94,20 @@
 
   const base::Time time_ = base::Time::FromTimeT(1410000000);
   provider::test::FakeTaskRunner task_runner_;
-  SecurityManager security_{{},
+  AuthManager auth_manager_{
+      {},
+      {{
+          59, 47, 77, 247, 129, 187, 188, 158, 172, 105, 246, 93, 102, 83, 8,
+          138, 176, 141, 37, 63, 223, 40, 153, 121, 134, 23, 120, 106, 24, 205,
+          7, 135,
+      }}};
+  SecurityManager security_{&auth_manager_,
                             {PairingType::kEmbeddedCode},
                             "1234",
                             false,
                             &task_runner_};
 };
 
-TEST_F(SecurityManagerTest, RandomSecret) {
-  EXPECT_GE(security_.GetSecret().size(), 32);
-  EXPECT_TRUE(IsBase64(security_.GetSecret()));
-}
-
-TEST_F(SecurityManagerTest, DifferentSecret) {
-  SecurityManager security{{}, {}, "", false, &task_runner_};
-  EXPECT_NE(security_.GetSecret(), security.GetSecret());
-}
-
-TEST_F(SecurityManagerTest, ExternalSecret) {
-  const std::string kSecret = "T1SDv9CVGNO82zHKeRrUSzpAzjb1hmRyzXGotsn1gcU=";
-  SecurityManager security{kSecret, {}, "", false, &task_runner_};
-  EXPECT_EQ(kSecret, security.GetSecret());
-}
-
 TEST_F(SecurityManagerTest, IsBase64) {
   EXPECT_TRUE(IsBase64(
       security_.CreateAccessToken(UserInfo{AuthScope::kUser, 7}, time_)));
@@ -155,15 +139,17 @@
 }
 
 TEST_F(SecurityManagerTest, CreateTokenDifferentInstance) {
+  AuthManager auth{{}, {}};
   EXPECT_NE(security_.CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_),
-            SecurityManager({}, {}, "", false, &task_runner_)
+            SecurityManager(&auth, {}, "", false, &task_runner_)
                 .CreateAccessToken(UserInfo{AuthScope::kUser, 123}, time_));
 }
 
 TEST_F(SecurityManagerTest, ParseAccessToken) {
   // Multiple attempts with random secrets.
   for (size_t i = 0; i < 1000; ++i) {
-    SecurityManager security{{}, {}, "", false, &task_runner_};
+    AuthManager auth{{}, {}};
+    SecurityManager security{&auth, {}, "", false, &task_runner_};
 
     std::string token =
         security.CreateAccessToken(UserInfo{AuthScope::kUser, 5}, time_);
diff --git a/src/privet/wifi_delegate.h b/src/privet/wifi_delegate.h
index ae5e520..9bd5157 100644
--- a/src/privet/wifi_delegate.h
+++ b/src/privet/wifi_delegate.h
@@ -18,7 +18,7 @@
 class WifiDelegate {
  public:
   WifiDelegate() = default;
-  virtual ~WifiDelegate() = default;
+  virtual ~WifiDelegate() {}
 
   // Returns status of the WiFi connection.
   virtual const ConnectionState& GetConnectionState() const = 0;
diff --git a/src/privet/wifi_ssid_generator_unittest.cc b/src/privet/wifi_ssid_generator_unittest.cc
index 398e105..10680c8 100644
--- a/src/privet/wifi_ssid_generator_unittest.cc
+++ b/src/privet/wifi_ssid_generator_unittest.cc
@@ -23,7 +23,7 @@
 };
 
 TEST_F(WifiSsidGeneratorTest, GenerateFlagsNoHostedAp) {
-  EXPECT_EQ(ssid_generator_.GenerateFlags().size(), 2);
+  EXPECT_EQ(ssid_generator_.GenerateFlags().size(), 2u);
 
   wifi_.connection_state_ = ConnectionState{ConnectionState::kUnconfigured};
   gcd_.connection_state_ = ConnectionState{ConnectionState::kUnconfigured};
@@ -42,7 +42,7 @@
 TEST_F(WifiSsidGeneratorTest, GenerateFlagsWithHostedAp) {
   EXPECT_CALL(wifi_, GetHostedSsid())
       .WillRepeatedly(Return(ssid_generator_.GenerateSsid()));
-  EXPECT_EQ(ssid_generator_.GenerateFlags().size(), 2);
+  EXPECT_EQ(ssid_generator_.GenerateFlags().size(), 2u);
 
   wifi_.connection_state_ = ConnectionState{ConnectionState::kUnconfigured};
   gcd_.connection_state_ = ConnectionState{ConnectionState::kUnconfigured};
@@ -59,7 +59,7 @@
 }
 
 TEST_F(WifiSsidGeneratorTest, GenerateSsid31orLess) {
-  EXPECT_LE(ssid_generator_.GenerateSsid().size(), 31);
+  EXPECT_LE(ssid_generator_.GenerateSsid().size(), 31u);
 }
 
 TEST_F(WifiSsidGeneratorTest, GenerateSsidValue) {
diff --git a/src/states/error_codes.cc b/src/states/error_codes.cc
deleted file mode 100644
index 4b12a45..0000000
--- a/src/states/error_codes.cc
+++ /dev/null
@@ -1,20 +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 "src/states/error_codes.h"
-
-namespace weave {
-namespace errors {
-namespace state {
-
-const char kDomain[] = "buffet_state";
-
-const char kPackageNameMissing[] = "package_name_missing";
-const char kPropertyNameMissing[] = "property_name_missing";
-const char kPropertyNotDefined[] = "property_not_defined";
-const char kPropertyRedefinition[] = "property_redefinition";
-
-}  // namespace state
-}  // namespace errors
-}  // namespace weave
diff --git a/src/states/error_codes.h b/src/states/error_codes.h
deleted file mode 100644
index 676a199..0000000
--- a/src/states/error_codes.h
+++ /dev/null
@@ -1,25 +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_SRC_STATES_ERROR_CODES_H_
-#define LIBWEAVE_SRC_STATES_ERROR_CODES_H_
-
-namespace weave {
-namespace errors {
-namespace state {
-
-// Error domain for state definitions.
-extern const char kDomain[];
-
-// State-specific error codes.
-extern const char kPackageNameMissing[];
-extern const char kPropertyNameMissing[];
-extern const char kPropertyNotDefined[];
-extern const char kPropertyRedefinition[];
-
-}  // namespace state
-}  // namespace errors
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_STATES_ERROR_CODES_H_
diff --git a/src/states/mock_state_change_queue_interface.h b/src/states/mock_state_change_queue_interface.h
deleted file mode 100644
index 78c6c82..0000000
--- a/src/states/mock_state_change_queue_interface.h
+++ /dev/null
@@ -1,48 +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_SRC_STATES_MOCK_STATE_CHANGE_QUEUE_INTERFACE_H_
-#define LIBWEAVE_SRC_STATES_MOCK_STATE_CHANGE_QUEUE_INTERFACE_H_
-
-#include <vector>
-
-#include <gmock/gmock.h>
-
-#include "src/states/state_change_queue_interface.h"
-
-namespace weave {
-
-class MockStateChangeQueueInterface : public StateChangeQueueInterface {
- public:
-  MOCK_CONST_METHOD0(IsEmpty, bool());
-  MOCK_METHOD2(MockNotifyPropertiesUpdated,
-               bool(base::Time timestamp,
-                    const base::DictionaryValue& changed_properties));
-  MOCK_METHOD0(MockGetAndClearRecordedStateChanges,
-               std::vector<StateChange>&());
-  MOCK_CONST_METHOD0(GetLastStateChangeId, UpdateID());
-  MOCK_METHOD1(MockAddOnStateUpdatedCallback,
-               base::CallbackList<void(UpdateID)>::Subscription*(
-                   const base::Callback<void(UpdateID)>&));
-  MOCK_METHOD1(NotifyStateUpdatedOnServer, void(UpdateID));
-
- private:
-  bool NotifyPropertiesUpdated(
-      base::Time timestamp,
-      std::unique_ptr<base::DictionaryValue> changed_properties) override {
-    return MockNotifyPropertiesUpdated(timestamp, *changed_properties);
-  }
-  std::vector<StateChange> GetAndClearRecordedStateChanges() override {
-    return std::move(MockGetAndClearRecordedStateChanges());
-  }
-
-  Token AddOnStateUpdatedCallback(
-      const base::Callback<void(UpdateID)>& callback) override {
-    return Token{MockAddOnStateUpdatedCallback(callback)};
-  }
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_STATES_MOCK_STATE_CHANGE_QUEUE_INTERFACE_H_
diff --git a/src/states/state_change_queue.cc b/src/states/state_change_queue.cc
index 87cc18d..effe7f3 100644
--- a/src/states/state_change_queue.cc
+++ b/src/states/state_change_queue.cc
@@ -15,13 +15,13 @@
 
 bool StateChangeQueue::NotifyPropertiesUpdated(
     base::Time timestamp,
-    std::unique_ptr<base::DictionaryValue> changed_properties) {
+    const base::DictionaryValue& changed_properties) {
   auto& stored_changes = state_changes_[timestamp];
   // Merge the old property set.
   if (stored_changes)
-    stored_changes->MergeDictionary(changed_properties.get());
+    stored_changes->MergeDictionary(&changed_properties);
   else
-    stored_changes = std::move(changed_properties);
+    stored_changes.reset(changed_properties.DeepCopy());
 
   while (state_changes_.size() > max_queue_size_) {
     // Queue is full.
@@ -37,7 +37,6 @@
     std::swap(element_old->second, element_new->second);
     state_changes_.erase(element_old);
   }
-  ++last_change_id_;
   return true;
 }
 
@@ -51,15 +50,4 @@
   return changes;
 }
 
-StateChangeQueueInterface::Token StateChangeQueue::AddOnStateUpdatedCallback(
-    const base::Callback<void(UpdateID)>& callback) {
-  if (state_changes_.empty())
-    callback.Run(last_change_id_);
-  return Token{callbacks_.Add(callback).release()};
-}
-
-void StateChangeQueue::NotifyStateUpdatedOnServer(UpdateID update_id) {
-  callbacks_.Notify(update_id);
-}
-
 }  // namespace weave
diff --git a/src/states/state_change_queue.h b/src/states/state_change_queue.h
index 314f746..3aef8d5 100644
--- a/src/states/state_change_queue.h
+++ b/src/states/state_change_queue.h
@@ -6,29 +6,36 @@
 #define LIBWEAVE_SRC_STATES_STATE_CHANGE_QUEUE_H_
 
 #include <map>
+#include <memory>
 #include <vector>
 
 #include <base/macros.h>
-
-#include "src/states/state_change_queue_interface.h"
+#include <base/time/time.h>
+#include <base/values.h>
 
 namespace weave {
 
+// A simple notification record event to track device state changes.
+// The |timestamp| records the time of the state change.
+// |changed_properties| contains a property set with the new property values
+// which were updated at the time the event was recorded.
+struct StateChange {
+  StateChange(base::Time time,
+              std::unique_ptr<base::DictionaryValue> properties)
+      : timestamp{time}, changed_properties{std::move(properties)} {}
+  base::Time timestamp;
+  std::unique_ptr<base::DictionaryValue> changed_properties;
+};
+
 // An object to record and retrieve device state change notification events.
-class StateChangeQueue : public StateChangeQueueInterface {
+class StateChangeQueue {
  public:
   explicit StateChangeQueue(size_t max_queue_size);
 
-  // Overrides from StateChangeQueueInterface.
-  bool IsEmpty() const override { return state_changes_.empty(); }
   bool NotifyPropertiesUpdated(
       base::Time timestamp,
-      std::unique_ptr<base::DictionaryValue> changed_properties) override;
-  std::vector<StateChange> GetAndClearRecordedStateChanges() override;
-  UpdateID GetLastStateChangeId() const override { return last_change_id_; }
-  Token AddOnStateUpdatedCallback(
-      const base::Callback<void(UpdateID)>& callback) override;
-  void NotifyStateUpdatedOnServer(UpdateID update_id) override;
+      const base::DictionaryValue& changed_properties);
+  std::vector<StateChange> GetAndClearRecordedStateChanges();
 
  private:
   // Maximum queue size. If it is full, the oldest state update records are
@@ -38,13 +45,6 @@
   // Accumulated list of device state change notifications.
   std::map<base::Time, std::unique_ptr<base::DictionaryValue>> state_changes_;
 
-  // An ID of last state change update. Each NotifyPropertiesUpdated()
-  // invocation increments this value by 1.
-  UpdateID last_change_id_{0};
-
-  // Callback list for state change queue even sinks.
-  base::CallbackList<void(UpdateID)> callbacks_;
-
   DISALLOW_COPY_AND_ASSIGN(StateChangeQueue);
 };
 
diff --git a/src/states/state_change_queue_interface.h b/src/states/state_change_queue_interface.h
deleted file mode 100644
index 81a2cfc..0000000
--- a/src/states/state_change_queue_interface.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_SRC_STATES_STATE_CHANGE_QUEUE_INTERFACE_H_
-#define LIBWEAVE_SRC_STATES_STATE_CHANGE_QUEUE_INTERFACE_H_
-
-#include <vector>
-
-#include <base/callback_list.h>
-#include <base/time/time.h>
-
-#include "src/commands/schema_utils.h"
-
-namespace weave {
-
-// A simple notification record event to track device state changes.
-// The |timestamp| records the time of the state change.
-// |changed_properties| contains a property set with the new property values
-// which were updated at the time the event was recorded.
-struct StateChange {
-  StateChange(base::Time time,
-              std::unique_ptr<base::DictionaryValue> properties)
-      : timestamp{time}, changed_properties{std::move(properties)} {}
-  base::Time timestamp;
-  std::unique_ptr<base::DictionaryValue> changed_properties;
-};
-
-// An abstract interface to StateChangeQueue to record and retrieve state
-// change notification events.
-class StateChangeQueueInterface {
- public:
-  using UpdateID = uint64_t;
-  using Token =
-      std::unique_ptr<base::CallbackList<void(UpdateID)>::Subscription>;
-
-  // Returns true if the state change notification queue is empty.
-  virtual bool IsEmpty() const = 0;
-
-  // Called by StateManager when device state properties are updated.
-  virtual bool NotifyPropertiesUpdated(
-      base::Time timestamp,
-      std::unique_ptr<base::DictionaryValue> changed_properties) = 0;
-
-  // Returns the recorded state changes since last time this method was called.
-  virtual std::vector<StateChange> GetAndClearRecordedStateChanges() = 0;
-
-  // Returns an ID of last state change update. Each NotifyPropertiesUpdated()
-  // invocation increments this value by 1.
-  virtual UpdateID GetLastStateChangeId() const = 0;
-
-  // Subscribes for device state update notifications from cloud server.
-  // The |callback| will be called every time a state patch with given ID is
-  // successfully received and processed by GCD server.
-  // Returns a subscription token. As soon as this token is destroyed, the
-  // respective callback is removed from the callback list.
-  virtual Token AddOnStateUpdatedCallback(
-      const base::Callback<void(UpdateID)>& callback) WARN_UNUSED_RESULT = 0;
-
-  virtual void NotifyStateUpdatedOnServer(UpdateID update_id) = 0;
-
- protected:
-  // No one should attempt do destroy the queue through the interface.
-  virtual ~StateChangeQueueInterface() {}
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_STATES_STATE_CHANGE_QUEUE_INTERFACE_H_
diff --git a/src/states/state_change_queue_unittest.cc b/src/states/state_change_queue_unittest.cc
index 5fbc012..57cf490 100644
--- a/src/states/state_change_queue_unittest.cc
+++ b/src/states/state_change_queue_unittest.cc
@@ -5,9 +5,9 @@
 #include "src/states/state_change_queue.h"
 
 #include <gtest/gtest.h>
+#include <weave/test/unittest_utils.h>
 
 #include "src/bind_lambda.h"
-#include "src/commands/unittest_utils.h"
 
 namespace weave {
 
@@ -23,23 +23,17 @@
 };
 
 TEST_F(StateChangeQueueTest, Empty) {
-  EXPECT_TRUE(queue_->IsEmpty());
-  EXPECT_EQ(0, queue_->GetLastStateChangeId());
   EXPECT_TRUE(queue_->GetAndClearRecordedStateChanges().empty());
 }
 
 TEST_F(StateChangeQueueTest, UpdateOne) {
   auto timestamp = base::Time::Now();
   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(
-      timestamp, CreateDictionaryValue("{'prop': {'name': 23}}")));
-  EXPECT_FALSE(queue_->IsEmpty());
-  EXPECT_EQ(1, queue_->GetLastStateChangeId());
+      timestamp, *CreateDictionaryValue("{'prop': {'name': 23}}")));
   auto changes = queue_->GetAndClearRecordedStateChanges();
-  EXPECT_EQ(1, queue_->GetLastStateChangeId());
-  ASSERT_EQ(1, changes.size());
+  ASSERT_EQ(1u, changes.size());
   EXPECT_EQ(timestamp, changes.front().timestamp);
   EXPECT_JSON_EQ("{'prop':{'name': 23}}", *changes.front().changed_properties);
-  EXPECT_TRUE(queue_->IsEmpty());
   EXPECT_TRUE(queue_->GetAndClearRecordedStateChanges().empty());
 }
 
@@ -50,19 +44,16 @@
   const std::string state2 =
       "{'prop': {'name1': 17, 'name2': 1.0, 'name3': false}}";
   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(
-      timestamp1, CreateDictionaryValue(state1)));
+      timestamp1, *CreateDictionaryValue(state1)));
   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(
-      timestamp2, CreateDictionaryValue(state2)));
+      timestamp2, *CreateDictionaryValue(state2)));
 
-  EXPECT_EQ(2, queue_->GetLastStateChangeId());
-  EXPECT_FALSE(queue_->IsEmpty());
   auto changes = queue_->GetAndClearRecordedStateChanges();
-  ASSERT_EQ(2, changes.size());
+  ASSERT_EQ(2u, changes.size());
   EXPECT_EQ(timestamp1, changes[0].timestamp);
   EXPECT_JSON_EQ(state1, *changes[0].changed_properties);
   EXPECT_EQ(timestamp2, changes[1].timestamp);
   EXPECT_JSON_EQ(state2, *changes[1].changed_properties);
-  EXPECT_TRUE(queue_->IsEmpty());
   EXPECT_TRUE(queue_->GetAndClearRecordedStateChanges().empty());
 }
 
@@ -71,20 +62,20 @@
   base::TimeDelta time_delta = base::TimeDelta::FromMinutes(1);
 
   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(
-      timestamp, CreateDictionaryValue("{'prop': {'name1': 1}}")));
+      timestamp, *CreateDictionaryValue("{'prop': {'name1': 1}}")));
 
   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(
-      timestamp, CreateDictionaryValue("{'prop': {'name2': 2}}")));
+      timestamp, *CreateDictionaryValue("{'prop': {'name2': 2}}")));
 
   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(
-      timestamp, CreateDictionaryValue("{'prop': {'name1': 3}}")));
+      timestamp, *CreateDictionaryValue("{'prop': {'name1': 3}}")));
 
   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(
-      timestamp + time_delta, CreateDictionaryValue("{'prop': {'name1': 4}}")));
+      timestamp + time_delta,
+      *CreateDictionaryValue("{'prop': {'name1': 4}}")));
 
   auto changes = queue_->GetAndClearRecordedStateChanges();
-  EXPECT_EQ(4, queue_->GetLastStateChangeId());
-  ASSERT_EQ(2, changes.size());
+  ASSERT_EQ(2u, changes.size());
 
   const std::string expected1 = "{'prop': {'name1': 3, 'name2': 2}}";
   const std::string expected2 = "{'prop': {'name1': 4}}";
@@ -101,19 +92,19 @@
   base::TimeDelta time_delta2 = base::TimeDelta::FromMinutes(3);
 
   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(
-      start_time, CreateDictionaryValue("{'prop': {'name1': 1, 'name2': 2}}")));
+      start_time,
+      *CreateDictionaryValue("{'prop': {'name1': 1, 'name2': 2}}")));
 
   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(
       start_time + time_delta1,
-      CreateDictionaryValue("{'prop': {'name1': 3, 'name3': 4}}")));
+      *CreateDictionaryValue("{'prop': {'name1': 3, 'name3': 4}}")));
 
   ASSERT_TRUE(queue_->NotifyPropertiesUpdated(
       start_time + time_delta2,
-      CreateDictionaryValue("{'prop': {'name10': 10, 'name11': 11}}")));
+      *CreateDictionaryValue("{'prop': {'name10': 10, 'name11': 11}}")));
 
-  EXPECT_EQ(3, queue_->GetLastStateChangeId());
   auto changes = queue_->GetAndClearRecordedStateChanges();
-  ASSERT_EQ(2, changes.size());
+  ASSERT_EQ(2u, changes.size());
 
   const std::string expected1 =
       "{'prop': {'name1': 3, 'name2': 2, 'name3': 4}}";
@@ -126,26 +117,4 @@
   EXPECT_JSON_EQ(expected2, *changes[1].changed_properties);
 }
 
-TEST_F(StateChangeQueueTest, ImmediateStateChangeNotification) {
-  // When queue is empty, registering a new callback will trigger it.
-  bool called = false;
-  auto callback = [&called](StateChangeQueueInterface::UpdateID id) {
-    called = true;
-  };
-  queue_->AddOnStateUpdatedCallback(base::Bind(callback));
-  EXPECT_TRUE(called);
-}
-
-TEST_F(StateChangeQueueTest, DelayedStateChangeNotification) {
-  // When queue is not empty, registering a new callback will not trigger it.
-  ASSERT_TRUE(queue_->NotifyPropertiesUpdated(
-      base::Time::Now(),
-      CreateDictionaryValue("{'prop': {'name1': 1, 'name3': 2}}")));
-
-  auto callback = [](StateChangeQueueInterface::UpdateID id) {
-    FAIL() << "This should not be called";
-  };
-  queue_->AddOnStateUpdatedCallback(base::Bind(callback));
-}
-
 }  // namespace weave
diff --git a/src/states/state_manager.cc b/src/states/state_manager.cc
deleted file mode 100644
index a424588..0000000
--- a/src/states/state_manager.cc
+++ /dev/null
@@ -1,227 +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 "src/states/state_manager.h"
-
-#include <base/logging.h>
-#include <base/values.h>
-
-#include "src/json_error_codes.h"
-#include "src/states/error_codes.h"
-#include "src/states/state_change_queue_interface.h"
-#include "src/string_utils.h"
-#include "src/utils.h"
-
-namespace weave {
-
-StateManager::StateManager(StateChangeQueueInterface* state_change_queue)
-    : state_change_queue_(state_change_queue) {
-  CHECK(state_change_queue_) << "State change queue not specified";
-}
-
-StateManager::~StateManager() {}
-
-void StateManager::AddChangedCallback(const base::Closure& callback) {
-  on_changed_.push_back(callback);
-  callback.Run();  // Force to read current state.
-}
-
-std::unique_ptr<base::DictionaryValue> StateManager::GetState() const {
-  std::unique_ptr<base::DictionaryValue> dict{new base::DictionaryValue};
-  for (const auto& pair : packages_) {
-    auto pkg_value = pair.second->GetValuesAsJson();
-    CHECK(pkg_value);
-    dict->SetWithoutPathExpansion(pair.first, pkg_value.release());
-  }
-  return dict;
-}
-
-bool StateManager::SetProperty(const std::string& name,
-                               const base::Value& value,
-                               ErrorPtr* error) {
-  bool result = SetPropertyValue(name, value, base::Time::Now(), error);
-  for (const auto& cb : on_changed_)
-    cb.Run();
-  return result;
-}
-
-std::unique_ptr<base::Value> StateManager::GetProperty(
-    const std::string& name) const {
-  auto parts = SplitAtFirst(name, ".", true);
-  const std::string& package_name = parts.first;
-  const std::string& property_name = parts.second;
-  if (package_name.empty() || property_name.empty())
-    return nullptr;
-
-  const StatePackage* package = FindPackage(package_name);
-  if (!package)
-    return nullptr;
-
-  return package->GetPropertyValue(property_name, nullptr);
-}
-
-bool StateManager::SetPropertyValue(const std::string& full_property_name,
-                                    const base::Value& value,
-                                    const base::Time& timestamp,
-                                    ErrorPtr* error) {
-  auto parts = SplitAtFirst(full_property_name, ".", true);
-  const std::string& package_name = parts.first;
-  const std::string& property_name = parts.second;
-  const bool split = (full_property_name.find(".") != std::string::npos);
-
-  if (full_property_name.empty() || (split && property_name.empty())) {
-    Error::AddTo(error, FROM_HERE, errors::state::kDomain,
-                 errors::state::kPropertyNameMissing,
-                 "Property name is missing");
-    return false;
-  }
-  if (!split || package_name.empty()) {
-    Error::AddTo(error, FROM_HERE, errors::state::kDomain,
-                 errors::state::kPackageNameMissing,
-                 "Package name is missing in the property name");
-    return false;
-  }
-  StatePackage* package = FindPackage(package_name);
-  if (package == nullptr) {
-    Error::AddToPrintf(error, FROM_HERE, errors::state::kDomain,
-                       errors::state::kPropertyNotDefined,
-                       "Unknown state property package '%s'",
-                       package_name.c_str());
-    return false;
-  }
-  if (!package->SetPropertyValue(property_name, value, error))
-    return false;
-
-  std::unique_ptr<base::DictionaryValue> prop_set{new base::DictionaryValue};
-  prop_set->Set(full_property_name, value.DeepCopy());
-  state_change_queue_->NotifyPropertiesUpdated(timestamp, std::move(prop_set));
-  return true;
-}
-
-std::pair<StateChangeQueueInterface::UpdateID, std::vector<StateChange>>
-StateManager::GetAndClearRecordedStateChanges() {
-  return std::make_pair(state_change_queue_->GetLastStateChangeId(),
-                        state_change_queue_->GetAndClearRecordedStateChanges());
-}
-
-void StateManager::NotifyStateUpdatedOnServer(
-    StateChangeQueueInterface::UpdateID id) {
-  state_change_queue_->NotifyStateUpdatedOnServer(id);
-}
-
-bool StateManager::LoadStateDefinition(const base::DictionaryValue& dict,
-                                       ErrorPtr* error) {
-  for (base::DictionaryValue::Iterator iter(dict); !iter.IsAtEnd();
-       iter.Advance()) {
-    std::string package_name = iter.key();
-    if (package_name.empty()) {
-      Error::AddTo(error, FROM_HERE, errors::kErrorDomain,
-                   errors::kInvalidPackageError, "State package name is empty");
-      return false;
-    }
-    const base::DictionaryValue* package_dict = nullptr;
-    if (!iter.value().GetAsDictionary(&package_dict)) {
-      Error::AddToPrintf(error, FROM_HERE, errors::json::kDomain,
-                         errors::json::kObjectExpected,
-                         "State package '%s' must be an object",
-                         package_name.c_str());
-      return false;
-    }
-    StatePackage* package = FindOrCreatePackage(package_name);
-    CHECK(package) << "Unable to create state package " << package_name;
-    if (!package->AddSchemaFromJson(package_dict, error))
-      return false;
-  }
-
-  return true;
-}
-
-bool StateManager::LoadStateDefinitionFromJson(const std::string& json,
-                                               ErrorPtr* error) {
-  std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
-  if (!dict)
-    return false;
-  if (!LoadStateDefinition(*dict, error)) {
-    Error::AddToPrintf(error, FROM_HERE, errors::kErrorDomain,
-                       errors::kSchemaError,
-                       "Failed to load state definition: '%s'", json.c_str());
-    return false;
-  }
-  return true;
-}
-
-bool StateManager::SetProperties(const base::DictionaryValue& dict,
-                                 ErrorPtr* error) {
-  base::Time timestamp = base::Time::Now();
-  bool all_success = true;
-  for (base::DictionaryValue::Iterator iter(dict); !iter.IsAtEnd();
-       iter.Advance()) {
-    if (iter.key().empty()) {
-      Error::AddTo(error, FROM_HERE, errors::kErrorDomain,
-                   errors::kInvalidPackageError, "State package name is empty");
-      all_success = false;
-      continue;
-    }
-
-    const base::DictionaryValue* package_dict = nullptr;
-    if (!iter.value().GetAsDictionary(&package_dict)) {
-      Error::AddToPrintf(error, FROM_HERE, errors::json::kDomain,
-                         errors::json::kObjectExpected,
-                         "State package '%s' must be an object",
-                         iter.key().c_str());
-      all_success = false;
-      continue;
-    }
-
-    for (base::DictionaryValue::Iterator it_prop(*package_dict);
-         !it_prop.IsAtEnd(); it_prop.Advance()) {
-      if (!SetPropertyValue(iter.key() + "." + it_prop.key(), it_prop.value(),
-                            timestamp, error)) {
-        all_success = false;
-        continue;
-      }
-    }
-  }
-  for (const auto& cb : on_changed_)
-    cb.Run();
-  return all_success;
-}
-
-bool StateManager::SetPropertiesFromJson(const std::string& json,
-                                         ErrorPtr* error) {
-  std::unique_ptr<const base::DictionaryValue> dict = LoadJsonDict(json, error);
-  if (!dict)
-    return false;
-  if (!SetProperties(*dict, error)) {
-    Error::AddToPrintf(error, FROM_HERE, errors::kErrorDomain,
-                       errors::kSchemaError, "Failed to load defaults: '%s'",
-                       json.c_str());
-    return false;
-  }
-  return true;
-}
-
-StatePackage* StateManager::FindPackage(const std::string& package_name) {
-  auto it = packages_.find(package_name);
-  return (it != packages_.end()) ? it->second.get() : nullptr;
-}
-
-const StatePackage* StateManager::FindPackage(
-    const std::string& package_name) const {
-  auto it = packages_.find(package_name);
-  return (it != packages_.end()) ? it->second.get() : nullptr;
-}
-
-StatePackage* StateManager::FindOrCreatePackage(
-    const std::string& package_name) {
-  StatePackage* package = FindPackage(package_name);
-  if (package == nullptr) {
-    std::unique_ptr<StatePackage> new_package{new StatePackage(package_name)};
-    package = packages_.emplace(package_name, std::move(new_package))
-                  .first->second.get();
-  }
-  return package;
-}
-
-}  // namespace weave
diff --git a/src/states/state_manager.h b/src/states/state_manager.h
deleted file mode 100644
index 55faf76..0000000
--- a/src/states/state_manager.h
+++ /dev/null
@@ -1,88 +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_SRC_STATES_STATE_MANAGER_H_
-#define LIBWEAVE_SRC_STATES_STATE_MANAGER_H_
-
-#include <map>
-#include <memory>
-#include <set>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <base/callback.h>
-#include <base/macros.h>
-#include <weave/error.h>
-
-#include "src/states/state_change_queue_interface.h"
-#include "src/states/state_package.h"
-
-namespace base {
-class DictionaryValue;
-class Time;
-}  // namespace base
-
-namespace weave {
-
-// StateManager is the class that aggregates the device state fragments
-// provided by device daemons and makes the aggregate device state available
-// to the GCD cloud server and local clients.
-class StateManager final {
- public:
-  explicit StateManager(StateChangeQueueInterface* state_change_queue);
-  ~StateManager();
-
-  void AddChangedCallback(const base::Closure& callback);
-  bool LoadStateDefinition(const base::DictionaryValue& dict, ErrorPtr* error);
-  bool LoadStateDefinitionFromJson(const std::string& json, ErrorPtr* error);
-  bool SetProperties(const base::DictionaryValue& dict, ErrorPtr* error);
-  bool SetPropertiesFromJson(const std::string& json, ErrorPtr* error);
-  std::unique_ptr<base::Value> GetProperty(const std::string& name) const;
-  bool SetProperty(const std::string& name,
-                   const base::Value& value,
-                   ErrorPtr* error);
-  std::unique_ptr<base::DictionaryValue> GetState() const;
-
-  // Returns the recorded state changes since last time this method has been
-  // called.
-  std::pair<StateChangeQueueInterface::UpdateID, std::vector<StateChange>>
-  GetAndClearRecordedStateChanges();
-
-  // Called to notify that the state patch with |id| has been successfully sent
-  // to the server and processed.
-  void NotifyStateUpdatedOnServer(StateChangeQueueInterface::UpdateID id);
-
-  StateChangeQueueInterface* GetStateChangeQueue() const {
-    return state_change_queue_;
-  }
-
- private:
-  friend class BaseApiHandlerTest;
-  friend class StateManagerTest;
-
-  // Updates a single property value. |full_property_name| must be the full
-  // name of the property to update in format "package.property".
-  bool SetPropertyValue(const std::string& full_property_name,
-                        const base::Value& value,
-                        const base::Time& timestamp,
-                        ErrorPtr* error);
-
-  // Finds a package by its name. Returns nullptr if not found.
-  StatePackage* FindPackage(const std::string& package_name);
-  const StatePackage* FindPackage(const std::string& package_name) const;
-  // Finds a package by its name. If none exists, one will be created.
-  StatePackage* FindOrCreatePackage(const std::string& package_name);
-
-  StateChangeQueueInterface* state_change_queue_;  // Owned by Manager.
-  std::map<std::string, std::unique_ptr<StatePackage>> packages_;
-
-  std::vector<base::Closure> on_changed_;
-
-  DISALLOW_COPY_AND_ASSIGN(StateManager);
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_STATES_STATE_MANAGER_H_
diff --git a/src/states/state_manager_unittest.cc b/src/states/state_manager_unittest.cc
deleted file mode 100644
index 933e11b..0000000
--- a/src/states/state_manager_unittest.cc
+++ /dev/null
@@ -1,272 +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 "src/states/state_manager.h"
-
-#include <cstdlib>  // for abs().
-#include <vector>
-
-#include <base/bind.h>
-#include <base/values.h>
-#include <gmock/gmock.h>
-#include <gtest/gtest.h>
-#include <weave/provider/test/mock_config_store.h>
-
-#include "src/commands/schema_constants.h"
-#include "src/commands/unittest_utils.h"
-#include "src/states/error_codes.h"
-#include "src/states/mock_state_change_queue_interface.h"
-
-namespace weave {
-
-using testing::_;
-using testing::Return;
-using testing::ReturnRef;
-using test::CreateDictionaryValue;
-
-namespace {
-
-const char kBaseDefinition[] = R"({
-  "base": {
-    "manufacturer":{"type":"string"},
-    "serialNumber":{"type":"string"}
-  },
-  "device": {
-    "state_property":{"type":"string"}
-  }
-})";
-
-std::unique_ptr<base::DictionaryValue> GetTestSchema() {
-  return CreateDictionaryValue(kBaseDefinition);
-}
-
-const char kBaseDefaults[] = R"({
-  "base": {
-    "manufacturer":"Test Factory",
-    "serialNumber":"Test Model"
-  }
-})";
-
-std::unique_ptr<base::DictionaryValue> GetTestValues() {
-  return CreateDictionaryValue(kBaseDefaults);
-}
-
-MATCHER_P(IsState, str, "") {
-  return arg.Equals(CreateDictionaryValue(str).get());
-}
-
-}  // anonymous namespace
-
-class StateManagerTest : public ::testing::Test {
- public:
-  void SetUp() override {
-    // Initial expectations.
-    EXPECT_CALL(mock_state_change_queue_, IsEmpty()).Times(0);
-    EXPECT_CALL(mock_state_change_queue_, MockNotifyPropertiesUpdated(_, _))
-        .WillRepeatedly(Return(true));
-    EXPECT_CALL(mock_state_change_queue_, MockGetAndClearRecordedStateChanges())
-        .Times(0);
-    mgr_.reset(new StateManager(&mock_state_change_queue_));
-
-    EXPECT_CALL(*this, OnStateChanged()).Times(2);
-    mgr_->AddChangedCallback(
-        base::Bind(&StateManagerTest::OnStateChanged, base::Unretained(this)));
-
-    LoadStateDefinition(GetTestSchema().get(), nullptr);
-    ASSERT_TRUE(mgr_->SetProperties(*GetTestValues().get(), nullptr));
-  }
-  void TearDown() override { mgr_.reset(); }
-
-  void LoadStateDefinition(const base::DictionaryValue* json,
-                           ErrorPtr* error) {
-    ASSERT_TRUE(mgr_->LoadStateDefinition(*json, error));
-  }
-
-  bool SetPropertyValue(const std::string& name,
-                        const base::Value& value,
-                        ErrorPtr* error) {
-    return mgr_->SetPropertyValue(name, value, timestamp_, error);
-  }
-
-  MOCK_CONST_METHOD0(OnStateChanged, void());
-
-  base::Time timestamp_{base::Time::Now()};
-  std::unique_ptr<StateManager> mgr_;
-  testing::StrictMock<MockStateChangeQueueInterface> mock_state_change_queue_;
-};
-
-TEST(StateManager, Empty) {
-  testing::StrictMock<MockStateChangeQueueInterface> mock_state_change_queue;
-  StateManager manager(&mock_state_change_queue);
-}
-
-TEST_F(StateManagerTest, Initialized) {
-  auto expected = R"({
-    'base': {
-      'manufacturer': 'Test Factory',
-      'serialNumber': 'Test Model'
-    },
-    'device': {}
-  })";
-  EXPECT_JSON_EQ(expected, *mgr_->GetState());
-}
-
-TEST_F(StateManagerTest, LoadStateDefinition) {
-  auto dict = CreateDictionaryValue(R"({
-    'power': {
-      'battery_level':'integer'
-    }
-  })");
-  LoadStateDefinition(dict.get(), nullptr);
-
-  auto expected = R"({
-    'base': {
-      'manufacturer': 'Test Factory',
-      'serialNumber': 'Test Model'
-    },
-    'power': {},
-    'device': {}
-  })";
-  EXPECT_JSON_EQ(expected, *mgr_->GetState());
-}
-
-TEST_F(StateManagerTest, Startup) {
-  StateManager manager(&mock_state_change_queue_);
-
-  auto state_definition = R"({
-    "base": {
-      "firmwareVersion": {"type":"string"},
-      "localDiscoveryEnabled": {"type":"boolean"},
-      "localAnonymousAccessMaxRole": {
-        "type": "string",
-        "enum": ["none", "viewer", "user"]
-      },
-      "localPairingEnabled": {"type":"boolean"}
-    },
-    "power": {"battery_level":{"type":"integer"}}
-  })";
-  ASSERT_TRUE(manager.LoadStateDefinitionFromJson(state_definition, nullptr));
-
-  auto state_values = R"({
-    "base": {
-      "firmwareVersion": "unknown",
-      "localDiscoveryEnabled": false,
-      "localAnonymousAccessMaxRole": "none",
-      "localPairingEnabled": false
-    },
-    "power": {"battery_level":44}
-  })";
-  ASSERT_TRUE(manager.SetPropertiesFromJson(state_values, nullptr));
-
-  auto expected = R"({
-    'base': {
-      'firmwareVersion': 'unknown',
-      'localAnonymousAccessMaxRole': 'none',
-      'localDiscoveryEnabled': false,
-      'localPairingEnabled': false
-    },
-    'power': {
-      'battery_level': 44
-    }
-  })";
-  EXPECT_JSON_EQ(expected, *manager.GetState());
-}
-
-TEST_F(StateManagerTest, SetPropertyValue) {
-  const std::string state = "{'device': {'state_property': 'Test Value'}}";
-  EXPECT_CALL(mock_state_change_queue_,
-              MockNotifyPropertiesUpdated(timestamp_, IsState(state)))
-      .WillOnce(Return(true));
-  ASSERT_TRUE(SetPropertyValue("device.state_property",
-                               base::StringValue{"Test Value"}, nullptr));
-  auto expected = R"({
-    'base': {
-      'manufacturer': 'Test Factory',
-      'serialNumber': 'Test Model'
-    },
-    'device': {
-      'state_property': 'Test Value'
-    }
-  })";
-  EXPECT_JSON_EQ(expected, *mgr_->GetState());
-}
-
-TEST_F(StateManagerTest, SetPropertyValue_Error_NoName) {
-  ErrorPtr error;
-  ASSERT_FALSE(SetPropertyValue("", base::FundamentalValue{0}, &error));
-  EXPECT_EQ(errors::state::kDomain, error->GetDomain());
-  EXPECT_EQ(errors::state::kPropertyNameMissing, error->GetCode());
-}
-
-TEST_F(StateManagerTest, SetPropertyValue_Error_NoPackage) {
-  ErrorPtr error;
-  ASSERT_FALSE(
-      SetPropertyValue("state_property", base::FundamentalValue{0}, &error));
-  EXPECT_EQ(errors::state::kDomain, error->GetDomain());
-  EXPECT_EQ(errors::state::kPackageNameMissing, error->GetCode());
-}
-
-TEST_F(StateManagerTest, SetPropertyValue_Error_UnknownPackage) {
-  ErrorPtr error;
-  ASSERT_FALSE(
-      SetPropertyValue("power.level", base::FundamentalValue{0}, &error));
-  EXPECT_EQ(errors::state::kDomain, error->GetDomain());
-  EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
-}
-
-TEST_F(StateManagerTest, SetPropertyValue_Error_UnknownProperty) {
-  ASSERT_TRUE(
-      SetPropertyValue("base.level", base::FundamentalValue{0}, nullptr));
-}
-
-TEST_F(StateManagerTest, GetAndClearRecordedStateChanges) {
-  EXPECT_CALL(mock_state_change_queue_,
-              MockNotifyPropertiesUpdated(timestamp_, _))
-      .WillOnce(Return(true));
-  ASSERT_TRUE(SetPropertyValue("device.state_property",
-                               base::StringValue{"Test Value"}, nullptr));
-  std::vector<StateChange> expected_state;
-  const std::string expected_val = 
-      "{'device': {'state_property': 'Test Value'}}";
-  expected_state.emplace_back(
-      timestamp_,
-      CreateDictionaryValue(expected_val));
-  EXPECT_CALL(mock_state_change_queue_, MockGetAndClearRecordedStateChanges())
-      .WillOnce(ReturnRef(expected_state));
-  EXPECT_CALL(mock_state_change_queue_, GetLastStateChangeId())
-      .WillOnce(Return(0));
-  auto changes = mgr_->GetAndClearRecordedStateChanges();
-  ASSERT_EQ(1, changes.second.size());
-  EXPECT_EQ(timestamp_, changes.second.back().timestamp);
-  EXPECT_JSON_EQ(expected_val, *changes.second.back().changed_properties);
-}
-
-TEST_F(StateManagerTest, SetProperties) {
-  const std::string state = "{'base': {'manufacturer': 'No Name'}}";
-  EXPECT_CALL(mock_state_change_queue_,
-              MockNotifyPropertiesUpdated(_, IsState(state)))
-      .WillOnce(Return(true));
-
-  EXPECT_CALL(*this, OnStateChanged()).Times(1);
-  ASSERT_TRUE(mgr_->SetProperties(
-      *CreateDictionaryValue("{'base':{'manufacturer':'No Name'}}"), nullptr));
-
-  auto expected = R"({
-    'base': {
-      'manufacturer': 'No Name',
-      'serialNumber': 'Test Model'
-    },
-    'device': {}
-  })";
-  EXPECT_JSON_EQ(expected, *mgr_->GetState());
-}
-
-TEST_F(StateManagerTest, GetProperty) {
-  EXPECT_JSON_EQ("'Test Model'", *mgr_->GetProperty("base.serialNumber"));
-  EXPECT_EQ(nullptr, mgr_->GetProperty("device.state_property"));
-  EXPECT_EQ(nullptr, mgr_->GetProperty("device.unknown"));
-  EXPECT_EQ(nullptr, mgr_->GetProperty("unknown.state_property"));
-}
-
-}  // namespace weave
diff --git a/src/states/state_package.cc b/src/states/state_package.cc
deleted file mode 100644
index 55650b6..0000000
--- a/src/states/state_package.cc
+++ /dev/null
@@ -1,68 +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 "src/states/state_package.h"
-
-#include <base/logging.h>
-#include <base/values.h>
-
-#include "src/states/error_codes.h"
-
-namespace weave {
-
-StatePackage::StatePackage(const std::string& name) : name_(name) {}
-
-bool StatePackage::AddSchemaFromJson(const base::DictionaryValue* json,
-                                     ErrorPtr* error) {
-  // Scan first to make sure we have no property redefinitions.
-  for (base::DictionaryValue::Iterator it(*json); !it.IsAtEnd(); it.Advance()) {
-    if (types_.HasKey(it.key())) {
-      Error::AddToPrintf(error, FROM_HERE, errors::state::kDomain,
-                         errors::state::kPropertyRedefinition,
-                         "State property '%s.%s' is already defined",
-                         name_.c_str(), it.key().c_str());
-      return false;
-    }
-  }
-
-  types_.MergeDictionary(json);
-  return true;
-}
-
-bool StatePackage::AddValuesFromJson(const base::DictionaryValue* json,
-                                     ErrorPtr* error) {
-  for (base::DictionaryValue::Iterator it(*json); !it.IsAtEnd(); it.Advance()) {
-    if (!SetPropertyValue(it.key(), it.value(), error))
-      return false;
-  }
-  return true;
-}
-
-std::unique_ptr<base::DictionaryValue> StatePackage::GetValuesAsJson() const {
-  return std::unique_ptr<base::DictionaryValue>(values_.DeepCopy());
-}
-
-std::unique_ptr<base::Value> StatePackage::GetPropertyValue(
-    const std::string& property_name,
-    ErrorPtr* error) const {
-  const base::Value* value = nullptr;
-  if (!values_.Get(property_name, &value)) {
-    Error::AddToPrintf(error, FROM_HERE, errors::state::kDomain,
-                       errors::state::kPropertyNotDefined,
-                       "State property '%s.%s' is not defined", name_.c_str(),
-                       property_name.c_str());
-    return nullptr;
-  }
-
-  return std::unique_ptr<base::Value>(value->DeepCopy());
-}
-
-bool StatePackage::SetPropertyValue(const std::string& property_name,
-                                    const base::Value& value,
-                                    ErrorPtr* error) {
-  values_.Set(property_name, value.DeepCopy());
-  return true;
-}
-
-}  // namespace weave
diff --git a/src/states/state_package.h b/src/states/state_package.h
deleted file mode 100644
index 7632145..0000000
--- a/src/states/state_package.h
+++ /dev/null
@@ -1,73 +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_SRC_STATES_STATE_PACKAGE_H_
-#define LIBWEAVE_SRC_STATES_STATE_PACKAGE_H_
-
-#include <map>
-#include <memory>
-#include <string>
-
-#include <base/macros.h>
-#include <base/values.h>
-#include <weave/error.h>
-
-namespace weave {
-
-// A package is a set of related state properties. GCD specification defines
-// a number of standard state properties in "base" package such as
-// "base.manufacturer", "base.model", "base.firmwareVersion" and so on.
-class StatePackage final {
- public:
-  explicit StatePackage(const std::string& name);
-
-  // Loads state property definitions from a JSON object and adds them
-  // to the current package.
-  bool AddSchemaFromJson(const base::DictionaryValue* json, ErrorPtr* error);
-  // Loads a set of state property values from a JSON object and assigns them
-  // to existing properties.  A property must be defined prior to loading its
-  // value.  We use this when we load default values during buffet startup.
-  bool AddValuesFromJson(const base::DictionaryValue* json, ErrorPtr* error);
-
-  // Returns a set of state properties and their values as a JSON object.
-  // After being aggregated across multiple packages, this becomes the device
-  // state object passed to the GCD server or a local client in the format
-  // described by GCD specification, e.g.:
-  //  {
-  //    "base": {
-  //      "manufacturer":"...",
-  //      "model":"..."
-  //    },
-  //    "printer": {
-  //      "message": "Printer low on cyan ink"
-  //    }
-  //  }
-  std::unique_ptr<base::DictionaryValue> GetValuesAsJson() const;
-
-  // Gets the value for a specific state property. |property_name| must not
-  // include the package name as part of the property name.
-  std::unique_ptr<base::Value> GetPropertyValue(
-      const std::string& property_name,
-      ErrorPtr* error) const;
-  // Sets the value for a specific state property. |property_name| must not
-  // include the package name as part of the property name.
-  bool SetPropertyValue(const std::string& property_name,
-                        const base::Value& value,
-                        ErrorPtr* error);
-
-  // Returns the name of the this package.
-  const std::string& GetName() const { return name_; }
-
- private:
-  std::string name_;
-  base::DictionaryValue types_;
-  base::DictionaryValue values_;
-
-  friend class StatePackageTestHelper;
-  DISALLOW_COPY_AND_ASSIGN(StatePackage);
-};
-
-}  // namespace weave
-
-#endif  // LIBWEAVE_SRC_STATES_STATE_PACKAGE_H_
diff --git a/src/states/state_package_unittest.cc b/src/states/state_package_unittest.cc
deleted file mode 100644
index 5e1b933..0000000
--- a/src/states/state_package_unittest.cc
+++ /dev/null
@@ -1,289 +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 "src/states/state_package.h"
-
-#include <memory>
-#include <string>
-
-#include <base/values.h>
-#include <gtest/gtest.h>
-
-#include "src/commands/schema_constants.h"
-#include "src/commands/unittest_utils.h"
-#include "src/states/error_codes.h"
-
-namespace weave {
-
-using test::CreateDictionaryValue;
-
-class StatePackageTestHelper {
- public:
-  // Returns the state property definitions (types/constraints/etc).
-  static const base::DictionaryValue& GetTypes(const StatePackage& package) {
-    return package.types_;
-  }
-  // Returns the all state property values in this package.
-  static const base::DictionaryValue& GetValues(const StatePackage& package) {
-    return package.values_;
-  }
-};
-
-namespace {
-std::unique_ptr<base::DictionaryValue> GetTestSchema() {
-  return CreateDictionaryValue(R"({
-    'color': {
-      'type': 'string'
-    },
-    'direction': {
-      'additionalProperties': false,
-      'properties': {
-        'altitude': {
-          'maximum': 90.0,
-          'type': 'number'
-        },
-        'azimuth': {
-          'type': 'number'
-        }
-      },
-      'type': 'object',
-      'required': [ 'azimuth' ]
-    },
-    'iso': {
-      'enum': [50, 100, 200, 400],
-      'type': 'integer'
-    },
-    'light': {
-      'type': 'boolean'
-    }
-  })");
-}
-
-std::unique_ptr<base::DictionaryValue> GetTestValues() {
-  return CreateDictionaryValue(R"({
-      'light': true,
-      'color': 'white',
-      'direction': {'azimuth':57.2957795, 'altitude':89.9},
-      'iso': 200
-  })");
-}
-
-inline const base::DictionaryValue& GetTypes(const StatePackage& package) {
-  return StatePackageTestHelper::GetTypes(package);
-}
-// Returns the all state property values in this package.
-inline const base::DictionaryValue& GetValues(const StatePackage& package) {
-  return StatePackageTestHelper::GetValues(package);
-}
-
-}  // anonymous namespace
-
-class StatePackageTest : public ::testing::Test {
- public:
-  void SetUp() override {
-    package_.reset(new StatePackage("test"));
-    ASSERT_TRUE(package_->AddSchemaFromJson(GetTestSchema().get(), nullptr));
-    ASSERT_TRUE(package_->AddValuesFromJson(GetTestValues().get(), nullptr));
-  }
-  void TearDown() override { package_.reset(); }
-  std::unique_ptr<StatePackage> package_;
-};
-
-TEST(StatePackage, Empty) {
-  StatePackage package("test");
-  EXPECT_EQ("test", package.GetName());
-  EXPECT_TRUE(GetTypes(package).empty());
-  EXPECT_TRUE(GetValues(package).empty());
-}
-
-TEST(StatePackage, AddSchemaFromJson_OnEmpty) {
-  StatePackage package("test");
-  ASSERT_TRUE(package.AddSchemaFromJson(GetTestSchema().get(), nullptr));
-  EXPECT_EQ(4, GetTypes(package).size());
-  EXPECT_EQ(0, GetValues(package).size());
-
-  auto expected = R"({
-    'color': {
-      'type': 'string'
-    },
-    'direction': {
-      'additionalProperties': false,
-      'properties': {
-        'altitude': {
-          'maximum': 90.0,
-          'type': 'number'
-        },
-        'azimuth': {
-          'type': 'number'
-        }
-      },
-      'type': 'object',
-      'required': [ 'azimuth' ]
-    },
-    'iso': {
-      'enum': [50, 100, 200, 400],
-      'type': 'integer'
-    },
-    'light': {
-      'type': 'boolean'
-    }
-  })";
-  EXPECT_JSON_EQ(expected, GetTypes(package));
-
-  EXPECT_JSON_EQ("{}", *package.GetValuesAsJson());
-}
-
-TEST(StatePackage, AddValuesFromJson_OnEmpty) {
-  StatePackage package("test");
-  ASSERT_TRUE(package.AddSchemaFromJson(GetTestSchema().get(), nullptr));
-  ASSERT_TRUE(package.AddValuesFromJson(GetTestValues().get(), nullptr));
-  EXPECT_EQ(4, GetValues(package).size());
-  auto expected = R"({
-    'color': 'white',
-    'direction': {
-      'altitude': 89.9,
-      'azimuth': 57.2957795
-    },
-    'iso': 200,
-    'light': true
-  })";
-  EXPECT_JSON_EQ(expected, *package.GetValuesAsJson());
-}
-
-TEST_F(StatePackageTest, AddSchemaFromJson_AddMore) {
-  auto dict = CreateDictionaryValue(R"({'brightness':{
-      'enum': ['low', 'medium', 'high'],
-      'type': 'string'
-    }})");
-  ASSERT_TRUE(package_->AddSchemaFromJson(dict.get(), nullptr));
-  EXPECT_EQ(5, GetTypes(*package_).size());
-  EXPECT_EQ(4, GetValues(*package_).size());
-  auto expected = R"({
-    'brightness': {
-      'enum': ['low', 'medium', 'high'],
-      'type': 'string'
-    },
-    'color': {
-      'type': 'string'
-    },
-    'direction': {
-      'additionalProperties': false,
-      'properties': {
-        'altitude': {
-          'maximum': 90.0,
-          'type': 'number'
-        },
-        'azimuth': {
-          'type': 'number'
-        }
-      },
-      'type': 'object',
-      'required': [ 'azimuth' ]
-    },
-    'iso': {
-      'enum': [50, 100, 200, 400],
-      'type': 'integer'
-    },
-    'light': {
-      'type': 'boolean'
-    }
-  })";
-  EXPECT_JSON_EQ(expected, GetTypes(*package_));
-
-  expected = R"({
-    'color': 'white',
-    'direction': {
-      'altitude': 89.9,
-      'azimuth': 57.2957795
-    },
-    'iso': 200,
-    'light': true
-  })";
-  EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson());
-}
-
-TEST_F(StatePackageTest, AddValuesFromJson_AddMore) {
-  auto dict = CreateDictionaryValue(R"({'brightness':{
-      'enum': ['low', 'medium', 'high'],
-      'type': 'string'
-    }})");
-  ASSERT_TRUE(package_->AddSchemaFromJson(dict.get(), nullptr));
-  dict = CreateDictionaryValue("{'brightness':'medium'}");
-  ASSERT_TRUE(package_->AddValuesFromJson(dict.get(), nullptr));
-  EXPECT_EQ(5, GetValues(*package_).size());
-  auto expected = R"({
-    'brightness': 'medium',
-    'color': 'white',
-    'direction': {
-      'altitude': 89.9,
-      'azimuth': 57.2957795
-    },
-    'iso': 200,
-    'light': true
-  })";
-  EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson());
-}
-
-TEST_F(StatePackageTest, AddSchemaFromJson_Error_Redefined) {
-  auto dict = CreateDictionaryValue(R"({'color':
-    {'type':'string', 'enum':['white', 'blue', 'red']}})");
-  ErrorPtr error;
-  EXPECT_FALSE(package_->AddSchemaFromJson(dict.get(), &error));
-  EXPECT_EQ(errors::state::kDomain, error->GetDomain());
-  EXPECT_EQ(errors::state::kPropertyRedefinition, error->GetCode());
-}
-
-TEST_F(StatePackageTest, AddValuesFromJson_Error_Undefined) {
-  auto dict = CreateDictionaryValue("{'brightness':'medium'}");
-  EXPECT_TRUE(package_->AddValuesFromJson(dict.get(), nullptr));
-}
-
-TEST_F(StatePackageTest, GetPropertyValue) {
-  EXPECT_JSON_EQ("'white'", *package_->GetPropertyValue("color", nullptr));
-  EXPECT_JSON_EQ("true", *package_->GetPropertyValue("light", nullptr));
-  EXPECT_JSON_EQ("200", *package_->GetPropertyValue("iso", nullptr));
-  EXPECT_JSON_EQ("{'altitude': 89.9, 'azimuth': 57.2957795}",
-                 *package_->GetPropertyValue("direction", nullptr));
-}
-
-TEST_F(StatePackageTest, GetPropertyValue_Unknown) {
-  ErrorPtr error;
-  EXPECT_EQ(nullptr, package_->GetPropertyValue("volume", &error));
-  EXPECT_EQ(errors::state::kDomain, error->GetDomain());
-  EXPECT_EQ(errors::state::kPropertyNotDefined, error->GetCode());
-}
-
-TEST_F(StatePackageTest, SetPropertyValue_Simple) {
-  EXPECT_TRUE(
-      package_->SetPropertyValue("color", base::StringValue{"blue"}, nullptr));
-  EXPECT_JSON_EQ("'blue'", *package_->GetPropertyValue("color", nullptr));
-  EXPECT_TRUE(package_->SetPropertyValue("light", base::FundamentalValue{false},
-                                         nullptr));
-  bool light = false;
-  ASSERT_TRUE(
-      package_->GetPropertyValue("light", nullptr)->GetAsBoolean(&light));
-  EXPECT_FALSE(light);
-  EXPECT_TRUE(
-      package_->SetPropertyValue("iso", base::FundamentalValue{400}, nullptr));
-  EXPECT_JSON_EQ("400", *package_->GetPropertyValue("iso", nullptr));
-}
-
-TEST_F(StatePackageTest, SetPropertyValue_Object) {
-  EXPECT_TRUE(package_->SetPropertyValue(
-      "direction",
-      *CreateDictionaryValue("{'altitude': 45.0, 'azimuth': 15.0}"), nullptr));
-
-  auto expected = R"({
-    'color': 'white',
-    'direction': {
-      'altitude': 45.0,
-      'azimuth': 15.0
-    },
-    'iso': 200,
-    'light': true
-  })";
-  EXPECT_JSON_EQ(expected, *package_->GetValuesAsJson());
-}
-
-}  // namespace weave
diff --git a/src/string_utils.cc b/src/string_utils.cc
index b5e71b8..d4386f0 100644
--- a/src/string_utils.cc
+++ b/src/string_utils.cc
@@ -45,9 +45,6 @@
                                bool trim_whitespaces,
                                bool purge_empty_strings) {
   std::vector<std::string> tokens;
-  if (str.empty())
-    return tokens;
-
   for (std::string::size_type i = 0;;) {
     const std::string::size_type pos =
         delimiter.empty() ? (i + 1) : str.find(delimiter, i);
diff --git a/src/string_utils_unittest.cc b/src/string_utils_unittest.cc
index c26d7d8..7b76d35 100644
--- a/src/string_utils_unittest.cc
+++ b/src/string_utils_unittest.cc
@@ -17,21 +17,25 @@
   std::vector<std::string> parts;
 
   parts = Split("", ",", false, false);
-  EXPECT_EQ(0, parts.size());
+  EXPECT_EQ(1u, parts.size());
+  EXPECT_EQ("", parts[0]);
+
+  parts = Split("", ",", false, true);
+  EXPECT_EQ(0u, parts.size());
 
   parts = Split("abc", ",", false, false);
-  EXPECT_EQ(1, parts.size());
+  EXPECT_EQ(1u, parts.size());
   EXPECT_EQ("abc", parts[0]);
 
   parts = Split(",a,bc , d,  ,e, ", ",", true, true);
-  EXPECT_EQ(4, parts.size());
+  EXPECT_EQ(4u, parts.size());
   EXPECT_EQ("a", parts[0]);
   EXPECT_EQ("bc", parts[1]);
   EXPECT_EQ("d", parts[2]);
   EXPECT_EQ("e", parts[3]);
 
   parts = Split(",a,bc , d,  ,e, ", ",", false, true);
-  EXPECT_EQ(6, parts.size());
+  EXPECT_EQ(6u, parts.size());
   EXPECT_EQ("a", parts[0]);
   EXPECT_EQ("bc ", parts[1]);
   EXPECT_EQ(" d", parts[2]);
@@ -40,7 +44,7 @@
   EXPECT_EQ(" ", parts[5]);
 
   parts = Split(",a,bc , d,  ,e, ", ",", true, false);
-  EXPECT_EQ(7, parts.size());
+  EXPECT_EQ(7u, parts.size());
   EXPECT_EQ("", parts[0]);
   EXPECT_EQ("a", parts[1]);
   EXPECT_EQ("bc", parts[2]);
@@ -50,7 +54,7 @@
   EXPECT_EQ("", parts[6]);
 
   parts = Split(",a,bc , d,  ,e, ", ",", false, false);
-  EXPECT_EQ(7, parts.size());
+  EXPECT_EQ(7u, parts.size());
   EXPECT_EQ("", parts[0]);
   EXPECT_EQ("a", parts[1]);
   EXPECT_EQ("bc ", parts[2]);
@@ -60,12 +64,12 @@
   EXPECT_EQ(" ", parts[6]);
 
   parts = Split("abc:=xyz", ":=", false, false);
-  EXPECT_EQ(2, parts.size());
+  EXPECT_EQ(2u, parts.size());
   EXPECT_EQ("abc", parts[0]);
   EXPECT_EQ("xyz", parts[1]);
 
   parts = Split("abc", "", false, false);
-  EXPECT_EQ(3, parts.size());
+  EXPECT_EQ(3u, parts.size());
   EXPECT_EQ("a", parts[0]);
   EXPECT_EQ("b", parts[1]);
   EXPECT_EQ("c", parts[2]);
diff --git a/src/test/mock_command.cc b/src/test/mock_command.cc
deleted file mode 100644
index a8564c4..0000000
--- a/src/test/mock_command.cc
+++ /dev/null
@@ -1,30 +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 <weave/test/mock_command.h>
-
-#include <memory>
-#include <string>
-
-#include <base/values.h>
-
-#include "src/commands/unittest_utils.h"
-
-namespace weave {
-namespace test {
-
-std::unique_ptr<base::DictionaryValue> MockCommand::GetParameters() const {
-  return CreateDictionaryValue(MockGetParameters());
-}
-
-std::unique_ptr<base::DictionaryValue> MockCommand::GetProgress() const {
-  return CreateDictionaryValue(MockGetProgress());
-}
-
-std::unique_ptr<base::DictionaryValue> MockCommand::GetResults() const {
-  return CreateDictionaryValue(MockGetResults());
-}
-
-}  // namespace test
-}  // namespace weave
diff --git a/src/test/unittest_utils.cc b/src/test/unittest_utils.cc
index b9858e4..df8ccc0 100644
--- a/src/test/unittest_utils.cc
+++ b/src/test/unittest_utils.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 "src/commands/unittest_utils.h"
+#include <weave/test/unittest_utils.h>
 
 #include <base/json/json_reader.h>
 #include <base/json/json_writer.h>
diff --git a/src/weave_unittest.cc b/src/weave_unittest.cc
index 2e41852..66850cf 100644
--- a/src/weave_unittest.cc
+++ b/src/weave_unittest.cc
@@ -45,12 +45,25 @@
 using test::CreateDictionaryValue;
 using test::ValueToString;
 
-const char kCommandDefs[] = R"({
-  "base": {
-    "reboot": {},
-    "_shutdown": {
-      "parameters": {},
-      "results": {}
+const char kTraitDefs[] = R"({
+  "trait1": {
+    "commands": {
+      "reboot": {
+        "minimalRole": "user"
+      },
+      "shutdown": {
+        "minimalRole": "user",
+        "parameters": {},
+        "results": {}
+      }
+    },
+    "state": {
+      "firmwareVersion": {"type": "string"}
+    }
+  },
+  "trait2": {
+    "state": {
+      "battery_level": {"type": "integer"}
     }
   }
 })";
@@ -69,7 +82,7 @@
   "description": "Developer device",
   "stateValidationEnabled": true,
   "commandDefs":{
-    "base": {
+    "trait1": {
       "reboot": {
         "minimalRole": "user",
         "parameters": {"delay": {"type": "integer"}},
@@ -83,15 +96,39 @@
     }
   },
   "state":{
-    "base":{
-      "firmwareVersion":"FIRMWARE_VERSION",
-      "localAnonymousAccessMaxRole":"viewer",
-      "localDiscoveryEnabled":true,
-      "localPairingEnabled":true,
-      "network":{
+    "trait1": {"firmwareVersion":"FIRMWARE_VERSION"},
+    "trait2": {"battery_level":44}
+  },
+  "traits": {
+    "trait1": {
+      "commands": {
+        "reboot": {
+          "minimalRole": "user"
+        },
+        "shutdown": {
+          "minimalRole": "user",
+          "parameters": {},
+          "results": {}
+        }
+      },
+      "state": {
+        "firmwareVersion": {"type": "string"}
       }
     },
-    "power": {"battery_level":44}
+    "trait2": {
+      "state": {
+        "battery_level": {"type": "integer"}
+      }
+    }
+  },
+  "components": {
+    "myComponent": {
+      "traits": ["trait1", "trait2"],
+      "state": {
+        "trait1": {"firmwareVersion":"FIRMWARE_VERSION"},
+        "trait2": {"battery_level":44}
+      }
+    }
   }
 })";
 
@@ -124,10 +161,6 @@
   "refresh_token" : "REFRESH_TOKEN"
 })";
 
-const char kStateDefs[] = R"({"power": {"battery_level":"integer"}})";
-
-const char kStateDefaults[] = R"({"power": {"battery_level":44}})";
-
 MATCHER_P(MatchTxt, txt, "") {
   std::vector<std::string> txt_copy = txt;
   std::sort(txt_copy.begin(), txt_copy.end());
@@ -165,7 +198,6 @@
               .Times(AtLeast(1))
               .WillRepeatedly(Return("application/json; charset=utf-8"));
           EXPECT_CALL(*response, GetData())
-              .Times(AtLeast(1))
               .WillRepeatedly(Return(json_response));
           callback.Run(std::move(response), nullptr);
         })));
@@ -256,14 +288,17 @@
                   "/privet/v3/checkForUpdates", "/privet/v3/commandDefs",
                   "/privet/v3/commands/cancel", "/privet/v3/commands/execute",
                   "/privet/v3/commands/list", "/privet/v3/commands/status",
-                  "/privet/v3/pairing/cancel", "/privet/v3/pairing/confirm",
-                  "/privet/v3/pairing/start", "/privet/v3/setup/start",
-                  "/privet/v3/setup/status", "/privet/v3/state"}),
+                  "/privet/v3/components", "/privet/v3/pairing/cancel",
+                  "/privet/v3/pairing/confirm", "/privet/v3/pairing/start",
+                  "/privet/v3/setup/start", "/privet/v3/setup/status",
+                  "/privet/v3/state", "/privet/v3/traits"}),
               GetKeys(https_handlers_));
 
-    device_->AddCommandDefinitionsFromJson(kCommandDefs);
-    device_->AddStateDefinitionsFromJson(kStateDefs);
-    device_->SetStatePropertiesFromJson(kStateDefaults, nullptr);
+    device_->AddTraitDefinitionsFromJson(kTraitDefs);
+    EXPECT_TRUE(device_->AddComponent("myComponent", {"trait1", "trait2"},
+                                      nullptr));
+    EXPECT_TRUE(device_->SetStatePropertiesFromJson(
+        "myComponent", R"({"trait2": {"battery_level":44}})", nullptr));
 
     task_runner_.Run();
   }
@@ -321,7 +356,9 @@
   device_ = weave::Device::Create(&config_store_, &task_runner_, &http_client_,
                                   &network_, &dns_sd_, &http_server_, nullptr,
                                   &bluetooth_);
-  device_->AddCommandDefinitionsFromJson(kCommandDefs);
+  device_->AddTraitDefinitionsFromJson(kTraitDefs);
+  EXPECT_TRUE(device_->AddComponent("myComponent", {"trait1", "trait2"},
+                                    nullptr));
 
   task_runner_.Run();
 }
diff --git a/third_party/chromium/base/basictypes.h b/third_party/chromium/base/basictypes.h
index 154688d..d71abd9 100644
--- a/third_party/chromium/base/basictypes.h
+++ b/third_party/chromium/base/basictypes.h
@@ -16,7 +16,7 @@
 #include <stdint.h>  // For intptr_t.
 
 #include "base/macros.h"
-#include "base/build/build_config.h"
+#include "build/build_config.h"
 
 // DEPRECATED: Please use (u)int{8,16,32,64}_t instead (and include <stdint.h>).
 typedef int8_t int8;
diff --git a/third_party/chromium/base/bind_internal.h b/third_party/chromium/base/bind_internal.h
index 6d500a5..613f7a9 100644
--- a/third_party/chromium/base/bind_internal.h
+++ b/third_party/chromium/base/bind_internal.h
@@ -6,11 +6,11 @@
 #define BASE_BIND_INTERNAL_H_
 
 #include "base/bind_helpers.h"
-#include "base/build/build_config.h"
 #include "base/callback_internal.h"
 #include "base/memory/weak_ptr.h"
 #include "base/template_util.h"
 #include "base/tuple.h"
+#include "build/build_config.h"
 
 #if defined(OS_WIN)
 #include "base/bind_internal_win.h"
diff --git a/third_party/chromium/base/compiler_specific.h b/third_party/chromium/base/compiler_specific.h
index 8c2ba34..63297dc 100644
--- a/third_party/chromium/base/compiler_specific.h
+++ b/third_party/chromium/base/compiler_specific.h
@@ -5,7 +5,7 @@
 #ifndef BASE_COMPILER_SPECIFIC_H_
 #define BASE_COMPILER_SPECIFIC_H_
 
-#include "base/build/build_config.h"
+#include "build/build_config.h"
 
 #if defined(COMPILER_MSVC)
 
diff --git a/third_party/chromium/base/guid.h b/third_party/chromium/base/guid.h
index a43a223..11fd812 100644
--- a/third_party/chromium/base/guid.h
+++ b/third_party/chromium/base/guid.h
@@ -9,7 +9,7 @@
 
 #include "base/base_export.h"
 #include "base/basictypes.h"
-#include "base/build/build_config.h"
+#include "build/build_config.h"
 
 namespace base {
 
diff --git a/third_party/chromium/base/json/json_reader_unittest.cc b/third_party/chromium/base/json/json_reader_unittest.cc
index a5f2530..35becef 100644
--- a/third_party/chromium/base/json/json_reader_unittest.cc
+++ b/third_party/chromium/base/json/json_reader_unittest.cc
@@ -6,12 +6,12 @@
 
 #include <gtest/gtest.h>
 
-#include "base/build/build_config.h"
 #include "base/logging.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/utf_string_conversion_utils.h"
 #include "base/values.h"
+#include "build/build_config.h"
 
 namespace base {
 
diff --git a/third_party/chromium/base/location.cc b/third_party/chromium/base/location.cc
index 4b57912..98f874a 100644
--- a/third_party/chromium/base/location.cc
+++ b/third_party/chromium/base/location.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 "base/build/build_config.h"
+#include "build/build_config.h"
 
 #if defined(COMPILER_MSVC)
 #include <intrin.h>
diff --git a/third_party/chromium/base/logging.h b/third_party/chromium/base/logging.h
index 61e9f9d..339f8a6 100644
--- a/third_party/chromium/base/logging.h
+++ b/third_party/chromium/base/logging.h
@@ -12,7 +12,7 @@
 
 #include "base/base_export.h"
 #include "base/basictypes.h"
-#include "base/build/build_config.h"
+#include "build/build_config.h"
 
 //
 // Optional message capabilities
diff --git a/third_party/chromium/base/memory/ref_counted.h b/third_party/chromium/base/memory/ref_counted.h
index 23b9038..52e8f00 100644
--- a/third_party/chromium/base/memory/ref_counted.h
+++ b/third_party/chromium/base/memory/ref_counted.h
@@ -10,10 +10,10 @@
 #include <iosfwd>
 
 #include "base/base_export.h"
-#include "base/build/build_config.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/move.h"
+#include "build/build_config.h"
 
 namespace base {
 
diff --git a/third_party/chromium/base/posix/eintr_wrapper.h b/third_party/chromium/base/posix/eintr_wrapper.h
index 27de8fc..5a5dc75 100644
--- a/third_party/chromium/base/posix/eintr_wrapper.h
+++ b/third_party/chromium/base/posix/eintr_wrapper.h
@@ -16,7 +16,7 @@
 #ifndef BASE_POSIX_EINTR_WRAPPER_H_
 #define BASE_POSIX_EINTR_WRAPPER_H_
 
-#include "base/build/build_config.h"
+#include "build/build_config.h"
 
 #if defined(OS_POSIX)
 
diff --git a/third_party/chromium/base/template_util.h b/third_party/chromium/base/template_util.h
index f8f507b..4b1c808 100644
--- a/third_party/chromium/base/template_util.h
+++ b/third_party/chromium/base/template_util.h
@@ -8,7 +8,7 @@
 #include <cstddef>  // For size_t.
 #include <type_traits>
 
-#include "base/build/build_config.h"
+#include "build/build_config.h"
 
 namespace base {
 
diff --git a/third_party/chromium/base/time/time.h b/third_party/chromium/base/time/time.h
index f421539..1fe1b85 100644
--- a/third_party/chromium/base/time/time.h
+++ b/third_party/chromium/base/time/time.h
@@ -61,8 +61,8 @@
 
 #include "base/base_export.h"
 #include "base/basictypes.h"
-#include "base/build/build_config.h"
 #include "base/numerics/safe_math.h"
+#include "build/build_config.h"
 
 #if defined(OS_MACOSX)
 #include <CoreFoundation/CoreFoundation.h>
diff --git a/third_party/chromium/base/time/time_posix.cc b/third_party/chromium/base/time/time_posix.cc
index b625af6..e8e56c2 100644
--- a/third_party/chromium/base/time/time_posix.cc
+++ b/third_party/chromium/base/time/time_posix.cc
@@ -16,8 +16,8 @@
 #include <ostream>
 
 #include "base/basictypes.h"
-#include "base/build/build_config.h"
 #include "base/logging.h"
+#include "build/build_config.h"
 
 namespace {
 
@@ -117,7 +117,7 @@
 //   => Thu Jan 01 00:00:00 UTC 1970
 //   irb(main):011:0> Time.at(-11644473600).getutc()
 //   => Mon Jan 01 00:00:00 UTC 1601
-static const int64 kWindowsEpochDeltaSeconds = INT64_C(11644473600);
+static const int64 kWindowsEpochDeltaSeconds = 11644473600ll;
 
 // static
 const int64 Time::kWindowsEpochDeltaMicroseconds =
diff --git a/third_party/chromium/base/time/time_unittest.cc b/third_party/chromium/base/time/time_unittest.cc
index 43373e7..737796c 100644
--- a/third_party/chromium/base/time/time_unittest.cc
+++ b/third_party/chromium/base/time/time_unittest.cc
@@ -14,7 +14,7 @@
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/strings/stringprintf.h"
-#include "base/build/build_config.h"
+#include "build/build_config.h"
 
 namespace base {
 
@@ -584,7 +584,7 @@
   exploded.millisecond = 0;
   Time t = Time::FromUTCExploded(exploded);
   // Unix 1970 epoch.
-  EXPECT_EQ(INT64_C(11644473600000000), t.ToInternalValue());
+  EXPECT_EQ(11644473600000000ll, t.ToInternalValue());
 
   // We can't test 1601 epoch, since the system time functions on Linux
   // only compute years starting from 1900.
diff --git a/third_party/chromium/base/build/build_config.h b/third_party/chromium/build/build_config.h
similarity index 95%
rename from third_party/chromium/base/build/build_config.h
rename to third_party/chromium/build/build_config.h
index 904965a..347a636 100644
--- a/third_party/chromium/base/build/build_config.h
+++ b/third_party/chromium/build/build_config.h
@@ -147,6 +147,16 @@
 #define ARCH_CPU_32_BITS 1
 #define ARCH_CPU_LITTLE_ENDIAN 1
 #endif
+#elif defined(__mips__)
+#if defined(__LP64__)
+#define ARCH_CPU_MIPS64_FAMILY 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#else
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_BIG_ENDIAN 1
+#endif
 #else
 #error Please add support for your architecture in build/build_config.h
 #endif
diff --git a/third_party/libuweave/LICENSE b/third_party/libuweave/LICENSE
new file mode 100644
index 0000000..942662d
--- /dev/null
+++ b/third_party/libuweave/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2015, The Weave Authors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+     * Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+     * Redistributions in binary form must reproduce the above copyright
+       notice, this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+     * Neither the name of The Weave Authors nor the
+       names of its contributors may be used to endorse or promote products
+       derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/third_party/libuweave/README b/third_party/libuweave/README
new file mode 100644
index 0000000..f170307
--- /dev/null
+++ b/third_party/libuweave/README
@@ -0,0 +1,4 @@
+URL: https://weave.googlesource.com/weave/libuweave/+/master
+
+Description:
+Macaroon related code from libuweave.
diff --git a/third_party/libuweave/src/crypto_hmac.c b/third_party/libuweave/src/crypto_hmac.c
new file mode 100644
index 0000000..56bb754
--- /dev/null
+++ b/third_party/libuweave/src/crypto_hmac.c
@@ -0,0 +1,64 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/crypto_hmac.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+size_t uw_crypto_hmac_required_buffer_size_() {
+  return sizeof(HMAC_CTX);
+}
+
+bool uw_crypto_hmac_init_(uint8_t* state_buffer,
+                          size_t state_buffer_len,
+                          const uint8_t* key,
+                          size_t key_len) {
+  if (sizeof(HMAC_CTX) > state_buffer_len) {
+    return false;
+  }
+  HMAC_CTX* context = (HMAC_CTX*)state_buffer;
+  HMAC_CTX_init(context);
+  return HMAC_Init(context, key, key_len, EVP_sha256()) ? 0 : sizeof(HMAC_CTX);
+}
+
+bool uw_crypto_hmac_update_(uint8_t* state_buffer,
+                            size_t state_buffer_len,
+                            const uint8_t* data,
+                            size_t data_len) {
+  if (sizeof(HMAC_CTX) > state_buffer_len) {
+    return false;
+  }
+  HMAC_CTX* context = (HMAC_CTX*)state_buffer;
+  return HMAC_Update(context, data, data_len);
+}
+
+bool uw_crypto_hmac_final_(uint8_t* state_buffer,
+                           size_t state_buffer_len,
+                           uint8_t* truncated_digest,
+                           size_t truncated_digest_len) {
+  if (sizeof(HMAC_CTX) > state_buffer_len) {
+    return false;
+  }
+  HMAC_CTX* context = (HMAC_CTX*)state_buffer;
+
+  const size_t kFullDigestLen = (size_t)EVP_MD_size(EVP_sha256());
+  if (truncated_digest_len > kFullDigestLen) {
+    return false;
+  }
+
+  uint8_t digest[kFullDigestLen];
+  uint32_t len = kFullDigestLen;
+
+  bool result = HMAC_Final(context, digest, &len) && kFullDigestLen == len;
+  HMAC_CTX_cleanup(context);
+  if (result) {
+    memcpy(truncated_digest, digest, truncated_digest_len);
+  }
+  return result;
+}
diff --git a/third_party/libuweave/src/crypto_hmac.h b/third_party/libuweave/src/crypto_hmac.h
new file mode 100644
index 0000000..bac634a
--- /dev/null
+++ b/third_party/libuweave/src/crypto_hmac.h
@@ -0,0 +1,29 @@
+// 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 LIBUWEAVE_SRC_CRYPTO_HMAC_H_
+#define LIBUWEAVE_SRC_CRYPTO_HMAC_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// Return the minimum required number of bytes for the state_buffer used in the
+// init, update and final functions.
+size_t uw_crypto_hmac_required_buffer_size_();
+
+bool uw_crypto_hmac_init_(uint8_t* state_buffer,
+                          size_t state_buffer_len,
+                          const uint8_t* key,
+                          size_t key_len);
+bool uw_crypto_hmac_update_(uint8_t* state_buffer,
+                            size_t state_buffer_len,
+                            const uint8_t* data,
+                            size_t data_len);
+bool uw_crypto_hmac_final_(uint8_t* state_buffer,
+                           size_t state_buffer_len,
+                           uint8_t* truncated_digest,
+                           size_t truncated_digest_len);
+
+#endif  // LIBUWEAVE_SRC_CRYPTO_HMAC_H_
diff --git a/third_party/libuweave/src/crypto_utils.c b/third_party/libuweave/src/crypto_utils.c
new file mode 100644
index 0000000..75bd2e5
--- /dev/null
+++ b/third_party/libuweave/src/crypto_utils.c
@@ -0,0 +1,23 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/crypto_utils.h"
+
+bool uw_crypto_utils_equal_(const uint8_t* arr1,
+                            const uint8_t* arr2,
+                            size_t len) {
+  if (arr1 == NULL || arr2 == NULL) {
+    if (arr1 == NULL && arr2 == NULL && len == 0) {
+      return true;
+    }
+    return false;
+  }
+
+  uint8_t diff = 0;
+  for (size_t i = 0; i < len; i++) {
+    diff |= arr1[0] ^ arr2[0];
+  }
+
+  return 0 == diff;
+}
diff --git a/third_party/libuweave/src/crypto_utils.h b/third_party/libuweave/src/crypto_utils.h
new file mode 100644
index 0000000..54bfcae
--- /dev/null
+++ b/third_party/libuweave/src/crypto_utils.h
@@ -0,0 +1,22 @@
+// 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 LIBUWEAVE_SRC_CRYPTO_UTILS_H_
+#define LIBUWEAVE_SRC_CRYPTO_UTILS_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/**
+ * Check if two byte arrays are the same in constant time (the running time
+ * should only depend on the length of the given arrays). It's critical to use
+ * constant-time methods to compare secret data. Timing information can lead to
+ * full recovery of the secret data.
+ */
+bool uw_crypto_utils_equal_(const uint8_t* arr1,
+                            const uint8_t* arr2,
+                            size_t len);
+
+#endif  // LIBUWEAVE_SRC_CRYPTO_UTILS_H_
diff --git a/third_party/libuweave/src/macaroon.c b/third_party/libuweave/src/macaroon.c
new file mode 100644
index 0000000..d7e6491
--- /dev/null
+++ b/third_party/libuweave/src/macaroon.c
@@ -0,0 +1,126 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/macaroon.h"
+
+#include <string.h>
+
+#include "src/crypto_utils.h"
+
+static bool create_mac_tag_(const uint8_t* key, size_t key_len,
+                            const UwMacaroonCaveat* caveats, size_t num_caveats,
+                            uint8_t mac_tag[UW_MACAROON_MAC_LEN]) {
+  if (key == NULL || key_len == 0 || caveats == NULL || num_caveats == 0 ||
+      mac_tag == NULL) {
+    return false;
+  }
+
+  // Store the intermediate MAC tags in an internal buffer before we finish the
+  // whole computation.
+  // If we use the output buffer mac_tag directly and certain errors happen in
+  // the middle of this computation, mac_tag will probably contain a valid
+  // macaroon tag with large scope than expected.
+  uint8_t mac_tag_buff[UW_MACAROON_MAC_LEN];
+
+  // Compute the first tag by using the key
+  if (!uw_macaroon_caveat_sign_(key, key_len, &(caveats[0]), mac_tag_buff,
+                                UW_MACAROON_MAC_LEN)) {
+    return false;
+  }
+
+  // Compute the rest of the tags by using the tag as the key
+  for (size_t i = 1; i < num_caveats; i++) {
+    if (!uw_macaroon_caveat_sign_(mac_tag_buff, UW_MACAROON_MAC_LEN,
+                                  &(caveats[i]), mac_tag_buff,
+                                  UW_MACAROON_MAC_LEN)) {
+      return false;
+    }
+  }
+
+  memcpy(mac_tag, mac_tag_buff, UW_MACAROON_MAC_LEN);
+  return true;
+}
+
+bool uw_macaroon_new_from_mac_tag_(UwMacaroon* new_macaroon,
+                                   const uint8_t mac_tag[UW_MACAROON_MAC_LEN],
+                                   const UwMacaroonCaveat* caveats,
+                                   size_t num_caveats) {
+  if (new_macaroon == NULL || mac_tag == NULL || caveats == NULL ||
+      num_caveats == 0) {
+    return false;
+  }
+
+  memcpy(new_macaroon->mac_tag, mac_tag, UW_MACAROON_MAC_LEN);
+  new_macaroon->num_caveats = num_caveats;
+  new_macaroon->caveats = caveats;
+
+  return true;
+}
+
+bool uw_macaroon_new_from_root_key_(UwMacaroon* new_macaroon,
+                                    const uint8_t* root_key,
+                                    size_t root_key_len,
+                                    const UwMacaroonCaveat* caveats,
+                                    size_t num_caveats) {
+  if (new_macaroon == NULL || root_key == NULL || root_key_len == 0 ||
+      caveats == NULL || num_caveats == 0) {
+    return false;
+  }
+
+  if (!create_mac_tag_(root_key, root_key_len, caveats, num_caveats,
+                       new_macaroon->mac_tag)) {
+    return false;
+  }
+
+  new_macaroon->num_caveats = num_caveats;
+  new_macaroon->caveats = caveats;
+
+  return true;
+}
+
+bool uw_macaroon_verify_(const UwMacaroon* macaroon,
+                         const uint8_t* root_key,
+                         size_t root_key_len) {
+  if (macaroon == NULL || root_key == NULL) {
+    return false;
+  }
+
+  uint8_t mac_tag[UW_MACAROON_MAC_LEN] = {0};
+  if (!create_mac_tag_(root_key, root_key_len, macaroon->caveats,
+                       macaroon->num_caveats, mac_tag)) {
+    return false;
+  }
+
+  return uw_crypto_utils_equal_(mac_tag, macaroon->mac_tag,
+                                UW_MACAROON_MAC_LEN);
+}
+
+bool uw_macaroon_extend_(const UwMacaroon* old_macaroon,
+                         UwMacaroon* new_macaroon,
+                         const UwMacaroonCaveat* additional_caveat,
+                         uint8_t* buffer, size_t buffer_size) {
+  if (old_macaroon == NULL || new_macaroon == NULL ||
+      additional_caveat == NULL || buffer == NULL || buffer_size == 0) {
+    return false;
+  }
+
+  new_macaroon->num_caveats = old_macaroon->num_caveats + 1;
+
+  // Extend the caveat list
+  if ((new_macaroon->num_caveats) * sizeof(UwMacaroonCaveat) > buffer_size) {
+    // Not enough memory to store the extended caveat list
+    return false;
+  }
+  UwMacaroonCaveat* extended_list = (UwMacaroonCaveat*)buffer;
+  if (old_macaroon->caveats != NULL && extended_list != old_macaroon->caveats) {
+    memcpy(extended_list, old_macaroon->caveats,
+           (old_macaroon->num_caveats) * sizeof(UwMacaroonCaveat));
+  }
+  extended_list[old_macaroon->num_caveats] = *additional_caveat;
+  new_macaroon->caveats = extended_list;
+
+  // Compute the new MAC tag
+  return create_mac_tag_(old_macaroon->mac_tag, UW_MACAROON_MAC_LEN,
+                         additional_caveat, 1, new_macaroon->mac_tag);
+}
diff --git a/third_party/libuweave/src/macaroon.h b/third_party/libuweave/src/macaroon.h
new file mode 100644
index 0000000..98ada11
--- /dev/null
+++ b/third_party/libuweave/src/macaroon.h
@@ -0,0 +1,47 @@
+// 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 LIBUWEAVE_SRC_MACAROON_H_
+#define LIBUWEAVE_SRC_MACAROON_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "macaroon_caveat.h"
+
+#define UW_MACAROON_MAC_LEN 16
+
+// Note: If we are looking to make memory savings on MCUs,
+// at the cost of a little extra processing, we can make
+// the macaroon encoding the actual in-memory representation.
+// This can save much copying of macaroon data if need be.
+typedef struct {
+  uint8_t mac_tag[UW_MACAROON_MAC_LEN];
+  size_t num_caveats;
+  const UwMacaroonCaveat* caveats;
+} UwMacaroon;
+
+bool uw_macaroon_new_from_mac_tag_(UwMacaroon* new_macaroon,
+                                   const uint8_t mac_tag[UW_MACAROON_MAC_LEN],
+                                   const UwMacaroonCaveat* caveats,
+                                   size_t num_caveats);
+
+bool uw_macaroon_new_from_root_key_(UwMacaroon* new_macaroon,
+                                    const uint8_t* root_key,
+                                    size_t root_key_len,
+                                    const UwMacaroonCaveat* caveats,
+                                    size_t num_caveats);
+
+bool uw_macaroon_verify_(const UwMacaroon* macaroon,
+                         const uint8_t* root_key,
+                         size_t root_key_len);
+
+// Create a new macaroon with a new caveat
+bool uw_macaroon_extend_(const UwMacaroon* old_macaroon,
+                         UwMacaroon* new_macaroon,
+                         const UwMacaroonCaveat* additional_caveat,
+                         uint8_t* buffer, size_t buffer_size);
+
+#endif  // LIBUWEAVE_SRC_MACAROON_H_
diff --git a/third_party/libuweave/src/macaroon_caveat.c b/third_party/libuweave/src/macaroon_caveat.c
new file mode 100644
index 0000000..a04c30d
--- /dev/null
+++ b/third_party/libuweave/src/macaroon_caveat.c
@@ -0,0 +1,247 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/macaroon_caveat.h"
+
+#include <string.h>
+
+#include "src/crypto_hmac.h"
+#include "src/macaroon_context.h"
+#include "src/macaroon_encoding.h"
+
+// TODO(bozhu): Find a better way to pre-allocate memory for HMACc computations?
+// Are C99 variable-length arrays allowed on embedded devices?
+#define HMAC_STATE_BUFFER_SIZE 300
+
+static bool create_caveat_(UwMacaroonCaveatType type, const void* value,
+                           size_t value_len, uint8_t* buffer,
+                           size_t buffer_size, UwMacaroonCaveat* caveat) {
+  if (buffer == NULL || buffer_size == 0 || caveat == NULL) {
+    // Here value can be NULL, and value_len can be 0
+    return false;
+  }
+
+  caveat->bytes = buffer;
+  size_t encoded_str_len, total_str_len;
+
+  uint32_t unsigned_int = (uint32_t)type;
+  if (!uw_macaroon_encoding_encode_uint_(unsigned_int, buffer, buffer_size,
+                                         &encoded_str_len)) {
+    return false;
+  }
+  total_str_len = encoded_str_len;
+  buffer += encoded_str_len;
+  buffer_size -= encoded_str_len;
+
+  switch (type) {
+    case kUwMacaroonCaveatTypeStop:
+    case kUwMacaroonCaveatTypeSessionIdentifier:
+      // No value
+      encoded_str_len = 0;
+      break;
+
+    case kUwMacaroonCaveatTypeScope:
+    case kUwMacaroonCaveatTypeIssued:
+    case kUwMacaroonCaveatTypeTTL:
+    case kUwMacaroonCaveatTypeExpiration:
+      // Integer
+      if (value_len != sizeof(uint32_t)) {
+        // Wrong size for integers
+        return false;
+      }
+      unsigned_int = *((uint32_t*)value);
+      if (!uw_macaroon_encoding_encode_uint_(unsigned_int, buffer, buffer_size,
+                                             &encoded_str_len)) {
+        return false;
+      }
+      break;
+
+    case kUwMacaroonCaveatTypeIdentifier:
+      // Text string
+      if (!uw_macaroon_encoding_encode_text_str_((uint8_t*)value, value_len,
+                                                 buffer, buffer_size,
+                                                 &encoded_str_len)) {
+        return false;
+      }
+      break;
+
+    default:
+      // Should never reach here
+      return false;
+  }
+
+  total_str_len += encoded_str_len;
+  caveat->num_bytes = total_str_len;
+  return true;
+}
+
+bool uw_macaroon_caveat_create_without_value_(UwMacaroonCaveatType type,
+                                              uint8_t* buffer,
+                                              size_t buffer_size,
+                                              UwMacaroonCaveat* caveat) {
+  if (buffer == NULL || buffer_size == 0 || caveat == NULL) {
+    return false;
+  }
+  if (type != kUwMacaroonCaveatTypeStop &&
+      type != kUwMacaroonCaveatTypeSessionIdentifier) {
+    return false;
+  }
+
+  return create_caveat_(type, NULL, 0, buffer, buffer_size, caveat);
+}
+
+bool uw_macaroon_caveat_create_with_uint_(UwMacaroonCaveatType type,
+                                          uint32_t value, uint8_t* buffer,
+                                          size_t buffer_size,
+                                          UwMacaroonCaveat* caveat) {
+  if (buffer == NULL || buffer_size == 0 || caveat == NULL) {
+    return false;
+  }
+  if (type != kUwMacaroonCaveatTypeScope &&
+      type != kUwMacaroonCaveatTypeIssued &&
+      type != kUwMacaroonCaveatTypeTTL &&
+      type != kUwMacaroonCaveatTypeExpiration) {
+    return false;
+  }
+
+  return create_caveat_(type, &value, sizeof(uint32_t), buffer, buffer_size,
+                        caveat);
+}
+
+bool uw_macaroon_caveat_create_with_str_(UwMacaroonCaveatType type,
+                                         const uint8_t* str, size_t str_len,
+                                         uint8_t* buffer, size_t buffer_size,
+                                         UwMacaroonCaveat* caveat) {
+  if (buffer == NULL || buffer_size == 0 || caveat == NULL ||
+      (str == NULL && str_len != 0)) {
+    return false;
+  }
+  if (type != kUwMacaroonCaveatTypeIdentifier) {
+    return false;
+  }
+
+  return create_caveat_(type, str, str_len, buffer, buffer_size, caveat);
+}
+
+bool uw_macaroon_caveat_get_type_(const UwMacaroonCaveat* caveat,
+                                  UwMacaroonCaveatType* type) {
+  if (caveat == NULL || type == NULL) {
+    return false;
+  }
+
+  uint32_t unsigned_int;
+  if (!uw_macaroon_encoding_decode_uint_(caveat->bytes, caveat->num_bytes,
+                                         &unsigned_int)) {
+    return false;
+  }
+
+  *type = (UwMacaroonCaveatType)unsigned_int;
+
+  if (*type != kUwMacaroonCaveatTypeStop &&
+      *type != kUwMacaroonCaveatTypeScope &&
+      *type != kUwMacaroonCaveatTypeIdentifier &&
+      *type != kUwMacaroonCaveatTypeIssued &&
+      *type != kUwMacaroonCaveatTypeTTL &&
+      *type != kUwMacaroonCaveatTypeExpiration &&
+      *type != kUwMacaroonCaveatTypeSessionIdentifier) {
+    return false;
+  }
+
+  return true;
+}
+
+bool uw_macaroon_caveat_get_value_uint_(const UwMacaroonCaveat* caveat,
+                                        uint32_t* unsigned_int) {
+  if (caveat == NULL || unsigned_int == NULL) {
+    return false;
+  }
+
+  UwMacaroonCaveatType type;
+  if (!uw_macaroon_caveat_get_type_(caveat, &type)) {
+    return false;
+  }
+  if (type != kUwMacaroonCaveatTypeScope &&
+      type != kUwMacaroonCaveatTypeIssued &&
+      type != kUwMacaroonCaveatTypeTTL &&
+      type != kUwMacaroonCaveatTypeExpiration) {
+    // Wrong type
+    return false;
+  }
+
+  size_t offset;
+  if (!uw_macaroon_encoding_get_item_len_(caveat->bytes, caveat->num_bytes,
+                                          &offset)) {
+    return false;
+  }
+
+  return uw_macaroon_encoding_decode_uint_(
+      caveat->bytes + offset, caveat->num_bytes - offset, unsigned_int);
+}
+
+bool uw_macaroon_caveat_get_value_str_(const UwMacaroonCaveat* caveat,
+                                       const uint8_t** str, size_t* str_len) {
+  if (caveat == NULL || str == NULL || str_len == NULL) {
+    return false;
+  }
+
+  UwMacaroonCaveatType type;
+  if (!uw_macaroon_caveat_get_type_(caveat, &type)) {
+    return false;
+  }
+  if (type != kUwMacaroonCaveatTypeIdentifier) {
+    // Wrong type
+    return false;
+  }
+
+  size_t offset;
+  if (!uw_macaroon_encoding_get_item_len_(caveat->bytes, caveat->num_bytes,
+                                          &offset)) {
+    return false;
+  }
+
+  return uw_macaroon_encoding_decode_text_str_(
+      caveat->bytes + offset, caveat->num_bytes - offset, str, str_len);
+}
+
+bool uw_macaroon_caveat_sign_(const uint8_t* key, size_t key_len,
+                              const UwMacaroonCaveat* caveat, uint8_t* mac_tag,
+                              size_t mac_tag_size) {
+  if (key == NULL || key_len == 0 || caveat == NULL || mac_tag == NULL ||
+      mac_tag_size == 0) {
+    return false;
+  }
+
+  uint8_t hmac_state_buffer[HMAC_STATE_BUFFER_SIZE];
+  if (HMAC_STATE_BUFFER_SIZE < uw_crypto_hmac_required_buffer_size_()) {
+    return false;
+  }
+
+  if (!uw_crypto_hmac_init_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE, key,
+                            key_len)) {
+    return false;
+  }
+
+  if (!uw_crypto_hmac_update_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE,
+                              caveat->bytes, caveat->num_bytes)) {
+    return false;
+  }
+
+  const uint8_t* context;
+  size_t context_len;
+  UwMacaroonCaveatType caveat_type;
+
+  if ((!uw_macaroon_caveat_get_type_(caveat, &caveat_type)) ||
+      (!uw_macaroon_context_get_(caveat_type, &context, &context_len))) {
+    return false;
+  }
+  if (context != NULL && context_len != 0) {
+    if (!uw_crypto_hmac_update_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE,
+                                context, context_len)) {
+      return false;
+    }
+  }
+
+  return uw_crypto_hmac_final_(hmac_state_buffer, HMAC_STATE_BUFFER_SIZE,
+                               mac_tag, mac_tag_size);
+}
diff --git a/third_party/libuweave/src/macaroon_caveat.h b/third_party/libuweave/src/macaroon_caveat.h
new file mode 100644
index 0000000..5f2c384
--- /dev/null
+++ b/third_party/libuweave/src/macaroon_caveat.h
@@ -0,0 +1,51 @@
+// 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 LIBUWEAVE_SRC_MACAROON_CAVEAT_H_
+#define LIBUWEAVE_SRC_MACAROON_CAVEAT_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+  size_t num_bytes;
+  const uint8_t* bytes;
+} UwMacaroonCaveat;
+
+typedef enum {
+  kUwMacaroonCaveatTypeStop = 0,
+  kUwMacaroonCaveatTypeScope = 1,
+  kUwMacaroonCaveatTypeIdentifier = 2,
+  kUwMacaroonCaveatTypeIssued = 3,
+  kUwMacaroonCaveatTypeTTL = 4,
+  kUwMacaroonCaveatTypeExpiration = 5,
+  kUwMacaroonCaveatTypeSessionIdentifier = 16
+} UwMacaroonCaveatType;
+
+bool uw_macaroon_caveat_create_without_value_(UwMacaroonCaveatType type,
+                                              uint8_t* buffer,
+                                              size_t buffer_size,
+                                              UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_with_uint_(UwMacaroonCaveatType type,
+                                          uint32_t value, uint8_t* buffer,
+                                          size_t buffer_size,
+                                          UwMacaroonCaveat* new_caveat);
+bool uw_macaroon_caveat_create_with_str_(UwMacaroonCaveatType type,
+                                         const uint8_t* str, size_t str_len,
+                                         uint8_t* buffer, size_t buffer_size,
+                                         UwMacaroonCaveat* new_caveat);
+
+bool uw_macaroon_caveat_get_type_(const UwMacaroonCaveat* caveat,
+                                  UwMacaroonCaveatType* type);
+bool uw_macaroon_caveat_get_value_uint_(const UwMacaroonCaveat* caveat,
+                                        uint32_t* unsigned_int);
+bool uw_macaroon_caveat_get_value_str_(const UwMacaroonCaveat* caveat,
+                                       const uint8_t** str, size_t* str_len);
+
+bool uw_macaroon_caveat_sign_(const uint8_t* key, size_t key_len,
+                              const UwMacaroonCaveat* caveat, uint8_t* mac_tag,
+                              size_t mac_tag_size);
+
+#endif  // LIBUWEAVE_SRC_MACAROON_CAVEAT_H_
diff --git a/third_party/libuweave/src/macaroon_context.c b/third_party/libuweave/src/macaroon_context.c
new file mode 100644
index 0000000..7477784
--- /dev/null
+++ b/third_party/libuweave/src/macaroon_context.c
@@ -0,0 +1,22 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/macaroon_context.h"
+
+#include "src/macaroon_caveat.h"
+
+bool uw_macaroon_context_get_(UwMacaroonCaveatType type,
+                              const uint8_t** context, size_t* context_len) {
+  if (type != kUwMacaroonCaveatTypeSessionIdentifier) {
+    *context = NULL;
+    *context_len = 0;
+  }
+
+  // TODO(bozhu): Waiting for a proper way to obtain the session identifier.
+  // Have we already implemented something related to session identifiers?
+  *context = NULL;
+  *context_len = 0;
+
+  return true;
+}
diff --git a/third_party/libuweave/src/macaroon_context.h b/third_party/libuweave/src/macaroon_context.h
new file mode 100644
index 0000000..8522b69
--- /dev/null
+++ b/third_party/libuweave/src/macaroon_context.h
@@ -0,0 +1,17 @@
+// 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 UW_LIBUWEAVE_SRC_MACAROON_CONTEXT_
+#define UW_LIBUWEAVE_SRC_MACAROON_CONTEXT_
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include "src/macaroon_caveat.h"
+
+bool uw_macaroon_context_get_(UwMacaroonCaveatType type,
+                              const uint8_t** context, size_t* context_len);
+
+#endif  // UW_LIBUWEAVE_SRC_MACAROON_CONTEXT_
diff --git a/third_party/libuweave/src/macaroon_encoding.c b/third_party/libuweave/src/macaroon_encoding.c
new file mode 100644
index 0000000..214314d
--- /dev/null
+++ b/third_party/libuweave/src/macaroon_encoding.c
@@ -0,0 +1,353 @@
+// Copyright 2015 The Weave Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/macaroon_encoding.h"
+
+#include <string.h>
+
+#define MAJOR_TYPE_MASK 0xE0       // 0b11100000
+#define ADDITIONAL_DATA_MASK 0x1F  // 0b00011111
+
+#define FLAG_1BYTE_UINT 24
+#define FLAG_2BYTE_UINT 25
+#define FLAG_4BYTE_UINT 26
+// #define FLAG_8BYTE_UINT 27  // Do not support 8-byte
+
+typedef enum {
+  kCborMajorTypeUint = 0,          // type 0
+  kCborMajorTypeByteStr = 2 << 5,  // type 2
+  kCborMajorTypeTextStr = 3 << 5,  // type 3
+} CborMajorType;
+
+// -- Prototypes begin --
+static inline CborMajorType get_type_(const uint8_t* cbor);
+static inline uint8_t get_addtl_data_(const uint8_t* cbor);
+static inline void set_type_(CborMajorType type, uint8_t* cbor);
+static inline void set_addtl_data_(uint8_t addtl_data, uint8_t* cbor);
+
+// Compute the minimum number of bytes to store the unsigned integer.
+static inline size_t uint_min_len_(uint32_t unsigned_int);
+
+// Encoding or decoding without checking types
+static bool blindly_encode_uint_(uint32_t unsigned_int, uint8_t* buffer,
+                                 size_t buffer_size, size_t* result_len);
+static bool blindly_encode_str_(const uint8_t* str, size_t str_len,
+                                uint8_t* buffer, size_t buffer_size,
+                                size_t* result_len);
+static bool blindly_decode_uint_(const uint8_t* cbor, size_t cbor_len,
+                                 uint32_t* unsigned_int);
+static bool blindly_decode_str_(const uint8_t* cbor, size_t cbor_len,
+                                const uint8_t** out_str, size_t* out_str_len);
+// -- Prototypes end --
+
+bool uw_macaroon_encoding_get_item_len_(const uint8_t* cbor, size_t cbor_len,
+                                        size_t* first_item_len) {
+  if (cbor == NULL || cbor_len == 0 || first_item_len == NULL) {
+    return false;
+  }
+
+  CborMajorType type = get_type_(cbor);
+  if (type != kCborMajorTypeUint && type != kCborMajorTypeByteStr &&
+      type != kCborMajorTypeTextStr) {
+    // Other types are not supported
+    return false;
+  }
+
+  uint32_t unsigned_int;
+  if (!blindly_decode_uint_(cbor, cbor_len, &unsigned_int)) {
+    return false;
+  }
+
+  *first_item_len = uint_min_len_(unsigned_int) + 1;
+
+  if (type == kCborMajorTypeByteStr || type == kCborMajorTypeTextStr) {
+    *first_item_len += (size_t)unsigned_int;
+  }
+
+  if (*first_item_len > cbor_len) {
+    // Something is wrong. The CBOR string isn't long enough.
+    return false;
+  }
+  return true;
+}
+
+bool uw_macaroon_encoding_encode_uint_(const uint32_t unsigned_int,
+                                       uint8_t* buffer, size_t buffer_size,
+                                       size_t* resulting_cbor_len) {
+  if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) {
+    return false;
+  }
+
+  set_type_(kCborMajorTypeUint, buffer);
+  return blindly_encode_uint_(unsigned_int, buffer, buffer_size,
+                              resulting_cbor_len);
+}
+
+bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str, size_t str_len,
+                                           uint8_t* buffer, size_t buffer_size,
+                                           size_t* resulting_cbor_len) {
+  if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) {
+    return false;
+  }
+
+  set_type_(kCborMajorTypeByteStr, buffer);
+  return blindly_encode_str_(str, str_len, buffer, buffer_size,
+                             resulting_cbor_len);
+}
+
+bool uw_macaroon_encoding_encode_text_str_(const uint8_t* str, size_t str_len,
+                                           uint8_t* buffer, size_t buffer_size,
+                                           size_t* resulting_cbor_len) {
+  if (buffer == NULL || buffer_size == 0 || resulting_cbor_len == NULL) {
+    return false;
+  }
+
+  set_type_(kCborMajorTypeTextStr, buffer);
+  return blindly_encode_str_(str, str_len, buffer, buffer_size,
+                             resulting_cbor_len);
+}
+
+bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor, size_t cbor_len,
+                                       uint32_t* unsigned_int) {
+  if (cbor == NULL || cbor_len == 0 || unsigned_int == NULL) {
+    return false;
+  }
+
+  CborMajorType type = get_type_(cbor);
+  if (type != kCborMajorTypeUint) {
+    return false;
+  }
+
+  return blindly_decode_uint_(cbor, cbor_len, unsigned_int);
+}
+
+bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor, size_t cbor_len,
+                                           const uint8_t** out_str,
+                                           size_t* out_str_len) {
+  if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str_len == NULL) {
+    return false;
+  }
+
+  CborMajorType type = get_type_(cbor);
+  if (type != kCborMajorTypeByteStr) {
+    return false;
+  }
+
+  return blindly_decode_str_(cbor, cbor_len, out_str, out_str_len);
+}
+
+bool uw_macaroon_encoding_decode_text_str_(const uint8_t* cbor, size_t cbor_len,
+                                           const uint8_t** out_str,
+                                           size_t* out_str_len) {
+  if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str_len == NULL) {
+    return false;
+  }
+
+  CborMajorType type = get_type_(cbor);
+  if (type != kCborMajorTypeTextStr) {
+    return false;
+  }
+
+  return blindly_decode_str_(cbor, cbor_len, out_str, out_str_len);
+}
+
+static inline CborMajorType get_type_(const uint8_t* cbor) {
+  return (CborMajorType)((*cbor) & MAJOR_TYPE_MASK);
+}
+
+static inline uint8_t get_addtl_data_(const uint8_t* cbor) {
+  return (*cbor) & ADDITIONAL_DATA_MASK;
+}
+
+static inline void set_type_(CborMajorType type, uint8_t* cbor) {
+  *cbor = ((uint8_t)type) | ((*cbor) & ADDITIONAL_DATA_MASK);
+}
+
+static inline void set_addtl_data_(uint8_t addtl_data, uint8_t* cbor) {
+  *cbor = ((*cbor) & MAJOR_TYPE_MASK) | (addtl_data & ADDITIONAL_DATA_MASK);
+}
+
+static inline size_t uint_min_len_(uint32_t unsigned_int) {
+  if (unsigned_int < FLAG_1BYTE_UINT) {
+    return 0;  // Should be stored in the 5-bit additional data part
+  } else if (unsigned_int <= 0xFF) {
+    return 1;
+  } else if (unsigned_int <= 0xFFFF) {
+    return 2;
+  }
+  return 4;
+}
+
+// Write the unsigned int in the big-endian fashion by using the minimum number
+// of bytes in CBOR
+static inline bool write_uint_big_endian_(uint32_t unsigned_int, uint8_t* buff,
+                                          size_t buff_len) {
+  if (buff == NULL || buff_len == 0) {
+    return false;
+  }
+
+  size_t num_bytes = uint_min_len_(unsigned_int);
+  if (num_bytes > buff_len) {
+    // Not enough memory
+    return false;
+  }
+
+  switch (num_bytes) {
+    // Falling through intentionally
+    case 4:
+      *(buff++) = (uint8_t)(0xFF & (unsigned_int >> 24));
+      *(buff++) = (uint8_t)(0xFF & (unsigned_int >> 16));
+    case 2:
+      *(buff++) = (uint8_t)(0xFF & (unsigned_int >> 8));
+    case 1:
+      *(buff++) = (uint8_t)(0xFF & (unsigned_int));
+      break;
+
+    default:
+      return false;
+  }
+
+  return true;
+}
+
+// Read the unsigned int written in big-endian
+static inline bool read_uint_big_endian_(const uint8_t* bytes, size_t num_bytes,
+                                         uint32_t* unsigned_int) {
+  if (bytes == NULL || num_bytes == 0 || num_bytes > 4 ||
+      unsigned_int == NULL) {
+    return false;
+  }
+
+  *unsigned_int = 0;
+  switch (num_bytes) {
+    // Falling through intentionally
+    case 4:
+      *unsigned_int |= ((uint32_t)(*(bytes++))) << 24;
+      *unsigned_int |= ((uint32_t)(*(bytes++))) << 16;
+    case 2:
+      *unsigned_int |= ((uint32_t)(*(bytes++))) << 8;
+    case 1:
+      *unsigned_int |= ((uint32_t)(*(bytes++)));
+      break;
+
+    default:
+      return false;
+  }
+
+  return true;
+}
+
+static bool blindly_encode_uint_(uint32_t unsigned_int, uint8_t* buffer,
+                                 size_t buffer_size, size_t* result_len) {
+  if (buffer == NULL || buffer_size == 0 || result_len == NULL) {
+    return false;
+  }
+
+  // Don't need to set the data type in this function
+
+  *result_len = uint_min_len_(unsigned_int) + 1;
+
+  if (*result_len > buffer_size) {
+    // Not enough memory
+    return false;
+  }
+
+  switch (*result_len) {
+    case 1:
+      set_addtl_data_(unsigned_int, buffer);
+      return true;
+    case 2:  // 1 + 1
+      set_addtl_data_(FLAG_1BYTE_UINT, buffer);
+      break;
+    case 3:  // 1 + 2
+      set_addtl_data_(FLAG_2BYTE_UINT, buffer);
+      break;
+    case 5:  // 1 + 4
+      set_addtl_data_(FLAG_4BYTE_UINT, buffer);
+      break;
+    default:
+      // Wrong length
+      return false;
+  }
+
+  return write_uint_big_endian_(unsigned_int, buffer + 1, buffer_size - 1);
+}
+
+static bool blindly_encode_str_(const uint8_t* str, size_t str_len,
+                                uint8_t* buffer, size_t buffer_size,
+                                size_t* result_len) {
+  if (buffer == NULL || buffer_size == 0) {
+    return false;
+  }
+  if (str == NULL && str_len != 0) {
+    // str_len should be 0 for empty strings
+    return false;
+  }
+
+  // Don't need to set the data type in this function
+
+  if (!blindly_encode_uint_((uint32_t)str_len, buffer, buffer_size,
+                            result_len)) {
+    return false;
+  }
+
+  if (str_len == 0) {
+    return true;
+  }
+
+  if (str_len + (*result_len) > buffer_size) {
+    // Not enough memory
+    return false;
+  }
+
+  memcpy(buffer + (*result_len), str, str_len);
+  *result_len += str_len;
+  return true;
+}
+
+static bool blindly_decode_uint_(const uint8_t* cbor, size_t cbor_len,
+                                 uint32_t* unsigned_int) {
+  if (cbor == NULL || cbor_len == 0 || unsigned_int == NULL) {
+    return false;
+  }
+
+  uint8_t addtl_data = get_addtl_data_(cbor);
+  if (addtl_data < FLAG_1BYTE_UINT) {
+    *unsigned_int = (uint32_t)addtl_data;
+    return true;
+  }
+  if (addtl_data > FLAG_4BYTE_UINT) {
+    return false;
+  }
+
+  size_t uint_num_bytes = 1 << (addtl_data - (uint8_t)FLAG_1BYTE_UINT);
+  if (uint_num_bytes + 1 > cbor_len) {
+    // The CBOR string isn't long enough.
+    return false;
+  }
+
+  return read_uint_big_endian_(cbor + 1, uint_num_bytes, unsigned_int);
+}
+
+static bool blindly_decode_str_(const uint8_t* cbor, size_t cbor_len,
+                                const uint8_t** out_str, size_t* out_str_len) {
+  if (cbor == NULL || cbor_len == 0 || out_str == NULL || out_str == NULL) {
+    return false;
+  }
+
+  uint32_t unsigned_int;
+  if (!blindly_decode_uint_(cbor, cbor_len, &unsigned_int)) {
+    return false;
+  }
+
+  size_t offset = 1 + uint_min_len_(unsigned_int);
+  if (unsigned_int > (uint32_t)(cbor_len - offset)) {
+    // The CBOR string isn't long enough
+    return false;
+  }
+
+  *out_str = cbor + offset;
+  *out_str_len = unsigned_int;
+  return true;
+}
diff --git a/third_party/libuweave/src/macaroon_encoding.h b/third_party/libuweave/src/macaroon_encoding.h
new file mode 100644
index 0000000..2c11fd1
--- /dev/null
+++ b/third_party/libuweave/src/macaroon_encoding.h
@@ -0,0 +1,44 @@
+// 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 UW_LIBUWEAVE_SRC_MACAROON_ENCODING_
+#define UW_LIBUWEAVE_SRC_MACAROON_ENCODING_
+
+/*
+ * Utility functions to encode and decode canonical CBOR representations for
+ * cryptographic use, such as signatures. We only need to support a very small
+ * subset of the CBOR standard, since only these are used in our cryptographic
+ * designs. The supported data types are: unsigned integers (maximum 32 bits),
+ * byte strings, and text strings.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+// Get the number of bytes that is occupied by the first data item in the give
+// CBOR string.
+bool uw_macaroon_encoding_get_item_len_(const uint8_t* cbor, size_t cbor_len,
+                                        size_t* first_item_len);
+
+bool uw_macaroon_encoding_encode_uint_(const uint32_t unsigned_int,
+                                       uint8_t* buffer, size_t buffer_size,
+                                       size_t* resulting_cbor_len);
+bool uw_macaroon_encoding_encode_byte_str_(const uint8_t* str, size_t str_len,
+                                           uint8_t* buffer, size_t buffer_size,
+                                           size_t* resulting_cbor_len);
+bool uw_macaroon_encoding_encode_text_str_(const uint8_t* str, size_t str_len,
+                                           uint8_t* buffer, size_t buffer_size,
+                                           size_t* resulting_cbor_len);
+
+bool uw_macaroon_encoding_decode_uint_(const uint8_t* cbor, size_t cbor_len,
+                                       uint32_t* unsigned_int);
+bool uw_macaroon_encoding_decode_byte_str_(const uint8_t* cbor, size_t cbor_len,
+                                           const uint8_t** str,
+                                           size_t* str_len);
+bool uw_macaroon_encoding_decode_text_str_(const uint8_t* cbor, size_t cbor_len,
+                                           const uint8_t** str,
+                                           size_t* str_len);
+
+#endif  // UW_LIBUWEAVE_SRC_MACAROON_ENCODING_
diff --git a/third_party/libuweave/update.sh b/third_party/libuweave/update.sh
new file mode 100755
index 0000000..e34065d
--- /dev/null
+++ b/third_party/libuweave/update.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# 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.
+
+DIR=$(cd -P -- "$(dirname -- "$0")" && pwd -P)
+ROOT_DIR=$(cd -P -- "$(dirname -- "$0")/../.." && pwd -P)
+
+cd $ROOT_DIR
+
+git subtree add --prefix third_party/temp_libuweave \
+    https://weave.googlesource.com/weave/libuweave master --squash || exit 1
+
+mkdir -p third_party/libuweave/src
+pushd third_party
+git mv -kf temp_libuweave/LICENSE libuweave/
+git mv -kf temp_libuweave/src/crypto_hmac.h libuweave/src/crypto_hmac.h
+git mv -kf temp_libuweave/src/macaroon* libuweave/src/
+git mv -kf temp_libuweave/src/crypto_utils.* libuweave/src/
+popd
+
+git rm -rf third_party/temp_libuweave
+git reset --soft weave/master
+git commit -av
diff --git a/third_party/modp_b64/modp_b64.cc b/third_party/modp_b64/modp_b64.cc
index fdb8a40..6624ec5 100644
--- a/third_party/modp_b64/modp_b64.cc
+++ b/third_party/modp_b64/modp_b64.cc
@@ -45,16 +45,12 @@
 /* public header */
 #include "modp_b64.h"
 
-/*
- * If you are ripping this out of the library, comment out the next
- * line and uncomment the next lines as approrpiate
- */
-//#include "config.h"
+#include <build/build_config.h>
 
-/* if on motoral, sun, ibm; uncomment this */
-/* #define WORDS_BIGENDIAN 1 */
-/* else for Intel, Amd; uncomment this */
-/* #undef WORDS_BIGENDIAN */
+#undef WORDS_BIGENDIAN
+#if !defined(ARCH_CPU_LITTLE_ENDIAN)
+#define WORDS_BIGENDIAN 1
+#endif
 
 #include "modp_b64_data.h"
 
@@ -118,7 +114,7 @@
 }
 
 #ifdef WORDS_BIGENDIAN   /* BIG ENDIAN -- SUN / IBM / MOTOROLA */
-int modp_b64_decode(char* dest, const char* src, int len)
+size_t modp_b64_decode(char* dest, const char* src, size_t len)
 {
     if (len == 0) return 0;