[shill] Add support for setting properties.

This CL adds a framework for supporting RPC-exposed properties in Shill.
It also plumbs the code for setting properties on Service objects to prove
the approach.  Device and Manager settings will follow.

BUG=chromium-os:16343
TEST=build shill, run unit tests.

Change-Id: I55869453d6039e688f1a49be9dfb1ba1315efe0a
Reviewed-on: http://gerrit.chromium.org/gerrit/3004
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Chris Masone <cmasone@chromium.org>
diff --git a/Makefile b/Makefile
index 65742c4..2348b2a 100644
--- a/Makefile
+++ b/Makefile
@@ -42,6 +42,8 @@
 DBUS_HEADERS = $(DBUS_ADAPTOR_HEADERS) $(DBUS_PROXY_HEADERS)
 
 SHILL_OBJS = \
+	cellular.o \
+	cellular_service.o \
 	dbus_adaptor.o \
 	dbus_control.o \
 	device.o \
@@ -86,6 +88,7 @@
 	mock_control.o \
 	mock_device.o \
 	mock_service.o \
+	service_unittest.o \
 	shill_unittest.o \
 	testrunner.o
 
diff --git a/accessor_interface.h b/accessor_interface.h
new file mode 100644
index 0000000..85f3913
--- /dev/null
+++ b/accessor_interface.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_ACCESSOR_INTERFACE_
+#define SHILL_ACCESSOR_INTERFACE_
+
+#include <map>
+#include <string>
+#include <tr1/memory>
+
+#include <base/basictypes.h>
+
+namespace shill {
+
+// A templated abstract base class for objects that can be used to access
+// properties stored in objects that are meant to be made available over RPC.
+// The intended usage is that an object stores a maps of strings to
+// AccessorInterfaces of the appropriate type, and then uses
+// map[name]->Get() and map[name]->Set(value) to get and set the properties.
+template <class T>
+class AccessorInterface {
+ public:
+  AccessorInterface() {}
+  virtual ~AccessorInterface() {}
+
+  // Provides read-only access.
+  virtual const T &Get() = 0;
+  // Attempts to set the wrapped value.  Returns true upon success.
+  virtual bool Set(const T &value) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AccessorInterface);
+};
+
+// Using a smart pointer here allows pointers to classes derived from
+// AccessorInterface<> to be stored in maps and other STL container types.
+typedef std::tr1::shared_ptr<AccessorInterface<bool> > BoolAccessor;
+typedef std::tr1::shared_ptr<AccessorInterface<int32> > Int32Accessor;
+typedef std::tr1::shared_ptr<
+    AccessorInterface<std::map<std::string, std::string> > > StringmapAccessor;
+typedef std::tr1::shared_ptr<AccessorInterface<std::string> > StringAccessor;
+typedef std::tr1::shared_ptr<AccessorInterface<uint8> > Uint8Accessor;
+typedef std::tr1::shared_ptr<AccessorInterface<uint16> > Uint16Accessor;
+
+}  // namespace shill
+
+#endif  // SHILL_ACCESSOR_INTERFACE_
diff --git a/cellular.cc b/cellular.cc
new file mode 100644
index 0000000..92e8262
--- /dev/null
+++ b/cellular.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/cellular.h"
+
+#include <string>
+
+#include <base/logging.h>
+
+#include "shill/cellular_service.h"
+#include "shill/control_interface.h"
+#include "shill/device.h"
+#include "shill/device_info.h"
+#include "shill/manager.h"
+#include "shill/shill_event.h"
+
+using std::string;
+
+namespace shill {
+
+Cellular::Cellular(ControlInterface *control_interface,
+                   EventDispatcher *dispatcher,
+                   Manager *manager,
+                   const string &link,
+                   int interface_index)
+    : Device(control_interface,
+             dispatcher,
+             manager,
+             link,
+             interface_index),
+      service_(new CellularService(control_interface,
+                                   dispatcher,
+                                   this,
+                                   "service-" + link)),
+      service_registered_(false) {
+  VLOG(2) << "Cellular device " << link_name() << " initialized.";
+}
+
+Cellular::~Cellular() {
+  Stop();
+}
+
+void Cellular::Start() {
+  Device::Start();
+}
+
+void Cellular::Stop() {
+  manager_->DeregisterService(service_.get());
+  Device::Stop();
+}
+
+bool Cellular::TechnologyIs(const Device::Technology type) {
+  return type == Device::kCellular;
+}
+
+}  // namespace shill
diff --git a/cellular.h b/cellular.h
new file mode 100644
index 0000000..428aa21
--- /dev/null
+++ b/cellular.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_CELLULAR_
+#define SHILL_CELLULAR_
+
+#include <string>
+
+#include <base/basictypes.h>
+
+#include "shill/cellular_service.h"
+#include "shill/device.h"
+#include "shill/shill_event.h"
+
+namespace shill {
+
+class Cellular : public Device {
+ public:
+  Cellular(ControlInterface *control_interface,
+           EventDispatcher *dispatcher,
+           Manager *manager,
+           const std::string& link,
+           int interface_index);
+  ~Cellular();
+  void Start();
+  void Stop();
+  bool TechnologyIs(Device::Technology type);
+
+ private:
+  bool service_registered_;
+  ServiceRefPtr service_;
+  DISALLOW_COPY_AND_ASSIGN(Cellular);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_CELLULAR_
diff --git a/cellular_service.cc b/cellular_service.cc
new file mode 100644
index 0000000..a125c77
--- /dev/null
+++ b/cellular_service.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/cellular_service.h"
+
+#include <string>
+
+#include <base/logging.h>
+#include <chromeos/dbus/service_constants.h>
+
+#include "shill/cellular.h"
+#include "shill/control_interface.h"
+#include "shill/device.h"
+#include "shill/device_info.h"
+#include "shill/manager.h"
+#include "shill/shill_event.h"
+
+using std::string;
+
+namespace shill {
+CellularService::CellularService(ControlInterface *control_interface,
+                                 EventDispatcher *dispatcher,
+                                 Cellular *device,
+                                 const string &name)
+    : Service(control_interface, dispatcher, device, name),
+      cellular_(device),
+      strength_(0),
+      type_(flimflam::kTypeCellular) {
+
+  RegisterConstString(flimflam::kActivationStateProperty, &activation_state_);
+  RegisterConstString(flimflam::kOperatorNameProperty, &operator_name_);
+  RegisterConstString(flimflam::kOperatorCodeProperty, &operator_code_);
+  RegisterConstString(flimflam::kNetworkTechnologyProperty, &network_tech_);
+  RegisterConstString(flimflam::kRoamingStateProperty, &roaming_state_);
+  RegisterConstString(flimflam::kPaymentURLProperty, &payment_url_);
+
+  RegisterStringmap(flimflam::kCellularApnProperty, &apn_info_);
+  RegisterConstStringmap(flimflam::kCellularLastGoodApnProperty,
+                         &last_good_apn_info_);
+
+  RegisterConstUint8(flimflam::kSignalStrengthProperty, &strength_);
+  //  RegisterDerivedString(flimflam::kStateProperty,
+  //                    &Service::CalculateState,
+  //                    NULL);
+  RegisterConstString(flimflam::kTypeProperty, &type_);
+}
+
+CellularService::~CellularService() { }
+
+void CellularService::Connect() { }
+
+void CellularService::Disconnect() { }
+
+bool CellularService::Contains(const string &property) {
+  return (Service::Contains(property) ||
+          uint8_properties_.find(property) != uint8_properties_.end() ||
+          stringmap_properties_.find(property) != stringmap_properties_.end());
+}
+
+}  // namespace shill
diff --git a/cellular_service.h b/cellular_service.h
new file mode 100644
index 0000000..710c0b5
--- /dev/null
+++ b/cellular_service.h
@@ -0,0 +1,58 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_CELLULAR_SERVICE_
+#define SHILL_CELLULAR_SERVICE_
+
+#include <map>
+#include <string>
+
+#include <base/basictypes.h>
+
+#include "shill/cellular.h"
+#include "shill/device.h"
+#include "shill/shill_event.h"
+#include "shill/service.h"
+
+namespace shill {
+
+class Cellular;
+
+class CellularService : public Service {
+ public:
+  CellularService(ControlInterface *control_interface,
+                  EventDispatcher *dispatcher,
+                  Cellular *device,
+                  const std::string& name);
+  ~CellularService();
+  void Connect();
+  void Disconnect();
+
+  // Implementation of PropertyStoreInterface
+  bool Contains(const std::string &property);
+
+ protected:
+  virtual std::string CalculateState() { return "idle"; }
+
+  // Properties
+  std::string activation_state_;
+  std::string operator_name_;
+  std::string operator_code_;
+  std::string network_tech_;
+  std::string roaming_state_;
+  std::string payment_url_;
+  uint8 strength_;
+
+  std::map<std::string, std::string> apn_info_;
+  std::map<std::string, std::string> last_good_apn_info_;
+
+ private:
+  Cellular *cellular_;
+  const std::string type_;
+  DISALLOW_COPY_AND_ASSIGN(CellularService);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_CELLULAR_SERVICE_
diff --git a/dbus_adaptor.cc b/dbus_adaptor.cc
index 9b57ba2..748832c 100644
--- a/dbus_adaptor.cc
+++ b/dbus_adaptor.cc
@@ -35,6 +35,10 @@
                                  const string &name,
                                  const ::DBus::Variant &value,
                                  ::DBus::Error &error) {
+  if (!store->Contains(name)) {
+    Error(Error::kInvalidProperty, name + " is invalid.").ToDBusError(&error);
+    return false;
+  }
   bool set = false;
   Error e(Error::kInvalidArguments, "Could not write " + name);
 
@@ -46,6 +50,8 @@
     set = store->SetInt16Property(name, value.reader().get_int16(), &e);
   else if (DBusAdaptor::IsInt32(value.signature()))
     set = store->SetInt32Property(name, value.reader().get_int32(), &e);
+  else if (DBusAdaptor::IsPath(value.signature()))
+    set = store->SetStringProperty(name, value.reader().get_path(), &e);
   else if (DBusAdaptor::IsString(value.signature()))
     set = store->SetStringProperty(name, value.reader().get_string(), &e);
   else if (DBusAdaptor::IsStringmap(value.signature()))
@@ -95,6 +101,13 @@
 }
 
 // static
+::DBus::Variant DBusAdaptor::PathToVariant(const ::DBus::Path &value) {
+  ::DBus::Variant v;
+  v.writer().append_path(value.c_str());
+  return v;
+}
+
+// static
 ::DBus::Variant DBusAdaptor::StringToVariant(const string &value) {
   ::DBus::Variant v;
   v.writer().append_string(value.c_str());
@@ -153,6 +166,11 @@
 }
 
 // static
+bool DBusAdaptor::IsPath(::DBus::Signature signature) {
+  return signature == ::DBus::type< ::DBus::Path >::sig();
+}
+
+// static
 bool DBusAdaptor::IsString(::DBus::Signature signature) {
   return signature == ::DBus::type<string>::sig();
 }
diff --git a/dbus_adaptor.h b/dbus_adaptor.h
index 18aa3f3..13ede16 100644
--- a/dbus_adaptor.h
+++ b/dbus_adaptor.h
@@ -34,6 +34,7 @@
   static ::DBus::Variant ByteToVariant(uint8 value);
   static ::DBus::Variant Int16ToVariant(int16 value);
   static ::DBus::Variant Int32ToVariant(int32 value);
+  static ::DBus::Variant PathToVariant(const ::DBus::Path& value);
   static ::DBus::Variant StringToVariant(const std::string& value);
   static ::DBus::Variant StringmapToVariant(
       const std::map<std::string, std::string>& value);
@@ -46,6 +47,7 @@
   static bool IsByte(::DBus::Signature signature);
   static bool IsInt16(::DBus::Signature signature);
   static bool IsInt32(::DBus::Signature signature);
+  static bool IsPath(::DBus::Signature signature);
   static bool IsString(::DBus::Signature signature);
   static bool IsStringmap(::DBus::Signature signature);
   static bool IsStrings(::DBus::Signature signature);
diff --git a/dbus_adaptor_unittest.cc b/dbus_adaptor_unittest.cc
index 3a49895..22fd803 100644
--- a/dbus_adaptor_unittest.cc
+++ b/dbus_adaptor_unittest.cc
@@ -15,20 +15,22 @@
 #include "shill/manager.h"
 #include "shill/mock_control.h"
 #include "shill/mock_device.h"
+#include "shill/mock_property_store.h"
 #include "shill/mock_service.h"
+#include "shill/property_store_unittest.h"
 #include "shill/shill_event.h"
 
 using std::map;
 using std::string;
 using std::vector;
-using ::testing::Test;
 using ::testing::_;
-using ::testing::NiceMock;
 using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::Test;
 
 namespace shill {
 
-class DBusAdaptorTest : public Test {
+class DBusAdaptorTest : public PropertyStoreTest {
  public:
   DBusAdaptorTest()
       : ex_bool(true),
@@ -39,7 +41,6 @@
         ex_int32(-65536),
         ex_string("something"),
         ex_strings(1, ex_string),
-        manager_(&control_interface_, &dispatcher_),
         device_(new MockDevice(&control_interface_,
                                &dispatcher_,
                                &manager_,
@@ -51,15 +52,15 @@
                                  "mock")) {
     ex_stringmap[ex_string] = ex_string;
 
-    bool_v = DBusAdaptor::BoolToVariant(ex_bool);
-    byte_v = DBusAdaptor::ByteToVariant(ex_byte);
-    uint16_v = DBusAdaptor::Uint16ToVariant(ex_uint16);
-    uint32_v = DBusAdaptor::Uint32ToVariant(ex_uint32);
-    int16_v = DBusAdaptor::Int16ToVariant(ex_int16);
-    int32_v = DBusAdaptor::Int32ToVariant(ex_int32);
-    string_v = DBusAdaptor::StringToVariant(ex_string);
-    stringmap_v = DBusAdaptor::StringmapToVariant(ex_stringmap);
-    strings_v = DBusAdaptor::StringsToVariant(ex_strings);
+    bool_v_ = DBusAdaptor::BoolToVariant(ex_bool);
+    byte_v_ = DBusAdaptor::ByteToVariant(ex_byte);
+    uint16_v_ = DBusAdaptor::Uint16ToVariant(ex_uint16);
+    uint32_v_ = DBusAdaptor::Uint32ToVariant(ex_uint32);
+    int16_v_ = DBusAdaptor::Int16ToVariant(ex_int16);
+    int32_v_ = DBusAdaptor::Int32ToVariant(ex_int32);
+    string_v_ = DBusAdaptor::StringToVariant(ex_string);
+    stringmap_v_ = DBusAdaptor::StringmapToVariant(ex_stringmap);
+    strings_v_ = DBusAdaptor::StringsToVariant(ex_strings);
   }
 
   virtual ~DBusAdaptorTest() {}
@@ -74,118 +75,88 @@
   map<string, string> ex_stringmap;
   vector<string> ex_strings;
 
-  ::DBus::Variant bool_v;
-  ::DBus::Variant byte_v;
-  ::DBus::Variant uint16_v;
-  ::DBus::Variant uint32_v;
-  ::DBus::Variant int16_v;
-  ::DBus::Variant int32_v;
-  ::DBus::Variant string_v;
-  ::DBus::Variant stringmap_v;
-  ::DBus::Variant strings_v;
-
  protected:
-  MockControl control_interface_;
-  EventDispatcher dispatcher_;
-  Manager manager_;
   DeviceRefPtr device_;
   ServiceRefPtr service_;
 };
 
 TEST_F(DBusAdaptorTest, Conversions) {
   EXPECT_EQ(0, DBusAdaptor::BoolToVariant(0).reader().get_bool());
-  EXPECT_EQ(ex_bool, bool_v.reader().get_bool());
+  EXPECT_EQ(ex_bool, bool_v_.reader().get_bool());
 
   EXPECT_EQ(0, DBusAdaptor::ByteToVariant(0).reader().get_byte());
-  EXPECT_EQ(ex_byte, byte_v.reader().get_byte());
+  EXPECT_EQ(ex_byte, byte_v_.reader().get_byte());
 
   EXPECT_EQ(0, DBusAdaptor::Uint16ToVariant(0).reader().get_uint16());
-  EXPECT_EQ(ex_uint16, uint16_v.reader().get_uint16());
+  EXPECT_EQ(ex_uint16, uint16_v_.reader().get_uint16());
 
   EXPECT_EQ(0, DBusAdaptor::Int16ToVariant(0).reader().get_int16());
-  EXPECT_EQ(ex_int16, int16_v.reader().get_int16());
+  EXPECT_EQ(ex_int16, int16_v_.reader().get_int16());
 
   EXPECT_EQ(0, DBusAdaptor::Uint32ToVariant(0).reader().get_uint32());
-  EXPECT_EQ(ex_uint32, uint32_v.reader().get_uint32());
+  EXPECT_EQ(ex_uint32, uint32_v_.reader().get_uint32());
 
   EXPECT_EQ(0, DBusAdaptor::Int32ToVariant(0).reader().get_int32());
-  EXPECT_EQ(ex_int32, int32_v.reader().get_int32());
+  EXPECT_EQ(ex_int32, int32_v_.reader().get_int32());
 
   EXPECT_EQ(string(""), DBusAdaptor::StringToVariant("").reader().get_string());
-  EXPECT_EQ(ex_string, string_v.reader().get_string());
+  EXPECT_EQ(ex_string, string_v_.reader().get_string());
 
   EXPECT_EQ(ex_stringmap[ex_string],
-            (stringmap_v.operator map<string, string>()[ex_string]));
-  EXPECT_EQ(ex_strings[0], strings_v.operator vector<string>()[0]);
+            (stringmap_v_.operator map<string, string>()[ex_string]));
+  EXPECT_EQ(ex_strings[0], strings_v_.operator vector<string>()[0]);
 }
 
 TEST_F(DBusAdaptorTest, Signatures) {
-  EXPECT_TRUE(DBusAdaptor::IsBool(bool_v.signature()));
-  EXPECT_TRUE(DBusAdaptor::IsByte(byte_v.signature()));
-  EXPECT_TRUE(DBusAdaptor::IsInt16(int16_v.signature()));
-  EXPECT_TRUE(DBusAdaptor::IsInt32(int32_v.signature()));
-  EXPECT_TRUE(DBusAdaptor::IsString(string_v.signature()));
-  EXPECT_TRUE(DBusAdaptor::IsStringmap(stringmap_v.signature()));
-  EXPECT_TRUE(DBusAdaptor::IsStrings(strings_v.signature()));
-  EXPECT_TRUE(DBusAdaptor::IsUint16(uint16_v.signature()));
-  EXPECT_TRUE(DBusAdaptor::IsUint32(uint32_v.signature()));
+  EXPECT_TRUE(DBusAdaptor::IsBool(bool_v_.signature()));
+  EXPECT_TRUE(DBusAdaptor::IsByte(byte_v_.signature()));
+  EXPECT_TRUE(DBusAdaptor::IsInt16(int16_v_.signature()));
+  EXPECT_TRUE(DBusAdaptor::IsInt32(int32_v_.signature()));
+  EXPECT_TRUE(DBusAdaptor::IsString(string_v_.signature()));
+  EXPECT_TRUE(DBusAdaptor::IsStringmap(stringmap_v_.signature()));
+  EXPECT_TRUE(DBusAdaptor::IsStrings(strings_v_.signature()));
+  EXPECT_TRUE(DBusAdaptor::IsUint16(uint16_v_.signature()));
+  EXPECT_TRUE(DBusAdaptor::IsUint32(uint32_v_.signature()));
 
-  EXPECT_FALSE(DBusAdaptor::IsBool(byte_v.signature()));
-  EXPECT_FALSE(DBusAdaptor::IsStrings(string_v.signature()));
+  EXPECT_FALSE(DBusAdaptor::IsBool(byte_v_.signature()));
+  EXPECT_FALSE(DBusAdaptor::IsStrings(string_v_.signature()));
 }
 
 TEST_F(DBusAdaptorTest, Dispatch) {
+  MockPropertyStore store;
   ::DBus::Error error;
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&manager_, "", bool_v, error));
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&manager_, "", string_v, error));
 
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", strings_v, error));
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", int16_v, error));
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", int32_v, error));
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", uint16_v, error));
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", uint32_v, error));
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", stringmap_v, error));
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", byte_v, error));
+  EXPECT_CALL(store, Contains(_)).WillRepeatedly(Return(true));
+  EXPECT_CALL(store, SetBoolProperty("", _, _)).WillOnce(Return(true));
+  EXPECT_CALL(store, SetInt16Property("", _, _)).WillOnce(Return(true));
+  EXPECT_CALL(store, SetInt32Property("", _, _)).WillOnce(Return(true));
+  EXPECT_CALL(store, SetStringProperty("", _, _))
+      .WillOnce(Return(true));
+  EXPECT_CALL(store, SetStringmapProperty("", _, _))
+      .WillOnce(Return(true));
+  EXPECT_CALL(store, SetStringsProperty("", _, _))
+      .WillOnce(Return(true));
+  EXPECT_CALL(store, SetUint8Property("", _, _)).WillOnce(Return(true));
+  EXPECT_CALL(store, SetUint16Property("", _, _)).WillOnce(Return(true));
+  EXPECT_CALL(store, SetUint32Property("", _, _)).WillOnce(Return(true));
 
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(device_.get(), "", bool_v, error));
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(device_.get(), "", string_v, error));
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(device_.get(), "", int16_v, error));
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(device_.get(), "", int32_v, error));
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(device_.get(), "", uint16_v, error));
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(device_.get(), "", uint32_v, error));
+  string string_path("/false/path");
+  ::DBus::Path path(string_path);
+  ::DBus::Variant path_v = DBusAdaptor::PathToVariant(path);
+  EXPECT_CALL(store, SetStringProperty("", StrEq(string_path), _))
+      .WillOnce(Return(true));
 
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(device_.get(), "", byte_v, error));
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(device_.get(),
-                                           "",
-                                           strings_v,
-                                           error));
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(device_.get(),
-                                           "",
-                                           stringmap_v,
-                                           error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&store, "", bool_v_, error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&store, "", path_v, error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&store, "", string_v_, error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&store, "", strings_v_, error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&store, "", int16_v_, error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&store, "", int32_v_, error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&store, "", uint16_v_, error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&store, "", uint32_v_, error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&store, "", stringmap_v_, error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&store, "", byte_v_, error));
 
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(service_.get(), "", bool_v, error));
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(service_.get(), "", byte_v, error));
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(service_.get(), "", string_v, error));
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(service_.get(), "", int32_v, error));
-  EXPECT_TRUE(DBusAdaptor::DispatchOnType(service_.get(),
-                                          "",
-                                          stringmap_v,
-                                          error));
-
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(service_.get(), "", int16_v, error));
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(service_.get(),
-                                           "",
-                                           uint16_v,
-                                           error));
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(service_.get(),
-                                           "",
-                                           uint32_v,
-                                           error));
-  EXPECT_FALSE(DBusAdaptor::DispatchOnType(service_.get(),
-                                           "",
-                                           strings_v,
-                                           error));
 }
 
 }  // namespace shill
