Implement legacy_prop_name am: 3f452c00b7 am: 70aa221e57

Change-Id: I74ee1941d185fe08d3ad0aaa1d8461eeddb10051
diff --git a/Android.bp b/Android.bp
index 2768156..96eae8b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -69,7 +69,7 @@
 java_defaults {
     name: "sysprop-library-stub-defaults",
     srcs: [
-        "stub/android/os/SystemProperties.java",
+        "stub/**/*.java",
     ],
     installable: false,
     sdk_version: "core_current",
diff --git a/ApiChecker.cpp b/ApiChecker.cpp
index a628787..2b21ba9 100644
--- a/ApiChecker.cpp
+++ b/ApiChecker.cpp
@@ -76,6 +76,10 @@
       err += "Integer-as-bool of prop " + latest_prop.api_name() +
              " has been changed\n";
     }
+    if (latest_prop.legacy_prop_name() != current_prop.legacy_prop_name()) {
+      err += "Legacy prop of prop " + latest_prop.api_name() +
+             " has been changed\n";
+    }
   }
 
   if (!latest_empty) {
diff --git a/Common.cpp b/Common.cpp
index f81aa59..462b625 100644
--- a/Common.cpp
+++ b/Common.cpp
@@ -141,6 +141,11 @@
     return Errorf("Invalid prop name \"{}\"", prop.prop_name());
   }
 
+  std::string legacy_name = prop.legacy_prop_name();
+  if (!legacy_name.empty() && !IsCorrectPropertyName(legacy_name)) {
+    return Errorf("Invalid legacy prop name \"{}\"", legacy_name);
+  }
+
   static const std::regex vendor_regex(
       "(init\\.svc\\.|ro\\.|persist\\.)?vendor\\..+|ro\\.hardware\\..+");
   static const std::regex odm_regex(
diff --git a/CppGen.cpp b/CppGen.cpp
index 51e2f51..9c1602f 100644
--- a/CppGen.cpp
+++ b/CppGen.cpp
@@ -191,19 +191,23 @@
 }
 
 template <typename T>
-T GetProp(const char* key) {
+T GetProp(const char* key, const char* legacy = nullptr) {
+    std::string value;
 #ifdef __BIONIC__
-    T ret;
     auto pi = __system_property_find(key);
     if (pi != nullptr) {
         __system_property_read_callback(pi, [](void* cookie, const char*, const char* value, std::uint32_t) {
-            *static_cast<T*>(cookie) = TryParse<T>(value);
-        }, &ret);
+            *static_cast<std::string*>(cookie) = value;
+        }, &value);
     }
-    return ret;
 #else
-    return TryParse<T>(android::base::GetProperty(key, "").c_str());
+    value = android::base::GetProperty(key, "");
 #endif
+    if (value.empty() && legacy) {
+        ALOGD("prop %s doesn't exist; fallback to legacy prop %s", key, legacy);
+        return GetProp<T>(legacy);
+    }
+    return TryParse<T>(value.c_str());
 }
 
 )";
@@ -398,11 +402,18 @@
     const sysprop::Property& prop = props.prop(i);
     std::string prop_id = ApiNameToIdentifier(prop.api_name());
     std::string prop_type = GetCppPropTypeName(prop);
+    std::string prop_name = prop.prop_name();
+    std::string legacy_name = prop.legacy_prop_name();
 
     writer.Write("%s %s() {\n", prop_type.c_str(), prop_id.c_str());
     writer.Indent();
-    writer.Write("return GetProp<%s>(\"%s\");\n", prop_type.c_str(),
-                 prop.prop_name().c_str());
+    if (legacy_name.empty()) {
+      writer.Write("return GetProp<%s>(\"%s\");\n", prop_type.c_str(),
+                   prop_name.c_str());
+    } else {
+      writer.Write("return GetProp<%s>(\"%s\", \"%s\");\n", prop_type.c_str(),
+                   prop_name.c_str(), legacy_name.c_str());
+    }
     writer.Dedent();
     writer.Write("}\n");
 
diff --git a/JavaGen.cpp b/JavaGen.cpp
index 5912607..9b22f80 100644
--- a/JavaGen.cpp
+++ b/JavaGen.cpp
@@ -39,6 +39,7 @@
 
 constexpr const char* kJavaFileImports =
     R"(import android.os.SystemProperties;
+import android.util.Log;
 
 import java.lang.StringBuilder;
 import java.util.ArrayList;
