Change schema of sysprop description file

Underlying property name for schematized property have been generated
with owner, prefix, readonly, and name field. This is too restrictive
and uncomfortable as

1) Names of API should be same with names of its underlying properties.
For example, if we schematize dalvik.vm.systemservercompilerfilter, the
name of API should look like "some.module.systemservercompilerfilter()"
which looks not so good.
2) The name generation rule is complicated and not intuitive.
3) There's no nice way to schematize props with prefix persist or ro.
4) Representing accessibility with only one field "readonly" is too
narrow.

So schema of sysprop description file is changed as follows:

1) prefix field was too complex, so it is removed.
2) "readonly" boolean field is changed to "access" enum field which can
hold one of: Readonly | Writeonce | ReadWrite. "ro." prefix for property
name is mandatory for Readonly and Writeonce properties. For ReadWrite
properties. "ro." prefix should not exist.
3) One can optionally set "prop_name" which will directly be used as
the underlying property. If "prop_name" is not set, owner, access, and
api_name field will be used as before.

Bug: 80125326
Test: mma -j && run sysprop_test && try to make java_library and
cc_library_shared with sysprop description file

Change-Id: I485b4d1e993f99d2875dce66ec7f077c71aba985
diff --git a/Common.cpp b/Common.cpp
index 362eb26..59b4f61 100644
--- a/Common.cpp
+++ b/Common.cpp
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <cctype>
 #include <cerrno>
 #include <cmath>
