/* * Copyright (C) 2018 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. */ #include #include #include #include #include #include "JavaGen.h" namespace { constexpr const char* kTestSyspropFile = R"(owner: Vendor module: "com.somecompany.TestProperties" prop { api_name: "test_double" type: Double prop_name: "vendor.test_double" scope: Internal access: ReadWrite } prop { api_name: "test_int" type: Integer prop_name: "vendor.test_int" scope: Public access: ReadWrite } prop { api_name: "test_string" type: String prop_name: "vendor.test.string" scope: Public access: Readonly legacy_prop_name: "vendor.old.string" } prop { api_name: "test_enum" type: Enum prop_name: "vendor.test.enum" enum_values: "a|b|c|D|e|f|G" scope: Internal access: ReadWrite } prop { api_name: "test_BOOLeaN" type: Boolean prop_name: "ro.vendor.test.b" scope: Public access: Writeonce } prop { api_name: "vendor_os_test-long" type: Long scope: Public access: ReadWrite } prop { api_name: "test_double_list" type: DoubleList scope: Internal access: ReadWrite } prop { api_name: "test_list_int" type: IntegerList scope: Public access: ReadWrite } prop { api_name: "test_strlist" type: StringList scope: Public access: ReadWrite deprecated: true } prop { api_name: "el" type: EnumList enum_values: "enu|mva|lue" scope: Internal access: ReadWrite deprecated: true } )"; constexpr const char* kExpectedPublicOutput = R"s(// Generated by the sysprop generator. DO NOT EDIT! package com.somecompany; import android.os.SystemProperties; import android.util.Log; import java.lang.StringBuilder; import java.util.ArrayList; import java.util.function.Function; import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.StringJoiner; import java.util.stream.Collectors; public final class TestProperties { private TestProperties () {} private static Boolean tryParseBoolean(String str) { switch (str.toLowerCase(Locale.US)) { case "1": case "true": return Boolean.TRUE; case "0": case "false": return Boolean.FALSE; default: return null; } } private static Integer tryParseInteger(String str) { try { return Integer.valueOf(str); } catch (NumberFormatException e) { return null; } } private static Integer tryParseUInt(String str) { try { return Integer.parseUnsignedInt(str); } catch (NumberFormatException e) { return null; } } private static Long tryParseLong(String str) { try { return Long.valueOf(str); } catch (NumberFormatException e) { return null; } } private static Long tryParseULong(String str) { try { return Long.parseUnsignedLong(str); } catch (NumberFormatException e) { return null; } } private static Double tryParseDouble(String str) { try { return Double.valueOf(str); } catch (NumberFormatException e) { return null; } } private static String tryParseString(String str) { return "".equals(str) ? null : str; } private static > T tryParseEnum(Class enumType, String str) { try { return Enum.valueOf(enumType, str.toUpperCase(Locale.US)); } catch (IllegalArgumentException e) { return null; } } private static List tryParseList(Function elementParser, String str) { if ("".equals(str)) return new ArrayList<>(); List ret = new ArrayList<>(); int p = 0; for (;;) { StringBuilder sb = new StringBuilder(); while (p < str.length() && str.charAt(p) != ',') { if (str.charAt(p) == '\\') ++p; if (p == str.length()) break; sb.append(str.charAt(p++)); } ret.add(elementParser.apply(sb.toString())); if (p == str.length()) break; ++p; } return ret; } private static > List tryParseEnumList(Class enumType, String str) { if ("".equals(str)) return new ArrayList<>(); List ret = new ArrayList<>(); for (String element : str.split(",")) { ret.add(tryParseEnum(enumType, element)); } return ret; } private static String escape(String str) { return str.replaceAll("([\\\\,])", "\\\\$1"); } private static String formatList(List list) { StringJoiner joiner = new StringJoiner(","); for (T element : list) { joiner.add(element == null ? "" : escape(element.toString())); } return joiner.toString(); } private static String formatUIntList(List list) { StringJoiner joiner = new StringJoiner(","); for (Integer element : list) { joiner.add(element == null ? "" : escape(Integer.toUnsignedString(element))); } return joiner.toString(); } private static String formatULongList(List list) { StringJoiner joiner = new StringJoiner(","); for (Long element : list) { joiner.add(element == null ? "" : escape(Long.toUnsignedString(element))); } return joiner.toString(); } private static > String formatEnumList(List list, Function elementFormatter) { StringJoiner joiner = new StringJoiner(","); for (T element : list) { joiner.add(element == null ? "" : elementFormatter.apply(element)); } return joiner.toString(); } public static Optional test_int() { String value = SystemProperties.get("vendor.test_int"); return Optional.ofNullable(tryParseInteger(value)); } public static void test_int(Integer value) { SystemProperties.set("vendor.test_int", value == null ? "" : value.toString()); } public static Optional test_string() { String value = SystemProperties.get("vendor.test.string"); if ("".equals(value)) { Log.v("TestProperties", "prop vendor.test.string doesn't exist; fallback to legacy prop vendor.old.string"); value = SystemProperties.get("vendor.old.string"); } return Optional.ofNullable(tryParseString(value)); } public static Optional test_BOOLeaN() { String value = SystemProperties.get("ro.vendor.test.b"); return Optional.ofNullable(tryParseBoolean(value)); } public static void test_BOOLeaN(Boolean value) { SystemProperties.set("ro.vendor.test.b", value == null ? "" : value.toString()); } public static Optional vendor_os_test_long() { String value = SystemProperties.get("vendor.vendor_os_test-long"); return Optional.ofNullable(tryParseLong(value)); } public static void vendor_os_test_long(Long value) { SystemProperties.set("vendor.vendor_os_test-long", value == null ? "" : value.toString()); } public static List test_list_int() { String value = SystemProperties.get("vendor.test_list_int"); return tryParseList(v -> tryParseInteger(v), value); } public static void test_list_int(List value) { SystemProperties.set("vendor.test_list_int", value == null ? "" : formatList(value)); } @Deprecated public static List test_strlist() { String value = SystemProperties.get("vendor.test_strlist"); return tryParseList(v -> tryParseString(v), value); } @Deprecated public static void test_strlist(List value) { SystemProperties.set("vendor.test_strlist", value == null ? "" : formatList(value)); } } )s"; constexpr const char* kExpectedInternalOutput = R"s(// Generated by the sysprop generator. DO NOT EDIT! package com.somecompany; import android.os.SystemProperties; import android.util.Log; import java.lang.StringBuilder; import java.util.ArrayList; import java.util.function.Function; import java.util.List; import java.util.Locale; import java.util.Optional; import java.util.StringJoiner; import java.util.stream.Collectors; public final class TestProperties { private TestProperties () {} private static Boolean tryParseBoolean(String str) { switch (str.toLowerCase(Locale.US)) { case "1": case "true": return Boolean.TRUE; case "0": case "false": return Boolean.FALSE; default: return null; } } private static Integer tryParseInteger(String str) { try { return Integer.valueOf(str); } catch (NumberFormatException e) { return null; } } private static Integer tryParseUInt(String str) { try { return Integer.parseUnsignedInt(str); } catch (NumberFormatException e) { return null; } } private static Long tryParseLong(String str) { try { return Long.valueOf(str); } catch (NumberFormatException e) { return null; } } private static Long tryParseULong(String str) { try { return Long.parseUnsignedLong(str); } catch (NumberFormatException e) { return null; } } private static Double tryParseDouble(String str) { try { return Double.valueOf(str); } catch (NumberFormatException e) { return null; } } private static String tryParseString(String str) { return "".equals(str) ? null : str; } private static > T tryParseEnum(Class enumType, String str) { try { return Enum.valueOf(enumType, str.toUpperCase(Locale.US)); } catch (IllegalArgumentException e) { return null; } } private static List tryParseList(Function elementParser, String str) { if ("".equals(str)) return new ArrayList<>(); List ret = new ArrayList<>(); int p = 0; for (;;) { StringBuilder sb = new StringBuilder(); while (p < str.length() && str.charAt(p) != ',') { if (str.charAt(p) == '\\') ++p; if (p == str.length()) break; sb.append(str.charAt(p++)); } ret.add(elementParser.apply(sb.toString())); if (p == str.length()) break; ++p; } return ret; } private static > List tryParseEnumList(Class enumType, String str) { if ("".equals(str)) return new ArrayList<>(); List ret = new ArrayList<>(); for (String element : str.split(",")) { ret.add(tryParseEnum(enumType, element)); } return ret; } private static String escape(String str) { return str.replaceAll("([\\\\,])", "\\\\$1"); } private static String formatList(List list) { StringJoiner joiner = new StringJoiner(","); for (T element : list) { joiner.add(element == null ? "" : escape(element.toString())); } return joiner.toString(); } private static String formatUIntList(List list) { StringJoiner joiner = new StringJoiner(","); for (Integer element : list) { joiner.add(element == null ? "" : escape(Integer.toUnsignedString(element))); } return joiner.toString(); } private static String formatULongList(List list) { StringJoiner joiner = new StringJoiner(","); for (Long element : list) { joiner.add(element == null ? "" : escape(Long.toUnsignedString(element))); } return joiner.toString(); } private static > String formatEnumList(List list, Function elementFormatter) { StringJoiner joiner = new StringJoiner(","); for (T element : list) { joiner.add(element == null ? "" : elementFormatter.apply(element)); } return joiner.toString(); } public static Optional test_double() { String value = SystemProperties.get("vendor.test_double"); return Optional.ofNullable(tryParseDouble(value)); } public static void test_double(Double value) { SystemProperties.set("vendor.test_double", value == null ? "" : value.toString()); } public static Optional test_int() { String value = SystemProperties.get("vendor.test_int"); return Optional.ofNullable(tryParseInteger(value)); } public static void test_int(Integer value) { SystemProperties.set("vendor.test_int", value == null ? "" : value.toString()); } public static Optional test_string() { String value = SystemProperties.get("vendor.test.string"); if ("".equals(value)) { Log.v("TestProperties", "prop vendor.test.string doesn't exist; fallback to legacy prop vendor.old.string"); value = SystemProperties.get("vendor.old.string"); } return Optional.ofNullable(tryParseString(value)); } public static enum test_enum_values { A("a"), B("b"), C("c"), D("D"), E("e"), F("f"), G("G"); private final String propValue; private test_enum_values(String propValue) { this.propValue = propValue; } public String getPropValue() { return propValue; } } public static Optional test_enum() { String value = SystemProperties.get("vendor.test.enum"); return Optional.ofNullable(tryParseEnum(test_enum_values.class, value)); } public static void test_enum(test_enum_values value) { SystemProperties.set("vendor.test.enum", value == null ? "" : value.getPropValue()); } public static Optional test_BOOLeaN() { String value = SystemProperties.get("ro.vendor.test.b"); return Optional.ofNullable(tryParseBoolean(value)); } public static void test_BOOLeaN(Boolean value) { SystemProperties.set("ro.vendor.test.b", value == null ? "" : value.toString()); } public static Optional vendor_os_test_long() { String value = SystemProperties.get("vendor.vendor_os_test-long"); return Optional.ofNullable(tryParseLong(value)); } public static void vendor_os_test_long(Long value) { SystemProperties.set("vendor.vendor_os_test-long", value == null ? "" : value.toString()); } public static List test_double_list() { String value = SystemProperties.get("vendor.test_double_list"); return tryParseList(v -> tryParseDouble(v), value); } public static void test_double_list(List value) { SystemProperties.set("vendor.test_double_list", value == null ? "" : formatList(value)); } public static List test_list_int() { String value = SystemProperties.get("vendor.test_list_int"); return tryParseList(v -> tryParseInteger(v), value); } public static void test_list_int(List value) { SystemProperties.set("vendor.test_list_int", value == null ? "" : formatList(value)); } @Deprecated public static List test_strlist() { String value = SystemProperties.get("vendor.test_strlist"); return tryParseList(v -> tryParseString(v), value); } @Deprecated public static void test_strlist(List value) { SystemProperties.set("vendor.test_strlist", value == null ? "" : formatList(value)); } public static enum el_values { ENU("enu"), MVA("mva"), LUE("lue"); private final String propValue; private el_values(String propValue) { this.propValue = propValue; } public String getPropValue() { return propValue; } } @Deprecated public static List el() { String value = SystemProperties.get("vendor.el"); return tryParseEnumList(el_values.class, value); } @Deprecated public static void el(List value) { SystemProperties.set("vendor.el", value == null ? "" : formatEnumList(value, el_values::getPropValue)); } } )s"; } // namespace using namespace std::string_literals; TEST(SyspropTest, JavaGenTest) { TemporaryFile temp_file; // strlen is optimized for constants, so don't worry about it. ASSERT_EQ(write(temp_file.fd, kTestSyspropFile, strlen(kTestSyspropFile)), strlen(kTestSyspropFile)); close(temp_file.fd); temp_file.fd = -1; TemporaryDir temp_dir; std::pair tests[] = { {sysprop::Scope::Internal, kExpectedInternalOutput}, {sysprop::Scope::Public, kExpectedPublicOutput}, }; for (auto [scope, expected_output] : tests) { ASSERT_RESULT_OK(GenerateJavaLibrary(temp_file.path, scope, temp_dir.path)); std::string java_output_path = temp_dir.path + "/com/somecompany/TestProperties.java"s; std::string java_output; ASSERT_TRUE( android::base::ReadFileToString(java_output_path, &java_output, true)); EXPECT_EQ(java_output, expected_output); unlink(java_output_path.c_str()); rmdir((temp_dir.path + "/com/somecompany"s).c_str()); rmdir((temp_dir.path + "/com"s).c_str()); } }