shill: allow property accessors to return an error message if a
property's value cannot be changed.

also, reduce some code duplication in property_store.cc

BUG=chromium-os:21384
TEST=unittests

Change-Id: Iaac8d40bbb9e9a1341d6c6d01642885d88ac0e27
Reviewed-on: http://gerrit.chromium.org/gerrit/8925
Reviewed-by: mukesh agrawal <quiche@chromium.org>
Tested-by: mukesh agrawal <quiche@chromium.org>
diff --git a/accessor_interface.h b/accessor_interface.h
index 3a62696..7186a54 100644
--- a/accessor_interface.h
+++ b/accessor_interface.h
@@ -15,6 +15,8 @@
 
 namespace shill {
 
+class Error;
+
 // 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
@@ -28,8 +30,8 @@
 
   // 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;
+  // Attempts to set the wrapped value. Sets |error| on failure.
+  virtual void Set(const T &value, Error *error) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(AccessorInterface);
diff --git a/cellular.cc b/cellular.cc
index 81a3212..ff13300 100644
--- a/cellular.cc
+++ b/cellular.cc
@@ -1010,7 +1010,7 @@
 void Cellular::HelpRegisterDerivedStrIntPair(
     const string &name,
     StrIntPair(Cellular::*get)(void),
-    bool(Cellular::*set)(const StrIntPair&)) {
+    void(Cellular::*set)(const StrIntPair&, Error *)) {
   mutable_store()->RegisterDerivedStrIntPair(
       name,
       StrIntPairAccessor(
diff --git a/cellular.h b/cellular.h
index 07bdfe9..d3359d9 100644
--- a/cellular.h
+++ b/cellular.h
@@ -239,12 +239,10 @@
 
   StrIntPair SimLockStatusToProperty();
 
-  void HelpRegisterDerivedStringmaps(const std::string &name,
-                                     Stringmaps(Cellular::*get)(void),
-                                     bool(Cellular::*set)(const Stringmaps&));
-  void HelpRegisterDerivedStrIntPair(const std::string &name,
-                                     StrIntPair(Cellular::*get)(void),
-                                     bool(Cellular::*set)(const StrIntPair&));
+  void HelpRegisterDerivedStrIntPair(
+      const std::string &name,
+      StrIntPair(Cellular::*get)(void),
+      void(Cellular::*set)(const StrIntPair&, Error *));
 
   void InitProxies();
 
diff --git a/dbus_adaptor.cc b/dbus_adaptor.cc
index d1c082d..dc69d9d 100644
--- a/dbus_adaptor.cc
+++ b/dbus_adaptor.cc
@@ -42,43 +42,43 @@
                                  const string &name,
                                  const ::DBus::Variant &value,
                                  ::DBus::Error *error) {
-  bool set = false;
-  Error e(Error::kInvalidArguments, "Could not write " + name);
+  Error e;
 
   if (DBusAdaptor::IsBool(value.signature()))
-    set = store->SetBoolProperty(name, value.reader().get_bool(), &e);
+    store->SetBoolProperty(name, value.reader().get_bool(), &e);
   else if (DBusAdaptor::IsByte(value.signature()))
-    set = store->SetUint8Property(name, value.reader().get_byte(), &e);
+    store->SetUint8Property(name, value.reader().get_byte(), &e);
   else if (DBusAdaptor::IsInt16(value.signature()))
-    set = store->SetInt16Property(name, value.reader().get_int16(), &e);
+    store->SetInt16Property(name, value.reader().get_int16(), &e);
   else if (DBusAdaptor::IsInt32(value.signature()))
-    set = store->SetInt32Property(name, value.reader().get_int32(), &e);
+    store->SetInt32Property(name, value.reader().get_int32(), &e);
   else if (DBusAdaptor::IsPath(value.signature()))
-    set = store->SetStringProperty(name, value.reader().get_path(), &e);
+    store->SetStringProperty(name, value.reader().get_path(), &e);
   else if (DBusAdaptor::IsString(value.signature()))
-    set = store->SetStringProperty(name, value.reader().get_string(), &e);
+    store->SetStringProperty(name, value.reader().get_string(), &e);
   else if (DBusAdaptor::IsStringmap(value.signature()))
-    set = store->SetStringmapProperty(name,
-                                      value.operator map<string, string>(),
-                                      &e);
-  else if (DBusAdaptor::IsStringmaps(value.signature()))
+    store->SetStringmapProperty(name,
+                                value.operator map<string, string>(),
+                                &e);
+  else if (DBusAdaptor::IsStringmaps(value.signature())) {
     VLOG(1) << " can't yet handle setting type " << value.signature();
-  else if (DBusAdaptor::IsStrings(value.signature()))
-    set = store->SetStringsProperty(name, value.operator vector<string>(), &e);
+    e.Populate(Error::kInternalError);
+  } else if (DBusAdaptor::IsStrings(value.signature()))
+    store->SetStringsProperty(name, value.operator vector<string>(), &e);
   else if (DBusAdaptor::IsUint16(value.signature()))
-    set = store->SetUint16Property(name, value.reader().get_uint16(), &e);
+    store->SetUint16Property(name, value.reader().get_uint16(), &e);
   else if (DBusAdaptor::IsUint32(value.signature()))
-    set = store->SetUint32Property(name, value.reader().get_uint32(), &e);
-  else
+    store->SetUint32Property(name, value.reader().get_uint32(), &e);
+  else {
     NOTREACHED() << " unknown type: " << value.signature();
-
-  if (!set && error) {
-    if (!store->Contains(name))
-      Error(Error::kInvalidProperty, name + " is invalid.").ToDBusError(error);
-    else
-      e.ToDBusError(error);
+    e.Populate(Error::kInternalError);
   }
-  return set;
+
+  if (error != NULL) {
+    e.ToDBusError(error);
+  }
+
+  return e.IsSuccess();
 }
 
 // static
diff --git a/device.cc b/device.cc
index 12300a9..583cf0f 100644
--- a/device.cc
+++ b/device.cc
@@ -257,9 +257,10 @@
   return ipconfig_->RequestIP();
 }
 
-void Device::HelpRegisterDerivedStrings(const string &name,
-                                        Strings(Device::*get)(void),
-                                        bool(Device::*set)(const Strings&)) {
+void Device::HelpRegisterDerivedStrings(
+    const string &name,
+    Strings(Device::*get)(void),
+    void(Device::*set)(const Strings&, Error *)) {
   store_.RegisterDerivedStrings(
       name,
       StringsAccessor(new CustomAccessor<Device, Strings>(this, get, set)));
diff --git a/device.h b/device.h
index 17e7775..85ad698 100644
--- a/device.h
+++ b/device.h
@@ -132,7 +132,7 @@
 
   void HelpRegisterDerivedStrings(const std::string &name,
                                   Strings(Device::*get)(void),
-                                  bool(Device::*set)(const Strings&));
+                                  void(Device::*set)(const Strings&, Error *));
 
   // Property getters reserved for subclasses
   ControlInterface *control_interface() const { return control_interface_; }
diff --git a/manager.cc b/manager.cc
index a99ac00..f8ebd53 100644
--- a/manager.cc
+++ b/manager.cc
@@ -259,17 +259,19 @@
   return NULL;
 }
 
-void Manager::HelpRegisterDerivedString(const string &name,
-                                    string(Manager::*get)(void),
-                                    bool(Manager::*set)(const string&)) {
+void Manager::HelpRegisterDerivedString(
+    const string &name,
+    string(Manager::*get)(void),
+    void(Manager::*set)(const string&, Error *)) {
   store_.RegisterDerivedString(
       name,
       StringAccessor(new CustomAccessor<Manager, string>(this, get, set)));
 }
 
-void Manager::HelpRegisterDerivedStrings(const string &name,
-                                     Strings(Manager::*get)(void),
-                                     bool(Manager::*set)(const Strings&)) {
+void Manager::HelpRegisterDerivedStrings(
+    const string &name,
+    Strings(Manager::*get)(void),
+    void(Manager::*set)(const Strings &, Error *)) {
   store_.RegisterDerivedStrings(
       name,
       StringsAccessor(new CustomAccessor<Manager, Strings>(this, get, set)));
diff --git a/manager.h b/manager.h
index ede4f49..289ee09 100644
--- a/manager.h
+++ b/manager.h
@@ -102,12 +102,14 @@
   std::vector<std::string> EnumerateWatchedServices();
   std::string GetActiveProfileName();
 
-  void HelpRegisterDerivedString(const std::string &name,
-                                 std::string(Manager::*get)(void),
-                                 bool(Manager::*set)(const std::string&));
-  void HelpRegisterDerivedStrings(const std::string &name,
-                                  Strings(Manager::*get)(void),
-                                  bool(Manager::*set)(const Strings&));
+  void HelpRegisterDerivedString(
+      const std::string &name,
+      std::string(Manager::*get)(void),
+      void(Manager::*set)(const std::string&, Error *));
+  void HelpRegisterDerivedStrings(
+      const std::string &name,
+      Strings(Manager::*get)(void),
+      void(Manager::*set)(const Strings&, Error *));
 
   bool OrderServices(ServiceRefPtr a, ServiceRefPtr b);
   void SortServices();
diff --git a/profile.cc b/profile.cc
index b24b80e..4111588 100644
--- a/profile.cc
+++ b/profile.cc
@@ -183,9 +183,10 @@
   return rpc_ids;
 }
 
-void Profile::HelpRegisterDerivedStrings(const string &name,
-                                         Strings(Profile::*get)(void),
-                                         bool(Profile::*set)(const Strings&)) {
+void Profile::HelpRegisterDerivedStrings(
+    const string &name,
+    Strings(Profile::*get)(void),
+    void(Profile::*set)(const Strings&, Error *)) {
   store_.RegisterDerivedStrings(
       name,
       StringsAccessor(new CustomAccessor<Profile, Strings>(this, get, set)));
diff --git a/profile.h b/profile.h
index 0b73950..e0d6a0a 100644
--- a/profile.h
+++ b/profile.h
@@ -116,9 +116,10 @@
     return false;
   }
 
-  void HelpRegisterDerivedStrings(const std::string &name,
-                                  Strings(Profile::*get)(void),
-                                  bool(Profile::*set)(const Strings&));
+  void HelpRegisterDerivedStrings(
+      const std::string &name,
+      Strings(Profile::*get)(void),
+      void(Profile::*set)(const Strings&, Error *));
 
   // Persists |services_| to disk.
   bool SaveServices(StoreInterface *storage);
diff --git a/property_accessor.h b/property_accessor.h
index fda0623..542fd8d 100644
--- a/property_accessor.h
+++ b/property_accessor.h
@@ -9,6 +9,7 @@
 #include <base/logging.h>
 
 #include "shill/accessor_interface.h"
+#include "shill/error.h"
 
 namespace shill {
 
@@ -34,9 +35,8 @@
   virtual ~PropertyAccessor() {}
 
   const T &Get() { return *property_; }
-  bool Set(const T &value) {
+  void Set(const T &value, Error */*error*/) {
     *property_ = value;
-    return true;
   }
 
  private:
@@ -53,7 +53,11 @@
   virtual ~ConstPropertyAccessor() {}
 
   const T &Get() { return *property_; }
-  bool Set(const T &/*value*/) { return false; }
+  void Set(const T &/*value*/, Error *error) {
+    // TODO(quiche): check if this is the right error.
+    // (maybe Error::kPermissionDenied instead?)
+    error->Populate(Error::kInvalidArguments, "Property is read-only");
+  }
 
  private:
   const T * const property_;
@@ -70,7 +74,9 @@
   // |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&))
+  CustomAccessor(C *target,
+                 T(C::*getter)(),
+                 void(C::*setter)(const T&, Error *))
       : target_(target),
         getter_(getter),
         setter_(setter) {
@@ -80,7 +86,13 @@
   virtual ~CustomAccessor() {}
 
   const T &Get() { return storage_ = (target_->*getter_)(); }
-  bool Set(const T &value) { return setter_ && (target_->*setter_)(value); }
+  void Set(const T &value, Error *error) {
+    if (setter_) {
+      (target_->*setter_)(value, error);
+    } else {
+      error->Populate(Error::kInvalidArguments, "Property is read-only");
+    }
+  }
 
  private:
   C * const target_;
@@ -88,7 +100,7 @@
   // return a reference.
   T storage_;
   T(C::*getter_)(void);
-  bool(C::*setter_)(const T&);
+  void(C::*setter_)(const T&, Error *);
   DISALLOW_COPY_AND_ASSIGN(CustomAccessor);
 };
 
diff --git a/property_accessor_unittest.cc b/property_accessor_unittest.cc
index 7e9a150..4303d13 100644
--- a/property_accessor_unittest.cc
+++ b/property_accessor_unittest.cc
@@ -13,6 +13,8 @@
 #include <gtest/gtest.h>
 #include <gmock/gmock.h>
 
+#include "shill/error.h"
+
 using std::map;
 using std::string;
 using std::vector;
@@ -24,22 +26,27 @@
 TEST(PropertyAccessorTest, SignedIntCorrectness) {
   int32 int_store = 0;
   {
+    Error error;
     Int32Accessor accessor(new PropertyAccessor<int32>(&int_store));
     EXPECT_EQ(int_store, accessor->Get());
 
     int32 expected_int32 = 127;
-    ASSERT_TRUE(accessor->Set(expected_int32));
+    accessor->Set(expected_int32, &error);
+    ASSERT_TRUE(error.IsSuccess());
     EXPECT_EQ(expected_int32, accessor->Get());
 
     int_store = std::numeric_limits<int32>::max();
     EXPECT_EQ(std::numeric_limits<int32>::max(), accessor->Get());
   }
   {
+    Error error;
     Int32Accessor accessor(new ConstPropertyAccessor<int32>(&int_store));
     EXPECT_EQ(int_store, accessor->Get());
 
     int32 expected_int32 = 127;
-    ASSERT_FALSE(accessor->Set(expected_int32));
+    accessor->Set(expected_int32, &error);
+    ASSERT_FALSE(error.IsSuccess());
+    EXPECT_EQ(Error::kInvalidArguments, error.type());
     EXPECT_EQ(int_store, accessor->Get());
 
     int_store = std::numeric_limits<int32>::max();
@@ -50,22 +57,27 @@
 TEST(PropertyAccessorTest, UnsignedIntCorrectness) {
   uint32 int_store = 0;
   {
+    Error error;
     Uint32Accessor accessor(new PropertyAccessor<uint32>(&int_store));
     EXPECT_EQ(int_store, accessor->Get());
 
     uint32 expected_uint32 = 127;
-    ASSERT_TRUE(accessor->Set(expected_uint32));
+    accessor->Set(expected_uint32, &error);
+    ASSERT_TRUE(error.IsSuccess());
     EXPECT_EQ(expected_uint32, accessor->Get());
 
     int_store = std::numeric_limits<uint32>::max();
     EXPECT_EQ(std::numeric_limits<uint32>::max(), accessor->Get());
   }
   {
+    Error error;
     Uint32Accessor accessor(new ConstPropertyAccessor<uint32>(&int_store));
     EXPECT_EQ(int_store, accessor->Get());
 
     uint32 expected_uint32 = 127;
-    ASSERT_FALSE(accessor->Set(expected_uint32));
+    accessor->Set(expected_uint32, &error);
+    ASSERT_FALSE(error.IsSuccess());
+    EXPECT_EQ(Error::kInvalidArguments, error.type());
     EXPECT_EQ(int_store, accessor->Get());
 
     int_store = std::numeric_limits<uint32>::max();
@@ -76,22 +88,27 @@
 TEST(PropertyAccessorTest, StringCorrectness) {
   string storage;
   {
+    Error error;
     StringAccessor accessor(new PropertyAccessor<string>(&storage));
     EXPECT_EQ(storage, accessor->Get());
 
     string expected_string("what");
-    ASSERT_TRUE(accessor->Set(expected_string));
+    accessor->Set(expected_string, &error);
+    ASSERT_TRUE(error.IsSuccess());
     EXPECT_EQ(expected_string, accessor->Get());
 
     storage = "nooooo";
     EXPECT_EQ(storage, accessor->Get());
   }
   {
+    Error error;
     StringAccessor accessor(new ConstPropertyAccessor<string>(&storage));
     EXPECT_EQ(storage, accessor->Get());
 
     string expected_string("what");
-    ASSERT_FALSE(accessor->Set(expected_string));
+    accessor->Set(expected_string, &error);
+    ASSERT_FALSE(error.IsSuccess());
+    EXPECT_EQ(Error::kInvalidArguments, error.type());
     EXPECT_EQ(storage, accessor->Get());
 
     storage = "nooooo";
diff --git a/property_store.cc b/property_store.cc
index 9fb61ba..8cc67b1 100644
--- a/property_store.cc
+++ b/property_store.cc
@@ -42,101 +42,57 @@
 bool PropertyStore::SetBoolProperty(const std::string& name,
                                     bool value,
                                     Error *error) {
-  VLOG(2) << "Setting " << name << " as a bool.";
-  bool set = (ContainsKey(bool_properties_, name) &&
-              bool_properties_[name]->Set(value));
-  if (!set && error)
-    error->Populate(Error::kInvalidArguments, name + " is not a R/W bool.");
-  return set;
+  return SetProperty(name, value, error, bool_properties_, "a bool");
 }
 
 bool PropertyStore::SetInt16Property(const std::string& name,
                                      int16 value,
                                      Error *error) {
-  VLOG(2) << "Setting " << name << " as an int16.";
-  bool set = (ContainsKey(int16_properties_, name) &&
-              int16_properties_[name]->Set(value));
-  if (!set && error)
-    error->Populate(Error::kInvalidArguments, name + " is not a R/W int16.");
-  return set;
+  return SetProperty(name, value, error, int16_properties_, "an int16");
 }
 
 bool PropertyStore::SetInt32Property(const std::string& name,
                                      int32 value,
                                      Error *error) {
-  VLOG(2) << "Setting " << name << " as an int32.";
-  bool set = (ContainsKey(int32_properties_, name) &&
-              int32_properties_[name]->Set(value));
-  if (!set && error)
-    error->Populate(Error::kInvalidArguments, name + " is not a R/W int32.");
-  return set;
+  return SetProperty(name, value, error, int32_properties_, "an int32.");
 }
 
 bool PropertyStore::SetStringProperty(const std::string& name,
                                       const std::string& value,
                                       Error *error) {
-  VLOG(2) << "Setting " << name << " as a string.";
-  bool set = (ContainsKey(string_properties_, name) &&
-              string_properties_[name]->Set(value));
-  if (!set && error)
-    error->Populate(Error::kInvalidArguments, name + " is not a R/W string.");
-  return set;
+  return SetProperty(name, value, error, string_properties_, "a string");
 }
 
 bool PropertyStore::SetStringmapProperty(
     const std::string& name,
     const std::map<std::string, std::string>& values,
     Error *error) {
-  VLOG(2) << "Setting " << name << " as a string map.";
-  bool set = (ContainsKey(stringmap_properties_, name) &&
-              stringmap_properties_[name]->Set(values));
-  if (!set && error)
-    error->Populate(Error::kInvalidArguments, name + " is not a R/W strmap.");
-  return set;
+  return SetProperty(name, values, error, stringmap_properties_,
+                     "a string map");
 }
 
 bool PropertyStore::SetStringsProperty(const std::string& name,
                                        const std::vector<std::string>& values,
                                        Error *error) {
-  VLOG(2) << "Setting " << name << " as a string list.";
-  bool set = (ContainsKey(strings_properties_, name) &&
-              strings_properties_[name]->Set(values));
-  if (!set && error)
-    error->Populate(Error::kInvalidArguments, name + " is not a R/W str list.");
-  return set;
+  return SetProperty(name, values, error, strings_properties_, "a string list");
 }
 
 bool PropertyStore::SetUint8Property(const std::string& name,
                                      uint8 value,
                                      Error *error) {
-  VLOG(2) << "Setting " << name << " as a uint8.";
-  bool set = (ContainsKey(uint8_properties_, name) &&
-              uint8_properties_[name]->Set(value));
-  if (!set && error)
-    error->Populate(Error::kInvalidArguments, name + " is not a R/W uint8.");
-  return set;
+  return SetProperty(name, value, error, uint8_properties_, "a uint8");
 }
 
 bool PropertyStore::SetUint16Property(const std::string& name,
                                       uint16 value,
                                       Error *error) {
-  VLOG(2) << "Setting " << name << " as a uint16.";
-  bool set = (ContainsKey(uint16_properties_, name) &&
-              uint16_properties_[name]->Set(value));
-  if (!set && error)
-    error->Populate(Error::kInvalidArguments, name + " is not a R/W uint16.");
-  return set;
+  return SetProperty(name, value, error, uint16_properties_, "a uint16");
 }
 
 bool PropertyStore::SetUint32Property(const std::string& name,
                                       uint32 value,
                                       Error *error) {
-  VLOG(2) << "Setting " << name << " as a uint32.";
-  bool set = (ContainsKey(uint32_properties_, name) &&
-              uint32_properties_[name]->Set(value));
-  if (!set && error)
-    error->Populate(Error::kInvalidArguments, name + " is not a R/W uint32.");
-  return set;
+  return SetProperty(name, value, error, uint32_properties_, "a uint32");
 }
 
 PropertyConstIterator<bool> PropertyStore::GetBoolPropertiesIter() const {
@@ -295,4 +251,28 @@
   strintpair_properties_[name] = acc;
 }
 
+// private
+template <class V>
+bool PropertyStore::SetProperty(
+    const string &name,
+    const V &value,
+    Error *error,
+    map< string, std::tr1::shared_ptr< AccessorInterface<V> > >&collection,
+    const string &value_type_english) {
+  VLOG(2) << "Setting " << name << " as " << value_type_english << ".";
+  if (ContainsKey(collection, name)) {
+    collection[name]->Set(value, error);
+  } else {
+    if (Contains(name)) {
+      error->Populate(
+          Error::kInvalidArguments,
+          "Property " + name + " is not " + value_type_english + ".");
+    } else {
+      error->Populate(
+          Error::kInvalidProperty, "Property " + name + " does not exist.");
+    }
+  }
+  return error->IsSuccess();
+};
+
 }  // namespace shill
diff --git a/property_store.h b/property_store.h
index 2edc333..696ea17 100644
--- a/property_store.h
+++ b/property_store.h
@@ -68,7 +68,13 @@
                                  uint32 value,
                                  Error *error);
 
-  // Accessors for iterators over property maps.
+  // We do not provide methods for reading individual properties,
+  // because we don't need them to implement the flimflam API. (The flimflam
+  // API only allows fetching all properties at once -- not individual
+  // properties.)
+
+  // Accessors for iterators over property maps. Useful for dumping all
+  // properties.
   PropertyConstIterator<bool> GetBoolPropertiesIter() const;
   PropertyConstIterator<int16> GetInt16PropertiesIter() const;
   PropertyConstIterator<int32> GetInt32PropertiesIter() const;
@@ -112,6 +118,14 @@
                                  const StrIntPairAccessor &accessor);
 
  private:
+  template <class V>
+  bool SetProperty(
+      const std::string &name,
+      const V &value,
+      Error *error,
+      std::map< std::string, std::tr1::shared_ptr< AccessorInterface<V> > > &,
+      const std::string &value_type_english);
+
   // These are std::maps instead of something cooler because the common
   // operation is iterating through them and returning all properties.
   std::map<std::string, BoolAccessor> bool_properties_;
diff --git a/property_store_unittest.cc b/property_store_unittest.cc
index 3149146..26f8382 100644
--- a/property_store_unittest.cc
+++ b/property_store_unittest.cc
@@ -62,7 +62,8 @@
     DBusAdaptor::Uint32ToVariant(0);
 
 PropertyStoreTest::PropertyStoreTest()
-    : invalid_args_(Error::GetName(Error::kInvalidArguments)),
+    : internal_error_(Error::GetName(Error::kInternalError)),
+      invalid_args_(Error::GetName(Error::kInvalidArguments)),
       invalid_prop_(Error::GetName(Error::kInvalidProperty)),
       path_(dir_.CreateUniqueTempDir() ? dir_.path().value() : ""),
       manager_(control_interface(),
@@ -97,10 +98,17 @@
            PropertyStoreTest::kInt16V,
            PropertyStoreTest::kInt32V,
            PropertyStoreTest::kStringmapV,
-           PropertyStoreTest::kStringmapsV,
            PropertyStoreTest::kStringsV,
            PropertyStoreTest::kStrIntPairV,
            PropertyStoreTest::kUint16V,
            PropertyStoreTest::kUint32V));
 
+TEST_F(PropertyStoreTest, SetStringmapsProperty) {
+  PropertyStore store;
+  ::DBus::Error error;
+  EXPECT_FALSE(DBusAdaptor::DispatchOnType(
+      &store, "", PropertyStoreTest::kStringmapsV, &error));
+  EXPECT_EQ(internal_error(), error.name());
+}
+
 }  // namespace shill
diff --git a/property_store_unittest.h b/property_store_unittest.h
index 639649f..42a0e8d 100644
--- a/property_store_unittest.h
+++ b/property_store_unittest.h
@@ -58,10 +58,12 @@
   const std::string &run_path() const { return path_; }
   const std::string &storage_path() const { return path_; }
 
+  const std::string &internal_error() const { return internal_error_; }
   const std::string &invalid_args() const { return invalid_args_; }
   const std::string &invalid_prop() const { return invalid_prop_; }
 
  private:
+  const std::string internal_error_;
   const std::string invalid_args_;
   const std::string invalid_prop_;
   ScopedTempDir dir_;
diff --git a/service.cc b/service.cc
index 489fe5c..e38893e 100644
--- a/service.cc
+++ b/service.cc
@@ -335,17 +335,19 @@
   }
 }
 
-void Service::HelpRegisterDerivedBool(const string &name,
-                                      bool(Service::*get)(void),
-                                      bool(Service::*set)(const bool&)) {
+void Service::HelpRegisterDerivedBool(
+    const string &name,
+    bool(Service::*get)(void),
+    void(Service::*set)(const bool&, Error *)) {
   store_.RegisterDerivedBool(
       name,
       BoolAccessor(new CustomAccessor<Service, bool>(this, get, set)));
 }
 
-void Service::HelpRegisterDerivedString(const string &name,
-                                        string(Service::*get)(void),
-                                        bool(Service::*set)(const string&)) {
+void Service::HelpRegisterDerivedString(
+    const string &name,
+    string(Service::*get)(void),
+    void(Service::*set)(const string&, Error *)) {
   store_.RegisterDerivedString(
       name,
       StringAccessor(new CustomAccessor<Service, string>(this, get, set)));
diff --git a/service.h b/service.h
index d6912e8..9c21aee 100644
--- a/service.h
+++ b/service.h
@@ -176,12 +176,14 @@
 
   virtual std::string CalculateState();
 
-  void HelpRegisterDerivedBool(const std::string &name,
-                               bool(Service::*get)(void),
-                               bool(Service::*set)(const bool&));
-  void HelpRegisterDerivedString(const std::string &name,
-                                 std::string(Service::*get)(void),
-                                 bool(Service::*set)(const std::string&));
+  void HelpRegisterDerivedBool(
+      const std::string &name,
+      bool(Service::*get)(void),
+      void(Service::*set)(const bool&, Error *));
+  void HelpRegisterDerivedString(
+      const std::string &name,
+      std::string(Service::*get)(void),
+      void(Service::*set)(const std::string&, Error *));
 
   // Assigns |value| to |key| in |storage| if |value| is non-empty and |save| is
   // true. Otherwise, removes |key| from |storage|. If |crypted| is true, the