@@ -40,40 +41,58 @@
 
 namespace {
 
+std::string GenerateDefaultPropName(const sysprop::Properties& props,
+                                    const sysprop::Property& prop);
 bool IsCorrectIdentifier(const std::string& name);
-bool IsCorrectPropertyName(const std::string& name);
 bool ValidateProp(const sysprop::Properties& props,
                   const sysprop::Property& prop, std::string* err);
 bool ValidateProps(const sysprop::Properties& props, std::string* err);
 
+std::string GenerateDefaultPropName(const sysprop::Properties& props,
+                                    const sysprop::Property& prop) {
+  std::string ret;
+
+  if (prop.access() != sysprop::ReadWrite) ret = "ro.";
+
+  switch (props.owner()) {
+    case sysprop::Vendor:
+      ret += "vendor.";
+      break;
+    case sysprop::Odm:
+      ret += "odm.";
+      break;
+    default:
+      break;
+  }
+
+  ret += prop.api_name();
+
+  return ret;
+}
+
 bool IsCorrectIdentifier(const std::string& name) {
   if (name.empty()) return false;
   if (std::isalpha(name[0]) == 0 && name[0] != '_') return false;
 
-  for (size_t i = 1; i < name.size(); ++i) {
-    if (std::isalpha(name[i]) || name[i] == '_') continue;
-    if (std::isdigit(name[i])) continue;
-
-    return false;
-  }
-
-  return true;
+  return std::all_of(name.begin() + 1, name.end(), [](char ch) {
+    return std::isalnum(ch) != 0 || ch == '_';
+  });
 }
 
-bool IsCorrectPropertyName(const std::string& name) {
+bool IsCorrectPropertyOrApiName(const std::string& name) {
   if (name.empty()) return false;
 
-  for (const std::string& token : android::base::Split(name, ".")) {
-    if (!IsCorrectIdentifier(token)) return false;
-  }
+  static std::unordered_set<char> allowed{'_', '-', '.'};
 
-  return true;
+  return std::all_of(name.begin(), name.end(), [](char ch) {
+    return std::isalnum(ch) != 0 || allowed.count(ch) != 0;
+  });
 }
 
 bool ValidateProp(const sysprop::Properties& props,
                   const sysprop::Property& prop, std::string* err) {
-  if (!IsCorrectPropertyName(prop.name())) {
-    if (err) *err = "Invalid prop name \"" + prop.name() + "\"";
+  if (!IsCorrectPropertyOrApiName(prop.api_name())) {
+    if (err) *err = "Invalid API name \"" + prop.api_name() + "\"";
     return false;
   }
 
@@ -81,15 +100,16 @@
     std::vector<std::string> names =
         android::base::Split(prop.enum_values(), "|");
     if (names.empty()) {
-      if (err) *err = "Enum values are empty for prop \"" + prop.name() + "\"";
+      if (err)
+        *err = "Enum values are empty for API \"" + prop.api_name() + "\"";
       return false;
     }
 
     for (const std::string& name : names) {
       if (!IsCorrectIdentifier(name)) {
         if (err)
-          *err = "Invalid enum value \"" + name + "\" for prop \"" +
-                 prop.name() + "\"";
+          *err = "Invalid enum value \"" + name + "\" for API \"" +
+                 prop.api_name() + "\"";
         return false;
       }
     }
@@ -98,22 +118,71 @@
     for (const std::string& name : names) {
       if (!name_set.insert(name).second) {
         if (err)
-          *err = "Duplicated enum value \"" + name + "\" for prop \"" +
-                 prop.name() + "\"";
+          *err = "Duplicated enum value \"" + name + "\" for API \"" +
+                 prop.api_name() + "\"";
         return false;
       }
     }
   }
 
-  if (props.owner() == sysprop::Platform) {
-    std::string full_name = props.prefix() + prop.name();
-    if (android::base::StartsWith(full_name, "vendor.") ||
-        android::base::StartsWith(full_name, "odm.")) {
-      if (err)
-        *err = "Prop \"" + prop.name() +
-               "\" owned by platform cannot have vendor. or odm. namespace";
-      return false;
-    }
+  std::string prop_name = prop.prop_name();
+  if (prop_name.empty()) prop_name = GenerateDefaultPropName(props, prop);
+
+  if (!IsCorrectPropertyOrApiName(prop_name)) {
+    if (err) *err = "Invalid prop name \"" + prop.prop_name() + "\"";
+    return false;
+  }
+
+  static const std::regex vendor_regex("([^.]+\\.)?vendor\\..+");
+  static const std::regex odm_regex("([^.]+\\.)?odm\\..+");
+
+  switch (props.owner()) {
+    case sysprop::Platform:
+      if (std::regex_match(prop_name, vendor_regex) ||
+          std::regex_match(prop_name, odm_regex)) {
+        if (err)
+          *err = "Prop \"" + prop_name +
+                 "\" owned by platform cannot have vendor. or odm. namespace";
+        return false;
+      }
+      break;
+    case sysprop::Vendor:
+      if (!std::regex_match(prop_name, vendor_regex)) {
+        if (err)
+          *err = "Prop \"" + prop_name +
+                 "\" owned by vendor should have vendor. namespace";
+        return false;
+      }
+      break;
+    case sysprop::Odm:
+      if (!std::regex_match(prop_name, odm_regex)) {
+        if (err)
+          *err = "Prop \"" + prop_name +
+                 "\" owned by odm should have odm. namespace";
+        return false;
+      }
+      break;
+  }
+
+  switch (prop.access()) {
+    case sysprop::ReadWrite:
+      if (android::base::StartsWith(prop_name, "ro.")) {
+        if (err) {
+          *err = "Prop \"" + prop_name +
+                 "\" is ReadWrite and also have prefix \"ro.\"";
+        }
+        return false;
+      }
+      break;
+    default:
+      if (!android::base::StartsWith(prop_name, "ro.")) {
+        if (err) {
+          *err = "Prop \"" + prop_name +
+                 "\" isn't ReadWrite, but don't have prefix \"ro.\"";
+        }
+        return false;
+      }
+      break;
   }
 
   return true;
@@ -133,11 +202,6 @@
     }
   }
 
-  if (!props.prefix().empty() && !IsCorrectPropertyName(props.prefix())) {
-    if (err) *err = "Invalid prefix \"" + props.prefix() + "\"";
-    return false;
-  }
-
   if (props.prop_size() == 0) {
     if (err) *err = "There is no defined property";
     return false;
@@ -152,10 +216,10 @@
 
   for (int i = 0; i < props.prop_size(); ++i) {
     const auto& prop = props.prop(i);
-    auto res = prop_names.insert(PropNameToIdentifier(prop.name()));
+    auto res = prop_names.insert(ApiNameToIdentifier(prop.api_name()));
 
     if (!res.second) {
-      if (err) *err = "Duplicated prop name \"" + prop.name() + "\"";
+      if (err) *err = "Duplicated API name \"" + prop.api_name() + "\"";
       return false;
     }
   }
@@ -218,13 +282,15 @@
   for (int i = 0; i < props->prop_size(); ++i) {
     // set each optional field to its default value
     sysprop::Property& prop = *props->mutable_prop(i);
-    if (!prop.has_readonly()) prop.set_readonly(true);
+    if (prop.prop_name().empty())
+      prop.set_prop_name(GenerateDefaultPropName(*props, prop));
   }
 
   return true;
 }
 
-std::string PropNameToIdentifier(const std::string& name) {
-  static const std::regex kRegexDot{"\\."};
-  return std::regex_replace(name, kRegexDot, "_");
+std::string ApiNameToIdentifier(const std::string& name) {
+  static const std::regex kRegexAllowed{"-|\\."};
+  return (isdigit(name[0]) ? "_" : "") +
+         std::regex_replace(name, kRegexAllowed, "_");
 }
diff --git a/Common.h b/Common.h
index 8963a57..4a5d921 100644
--- a/Common.h
+++ b/Common.h
@@ -28,6 +28,6 @@
 bool IsDirectory(const std::string& path);
 bool ParseProps(const std::string& file_path, sysprop::Properties* props,
                 std::string* err);
-std::string PropNameToIdentifier(const std::string& name);
+std::string ApiNameToIdentifier(const std::string& name);
 
 #endif  // SYSTEM_TOOLS_SYSPROP_COMMON_H_
diff --git a/CppGen.cpp b/CppGen.cpp
index 7bb1e06..7217041 100644
--- a/CppGen.cpp
+++ b/CppGen.cpp
@@ -107,7 +107,7 @@
 }
 
 template <> [[maybe_unused]] std::optional<std::string> DoParse(const char* str) {
-    return std::make_optional(str);
+    return *str == '\0' ? std::nullopt : std::make_optional(str);
 }
 
 template <typename Vec> [[maybe_unused]] std::optional<Vec> DoParseList(const char* str) {
@@ -197,7 +197,7 @@
 }
 
 std::string GetCppEnumName(const sysprop::Property& prop) {
-  return PropNameToIdentifier(prop.name()) + "_values";
+  return ApiNameToIdentifier(prop.api_name()) + "_values";
 }
 
 std::string GetCppPropTypeName(const sysprop::Property& prop) {
@@ -253,7 +253,7 @@
     if (i > 0) writer.Write("\n");
 
     const sysprop::Property& prop = props.prop(i);
-    std::string prop_id = PropNameToIdentifier(prop.name());
+    std::string prop_id = ApiNameToIdentifier(prop.api_name());
     std::string prop_type = GetCppPropTypeName(prop);
 
     if (prop.type() == sysprop::Enum || prop.type() == sysprop::EnumList) {
@@ -269,7 +269,7 @@
 
     writer.Write("std::optional<%s> %s();\n", prop_type.c_str(),
                  prop_id.c_str());
-    if (!prop.readonly()) {
+    if (prop.access() != sysprop::Readonly) {
       writer.Write("bool %s(const %s& value);\n", prop_id.c_str(),
                    prop_type.c_str());
     }
@@ -304,7 +304,7 @@
       continue;
     }
 
-    std::string prop_id = PropNameToIdentifier(prop.name());
+    std::string prop_id = ApiNameToIdentifier(prop.api_name());
     std::string enum_name = GetCppEnumName(prop);
 
     writer.Write("constexpr const std::pair<const char*, %s> %s_list[] = {\n",
@@ -335,7 +335,7 @@
     writer.Dedent();
     writer.Write("}\n\n");
 
-    if (!prop.readonly()) {
+    if (prop.access() != sysprop::Readonly) {
       writer.Write("std::string FormatValue(%s value) {\n", enum_name.c_str());
       writer.Indent();
       writer.Write("for (auto [name, val] : %s_list) {\n", prop_id.c_str());
@@ -348,14 +348,11 @@
       writer.Dedent();
       writer.Write("}\n");
 
-      std::string prefix = (prop.readonly() ? "ro." : "") + props.prefix();
-      if (!prefix.empty() && prefix.back() != '.') prefix.push_back('.');
-
       writer.Write(
           "LOG(FATAL) << \"Invalid value \" << "
           "static_cast<std::int32_t>(value) << "
-          "\" for property \" << \"%s%s\";\n",
-          prefix.c_str(), prop_id.c_str());
+          "\" for property \" << \"%s\";\n",
+          prop.prop_name().c_str());
 
       writer.Write("__builtin_unreachable();\n");
       writer.Dedent();
@@ -371,10 +368,8 @@
     if (i > 0) writer.Write("\n");
 
     const sysprop::Property& prop = props.prop(i);
-    std::string prop_id = PropNameToIdentifier(prop.name());
+    std::string prop_id = ApiNameToIdentifier(prop.api_name());
     std::string prop_type = GetCppPropTypeName(prop);
-    std::string prefix = (prop.readonly() ? "ro." : "") + props.prefix();
-    if (!prefix.empty() && prefix.back() != '.') prefix.push_back('.');
 
     if (prop.type() == sysprop::Enum || prop.type() == sysprop::EnumList) {
       std::string enum_name = GetCppEnumName(prop);
@@ -383,19 +378,19 @@
     writer.Write("std::optional<%s> %s() {\n", prop_type.c_str(),
                  prop_id.c_str());
     writer.Indent();
-    writer.Write("return GetProp<%s>(\"%s%s\");\n", prop_type.c_str(),
-                 prefix.c_str(), prop.name().c_str());
+    writer.Write("return GetProp<%s>(\"%s\");\n", prop_type.c_str(),
+                 prop.prop_name().c_str());
     writer.Dedent();
     writer.Write("}\n");
 
-    if (!prop.readonly()) {
+    if (prop.access() != sysprop::Readonly) {
       writer.Write("\nbool %s(const %s& value) {\n", prop_id.c_str(),
                    prop_type.c_str());
       writer.Indent();
       writer.Write(
-          "return __system_property_set(\"%s%s\", "
+          "return __system_property_set(\"%s\", "
           "%s.c_str()) == 0;\n",
-          prefix.c_str(), prop.name().c_str(),
+          prop.prop_name().c_str(),
           prop.type() == sysprop::String ? "value" : "FormatValue(value)");
       writer.Dedent();
       writer.Write("}\n");
diff --git a/JavaGen.cpp b/JavaGen.cpp
index 95dd3b6..481e6d8 100644
--- a/JavaGen.cpp
+++ b/JavaGen.cpp
@@ -85,7 +85,7 @@
 }
 
 private static String tryParseString(String str) {
-    return str;
+    return str.length() == 0 ? null : str;
 }
 
 private static <T extends Enum<T>> T tryParseEnum(Class<T> enumType, String str) {
@@ -143,12 +143,12 @@
 std::string GetJavaPackageName(const sysprop::Properties& props);
 std::string GetJavaClassName(const sysprop::Properties& props);
 bool IsListProp(const sysprop::Property& prop);
-void WriteJavaAnnotation(CodeWriter& writer, const sysprop::Property& prop);
+void WriteJavaAnnotation(CodeWriter& writer, sysprop::Scope scope);
 bool GenerateJavaClass(const sysprop::Properties& props,
                        std::string* java_result, std::string* err);
 
 std::string GetJavaEnumTypeName(const sysprop::Property& prop) {
-  return PropNameToIdentifier(prop.name()) + "_values";
+  return ApiNameToIdentifier(prop.api_name()) + "_values";
 }
 
 std::string GetJavaTypeName(const sysprop::Property& prop) {
@@ -255,8 +255,8 @@
   }
 }
 
-void WriteJavaAnnotation(CodeWriter& writer, const sysprop::Property& prop) {
-  switch (prop.scope()) {
+void WriteJavaAnnotation(CodeWriter& writer, sysprop::Scope scope) {
+  switch (scope) {
     case sysprop::System:
       writer.Write("@SystemApi\n");
       break;
@@ -288,13 +288,11 @@
 
     const sysprop::Property& prop = props.prop(i);
 
-    std::string prop_id = PropNameToIdentifier(prop.name()).c_str();
+    std::string prop_id = ApiNameToIdentifier(prop.api_name()).c_str();
     std::string prop_type = GetJavaTypeName(prop);
-    std::string prefix = (prop.readonly() ? "ro." : "") + props.prefix();
-    if (!prefix.empty() && prefix.back() != '.') prefix.push_back('.');
 
     if (prop.type() == sysprop::Enum || prop.type() == sysprop::EnumList) {
-      WriteJavaAnnotation(writer, prop);
+      WriteJavaAnnotation(writer, prop.scope());
       writer.Write("public static enum %s {\n",
                    GetJavaEnumTypeName(prop).c_str());
       writer.Indent();
@@ -306,29 +304,29 @@
       writer.Write("}\n\n");
     }
 
-    WriteJavaAnnotation(writer, prop);
+    WriteJavaAnnotation(writer, prop.scope());
 
     writer.Write("public static Optional<%s> %s() {\n", prop_type.c_str(),
                  prop_id.c_str());
     writer.Indent();
-    writer.Write("String value = SystemProperties.get(\"%s%s\");\n",
-                 prefix.c_str(), prop.name().c_str());
+    writer.Write("String value = SystemProperties.get(\"%s\");\n",
+                 prop.prop_name().c_str());
     writer.Write("return Optional.ofNullable(%s);\n",
                  GetParsingExpression(prop).c_str());
     writer.Dedent();
     writer.Write("}\n");
 
-    if (!prop.readonly()) {
+    if (prop.access() != sysprop::Readonly) {
       writer.Write("\n");
-      WriteJavaAnnotation(writer, prop);
+      WriteJavaAnnotation(writer, sysprop::Internal);
       writer.Write("public static void %s(%s value) {\n", prop_id.c_str(),
                    prop_type.c_str());
       writer.Indent();
-      writer.Write("SystemProperties.set(\"%s%s\", %s);\n", prefix.c_str(),
-                   prop.name().c_str(),
+      writer.Write("SystemProperties.set(\"%s\", %s);\n",
+                   prop.prop_name().c_str(),
                    IsListProp(prop) ? "formatList(value)" : "value.toString()");
       writer.Dedent();
-      writer.Write("}\n\n");
+      writer.Write("}\n");
     }
   }
 
diff --git a/sysprop.proto b/sysprop.proto
index 3075077..7b0f21c 100644
--- a/sysprop.proto
+++ b/sysprop.proto
@@ -18,10 +18,10 @@
 
 package sysprop;
 
-enum Scope {
-  Public = 0;
-  System = 1;
-  Internal = 2;
+enum Access {
+  Readonly = 0;
+  Writeonce = 1;
+  ReadWrite = 2;
 }
 
 enum Owner {
@@ -30,6 +30,12 @@
   Odm = 2;
 }
 
+enum Scope {
+  Public = 0;
+  System = 1;
+  Internal = 2;
+}
+
 enum Type {
   Boolean = 0;
   Integer = 1;
@@ -41,22 +47,22 @@
   BooleanList = 20;
   IntegerList = 21;
   LongList = 22;
-  DoubleList = 24;
-  StringList = 25;
-  EnumList = 26;
+  DoubleList = 23;
+  StringList = 24;
+  EnumList = 25;
 }
 
 message Property {
-  required string name = 1;
+  required string api_name = 1;
   required Type type = 2;
-  required Scope scope = 3;
-  optional bool readonly = 4;
-  optional string enum_values = 50;
+  required Access access = 3;
+  required Scope scope = 4;
+  optional string prop_name = 5;
+  optional string enum_values = 6;
 }
 
 message Properties {
   required Owner owner = 1;
   required string module = 2;
-  optional string prefix = 3;
-  repeated Property prop = 4;
+  repeated Property prop = 3;
 }
diff --git a/tests/CppGenTest.cpp b/tests/CppGenTest.cpp
index bb94e05..595c5b5 100644
--- a/tests/CppGenTest.cpp
+++ b/tests/CppGenTest.cpp
@@ -28,88 +28,92 @@
 
 constexpr const char* kTestSyspropFile =
     R"(owner: Platform
-module: "android.os.PlatformProperties"
-prefix: "android.os"
+module: "android.PlatformProperties"
 
 prop {
-    name: "test_double"
+    api_name: "test_double"
     type: Double
+    prop_name: "android.test_double"
     scope: Internal
-    readonly: false
+    access: ReadWrite
 }
 prop {
-    name: "test_int"
+    api_name: "test_int"
     type: Integer
+    prop_name: "android.test_int"
     scope: Public
-    readonly: false
+    access: ReadWrite
 }
 prop {
-    name: "test.string"
+    api_name: "test.string"
     type: String
+    prop_name: "android.test.string"
     scope: System
-    readonly: false
+    access: ReadWrite
 }
 
 prop {
-    name: "test.enum"
+    api_name: "test.enum"
     type: Enum
+    prop_name: "android.test.enum"
     enum_values: "a|b|c|D|e|f|G"
     scope: Internal
-    readonly: false
+    access: ReadWrite
 }
 prop {
-    name: "test_BOOLeaN"
+    api_name: "test_BOOLeaN"
     type: Boolean
+    prop_name: "ro.android.test.b"
     scope: Public
-    readonly: false
+    access: Writeonce
 }
 prop {
-    name: "longlonglongLONGLONGlongLONGlongLONG"
+    api_name: "android.os_test-long"
     type: Long
     scope: System
-    readonly: false
+    access: ReadWrite
 }
 
 prop {
-    name: "test_double_list"
+    api_name: "test_double_list"
     type: DoubleList
     scope: Internal
-    readonly: false
+    access: ReadWrite
 }
 prop {
-    name: "test_list_int"
+    api_name: "test_list_int"
     type: IntegerList
     scope: Public
-    readonly: false
+    access: ReadWrite
 }
 prop {
-    name: "test.strlist"
+    api_name: "test.strlist"
     type: StringList
     scope: System
-    readonly: false
+    access: ReadWrite
 }
 
 prop {
-    name: "el"
+    api_name: "el"
     type: EnumList
     enum_values: "enu|mva|lue"
     scope: Internal
-    readonly: false
+    access: ReadWrite
 }
 )";
 
 constexpr const char* kExpectedHeaderOutput =
     R"(// Generated by the sysprop generator. DO NOT EDIT!
 
-#ifndef SYSPROPGEN_android_os_PlatformProperties_H_
-#define SYSPROPGEN_android_os_PlatformProperties_H_
+#ifndef SYSPROPGEN_android_PlatformProperties_H_
+#define SYSPROPGEN_android_PlatformProperties_H_
 
 #include <cstdint>
 #include <optional>
 #include <string>
 #include <vector>
 
-namespace android::os::PlatformProperties {
+namespace android::PlatformProperties {
 
 std::optional<double> test_double();
 bool test_double(const double& value);
@@ -136,8 +140,8 @@
 std::optional<bool> test_BOOLeaN();
 bool test_BOOLeaN(const bool& value);
 
-std::optional<std::int64_t> longlonglongLONGLONGlongLONGlongLONG();
-bool longlonglongLONGLONGlongLONGlongLONG(const std::int64_t& value);
+std::optional<std::int64_t> android_os_test_long();
+bool android_os_test_long(const std::int64_t& value);
 
 std::optional<std::vector<double>> test_double_list();
 bool test_double_list(const std::vector<double>& value);
@@ -157,9 +161,9 @@
 std::optional<std::vector<el_values>> el();
 bool el(const std::vector<el_values>& value);
 
-}  // namespace android::os::PlatformProperties
+}  // namespace android::PlatformProperties
 
-#endif  // SYSPROPGEN_android_os_PlatformProperties_H_
+#endif  // SYSPROPGEN_android_PlatformProperties_H_
 )";
 
 constexpr const char* kExpectedSourceOutput =
@@ -182,7 +186,7 @@
 
 namespace {
 
-using namespace android::os::PlatformProperties;
+using namespace android::PlatformProperties;
 
 template <typename T> std::optional<T> DoParse(const char* str);
 
@@ -212,7 +216,7 @@
             return name;
         }
     }
-    LOG(FATAL) << "Invalid value " << static_cast<std::int32_t>(value) << " for property " << "android.os.test_enum";
+    LOG(FATAL) << "Invalid value " << static_cast<std::int32_t>(value) << " for property " << "android.test.enum";
     __builtin_unreachable();
 }
 
@@ -238,7 +242,7 @@
             return name;
         }
     }
-    LOG(FATAL) << "Invalid value " << static_cast<std::int32_t>(value) << " for property " << "android.os.el";
+    LOG(FATAL) << "Invalid value " << static_cast<std::int32_t>(value) << " for property " << "el";
     __builtin_unreachable();
 }
 
@@ -290,7 +294,7 @@
 }
 
 template <> [[maybe_unused]] std::optional<std::string> DoParse(const char* str) {
-    return std::make_optional(str);
+    return *str == '\0' ? std::nullopt : std::make_optional(str);
 }
 
 template <typename Vec> [[maybe_unused]] std::optional<Vec> DoParseList(const char* str) {
@@ -360,89 +364,89 @@
 
 }  // namespace
 
-namespace android::os::PlatformProperties {
+namespace android::PlatformProperties {
 
 std::optional<double> test_double() {
-    return GetProp<double>("android.os.test_double");
+    return GetProp<double>("android.test_double");
 }
 
 bool test_double(const double& value) {
-    return __system_property_set("android.os.test_double", FormatValue(value).c_str()) == 0;
+    return __system_property_set("android.test_double", FormatValue(value).c_str()) == 0;
 }
 
 std::optional<std::int32_t> test_int() {
-    return GetProp<std::int32_t>("android.os.test_int");
+    return GetProp<std::int32_t>("android.test_int");
 }
 
 bool test_int(const std::int32_t& value) {
-    return __system_property_set("android.os.test_int", FormatValue(value).c_str()) == 0;
+    return __system_property_set("android.test_int", FormatValue(value).c_str()) == 0;
 }
 
 std::optional<std::string> test_string() {
-    return GetProp<std::string>("android.os.test.string");
+    return GetProp<std::string>("android.test.string");
 }
 
 bool test_string(const std::string& value) {
-    return __system_property_set("android.os.test.string", value.c_str()) == 0;
+    return __system_property_set("android.test.string", value.c_str()) == 0;
 }
 
 std::optional<test_enum_values> test_enum() {
-    return GetProp<test_enum_values>("android.os.test.enum");
+    return GetProp<test_enum_values>("android.test.enum");
 }
 
 bool test_enum(const test_enum_values& value) {
-    return __system_property_set("android.os.test.enum", FormatValue(value).c_str()) == 0;
+    return __system_property_set("android.test.enum", FormatValue(value).c_str()) == 0;
 }
 
 std::optional<bool> test_BOOLeaN() {
-    return GetProp<bool>("android.os.test_BOOLeaN");
+    return GetProp<bool>("ro.android.test.b");
 }
 
 bool test_BOOLeaN(const bool& value) {
-    return __system_property_set("android.os.test_BOOLeaN", FormatValue(value).c_str()) == 0;
+    return __system_property_set("ro.android.test.b", FormatValue(value).c_str()) == 0;
 }
 
-std::optional<std::int64_t> longlonglongLONGLONGlongLONGlongLONG() {
-    return GetProp<std::int64_t>("android.os.longlonglongLONGLONGlongLONGlongLONG");
+std::optional<std::int64_t> android_os_test_long() {
+    return GetProp<std::int64_t>("android.os_test-long");
 }
 
-bool longlonglongLONGLONGlongLONGlongLONG(const std::int64_t& value) {
-    return __system_property_set("android.os.longlonglongLONGLONGlongLONGlongLONG", FormatValue(value).c_str()) == 0;
+bool android_os_test_long(const std::int64_t& value) {
+    return __system_property_set("android.os_test-long", FormatValue(value).c_str()) == 0;
 }
 
 std::optional<std::vector<double>> test_double_list() {
-    return GetProp<std::vector<double>>("android.os.test_double_list");
+    return GetProp<std::vector<double>>("test_double_list");
 }
 
 bool test_double_list(const std::vector<double>& value) {
-    return __system_property_set("android.os.test_double_list", FormatValue(value).c_str()) == 0;
+    return __system_property_set("test_double_list", FormatValue(value).c_str()) == 0;
 }
 
 std::optional<std::vector<std::int32_t>> test_list_int() {
-    return GetProp<std::vector<std::int32_t>>("android.os.test_list_int");
+    return GetProp<std::vector<std::int32_t>>("test_list_int");
 }
 
 bool test_list_int(const std::vector<std::int32_t>& value) {
-    return __system_property_set("android.os.test_list_int", FormatValue(value).c_str()) == 0;
+    return __system_property_set("test_list_int", FormatValue(value).c_str()) == 0;
 }
 
 std::optional<std::vector<std::string>> test_strlist() {
-    return GetProp<std::vector<std::string>>("android.os.test.strlist");
+    return GetProp<std::vector<std::string>>("test.strlist");
 }
 
 bool test_strlist(const std::vector<std::string>& value) {
-    return __system_property_set("android.os.test.strlist", FormatValue(value).c_str()) == 0;
+    return __system_property_set("test.strlist", FormatValue(value).c_str()) == 0;
 }
 
 std::optional<std::vector<el_values>> el() {
-    return GetProp<std::vector<el_values>>("android.os.el");
+    return GetProp<std::vector<el_values>>("el");
 }
 
 bool el(const std::vector<el_values>& value) {
-    return __system_property_set("android.os.el", FormatValue(value).c_str()) == 0;
+    return __system_property_set("el", FormatValue(value).c_str()) == 0;
 }
 
-}  // namespace android::os::PlatformProperties
+}  // namespace android::PlatformProperties
 )";
 
 }  // namespace
@@ -477,10 +481,10 @@
   std::string header_output;
   ASSERT_TRUE(android::base::ReadFileToString(header_output_path,
                                               &header_output, true));
-  ASSERT_EQ(header_output, kExpectedHeaderOutput);
+  EXPECT_EQ(header_output, kExpectedHeaderOutput);
 
   std::string source_output;
   ASSERT_TRUE(android::base::ReadFileToString(source_output_path,
                                               &source_output, true));
-  ASSERT_EQ(source_output, kExpectedSourceOutput);
+  EXPECT_EQ(source_output, kExpectedSourceOutput);
 }
diff --git a/tests/InvalidSyspropTest.cpp b/tests/InvalidSyspropTest.cpp
index 54aa775..fc1f822 100644
--- a/tests/InvalidSyspropTest.cpp
+++ b/tests/InvalidSyspropTest.cpp
@@ -30,16 +30,17 @@
     R"(
 owner: Vendor
 module: "com.error.DuplicatedField"
-prefix: "com.error"
 prop {
-    name: "dup"
+    api_name: "dup"
     type: Integer
     scope: Internal
+    access: Readonly
 }
 prop {
-    name: "dup"
+    api_name: "dup"
     type: Long
     scope: Public
+    access: ReadWrite
 }
 )";
 
@@ -47,18 +48,17 @@
     R"(
 owner: Vendor
 module: "com.google.EmptyProp"
-prefix: ""
 )";
 
-constexpr const char* kInvalidPropName =
+constexpr const char* kInvalidApiName =
     R"(
 owner: Odm
 module: "odm.invalid.prop.name"
-prefix: "invalid"
 prop {
-    name: "!@#$"
+    api_name: "!@#$"
     type: Integer
     scope: System
+    access: ReadWrite
 }
 )";
 
@@ -66,11 +66,11 @@
     R"(
 owner: Odm
 module: "test.manufacturer"
-prefix: "test"
 prop {
-    name: "empty_enum_value"
+    api_name: "empty_enum_value"
     type: Enum
     scope: Internal
+    access: ReadWrite
 }
 )";
 
@@ -78,12 +78,12 @@
     R"(
 owner: Vendor
 module: "vendor.module.name"
-prefix: ""
 prop {
-    name: "status"
+    api_name: "status"
     type: Enum
     enum_values: "on|off|intermediate|on"
     scope: Public
+    access: ReadWrite
 }
 )";
 