diff --git a/device.cc b/device.cc
index e7103d3..9aab0f0 100644
--- a/device.cc
+++ b/device.cc
@@ -10,6 +10,7 @@
 
 #include <base/logging.h>
 #include <base/memory/ref_counted.h>
+#include <chromeos/dbus/service_constants.h>
 
 #include "shill/control_interface.h"
 #include "shill/device.h"
@@ -33,6 +34,33 @@
       adaptor_(control_interface->CreateDeviceAdaptor(this)),
       interface_index_(interface_index),
       running_(false) {
+  known_properties_.push_back(flimflam::kNameProperty);
+  known_properties_.push_back(flimflam::kTypeProperty);
+  known_properties_.push_back(flimflam::kPoweredProperty);
+  known_properties_.push_back(flimflam::kScanningProperty);
+  // known_properties_.push_back(flimflam::kReconnectProperty);
+  known_properties_.push_back(flimflam::kScanIntervalProperty);
+  known_properties_.push_back(flimflam::kBgscanMethodProperty);
+  known_properties_.push_back(flimflam::kBgscanShortIntervalProperty);
+  known_properties_.push_back(flimflam::kBgscanSignalThresholdProperty);
+  known_properties_.push_back(flimflam::kNetworksProperty);
+  known_properties_.push_back(flimflam::kIPConfigsProperty);
+  known_properties_.push_back(flimflam::kCellularAllowRoamingProperty);
+  known_properties_.push_back(flimflam::kCarrierProperty);
+  known_properties_.push_back(flimflam::kMeidProperty);
+  known_properties_.push_back(flimflam::kImeiProperty);
+  known_properties_.push_back(flimflam::kImsiProperty);
+  known_properties_.push_back(flimflam::kEsnProperty);
+  known_properties_.push_back(flimflam::kMdnProperty);
+  known_properties_.push_back(flimflam::kModelIDProperty);
+  known_properties_.push_back(flimflam::kManufacturerProperty);
+  known_properties_.push_back(flimflam::kFirmwareRevisionProperty);
+  known_properties_.push_back(flimflam::kHardwareRevisionProperty);
+  known_properties_.push_back(flimflam::kPRLVersionProperty);
+  known_properties_.push_back(flimflam::kSIMLockStatusProperty);
+  known_properties_.push_back(flimflam::kFoundNetworksProperty);
+  known_properties_.push_back(flimflam::kDBusConnectionProperty);
+  known_properties_.push_back(flimflam::kDBusObjectProperty);
   // Initialize Interface monitor, so we can detect new interfaces
   VLOG(2) << "Device " << link_name_ << " index " << interface_index;
 }
