chromeos-dbus-bindings: write access property support

DBus properties are default to read-only, so update the access mode
for properties that have write access. Also set the validator callback
for those properties as well.

Applications can override the validate method to provide their own
implementation for verifying the value for each writable property.

BUG=chromium:440139
TEST=USE="asan clang" FEATURES=test emerge-$BOARD libchromeos buffet
     apmanager peerd

Change-Id: I3c8fd90218bb194f86fad7e9d76ff51f0e0861ec
Reviewed-on: https://chromium-review.googlesource.com/234281
Reviewed-by: Peter Qiu <zqiu@chromium.org>
Commit-Queue: Peter Qiu <zqiu@chromium.org>
Tested-by: Peter Qiu <zqiu@chromium.org>
diff --git a/chromeos-dbus-bindings/adaptor_generator.cc b/chromeos-dbus-bindings/adaptor_generator.cc
index deb26b1..de3e41c 100644
--- a/chromeos-dbus-bindings/adaptor_generator.cc
+++ b/chromeos-dbus-bindings/adaptor_generator.cc
@@ -196,6 +196,32 @@
     text->AddBlankLine();
   for (const auto& property : interface.properties) {
     string variable_name = NameParser{property.name}.MakeVariableName();
+    string write_access;
+    if (property.access == "write") {
+      write_access = "kWriteOnly";
+    } else if (property.access == "readwrite") {
+      write_access = "kReadWrite";
+    }
+    if (!write_access.empty()) {
+      text->AddLine(StringPrintf("%s_.SetAccessMode(", variable_name.c_str()));
+      text->PushOffset(kLineContinuationOffset);
+      text->AddLine(
+          StringPrintf(
+              "chromeos::dbus_utils::ExportedPropertyBase::Access::%s);",
+              write_access.c_str()));
+      text->PopOffset();
+      text->AddLine(StringPrintf("%s_.SetValidator(", variable_name.c_str()));
+      text->PushOffset(kLineContinuationOffset);
+      text->AddLineAndPushOffsetTo(
+          StringPrintf(
+              "base::Bind(&%s::Validate%s,",
+              NameParser{interface.name}.MakeAdaptorName(false).c_str(),
+              property.name.c_str()),
+          1, '(');
+      text->AddLine("base::Unretained(this)));");
+      text->PopOffset();
+      text->PopOffset();
+    }
     text->AddLine(StringPrintf("itf->AddProperty(\"%s\", &%s_);",
                                property.name.c_str(), variable_name.c_str()));
   }
@@ -412,6 +438,25 @@
                                variable_name.c_str()));
     block.PopOffset();
     block.AddLine("}");
+
+    // Validation method for property with write access.
+    if (property.access != "read") {
+      CHECK(signature.Parse(property.type, &type));
+      block.AddLine(StringPrintf("virtual bool Validate%s(",
+                                 property.name.c_str()));
+      block.PushOffset(kLineContinuationOffset);
+      // Explicitly specify the "value" parameter as const & to match the
+      // validator callback function signature.
+      block.AddLine(
+          StringPrintf(
+              "chromeos::ErrorPtr* /*error*/, const %s& /*value*/) {",
+              type.c_str()));
+      block.PopOffset();
+      block.PushOffset(kBlockOffset);
+      block.AddLine("return true;");
+      block.PopOffset();
+      block.AddLine("}");
+    }
   }
   text->AddBlock(block);
 }
diff --git a/chromeos-dbus-bindings/adaptor_generator_unittest.cc b/chromeos-dbus-bindings/adaptor_generator_unittest.cc
index d63054f..957fa4b 100644
--- a/chromeos-dbus-bindings/adaptor_generator_unittest.cc
+++ b/chromeos-dbus-bindings/adaptor_generator_unittest.cc
@@ -44,6 +44,9 @@
 const char kProperty0Name[] = "CharacterName";
 const char kProperty0Type[] = "s";
 const char kProperty0Access[] = "read";
+const char kProperty1Name[] = "WriteProperty";
+const char kProperty1Type[] = "s";
+const char kProperty1Access[] = "readwrite";
 
 const char kInterfaceName[] = "org.chromium.Test";
 const char kInterfaceName2[] = "org.chromium.Test2";
@@ -117,6 +120,12 @@
     signal_Mapping_ = itf->RegisterSignalOfType<SignalMappingType>("Mapping");
 
     itf->AddProperty("CharacterName", &character_name_);
+    write_property_.SetAccessMode(
+        chromeos::dbus_utils::ExportedPropertyBase::Access::kReadWrite);
+    write_property_.SetValidator(
+        base::Bind(&TestAdaptor::ValidateWriteProperty,
+                   base::Unretained(this)));
+    itf->AddProperty("WriteProperty", &write_property_);
   }
 
   void SendUpdateSignal() {
@@ -139,6 +148,17 @@
     character_name_.SetValue(character_name);
   }
 
+  std::string GetWriteProperty() const {
+    return write_property_.GetValue().Get<std::string>();
+  }
+  void SetWriteProperty(const std::string& write_property) {
+    write_property_.SetValue(write_property);
+  }
+  virtual bool ValidateWriteProperty(
+      chromeos::ErrorPtr* /*error*/, const std::string& /*value*/) {
+    return true;
+  }
+
   static dbus::ObjectPath GetObjectPath() {
     return dbus::ObjectPath{"/org/chromium/Test"};
   }
@@ -153,6 +173,7 @@
   std::weak_ptr<SignalMappingType> signal_Mapping_;
 
   chromeos::dbus_utils::ExportedProperty<std::string> character_name_;
+  chromeos::dbus_utils::ExportedProperty<std::string> write_property_;
 
   TestInterface* interface_;  // Owned by container of this adapter.
 
@@ -260,6 +281,10 @@
       kProperty0Name,
       kProperty0Type,
       kProperty0Access);
+  interface.properties.emplace_back(
+      kProperty1Name,
+      kProperty1Type,
+      kProperty1Access);
 
   Interface interface2;
   interface2.name = kInterfaceName2;