@@ -91,36 +91,69 @@
     R"(
 owner: Platform
 module: ""
-prefix: ""
 prop {
-    name: "integer"
+    api_name: "integer"
     type: Integer
     scope: Public
+    access: ReadWrite
 }
 )";
 
 constexpr const char* kInvalidNamespaceForPlatform =
     R"(
 owner: Platform
-module: "android.os.PlatformProperties"
-prefix: "vendor.buildprop"
+module: "android.PlatformProperties"
 prop {
-    name: "utclong"
+    api_name: "vendor.build.utc_long"
     type: Long
     scope: System
+    access: ReadWrite
+}
+)";
+
+constexpr const char* kRoPrefixForReadWriteProperty =
+    R"(
+owner: Vendor
+module: "com.android.VendorProp"
+prop {
+    api_name: "i_am_readwrite"
+    type: Long
+    scope: System
+    prop_name: "ro.vendor.i_am_readwrite"
+    access: ReadWrite
+}
+)";
+
+constexpr const char* kNoRoPrefixForReadonlyProperty =
+    R"(
+owner: Odm
+module: "com.android.OdmProp"
+prop {
+    api_name: "i.am.readonly"
+    type: Long
+    scope: System
+    prop_name: "odm.i_am_readwrite"
+    access: Readonly
 }
 )";
 
 constexpr const char* kTestCasesAndExpectedErrors[][2] = {
-    {kDuplicatedField, "Duplicated prop name \"dup\""},
+    {kDuplicatedField, "Duplicated API name \"dup\""},
     {kEmptyProp, "There is no defined property"},
-    {kInvalidPropName, "Invalid prop name \"!@#$\""},
-    {kEmptyEnumValues, "Invalid enum value \"\" for prop \"empty_enum_value\""},
-    {kDuplicatedEnumValue, "Duplicated enum value \"on\" for prop \"status\""},
+    {kInvalidApiName, "Invalid API name \"!@#$\""},
+    {kEmptyEnumValues, "Invalid enum value \"\" for API \"empty_enum_value\""},
+    {kDuplicatedEnumValue, "Duplicated enum value \"on\" for API \"status\""},
     {kInvalidModuleName, "Invalid module name \"\""},
     {kInvalidNamespaceForPlatform,
-     "Prop \"utclong\" owned by platform cannot have vendor. or odm. "
+     "Prop \"vendor.build.utc_long\" owned by platform cannot have vendor. or "
+     "odm. "
      "namespace"},
+    {kRoPrefixForReadWriteProperty,
+     "Prop \"ro.vendor.i_am_readwrite\" is ReadWrite and also have prefix "
+     "\"ro.\""},
+    {kNoRoPrefixForReadonlyProperty,
+     "Prop \"odm.i_am_readwrite\" isn't ReadWrite, but don't have prefix "
+     "\"ro.\""},
 };
 
 }  // namespace