@@ -65,16 +93,17 @@
   VLOG(2) << "Device " << link_name_ << " scan requested.";
 }
 
-bool Device::SetBoolProperty(const string& name, bool value, Error *error) {
-  VLOG(2) << "Setting " << name << " as a bool.";
-  // TODO(cmasone): Set actual properties.
-  return true;
+bool Device::Contains(const std::string &property) {
+  vector<string>::iterator it;
+  for (it = known_properties_.begin(); it != known_properties_.end(); ++it) {
+    if (property == *it)
+      return true;
+  }
+  return false;
 }
 
-bool Device::SetInt16Property(const std::string& name,
-                              int16 value,
-                              Error *error) {
-  VLOG(2) << "Setting " << name << " as an int16.";
+bool Device::SetBoolProperty(const string& name, bool value, Error *error) {
+  VLOG(2) << "Setting " << name << " as a bool.";
   // TODO(cmasone): Set actual properties.
   return true;
 }
@@ -87,14 +116,6 @@
   return true;
 }
 
-bool Device::SetStringProperty(const string& name,
-                               const string& value,
-                               Error *error) {
-  VLOG(2) << "Setting " << name << " as a string.";
-  // TODO(cmasone): Set actual properties.
-  return true;
-}
-
 bool Device::SetUint16Property(const std::string& name,
                                uint16 value,
                                Error *error) {
@@ -103,14 +124,6 @@
   return true;
 }
 
-bool Device::SetUint32Property(const std::string& name,
-                               uint32 value,
-                               Error *error) {
-  VLOG(2) << "Setting " << name << " as a uint32.";
-  // TODO(cmasone): Set actual properties.
-  return true;
-}
-
 const string& Device::UniqueName() const {
   // TODO(pstew): link_name is only run-time unique and won't persist
   return link_name();
diff --git a/device.h b/device.h
index 4fd96d6..133b7a5 100644
--- a/device.h
+++ b/device.h
@@ -67,14 +67,16 @@
   virtual void ConfigIP() {}
 
   // Implementation of PropertyStoreInterface
-  bool SetBoolProperty(const std::string& name, bool value, Error *error);
-  bool SetInt16Property(const std::string& name, int16 value, Error *error);
-  bool SetInt32Property(const std::string& name, int32 value, Error *error);
-  bool SetStringProperty(const std::string& name,
-                         const std::string& value,
-                         Error *error);
-  bool SetUint16Property(const std::string& name, uint16 value, Error *error);
-  bool SetUint32Property(const std::string& name, uint32 value, Error *error);
+  virtual bool Contains(const std::string &property);
+  virtual bool SetBoolProperty(const std::string& name,
+                               bool value,
+                               Error *error);
+  virtual bool SetInt32Property(const std::string& name,
+                                int32 value,
+                                Error *error);
+  virtual bool SetUint16Property(const std::string& name,
+                                 uint16 value,
+                                 Error *error);
 
   const std::string &link_name() const { return link_name_; }
 
@@ -102,6 +104,7 @@
   bool running_;
   Manager *manager_;
   IPConfigRefPtr ipconfig_;
+  std::vector<std::string> known_properties_;
 
  private:
   friend class DeviceAdaptorInterface;
diff --git a/device_unittest.cc b/device_unittest.cc
index 0b81954..6d84221 100644
--- a/device_unittest.cc
+++ b/device_unittest.cc
@@ -2,16 +2,33 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include <gtest/gtest.h>
-
 #include "shill/device.h"
-#include "shill/dhcp_provider.h"
-#include "shill/mock_control.h"
-#include "shill/mock_glib.h"
 
-using testing::_;
-using testing::Return;
-using testing::Test;
+#include <map>
+#include <string>
+#include <vector>
+
+#include <dbus-c++/dbus.h>
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "shill/dbus_adaptor.h"
+#include "shill/dhcp_provider.h"
+#include "shill/manager.h"
+#include "shill/mock_control.h"
+#include "shill/mock_device.h"
+#include "shill/mock_glib.h"
+#include "shill/property_store_unittest.h"
+#include "shill/shill_event.h"
+
+using std::map;
+using std::string;
+using std::vector;
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::Test;
 
 namespace shill {
 
@@ -19,12 +36,13 @@
 const char kDeviceName[] = "testdevice";
 }  // namespace {}
 
-class DeviceTest : public Test {
+class DeviceTest : public PropertyStoreTest {
  public:
   DeviceTest()
       : device_(new Device(&control_interface_, NULL, NULL, kDeviceName, 0)) {
     DHCPProvider::GetInstance()->glib_ = &glib_;
   }
+  virtual ~DeviceTest() {}
 
  protected:
   MockGLib glib_;
@@ -32,6 +50,57 @@
   DeviceRefPtr device_;
 };
 
+TEST_F(DeviceTest, Contains) {
+  EXPECT_TRUE(device_->Contains(flimflam::kNameProperty));
+  EXPECT_FALSE(device_->Contains(""));
+}
+
+TEST_F(DeviceTest, Dispatch) {
+  ::DBus::Error error;
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(device_.get(),
+                                          flimflam::kPoweredProperty,
+                                          bool_v_,
+                                          error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(
+      device_.get(),
+      flimflam::kBgscanSignalThresholdProperty,
+      int32_v_,
+      error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(device_.get(),
+                                          flimflam::kScanIntervalProperty,
+                                          uint16_v_,
+                                          error));
+
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(device_.get(), "", byte_v_, error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(device_.get(),
+                                           "",
+                                           stringmap_v_,
+                                           error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(device_.get(),
+                                           "",
+                                           uint32_v_,
+                                           error));
+  EXPECT_EQ(invalid_prop_, error.name());
+
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(device_.get(),
+                                           flimflam::kCarrierProperty,
+                                           string_v_,
+                                           error));
+  EXPECT_EQ(invalid_args_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(device_.get(),
+                                           flimflam::kNetworksProperty,
+                                           strings_v_,
+                                           error));
+  EXPECT_EQ(invalid_args_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(device_.get(),
+                                           flimflam::kPRLVersionProperty,
+                                           int16_v_,
+                                           error));
+  EXPECT_EQ(invalid_args_, error.name());
+}
+
 TEST_F(DeviceTest, TechnologyIs) {
   EXPECT_FALSE(device_->TechnologyIs(Device::kEthernet));
 }
diff --git a/error.h b/error.h
index 6548467..cf04a3b 100644
--- a/error.h
+++ b/error.h
@@ -31,6 +31,7 @@
     kPinError,
     kNumErrors
   };
+  static const char * const kErrorNames[kNumErrors];
 
   Error(Type type, const std::string& message);
   virtual ~Error();
@@ -40,8 +41,6 @@
   void ToDBusError(::DBus::Error *error);
 
  private:
-  static const char * const kErrorNames[kNumErrors];
-
   Type type_;
   std::string message_;
 
diff --git a/ethernet_service.cc b/ethernet_service.cc
index b3e31f7..fe04655 100644
--- a/ethernet_service.cc
+++ b/ethernet_service.cc
@@ -12,6 +12,7 @@
 #include <string>
 
 #include <base/logging.h>
+#include <chromeos/dbus/service_constants.h>
 
 #include "shill/control_interface.h"
 #include "shill/device.h"
@@ -29,8 +30,11 @@
                                  Ethernet *device,
                                  const string &name)
     : Service(control_interface, dispatcher, device, name),
-      ethernet_(device) {
+      ethernet_(device),
+      type_(flimflam::kTypeEthernet) {
   set_auto_connect(true);
+
+  RegisterConstString(flimflam::kTypeProperty, &type_);
 }
 
 EthernetService::~EthernetService() { }
diff --git a/ethernet_service.h b/ethernet_service.h
index 9478691..1886854 100644
--- a/ethernet_service.h
+++ b/ethernet_service.h
@@ -26,8 +26,13 @@
   ~EthernetService();
   void Connect();
   void Disconnect();
+
+ protected:
+  virtual std::string CalculateState() { return "idle"; }
+
  private:
   Ethernet *ethernet_;
+  const std::string type_;
   DISALLOW_COPY_AND_ASSIGN(EthernetService);
 };
 
diff --git a/manager.cc b/manager.cc
index fab11b0..b19268d 100644
--- a/manager.cc
+++ b/manager.cc
@@ -11,6 +11,7 @@
 
 #include <base/logging.h>
 #include <base/memory/ref_counted.h>
+#include <chromeos/dbus/service_constants.h>
 
 #include "shill/adaptor_interfaces.h"
 #include "shill/control_interface.h"
@@ -29,8 +30,24 @@
                  EventDispatcher *dispatcher)
   : adaptor_(control_interface->CreateManagerAdaptor(this)),
     device_info_(control_interface, dispatcher, this),
-    running_(false) {
+    running_(false),
+    offline_mode_(false),
+    state_(flimflam::kStateOffline) {
   // Initialize Interface monitor, so we can detect new interfaces
+  known_properties_.push_back(flimflam::kStateProperty);
+  known_properties_.push_back(flimflam::kAvailableTechnologiesProperty);
+  known_properties_.push_back(flimflam::kEnabledTechnologiesProperty);
+  known_properties_.push_back(flimflam::kConnectedTechnologiesProperty);
+  known_properties_.push_back(flimflam::kDefaultTechnologyProperty);
+  known_properties_.push_back(flimflam::kCheckPortalListProperty);
+  known_properties_.push_back(flimflam::kCountryProperty);
+  known_properties_.push_back(flimflam::kOfflineModeProperty);
+  known_properties_.push_back(flimflam::kPortalURLProperty);
+  known_properties_.push_back(flimflam::kActiveProfileProperty);
+  known_properties_.push_back(flimflam::kProfilesProperty);
+  known_properties_.push_back(flimflam::kDevicesProperty);
+  known_properties_.push_back(flimflam::kServicesProperty);
+  known_properties_.push_back(flimflam::kServiceWatchListProperty);
   VLOG(2) << "Manager initialized.";
 }
 
@@ -110,18 +127,49 @@
   return NULL;
 }
 
+bool Manager::Contains(const std::string &property) {
+  vector<string>::iterator it;
+  for (it = known_properties_.begin(); it != known_properties_.end(); ++it) {
+    if (property == *it)
+      return true;
+  }
+  return false;
+}
+
 bool Manager::SetBoolProperty(const string& name, bool value, Error *error) {
   VLOG(2) << "Setting " << name << " as a bool.";
-  // TODO(cmasone): Set actual properties.
-  return true;
+  bool set = false;
+  if (name == flimflam::kOfflineModeProperty) {
+    offline_mode_ = value;
+    set = true;
+  }
+  if (!set && error)
+    error->Populate(Error::kInvalidArguments, name + " is not a R/W bool.");
+  return set;
 }
 
 bool Manager::SetStringProperty(const string& name,
                                 const string& value,
                                 Error *error) {
   VLOG(2) << "Setting " << name << " as a string.";
-  // TODO(cmasone): Set actual properties.
-  return true;
+  bool set = false;
+  if (name == flimflam::kActiveProfileProperty) {
+    active_profile_ = value;
+    set = true;
+  } else if (name == flimflam::kCountryProperty) {
+    country_ = value;
+    set = true;
+  } else if (name == flimflam::kPortalURLProperty) {
+    portal_url_ = value;
+    set = true;
+  } else if (name == flimflam::kCheckPortalListProperty) {
+    // parse string, update all services of specified technologies.
+    set = true;
+  }
+
+  if (!set && error)
+    error->Populate(Error::kInvalidArguments, name + " is not a R/W string.");
+  return set;
 }
 
 }  // namespace shill