@@ -209,17 +210,18 @@
 std::string GetParsingExpression(const sysprop::Property& prop) {
   switch (prop.type()) {
     case sysprop::Boolean:
-      return "tryParseBoolean(value)";
+      return "Optional.ofNullable(tryParseBoolean(value))";
     case sysprop::Integer:
-      return "tryParseInteger(value)";
+      return "Optional.ofNullable(tryParseInteger(value))";
     case sysprop::Long:
-      return "tryParseLong(value)";
+      return "Optional.ofNullable(tryParseLong(value))";
     case sysprop::Double:
-      return "tryParseDouble(value)";
+      return "Optional.ofNullable(tryParseDouble(value))";
     case sysprop::String:
-      return "tryParseString(value)";
+      return "Optional.ofNullable(tryParseString(value))";
     case sysprop::Enum:
-      return "tryParseEnum(" + GetJavaEnumTypeName(prop) + ".class, value)";
+      return "Optional.ofNullable(tryParseEnum(" + GetJavaEnumTypeName(prop) +
+             ".class, value))";
     case sysprop::EnumList:
       return "tryParseEnumList(" + GetJavaEnumTypeName(prop) +
              ".class, "
@@ -352,23 +354,31 @@
     if (IsListProp(prop)) {
       writer.Write("public static %s %s() {\n", prop_type.c_str(),
                    prop_id.c_str());
-      writer.Indent();
-      writer.Write("String value = SystemProperties.get(\"%s\");\n",
-                   prop.prop_name().c_str());
-      writer.Write("return %s;\n", GetParsingExpression(prop).c_str());
-      writer.Dedent();
-      writer.Write("}\n");
     } else {
       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\");\n",
+                 prop.prop_name().c_str());
+    if (!prop.legacy_prop_name().empty()) {
+      // SystemProperties.get() returns "" (empty string) when the property
+      // doesn't exist
+      writer.Write("if (\"\".equals(value)) {\n");
       writer.Indent();
-      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.Write(
+          "Log.d(\"%s\", \"prop %s doesn't exist; fallback to legacy prop "
+          "%s\");\n",
+          class_name.c_str(), prop.prop_name().c_str(),
+          prop.legacy_prop_name().c_str());
+      writer.Write("value = SystemProperties.get(\"%s\");\n",
+                   prop.legacy_prop_name().c_str());
       writer.Dedent();
       writer.Write("}\n");
     }
+    writer.Write("return %s;\n", GetParsingExpression(prop).c_str());
+    writer.Dedent();
+    writer.Write("}\n");
 
     if (prop.access() != sysprop::Readonly) {
       writer.Write("\n");
diff --git a/stub/android/util/Log.java b/stub/android/util/Log.java
new file mode 100644
index 0000000..a47ff84
--- /dev/null
+++ b/stub/android/util/Log.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+// Stub for cutting dependency from sysprop_library to framework.jar
+public class Log {
+    public static int d(String tag, String msg) {
+        return 0;
+    }
+    private Log() {
+    }
+}
diff --git a/sysprop.proto b/sysprop.proto
index 4bb850a..4833603 100644
--- a/sysprop.proto
+++ b/sysprop.proto
@@ -61,6 +61,7 @@
   string enum_values = 6;
   bool integer_as_bool = 7;
   bool deprecated = 8;
+  string legacy_prop_name = 9;
 }
 
 message Properties {
diff --git a/tests/CppGenTest.cpp b/tests/CppGenTest.cpp
index 6fc6466..1038680 100644
--- a/tests/CppGenTest.cpp
+++ b/tests/CppGenTest.cpp
@@ -49,6 +49,7 @@
     prop_name: "android.test.string"
     scope: Public
     access: ReadWrite
+    legacy_prop_name: "legacy.android.test.string"
 }
 prop {
     api_name: "test_enum"
@@ -57,6 +58,7 @@
     enum_values: "a|b|c|D|e|f|G"
     scope: Internal
     access: ReadWrite
+    legacy_prop_name: "android.test.old.enum"
 }
 prop {
     api_name: "test_BOOLeaN"
@@ -405,19 +407,23 @@
 }
 
 template <typename T>
-T GetProp(const char* key) {
+T GetProp(const char* key, const char* legacy = nullptr) {
+    std::string value;
 #ifdef __BIONIC__
-    T ret;
     auto pi = __system_property_find(key);
     if (pi != nullptr) {
         __system_property_read_callback(pi, [](void* cookie, const char*, const char* value, std::uint32_t) {
-            *static_cast<T*>(cookie) = TryParse<T>(value);
-        }, &ret);
+            *static_cast<std::string*>(cookie) = value;
+        }, &value);
     }
-    return ret;
 #else
-    return TryParse<T>(android::base::GetProperty(key, "").c_str());
+    value = android::base::GetProperty(key, "");
 #endif
+    if (value.empty() && legacy) {
+        ALOGD("prop %s doesn't exist; fallback to legacy prop %s", key, legacy);
+        return GetProp<T>(legacy);
+    }
+    return TryParse<T>(value.c_str());
 }
 
 }  // namespace
@@ -441,7 +447,7 @@
 }
 
 std::optional<std::string> test_string() {
-    return GetProp<std::optional<std::string>>("android.test.string");
+    return GetProp<std::optional<std::string>>("android.test.string", "legacy.android.test.string");
 }
 
 bool test_string(const std::optional<std::string>& value) {
@@ -449,7 +455,7 @@
 }
 
 std::optional<test_enum_values> test_enum() {
-    return GetProp<std::optional<test_enum_values>>("android.test.enum");
+    return GetProp<std::optional<test_enum_values>>("android.test.enum", "android.test.old.enum");
 }
 
 bool test_enum(const std::optional<test_enum_values>& value) {
diff --git a/tests/JavaGenTest.cpp b/tests/JavaGenTest.cpp
index 4dd8973..86a5689 100644
--- a/tests/JavaGenTest.cpp
+++ b/tests/JavaGenTest.cpp
@@ -57,6 +57,7 @@
     enum_values: "a|b|c|D|e|f|G"
     scope: Internal
     access: ReadWrite
+    legacy_prop_name: "vendor.old.test_enum"
 }
 prop {
     api_name: "test_BOOLeaN"
@@ -106,6 +107,7 @@
 package com.somecompany;
 
 import android.os.SystemProperties;
+import android.util.Log;
 
 import java.lang.StringBuilder;
 import java.util.ArrayList;
@@ -289,6 +291,7 @@
 package com.somecompany;
 
 import android.os.SystemProperties;
+import android.util.Log;
 
 import java.lang.StringBuilder;
 import java.util.ArrayList;
@@ -454,6 +457,10 @@
 
     public static Optional<test_enum_values> test_enum() {
         String value = SystemProperties.get("vendor.test.enum");
+        if ("".equals(value)) {
+            Log.d("TestProperties", "prop vendor.test.enum doesn't exist; fallback to legacy prop vendor.old.test_enum");
+            value = SystemProperties.get("vendor.old.test_enum");
+        }
         return Optional.ofNullable(tryParseEnum(test_enum_values.class, value));
     }