diff --git a/tests/JavaGenTest.cpp b/tests/JavaGenTest.cpp
index 33e25dc..07547dd 100644
--- a/tests/JavaGenTest.cpp
+++ b/tests/JavaGenTest.cpp
@@ -28,65 +28,76 @@
 constexpr const char* kTestSyspropFile =
     R"(owner: Vendor
 module: "com.somecompany.TestProperties"
-prefix: "com.somecompany"
 
 prop {
-    name: "test_double"
+    api_name: "test_double"
     type: Double
+    prop_name: "vendor.test_double"
     scope: Internal
+    access: ReadWrite
 }
 prop {
-    name: "test_int"
+    api_name: "test_int"
     type: Integer
+    prop_name: "vendor.test_int"
     scope: Public
+    access: ReadWrite
 }
 prop {
-    name: "test.string"
+    api_name: "test.string"
     type: String
+    prop_name: "vendor.test.string"
     scope: System
-    readonly: true
+    access: ReadWrite
 }
 
 prop {
-    name: "test.enum"
+    api_name: "test.enum"
     type: Enum
+    prop_name: "vendor.test.enum"
     enum_values: "a|b|c|D|e|f|G"
     scope: Internal
-    readonly: false
+    access: ReadWrite
 }
 prop {
-    name: "test_BOOLeaN"
+    api_name: "test_BOOLeaN"
     type: Boolean
+    prop_name: "ro.vendor.test.b"
     scope: Public
+    access: Writeonce
 }
 prop {
-    name: "longlonglongLONGLONGlongLONGlongLONG"
+    api_name: "vendor.os_test-long"
     type: Long
     scope: System
+    access: ReadWrite
 }
 
 prop {
-    name: "test_double_list"
+    api_name: "test_double_list"
     type: DoubleList
     scope: Internal
+    access: ReadWrite
 }
 prop {
-    name: "test_list_int"
+    api_name: "test_list_int"
     type: IntegerList
     scope: Public
+    access: ReadWrite
 }
 prop {
-    name: "test.strlist"
+    api_name: "test.strlist"
     type: StringList
     scope: System
-    readonly: false
+    access: ReadWrite
 }
 
 prop {
-    name: "el"
+    api_name: "el"
     type: EnumList
     enum_values: "enu|mva|lue"
     scope: Internal
+    access: ReadWrite
 }
 )";
 