diff --git a/manager.h b/manager.h
index 58d4407..bcfe6b4 100644
--- a/manager.h
+++ b/manager.h
@@ -29,7 +29,7 @@
   // A constructor for the Manager object
   Manager(ControlInterface *control_interface,
           EventDispatcher *dispatcher);
-  ~Manager();
+  virtual ~Manager();
   void Start();
   void Stop();
 
@@ -45,18 +45,31 @@
   ServiceRefPtr FindService(const std::string& name);
 
   // Implementation of PropertyStoreInterface
-  bool SetBoolProperty(const std::string& name, bool value, Error *error);
-
-  bool SetStringProperty(const std::string& name,
-                         const std::string& value,
-                         Error *error);
+  virtual bool Contains(const std::string &property);
+  virtual bool SetBoolProperty(const std::string &name,
+                               bool value,
+                               Error *error);
+  virtual bool SetStringProperty(const std::string &name,
+                                 const std::string &value,
+                                 Error *error);
 
  private:
   scoped_ptr<ManagerAdaptorInterface> adaptor_;
   DeviceInfo device_info_;
   bool running_;
+  std::vector<std::string> known_properties_;
   std::vector<DeviceRefPtr> devices_;
   std::vector<ServiceRefPtr> services_;
+
+  // Properties to be get/set via PropertyStoreInterface calls.
+  bool offline_mode_;
+  std::string state_;
+  std::string country_;
+  std::string portal_url_;
+
+  std::string active_profile_;  // This is supposed to be, essentially,
+                                // an RPC-visible object handle
+
   friend class ManagerAdaptorInterface;
 };
 
diff --git a/manager_dbus_adaptor.cc b/manager_dbus_adaptor.cc
index b53dc5f..91cd9f5 100644
--- a/manager_dbus_adaptor.cc
+++ b/manager_dbus_adaptor.cc
@@ -66,7 +66,9 @@
 void ManagerDBusAdaptor::SetProperty(const string &name,
                                      const ::DBus::Variant &value,
                                      ::DBus::Error &error) {
-  DBusAdaptor::DispatchOnType(manager_, name, value, error);
+  if (DBusAdaptor::DispatchOnType(manager_, name, value, error)) {
+    PropertyChanged(name, value);
+  }
 }
 
 string ManagerDBusAdaptor::GetState(::DBus::Error &error) {
diff --git a/manager_unittest.cc b/manager_unittest.cc
index 26a1ed9..74a88c3 100644
--- a/manager_unittest.cc
+++ b/manager_unittest.cc
@@ -1,19 +1,29 @@
 // Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
+
+#include "shill/manager.h"
+
 #include <glib.h>
 
 #include <base/callback_old.h>
 #include <base/logging.h>
 #include <base/memory/ref_counted.h>
 #include <base/message_loop.h>
+#include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
 
 #include "shill/mock_control.h"
-#include "shill/manager.h"
 #include "shill/mock_device.h"
 #include "shill/mock_service.h"
+#include "shill/property_store_unittest.h"
+#include "shill/shill_event.h"
+
+using std::map;
+using std::string;
+using std::vector;
+
 
 namespace shill {
 using ::testing::Test;
@@ -22,12 +32,10 @@
 using ::testing::Return;
 using std::vector;
 
-class ManagerTest : public Test {
+class ManagerTest : public PropertyStoreTest {
  public:
-  ManagerTest()
-      : manager_(&control_, &dispatcher_),
-        factory_(this) {
-  }
+  ManagerTest() : factory_(this) {}
+  virtual ~ManagerTest() {}
 
   bool IsDeviceRegistered(Device *device, Device::Technology tech) {
     vector<DeviceRefPtr> devices;
@@ -36,26 +44,30 @@
   }
 
  protected:
-  MockControl control_;
-  Manager manager_;
-  EventDispatcher dispatcher_;
   ScopedRunnableMethodFactory<ManagerTest> factory_;
 };
 
+TEST_F(ManagerTest, Contains) {
+  EXPECT_TRUE(manager_.Contains(flimflam::kStateProperty));
+  EXPECT_FALSE(manager_.Contains(""));
+}
+
 TEST_F(ManagerTest, DeviceRegistration) {
-  scoped_refptr<MockDevice> mock_device(new NiceMock<MockDevice>(&control_,
-                                                                 &dispatcher_,
-                                                                 &manager_,
-                                                                 "null0",
-                                                                 -1));
+  scoped_refptr<MockDevice> mock_device(
+      new NiceMock<MockDevice>(&control_interface_,
+                               &dispatcher_,
+                               &manager_,
+                               "null0",
+                               -1));
   ON_CALL(*mock_device.get(), TechnologyIs(Device::kEthernet))
       .WillByDefault(Return(true));
 
-  scoped_refptr<MockDevice> mock_device2(new NiceMock<MockDevice>(&control_,
-                                                                  &dispatcher_,
-                                                                  &manager_,
-                                                                  "null1",
-                                                                  -1));
+  scoped_refptr<MockDevice> mock_device2(
+      new NiceMock<MockDevice>(&control_interface_,
+                               &dispatcher_,
+                               &manager_,
+                               "null1",
+                               -1));
   ON_CALL(*mock_device2.get(), TechnologyIs(Device::kWifi))
       .WillByDefault(Return(true));
 
@@ -67,19 +79,21 @@
 }
 
 TEST_F(ManagerTest, DeviceDeregistration) {
-  scoped_refptr<MockDevice> mock_device(new NiceMock<MockDevice>(&control_,
-                                                                 &dispatcher_,
-                                                                 &manager_,
-                                                                 "null2",
-                                                                 -1));
+  scoped_refptr<MockDevice> mock_device(
+      new NiceMock<MockDevice>(&control_interface_,
+                               &dispatcher_,
+                               &manager_,
+                               "null2",
+                               -1));
   ON_CALL(*mock_device.get(), TechnologyIs(Device::kEthernet))
       .WillByDefault(Return(true));
 
-  scoped_refptr<MockDevice> mock_device2(new NiceMock<MockDevice>(&control_,
-                                                                  &dispatcher_,
-                                                                  &manager_,
-                                                                  "null2",
-                                                                  -1));
+  scoped_refptr<MockDevice> mock_device2(
+      new NiceMock<MockDevice>(&control_interface_,
+                               &dispatcher_,
+                               &manager_,
+                               "null2",
+                               -1));
   ON_CALL(*mock_device2.get(), TechnologyIs(Device::kWifi))
       .WillByDefault(Return(true));
 
@@ -97,7 +111,7 @@
 }
 
 TEST_F(ManagerTest, ServiceRegistration) {
-  scoped_refptr<MockDevice> device(new MockDevice(&control_,
+  scoped_refptr<MockDevice> device(new MockDevice(&control_interface_,
                                                   &dispatcher_,
                                                   &manager_,
                                                   "null3",
@@ -105,13 +119,13 @@
   const char kService1[] = "service1";
   const char kService2[] = "wifi_service2";
   scoped_refptr<MockService> mock_service(
-      new NiceMock<MockService>(&control_,
+      new NiceMock<MockService>(&control_interface_,
                                 &dispatcher_,
                                 device.get(),
                                 kService1));
 
   scoped_refptr<MockService> mock_service2(
-      new NiceMock<MockService>(&control_,
+      new NiceMock<MockService>(&control_interface_,
                                 &dispatcher_,
                                 device.get(),
                                 kService2));
@@ -123,4 +137,44 @@
   EXPECT_TRUE(manager_.FindService(kService2).get() != NULL);
 }
 
+TEST_F(ManagerTest, Dispatch) {
+  ::DBus::Error error;
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&manager_,
+                                          flimflam::kOfflineModeProperty,
+                                          bool_v_,
+                                          error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(&manager_,
+                                          flimflam::kActiveProfileProperty,
+                                          string_v_,
+                                          error));
+
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", bool_v_, error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", strings_v_, error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", int16_v_, error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", int32_v_, error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", uint16_v_, error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", uint32_v_, error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", stringmap_v_, error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_, "", byte_v_, error));
+  EXPECT_EQ(invalid_prop_, error.name());
+
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_,
+                                           flimflam::kActiveProfileProperty,
+                                           bool_v_,
+                                           error));
+  EXPECT_EQ(invalid_args_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(&manager_,
+                                           flimflam::kOfflineModeProperty,
+                                           string_v_,
+                                           error));
+  EXPECT_EQ(invalid_args_, error.name());
+}
+
 }  // namespace shill
diff --git a/mock_property_store.h b/mock_property_store.h
new file mode 100644
index 0000000..4971984
--- /dev/null
+++ b/mock_property_store.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_MOCK_PROPERTY_STORE_
+#define SHILL_MOCK_PROPERTY_STORE_
+
+#include "shill/property_store_interface.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/basictypes.h>
+
+namespace shill {
+
+class Error;
+
+class MockPropertyStore : public PropertyStoreInterface {
+ public:
+  MockPropertyStore() {}
+  virtual ~MockPropertyStore() {}
+  MOCK_METHOD1(Contains, bool(const std::string&));
+  MOCK_METHOD3(SetBoolProperty, bool(const std::string&, bool, Error*));
+  MOCK_METHOD3(SetInt16Property, bool(const std::string&, int16, Error*));
+  MOCK_METHOD3(SetInt32Property, bool(const std::string&, int32, Error*));
+  MOCK_METHOD3(SetStringProperty, bool(const std::string&,
+                                       const std::string&,
+                                       Error*));
+  MOCK_METHOD3(SetStringmapProperty,
+               bool(const std::string&,
+                    const std::map<std::string, std::string>&,
+                    Error*));
+  MOCK_METHOD3(SetStringsProperty, bool(const std::string&,
+                                        const std::vector<std::string>&,
+                                        Error*));
+  MOCK_METHOD3(SetUint8Property, bool(const std::string&, uint8, Error*));
+  MOCK_METHOD3(SetUint16Property, bool(const std::string&, uint16, Error*));
+  MOCK_METHOD3(SetUint32Property, bool(const std::string&, uint32, Error*));
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockPropertyStore);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_MOCK_PROPERTY_STORE_
diff --git a/mock_service.h b/mock_service.h
index 80cf1d8..78e766e 100644
--- a/mock_service.h
+++ b/mock_service.h
@@ -26,6 +26,7 @@
 
   MOCK_METHOD0(Connect, void(void));
   MOCK_METHOD0(Disconnect, void(void));
+  MOCK_METHOD0(CalculateState, std::string(void));
 
  private:
   DISALLOW_COPY_AND_ASSIGN(MockService);
diff --git a/property_accessor.h b/property_accessor.h
new file mode 100644
index 0000000..cb39182
--- /dev/null
+++ b/property_accessor.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_PROPERTY_ACCESSOR_
+#define SHILL_PROPERTY_ACCESSOR_
+
+#include <base/basictypes.h>
+
+#include "shill/accessor_interface.h"
+
+namespace shill {
+
+// Templated implementations of AccessorInterface<>.
+// PropertyAccessor<> and ConstPropertyAccessor<> respectively provide
+// R/W and R/O access to the value pointed to by |property|.
+// this allows a class to easily map strings to member variables, so that
+// pieces of state stored in the class can be queried or updated by name.
+//
+//   bool foo = true;
+//   map<string, BoolAccessor> accessors;
+//   accessors["foo"] = BoolAccessor(new PropertyAccessor<bool>(&foo));
+//   bool new_foo = accessors["foo"]->Get();  // new_foo == true
+//   accessors["foo"]->Set(false);  // returns true, because setting is allowed.
+//                                  // foo == false, new_foo == true
+//   new_foo = accessors["foo"]->Get();  // new_foo == false
+template <class T>
+class PropertyAccessor : public AccessorInterface<T> {
+ public:
+  explicit PropertyAccessor(T *property) : property_(property) {
+    DCHECK(property);
+  }
+  virtual ~PropertyAccessor() {}
+
+  const T &Get() { return *property_; }
+  bool Set(const T &value) {
+    *property_ = value;
+    return true;
+  }
+
+ private:
+  T * const property_;
+  DISALLOW_COPY_AND_ASSIGN(PropertyAccessor);
+};
+
+template <class T>
+class ConstPropertyAccessor : public AccessorInterface<T> {
+ public:
+  explicit ConstPropertyAccessor(const T *property) : property_(property) {
+    DCHECK(property);
+  }
+  virtual ~ConstPropertyAccessor() {}
+
+  const T &Get() { return *property_; }
+  bool Set(const T &value) { return false; }
+
+ private:
+  const T * const property_;
+  DISALLOW_COPY_AND_ASSIGN(ConstPropertyAccessor);
+};
+
+// CustomAccessor<> allows custom getter and setter methods to be provided.
+// Thus, if the state to be returned is to be derived on-demand, we can
+// still fit it into the AccessorInterface<> framework.
+template<class C, class T>
+class CustomAccessor : public AccessorInterface<T> {
+ public:
+  // |target| is the object on which to call the methods |getter| and |setter|
+  // |setter| is allowed to be NULL, in which case we will simply reject
+  // attempts to set via the accessor.
+  // It is an error to pass NULL for either of the other two arguments.
+  CustomAccessor(C *target, T(C::*getter)(), bool(C::*setter)(const T&))
+      : target_(target),
+        getter_(getter),
+        setter_(setter) {
+    DCHECK(target);
+    DCHECK(getter);
+  }
+  virtual ~CustomAccessor() {}
+
+  const T &Get() { return storage_ = (target_->*getter_)(); }
+  bool Set(const T &value) { return setter_ && (target_->*setter_)(value); }
+
+ private:
+  C * const target_;
+  // Get() returns a const&, so we need to have internal storage to which to
+  // return a reference.
+  T storage_;
+  T(C::*getter_)(void);
+  bool(C::*setter_)(const T&);
+  DISALLOW_COPY_AND_ASSIGN(CustomAccessor);
+};
+
+}  // namespace shill
+
+#endif  // SHILL_PROPERTY_ACCESSOR_
diff --git a/property_store_interface.h b/property_store_interface.h
index 585c531..c766d77 100644
--- a/property_store_interface.h
+++ b/property_store_interface.h
@@ -19,6 +19,8 @@
  public:
   virtual ~PropertyStoreInterface();
 
+  virtual bool Contains(const std::string& property) = 0;
+
   // Methods to allow the setting, by name, of properties stored in this object.
   // The property names are declared in chromeos/dbus/service_constants.h,
   // so that they may be shared with libcros.
diff --git a/property_store_unittest.h b/property_store_unittest.h
new file mode 100644
index 0000000..9e7ba6e
--- /dev/null
+++ b/property_store_unittest.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SHILL_PROPERTY_STORE_UNITTEST_H_
+#define SHILL_PROPERTY_STORE_UNITTEST_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <dbus-c++/dbus.h>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "shill/dbus_adaptor.h"
+#include "shill/error.h"
+#include "shill/manager.h"
+#include "shill/mock_control.h"
+#include "shill/property_store_interface.h"
+#include "shill/shill_event.h"
+
+namespace shill {
+
+class PropertyStoreTest : public ::testing::Test {
+ public:
+  PropertyStoreTest()
+      : bool_v_(DBusAdaptor::BoolToVariant(0)),
+        byte_v_(DBusAdaptor::ByteToVariant(0)),
+        uint16_v_(DBusAdaptor::Uint16ToVariant(0)),
+        uint32_v_(DBusAdaptor::Uint32ToVariant(0)),
+        int16_v_(DBusAdaptor::Int16ToVariant(0)),
+        int32_v_(DBusAdaptor::Int32ToVariant(0)),
+        string_v_(DBusAdaptor::StringToVariant("")),
+        stringmap_v_(DBusAdaptor::StringmapToVariant(
+            std::map<std::string, std::string>())),
+        strings_v_(DBusAdaptor::StringsToVariant(
+            std::vector<std::string>(1, ""))),
+        manager_(&control_interface_, &dispatcher_),
+        invalid_args_(Error::kErrorNames[Error::kInvalidArguments]),
+        invalid_prop_(Error::kErrorNames[Error::kInvalidProperty]) {
+  }
+
+  virtual ~PropertyStoreTest() {}
+
+ protected:
+  ::DBus::Variant bool_v_;
+  ::DBus::Variant byte_v_;
+  ::DBus::Variant uint16_v_;
+  ::DBus::Variant uint32_v_;
+  ::DBus::Variant int16_v_;
+  ::DBus::Variant int32_v_;
+  ::DBus::Variant string_v_;
+  ::DBus::Variant stringmap_v_;
+  ::DBus::Variant strings_v_;
+
+  MockControl control_interface_;
+  EventDispatcher dispatcher_;
+  Manager manager_;
+  std::string invalid_args_;
+  std::string invalid_prop_;
+};
+
+}  // namespace shill
+#endif  // SHILL_PROPERTY_STORE_UNITTEST_H_
diff --git a/service.cc b/service.cc
index 60e5ae4..82951ec 100644
--- a/service.cc
+++ b/service.cc
@@ -10,10 +10,13 @@
 #include <vector>
 
 #include <base/logging.h>
+#include <base/memory/scoped_ptr.h>
+#include <chromeos/dbus/service_constants.h>
 
 #include "shill/control_interface.h"
 #include "shill/device_config_interface.h"
 #include "shill/error.h"
+#include "shill/property_accessor.h"
 #include "shill/service.h"
 #include "shill/service_dbus_adaptor.h"
 
@@ -26,57 +29,165 @@
                  EventDispatcher *dispatcher,
                  DeviceConfigInterfaceRefPtr config_interface,
                  const string& name)
-    : dispatcher_(dispatcher),
+    : auto_connect_(false),
+      connectable_(false),
+      favorite_(false),
+      priority_(0),
+      save_credentials_(true),
+      dispatcher_(dispatcher),
       name_(name),
       available_(false),
       configured_(false),
-      auto_connect_(false),
       configuration_(NULL),
       connection_(NULL),
       config_interface_(config_interface),
       adaptor_(control_interface->CreateServiceAdaptor(this)) {
-  // Initialize Interface montior, so we can detect new interfaces
+
+  RegisterBool(flimflam::kAutoConnectProperty, &auto_connect_);
+  RegisterString(flimflam::kCheckPortalProperty, &check_portal_);
+  RegisterConstBool(flimflam::kConnectableProperty, &connectable_);
+  RegisterDerivedString(flimflam::kDeviceProperty, &Service::DeviceRPCID, NULL);
+
+  RegisterString(flimflam::kEapIdentityProperty, &eap_.identity);
+  RegisterString(flimflam::kEAPEAPProperty, &eap_.eap);
+  RegisterString(flimflam::kEapPhase2AuthProperty, &eap_.inner_eap);
+  RegisterString(flimflam::kEapAnonymousIdentityProperty,
+                 &eap_.anonymous_identity);
+  RegisterString(flimflam::kEAPClientCertProperty, &eap_.client_cert);
+  RegisterString(flimflam::kEAPCertIDProperty, &eap_.cert_id);
+  RegisterString(flimflam::kEapPrivateKeyProperty, &eap_.private_key);
+  RegisterString(flimflam::kEapPrivateKeyPasswordProperty,
+                 &eap_.private_key_password);
+  RegisterString(flimflam::kEAPKeyIDProperty, &eap_.key_id);
+  RegisterString(flimflam::kEapCaCertProperty, &eap_.ca_cert);
+  RegisterString(flimflam::kEapCaCertIDProperty, &eap_.ca_cert_id);
+  RegisterString(flimflam::kEAPPINProperty, &eap_.pin);
+  RegisterString(flimflam::kEapPasswordProperty, &eap_.password);
+  RegisterString(flimflam::kEapKeyMgmtProperty, &eap_.key_management);
+  RegisterBool(flimflam::kEapUseSystemCAsProperty, &eap_.use_system_cas);
+
+  RegisterConstString(flimflam::kErrorProperty, &error_);
+  RegisterConstBool(flimflam::kFavoriteProperty, &favorite_);
+  RegisterDerivedBool(flimflam::kIsActiveProperty, &Service::IsActive, NULL);
+  RegisterConstString(flimflam::kNameProperty, &name_);
+  RegisterInt32(flimflam::kPriorityProperty, &priority_);
+  RegisterDerivedString(flimflam::kProfileProperty,
+                        &Service::ProfileRPCID,
+                        NULL);
+  RegisterString(flimflam::kProxyConfigProperty, &proxy_config_);
+  RegisterBool(flimflam::kSaveCredentialsProperty, &save_credentials_);
+  RegisterDerivedString(flimflam::kStateProperty,
+                        &Service::CalculateState,
+                        NULL);
+
+  // TODO(cmasone): Create VPN Service with this property
+  // RegisterConstStringmap(flimflam::kProviderProperty, &provider_);
+
+  // TODO(cmasone): Add support for R/O properties that return DBus object paths
+  // flimflam::kDeviceProperty, flimflam::kProfileProperty
+
   VLOG(2) << "Service initialized.";
 }
 
 Service::~Service() {}
 
-bool Service::SetBoolProperty(const string& name, bool value, Error *error) {
-  VLOG(2) << "Setting " << name << " as a bool.";
-  // TODO(cmasone): Set actual properties.
-  return true;
+bool Service::Contains(const string &property) {
+  return (bool_properties_.find(property) != bool_properties_.end() ||
+          int32_properties_.find(property) != int32_properties_.end() ||
+          string_properties_.find(property) != string_properties_.end());
 }
 
-bool Service::SetInt32Property(const std::string& name,
-                              int32 value,
-                              Error *error) {
-  VLOG(2) << "Setting " << name << " as an int32.";
-  // TODO(cmasone): Set actual properties.
-  return true;
+bool Service::SetBoolProperty(const string& name, bool value, Error *error) {
+  VLOG(2) << "Setting " << name << " as a bool.";
+  bool set = bool_properties_[name]->Set(value);
+  if (!set && error)
+    error->Populate(Error::kInvalidArguments, name + " is not a R/W bool.");
+  return set;
+}
+
+bool Service::SetInt32Property(const string& name, int32 value, Error *error) {
+  VLOG(2) << "Setting " << name << " as an int32";
+  bool set = int32_properties_[name]->Set(value);
+  if (!set && error)
+    error->Populate(Error::kInvalidArguments, name + " is not a R/W int32.");
+  return set;
 }
 
 bool Service::SetStringProperty(const string& name,
                                 const string& value,
                                 Error *error) {
   VLOG(2) << "Setting " << name << " as a string.";
-  // TODO(cmasone): Set actual properties.
-  return true;
+  bool set = string_properties_[name]->Set(value);
+  if (!set && error)
+    error->Populate(Error::kInvalidArguments, name + " is not a R/W string.");
+  return set;
 }
 
 bool Service::SetStringmapProperty(const string& name,
-                                   const std::map<string, string>& values,
+                                   const map<string, string>& value,
                                    Error *error) {
-  VLOG(2) << "Setting " << name << " as a map of string:string";
-  // TODO(cmasone): Set actual properties.
-  return true;
+  VLOG(2) << "Setting " << name << " as a string map.";
+  bool set = stringmap_properties_[name]->Set(value);
+  if (!set && error)
+    error->Populate(Error::kInvalidArguments, name + " is not a R/W strmap.");
+  return set;
 }
 
-bool Service::SetUint8Property(const std::string& name,
-                               uint8 value,
-                               Error *error) {
-  VLOG(2) << "Setting " << name << " as a uint8.";
-  // TODO(cmasone): Set actual properties.
-  return true;
+void Service::RegisterBool(const string &name, bool *prop) {
+  bool_properties_[name] = BoolAccessor(new PropertyAccessor<bool>(prop));
+}
+
+void Service::RegisterConstBool(const string &name, const bool *prop) {
+  bool_properties_[name] = BoolAccessor(new ConstPropertyAccessor<bool>(prop));
+}
+
+void Service::RegisterDerivedBool(const string &name,
+                                  bool(Service::*get)(void),
+                                  bool(Service::*set)(const bool&)) {
+  bool_properties_[name] =
+      BoolAccessor(new CustomAccessor<Service, bool>(this, get, set));
+}
+
+void Service::RegisterInt32(const string &name, int32 *prop) {
+  int32_properties_[name] = Int32Accessor(new PropertyAccessor<int32>(prop));
+}
+
+void Service::RegisterString(const string &name, string *prop) {
+  string_properties_[name] = StringAccessor(new PropertyAccessor<string>(prop));
+}
+
+void Service::RegisterConstString(const string &name, const string *prop) {
+  string_properties_[name] =
+      StringAccessor(new ConstPropertyAccessor<string>(prop));
+}
+
+void Service::RegisterDerivedString(const string &name,
+                                    string(Service::*get)(void),
+                                    bool(Service::*set)(const string&)) {
+  string_properties_[name] =
+      StringAccessor(new CustomAccessor<Service, string>(this, get, set));
+}
+
+void Service::RegisterStringmap(const string &name,
+                                map<string, string> *prop) {
+  stringmap_properties_[name] =
+      StringmapAccessor(new PropertyAccessor<map<string, string> >(prop));
+}
+
+void Service::RegisterConstStringmap(const string &name,
+                                     const map<string, string> *prop) {
+  stringmap_properties_[name] =
+      StringmapAccessor(new ConstPropertyAccessor<map<string, string> >(prop));
+}
+
+void Service::RegisterConstUint8(const string &name, const uint8 *prop) {
+  uint8_properties_[name] =
+      Uint8Accessor(new ConstPropertyAccessor<uint8>(prop));
+}
+
+void Service::RegisterConstUint16(const string &name, const uint16 *prop) {
+  uint16_properties_[name] =
+      Uint16Accessor(new ConstPropertyAccessor<uint16>(prop));
 }
 
 }  // namespace shill
diff --git a/service.h b/service.h
index 0cd0107..3c7be8c 100644
--- a/service.h
+++ b/service.h
@@ -9,10 +9,13 @@
 #include <map>
 #include <vector>
 
+
+#include <base/hash_tables.h>
 #include <base/memory/ref_counted.h>
 #include <base/memory/scoped_ptr.h>
 
 #include "shill/device_config_interface.h"
+#include "shill/accessor_interface.h"
 #include "shill/property_store_interface.h"
 
 namespace shill {
@@ -53,6 +56,24 @@
     kServiceStateDisconnected,
     kServiceStateFailure
   };
+  struct EapCredentials {
+    EapCredentials() : use_system_cas(false) {}
+    std::string identity;
+    std::string eap;
+    std::string inner_eap;
+    std::string anonymous_identity;
+    std::string client_cert;
+    std::string cert_id;
+    std::string private_key;
+    std::string private_key_password;
+    std::string key_id;
+    std::string ca_cert;
+    std::string ca_cert_id;
+    bool use_system_cas;
+    std::string pin;
+    std::string password;
+    std::string key_management;
+  };
 
   // A constructor for the Service object
   Service(ControlInterface *control_interface,
@@ -63,16 +84,23 @@
   virtual void Connect() = 0;
   virtual void Disconnect() = 0;
 
+  virtual bool IsActive() { return false; }
+
   // Implementation of PropertyStoreInterface
-  bool SetBoolProperty(const std::string& name, bool value, Error *error);
-  bool SetInt32Property(const std::string& name, int32 value, Error *error);
-  bool SetStringProperty(const std::string& name,
-                         const std::string& value,
-                         Error *error);
-  bool SetStringmapProperty(const std::string& name,
-                            const std::map<std::string, std::string>& values,
-                            Error *error);
-  bool SetUint8Property(const std::string& name, uint8 value, Error *error);
+  virtual bool Contains(const std::string &property);
+  virtual bool SetBoolProperty(const std::string &name,
+                               bool value,
+                               Error *error);
+  virtual bool SetInt32Property(const std::string &name,
+                                int32 value,
+                                Error *error);
+  virtual bool SetStringProperty(const std::string &name,
+                                 const std::string &value,
+                                 Error *error);
+  virtual bool SetStringmapProperty(
+      const std::string &name,
+      const std::map<std::string, std::string> &values,
+      Error *error);
 
   // Returns a string that is guaranteed to uniquely identify this
   // Service instance.
@@ -82,18 +110,63 @@
   void set_auto_connect(bool connect) { auto_connect_ = connect; }
 
  protected:
+  virtual std::string CalculateState() = 0;
+
+  void RegisterBool(const std::string &name, bool *prop);
+  void RegisterConstBool(const std::string &name, const bool *prop);
+  void RegisterDerivedBool(const std::string &name,
+                           bool(Service::*get)(void),
+                           bool(Service::*set)(const bool&));
+  void RegisterInt32(const std::string &name, int32 *prop);
+  void RegisterString(const std::string &name, std::string *prop);
+  void RegisterConstString(const std::string &name, const std::string *prop);
+  void RegisterDerivedString(const std::string &name,
+                             std::string(Service::*get)(void),
+                             bool(Service::*set)(const std::string&));
+  void RegisterStringmap(const std::string &name,
+                         std::map<std::string, std::string> *prop);
+  void RegisterConstStringmap(const std::string &name,
+                              const std::map<std::string, std::string> *prop);
+  void RegisterConstUint8(const std::string &name, const uint8 *prop);
+  void RegisterConstUint16(const std::string &name, const uint16 *prop);
+
+  base::hash_map<std::string, BoolAccessor> bool_properties_;
+  base::hash_map<std::string, Int32Accessor> int32_properties_;
+  base::hash_map<std::string, StringAccessor> string_properties_;
+  base::hash_map<std::string, StringmapAccessor> stringmap_properties_;
+  base::hash_map<std::string, Uint8Accessor> uint8_properties_;
+  base::hash_map<std::string, Uint16Accessor> uint16_properties_;
+
+  // Properties
+  bool auto_connect_;
+  std::string check_portal_;
+  bool connectable_;
+  EapCredentials eap_;
+  std::string error_;
+  bool favorite_;
+  int32 priority_;
+  std::string proxy_config_;
+  bool save_credentials_;
+
   EventDispatcher *dispatcher_;
 
  private:
+  std::string DeviceRPCID() {
+    return "";  // Will need to call Device to get this.
+  }
+  std::string ProfileRPCID() {
+    return "";  // Will need to call Profile to get this.
+  }
+
   const std::string name_;
   bool available_;
   bool configured_;
-  bool auto_connect_;
   Configuration *configuration_;
   Connection *connection_;
   DeviceConfigInterfaceRefPtr config_interface_;
   Endpoint *endpoint_;
   scoped_ptr<ServiceAdaptorInterface> adaptor_;
+
   friend class ServiceAdaptorInterface;
   DISALLOW_COPY_AND_ASSIGN(Service);
 };
diff --git a/service_unittest.cc b/service_unittest.cc
new file mode 100644
index 0000000..a3be06f
--- /dev/null
+++ b/service_unittest.cc
@@ -0,0 +1,91 @@
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "shill/service.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <dbus-c++/dbus.h>
+#include <chromeos/dbus/service_constants.h>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "shill/dbus_adaptor.h"
+#include "shill/ethernet_service.h"
+#include "shill/manager.h"
+#include "shill/mock_control.h"
+#include "shill/mock_device.h"
+#include "shill/mock_service.h"
+#include "shill/property_store_unittest.h"
+#include "shill/shill_event.h"
+
+using std::map;
+using std::string;
+using std::vector;
+using ::testing::_;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::Test;
+
+namespace shill {
+
+class ServiceTest : public PropertyStoreTest {
+ public:
+  ServiceTest() {}
+  virtual ~ServiceTest() {}
+};
+
+TEST_F(ServiceTest, Dispatch) {
+  DeviceRefPtr device(new MockDevice(&control_interface_,
+                                     &dispatcher_,
+                                     &manager_,
+                                     "mock-device",
+                                     0));
+  ServiceRefPtr service(new MockService(&control_interface_,
+                                        &dispatcher_,
+                                        device.get(),
+                                        "mock-service"));
+  ::DBus::Error error;
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(service.get(),
+                                          flimflam::kSaveCredentialsProperty,
+                                          bool_v_,
+                                          error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(service.get(),
+                                          flimflam::kPriorityProperty,
+                                          int32_v_,
+                                          error));
+  EXPECT_TRUE(DBusAdaptor::DispatchOnType(service.get(),
+                                          flimflam::kEAPEAPProperty,
+                                          string_v_,
+                                          error));
+
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(service.get(),
+                                           flimflam::kFavoriteProperty,
+                                           bool_v_,
+                                           error));
+  EXPECT_EQ(invalid_args_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(service.get(), "", int16_v_, error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(service.get(), "", int32_v_, error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(service.get(),
+                                           "",
+                                           uint16_v_,
+                                           error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(service.get(),
+                                           "",
+                                           uint32_v_,
+                                           error));
+  EXPECT_EQ(invalid_prop_, error.name());
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(service.get(),
+                                           "",
+                                           strings_v_,
+                                           error));
+  EXPECT_EQ(invalid_prop_, error.name());
+}
+
+}  // namespace shill
diff --git a/wifi.cc b/wifi.cc
index a42dce4..9298f6d 100644
--- a/wifi.cc
+++ b/wifi.cc
@@ -119,19 +119,19 @@
 WiFi::WiFi(ControlInterface *control_interface,
            EventDispatcher *dispatcher,
            Manager *manager,
-           const string& link_name,
+           const string& link,
            int interface_index)
     : Device(control_interface,
              dispatcher,
              manager,
-             link_name,
+             link,
              interface_index),
       task_factory_(this),
       control_interface_(control_interface),
       dispatcher_(dispatcher),
       dbus_(DBus::Connection::SystemBus()),
       scan_pending_(false) {
-  VLOG(2) << "WiFi device " << link_name << " initialized.";
+  VLOG(2) << "WiFi device " << link_name() << " initialized.";
 }
 
 WiFi::~WiFi() {
diff --git a/wifi.h b/wifi.h
index f7e851a..bdd4415 100644
--- a/wifi.h
+++ b/wifi.h
@@ -26,7 +26,7 @@
   WiFi(ControlInterface *control_interface,
        EventDispatcher *dispatcher,
        Manager *manager,
-       const std::string& link_name,
+       const std::string& link,
        int interface_index);
   virtual ~WiFi();
   virtual void Start();
diff --git a/wifi_service.cc b/wifi_service.cc
index a12b111..614c67e 100644
--- a/wifi_service.cc
+++ b/wifi_service.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include <base/logging.h>
+#include <chromeos/dbus/service_constants.h>
 
 #include "shill/control_interface.h"
 #include "shill/device.h"
@@ -31,8 +32,23 @@
       task_factory_(this),
       wifi_(device),
       ssid_(ssid),
-      mode_(mode),
-      key_management_(key_management) {
+      mode_(mode) {
+  eap_.key_management = key_management;
+
+  // TODO(cmasone): Figure out if mode_ should be a string or what
+  // RegisterString(flimflam::kModeProperty, &mode_);
+  RegisterString(flimflam::kPassphraseProperty, &passphrase_);
+  RegisterBool(flimflam::kPassphraseRequiredProperty, &need_passphrase_);
+  RegisterConstString(flimflam::kSecurityProperty, &security_);
+
+  RegisterConstString(flimflam::kWifiAuthMode, &auth_mode_);
+  RegisterConstBool(flimflam::kWifiHiddenSsid, &hidden_ssid_);
+  RegisterConstUint16(flimflam::kWifiFrequency, &frequency_);
+  RegisterConstUint16(flimflam::kWifiPhyMode, &physical_mode_);
+  RegisterConstUint16(flimflam::kWifiHexSsid, &hex_ssid_);
+
+  RegisterConstUint8(flimflam::kSignalStrengthProperty, &strength_);
+  RegisterConstString(flimflam::kTypeProperty, &type_);
 }
 
 WiFiService::~WiFiService() {
@@ -48,6 +64,16 @@
       task_factory_.NewRunnableMethod(&WiFiService::RealConnect));
 }
 
+void WiFiService::Disconnect() {
+  // TODO(quiche) RemoveNetwork from supplicant
+  // XXX remove from favorite networks list?
+}
+
+bool WiFiService::Contains(const string &property) {
+  return (Service::Contains(property) ||
+          uint16_properties_.find(property) != uint16_properties_.end());
+}
+
 void WiFiService::RealConnect() {
   std::map<string, DBus::Variant> add_network_args;
   DBus::MessageIter mi;
@@ -56,7 +82,7 @@
   add_network_args[kSupplicantPropertyNetworkMode].writer().
       append_uint32(mode_);
   add_network_args[kSupplicantPropertyKeyMode].writer().
-      append_string(key_management_.c_str());
+      append_string(eap_.key_management.c_str());
   // TODO(quiche): figure out why we can't use operator<< without the
   // temporary variable.
   mi = add_network_args[kSupplicantPropertySSID].writer();
@@ -68,9 +94,4 @@
   // XXX add to favorite networks list?
 }
 
-void WiFiService::Disconnect() {
-  // TODO(quiche) RemoveNetwork from supplicant
-  // XXX remove from favorite networks list?
-}
-
 }  // namespace shill
diff --git a/wifi_service.h b/wifi_service.h
index fd5650a..e2cc2ad 100644
--- a/wifi_service.h
+++ b/wifi_service.h
@@ -28,6 +28,12 @@
   void Connect();
   void Disconnect();
 
+  // Implementation of PropertyStoreInterface
+  bool Contains(const std::string &property);
+
+ protected:
+  virtual std::string CalculateState() { return "idle"; }
+
  private:
   static const char kSupplicantPropertySSID[];
   static const char kSupplicantPropertyNetworkMode[];
@@ -35,11 +41,24 @@
 
   void RealConnect();
 
+  // Properties
+  std::string passphrase_;
+  bool need_passphrase_;
+  std::string security_;
+  uint8 strength_;
+  const std::string type_;
+  // TODO(cmasone): see if the below can be pulled from the endpoint associated
+  // with this service instead.
+  std::string auth_mode_;
+  bool hidden_ssid_;
+  uint16 frequency_;
+  uint16 physical_mode_;
+  uint16 hex_ssid_;
+
   ScopedRunnableMethodFactory<WiFiService> task_factory_;
   WiFi *wifi_;
   const std::vector<uint8_t> ssid_;
   uint32_t mode_;
-  const std::string key_management_;
   DISALLOW_COPY_AND_ASSIGN(WiFiService);
 };