@@ -145,7 +156,7 @@
     }
 
     private static String tryParseString(String str) {
-        return str;
+        return str.length() == 0 ? null : str;
     }
 
     private static <T extends Enum<T>> T tryParseEnum(Class<T> enumType, String str) {
@@ -196,22 +207,37 @@
 
     /** @hide */
     public static Optional<Double> test_double() {
-        String value = SystemProperties.get("ro.com.somecompany.test_double");
+        String value = SystemProperties.get("vendor.test_double");
         return Optional.ofNullable(tryParseDouble(value));
     }
 
+    /** @hide */
+    public static void test_double(Double value) {
+        SystemProperties.set("vendor.test_double", value.toString());
+    }
+
     public static Optional<Integer> test_int() {
-        String value = SystemProperties.get("ro.com.somecompany.test_int");
+        String value = SystemProperties.get("vendor.test_int");
         return Optional.ofNullable(tryParseInteger(value));
     }
 
+    /** @hide */
+    public static void test_int(Integer value) {
+        SystemProperties.set("vendor.test_int", value.toString());
+    }
+
     @SystemApi
     public static Optional<String> test_string() {
-        String value = SystemProperties.get("ro.com.somecompany.test.string");
+        String value = SystemProperties.get("vendor.test.string");
         return Optional.ofNullable(tryParseString(value));
     }
 
     /** @hide */
+    public static void test_string(String value) {
+        SystemProperties.set("vendor.test.string", value.toString());
+    }
+
+    /** @hide */
     public static enum test_enum_values {
         a,
         b,
@@ -224,50 +250,68 @@
 
     /** @hide */
     public static Optional<test_enum_values> test_enum() {
-        String value = SystemProperties.get("com.somecompany.test.enum");
+        String value = SystemProperties.get("vendor.test.enum");
         return Optional.ofNullable(tryParseEnum(test_enum_values.class, value));
     }
 
     /** @hide */
     public static void test_enum(test_enum_values value) {
-        SystemProperties.set("com.somecompany.test.enum", value.toString());
+        SystemProperties.set("vendor.test.enum", value.toString());
     }
 
-
     public static Optional<Boolean> test_BOOLeaN() {
-        String value = SystemProperties.get("ro.com.somecompany.test_BOOLeaN");
+        String value = SystemProperties.get("ro.vendor.test.b");
         return Optional.ofNullable(tryParseBoolean(value));
     }
 
+    /** @hide */
+    public static void test_BOOLeaN(Boolean value) {
+        SystemProperties.set("ro.vendor.test.b", value.toString());
+    }
+
     @SystemApi
-    public static Optional<Long> longlonglongLONGLONGlongLONGlongLONG() {
-        String value = SystemProperties.get("ro.com.somecompany.longlonglongLONGLONGlongLONGlongLONG");
+    public static Optional<Long> vendor_os_test_long() {
+        String value = SystemProperties.get("vendor.vendor.os_test-long");
         return Optional.ofNullable(tryParseLong(value));
     }
 
     /** @hide */
+    public static void vendor_os_test_long(Long value) {
+        SystemProperties.set("vendor.vendor.os_test-long", value.toString());
+    }
+
+    /** @hide */
     public static Optional<List<Double>> test_double_list() {
-        String value = SystemProperties.get("ro.com.somecompany.test_double_list");
+        String value = SystemProperties.get("vendor.test_double_list");
         return Optional.ofNullable(tryParseList(v -> tryParseDouble(v), value));
     }
 
+    /** @hide */
+    public static void test_double_list(List<Double> value) {
+        SystemProperties.set("vendor.test_double_list", formatList(value));
+    }
+
     public static Optional<List<Integer>> test_list_int() {
-        String value = SystemProperties.get("ro.com.somecompany.test_list_int");
+        String value = SystemProperties.get("vendor.test_list_int");
         return Optional.ofNullable(tryParseList(v -> tryParseInteger(v), value));
     }
 
+    /** @hide */
+    public static void test_list_int(List<Integer> value) {
+        SystemProperties.set("vendor.test_list_int", formatList(value));
+    }
+
     @SystemApi
     public static Optional<List<String>> test_strlist() {
-        String value = SystemProperties.get("com.somecompany.test.strlist");
+        String value = SystemProperties.get("vendor.test.strlist");
         return Optional.ofNullable(tryParseList(v -> tryParseString(v), value));
     }
 
-    @SystemApi
+    /** @hide */
     public static void test_strlist(List<String> value) {
-        SystemProperties.set("com.somecompany.test.strlist", formatList(value));
+        SystemProperties.set("vendor.test.strlist", formatList(value));
     }
 
-
     /** @hide */
     public static enum el_values {
         enu,
@@ -277,9 +321,14 @@
 
     /** @hide */
     public static Optional<List<el_values>> el() {
-        String value = SystemProperties.get("ro.com.somecompany.el");
+        String value = SystemProperties.get("vendor.el");
         return Optional.ofNullable(tryParseEnumList(el_values.class, value));
     }
+
+    /** @hide */
+    public static void el(List<el_values> value) {
+        SystemProperties.set("vendor.el", formatList(value));
+    }
 }
 )";
 
@@ -304,12 +353,11 @@
 
   std::string java_output_path =
       temp_dir.path + "/com/somecompany/TestProperties.java"s;
-  std::string jni_output_path = temp_dir.path + "/TestProperties_jni.cpp"s;
 
   std::string java_output;
   ASSERT_TRUE(
       android::base::ReadFileToString(java_output_path, &java_output, true));
-  ASSERT_EQ(java_output, kExpectedJavaOutput);
+  EXPECT_EQ(java_output, kExpectedJavaOutput);
 
   unlink(java_output_path.c_str());
   rmdir((temp_dir.path + "/com/somecompany"s).c_str());