summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorInseob Kim <inseob@google.com>2019-07-30 18:36:28 +0900
committerInseob Kim <inseob@google.com>2019-08-09 19:01:19 +0900
commita9565afdb024c77b8192f0b7720847ee92a058d9 (patch)
tree5fdbb6b80416b59237b0885264b4f773a242b733
parent0dfe426db387e86f659dec16687dff4249d782b1 (diff)
downloadplatform_system_tools_sysprop-a9565afdb024c77b8192f0b7720847ee92a058d9.tar.gz
platform_system_tools_sysprop-a9565afdb024c77b8192f0b7720847ee92a058d9.tar.bz2
platform_system_tools_sysprop-a9565afdb024c77b8192f0b7720847ee92a058d9.zip
Implement sysprop_library API stability check
sysprop_library now checks the API stability itself, cutting dependency on java_sdk_library. Under the directory {module_dir}/api, {module_name}-current.txt and {module_name}-latest.txt hold API signatures. When sysprop_library is built, or a user run "m {module_name}-check-api" command, API check is performed. First, current.txt must have exactly same signature with built sysprop_library module. Second, current.txt must be compatible with latest.txt. Build system emits a handy error message to generate/update those API files, in case of missing or mismatching. Also, a script file for freezing API files is introduced. Bug: 131637873 Test: 1) m && boot blueline Test: 2) m {sysprop_library} performs API check Test: 3) manual test for check-api, freezing api Change-Id: I95e0e38a24e78bb9526248ea7efc12d7fbf2d35a
-rw-r--r--Android.bp20
-rw-r--r--JavaGen.cpp58
-rw-r--r--JavaMain.cpp20
-rw-r--r--include/JavaGen.h5
-rw-r--r--stub/android/os/SystemProperties.java28
-rw-r--r--tests/JavaGenTest.cpp192
6 files changed, 245 insertions, 78 deletions
diff --git a/Android.bp b/Android.bp
index e91a8d4..9e8aca8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -57,3 +57,23 @@ cc_test_host {
"JavaGen.cpp",
"tests/*.cpp"],
}
+
+java_defaults {
+ name: "sysprop-library-stub-defaults",
+ srcs: [
+ "stub/android/os/SystemProperties.java",
+ ],
+ installable: false,
+ sdk_version: "core_current",
+}
+
+java_library {
+ name: "sysprop-library-stub-platform",
+ defaults: ["sysprop-library-stub-defaults"],
+}
+
+java_library {
+ name: "sysprop-library-stub-vendor",
+ defaults: ["sysprop-library-stub-defaults"],
+ soc_specific: true,
+}
diff --git a/JavaGen.cpp b/JavaGen.cpp
index ba7409d..5cf9c5c 100644
--- a/JavaGen.cpp
+++ b/JavaGen.cpp
@@ -40,8 +40,7 @@ namespace {
constexpr const char* kIndent = " ";
constexpr const char* kJavaFileImports =
- R"(import android.annotation.SystemApi;
-import android.os.SystemProperties;
+ R"(import android.os.SystemProperties;
import java.util.ArrayList;
import java.util.function.Function;
@@ -157,8 +156,8 @@ std::string GetJavaPackageName(const sysprop::Properties& props);
std::string GetJavaClassName(const sysprop::Properties& props);
std::string GetParsingExpression(const sysprop::Property& prop);
std::string GetFormattingExpression(const sysprop::Property& prop);
-void WriteJavaAnnotation(CodeWriter& writer, sysprop::Scope scope);
-std::string GenerateJavaClass(const sysprop::Properties& props);
+std::string GenerateJavaClass(const sysprop::Properties& props,
+ sysprop::Scope scope);
std::string GetJavaEnumTypeName(const sysprop::Property& prop) {
return ApiNameToIdentifier(prop.api_name()) + "_values";
@@ -277,33 +276,8 @@ std::string GetJavaClassName(const sysprop::Properties& props) {
return module.substr(module.rfind('.') + 1);
}
-void WriteJavaAnnotation(CodeWriter& writer, sysprop::Scope scope) {
- switch (scope) {
- // This is to restrict access from modules linking against SDK.
- // TODO(b/131637873): remove this after cutting dependency on
- // java_sdk_library
- case sysprop::Public:
- writer.Write("/** @hide */\n");
- writer.Write("@SystemApi\n");
- break;
- case sysprop::Internal:
- writer.Write("/** @hide */\n");
- break;
- default:
- break;
- }
-}
-
-std::string GenerateJavaClass(const sysprop::Properties& props) {
- sysprop::Scope classScope = sysprop::Internal;
-
- for (int i = 0; i < props.prop_size(); ++i) {
- // Get least restrictive scope among props. For example, if all props
- // are internal, class can be as well internal. However, class should
- // be public or system if at least one prop is so.
- classScope = std::min(classScope, props.prop(i).scope());
- }
-
+std::string GenerateJavaClass(const sysprop::Properties& props,
+ sysprop::Scope scope) {
std::string package_name = GetJavaPackageName(props);
std::string class_name = GetJavaClassName(props);
@@ -311,24 +285,23 @@ std::string GenerateJavaClass(const sysprop::Properties& props) {
writer.Write("%s", kGeneratedFileFooterComments);
writer.Write("package %s;\n\n", package_name.c_str());
writer.Write("%s", kJavaFileImports);
- WriteJavaAnnotation(writer, classScope);
writer.Write("public final class %s {\n", class_name.c_str());
writer.Indent();
writer.Write("private %s () {}\n\n", class_name.c_str());
writer.Write("%s", kJavaParsersAndFormatters);
for (int i = 0; i < props.prop_size(); ++i) {
- writer.Write("\n");
-
const sysprop::Property& prop = props.prop(i);
+ // skip if scope is internal and we are generating public class
+ if (prop.scope() > scope) continue;
+
+ writer.Write("\n");
+
std::string prop_id = ApiNameToIdentifier(prop.api_name()).c_str();
std::string prop_type = GetJavaTypeName(prop);
if (prop.type() == sysprop::Enum || prop.type() == sysprop::EnumList) {
- if (prop.scope() != classScope) {
- WriteJavaAnnotation(writer, prop.scope());
- }
writer.Write("public static enum %s {\n",
GetJavaEnumTypeName(prop).c_str());
writer.Indent();
@@ -361,9 +334,6 @@ std::string GenerateJavaClass(const sysprop::Properties& props) {
writer.Write("}\n\n");
}
- if (prop.scope() != classScope) {
- WriteJavaAnnotation(writer, prop.scope());
- }
if (prop.deprecated()) {
writer.Write("@Deprecated\n");
}
@@ -389,11 +359,8 @@ std::string GenerateJavaClass(const sysprop::Properties& props) {
writer.Write("}\n");
}
- if (prop.access() != sysprop::Readonly) {
+ if (prop.access() != sysprop::Readonly && scope == sysprop::Internal) {
writer.Write("\n");
- if (classScope != sysprop::Internal) {
- WriteJavaAnnotation(writer, sysprop::Internal);
- }
if (prop.deprecated()) {
writer.Write("@Deprecated\n");
}
@@ -417,6 +384,7 @@ std::string GenerateJavaClass(const sysprop::Properties& props) {
} // namespace
Result<void> GenerateJavaLibrary(const std::string& input_file_path,
+ sysprop::Scope scope,
const std::string& java_output_dir) {
sysprop::Properties props;
@@ -426,7 +394,7 @@ Result<void> GenerateJavaLibrary(const std::string& input_file_path,
return res.error();
}
- std::string java_result = GenerateJavaClass(props);
+ std::string java_result = GenerateJavaClass(props, scope);
std::string package_name = GetJavaPackageName(props);
std::string java_package_dir =
java_output_dir + "/" + std::regex_replace(package_name, kRegexDot, "/");
diff --git a/JavaMain.cpp b/JavaMain.cpp
index 5dc5d27..723e2d3 100644
--- a/JavaMain.cpp
+++ b/JavaMain.cpp
@@ -25,6 +25,7 @@
#include <getopt.h>
#include "JavaGen.h"
+#include "sysprop.pb.h"
using android::base::Errorf;
using android::base::Result;
@@ -34,10 +35,14 @@ namespace {
struct Arguments {
std::string input_file_path;
std::string java_output_dir;
+ sysprop::Scope scope;
};
[[noreturn]] void PrintUsage(const char* exe_name) {
- std::printf("Usage: %s [--java-output-dir dir] sysprop_file\n", exe_name);
+ std::printf(
+ "Usage: %s --scope (internal|public) --java-output-dir dir "
+ "sysprop_file\n",
+ exe_name);
std::exit(EXIT_FAILURE);
}
@@ -45,6 +50,7 @@ Result<void> ParseArgs(int argc, char* argv[], Arguments* args) {
for (;;) {
static struct option long_options[] = {
{"java-output-dir", required_argument, 0, 'j'},
+ {"scope", required_argument, 0, 's'},
};
int opt = getopt_long_only(argc, argv, "", long_options, nullptr);
@@ -54,6 +60,15 @@ Result<void> ParseArgs(int argc, char* argv[], Arguments* args) {
case 'j':
args->java_output_dir = optarg;
break;
+ case 's':
+ if (strcmp(optarg, "public") == 0) {
+ args->scope = sysprop::Scope::Public;
+ } else if (strcmp(optarg, "internal") == 0) {
+ args->scope = sysprop::Scope::Internal;
+ } else {
+ return Errorf("Invalid option {} for scope", optarg);
+ }
+ break;
default:
PrintUsage(argv[0]);
}
@@ -83,7 +98,8 @@ int main(int argc, char* argv[]) {
PrintUsage(argv[0]);
}
- if (auto res = GenerateJavaLibrary(args.input_file_path, args.java_output_dir);
+ if (auto res = GenerateJavaLibrary(args.input_file_path, args.scope,
+ args.java_output_dir);
!res) {
LOG(FATAL) << "Error during generating java sysprop from "
<< args.input_file_path << ": " << res.error();
diff --git a/include/JavaGen.h b/include/JavaGen.h
index 71dc407..9794626 100644
--- a/include/JavaGen.h
+++ b/include/JavaGen.h
@@ -19,5 +19,8 @@
#include <android-base/result.h>
#include <string>
+#include "sysprop.pb.h"
+
android::base::Result<void> GenerateJavaLibrary(
- const std::string& input_file_path, const std::string& java_output_dir); \ No newline at end of file
+ const std::string& input_file_path, sysprop::Scope scope,
+ const std::string& java_output_dir); \ No newline at end of file
diff --git a/stub/android/os/SystemProperties.java b/stub/android/os/SystemProperties.java
new file mode 100644
index 0000000..7309abc
--- /dev/null
+++ b/stub/android/os/SystemProperties.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 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.os;
+
+// Stub for cutting dependency from sysprop_library to framework.jar
+public class SystemProperties {
+ public static String get(String key) {
+ return null;
+ }
+ public static void set(String key, String val) {
+ }
+ private SystemProperties() {
+ }
+}
diff --git a/tests/JavaGenTest.cpp b/tests/JavaGenTest.cpp
index f148e2c..a96d103 100644
--- a/tests/JavaGenTest.cpp
+++ b/tests/JavaGenTest.cpp
@@ -100,12 +100,155 @@ prop {
}
)";
-constexpr const char* kExpectedJavaOutput =
+constexpr const char* kExpectedPublicOutput =
+ R"(// Generated by the sysprop generator. DO NOT EDIT!
+
+package com.somecompany;
+
+import android.os.SystemProperties;
+
+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 Long tryParseLong(String str) {
+ try {
+ return Long.valueOf(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 extends Enum<T>> T tryParseEnum(Class<T> enumType, String str) {
+ try {
+ return Enum.valueOf(enumType, str.toUpperCase(Locale.US));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ private static <T> List<T> tryParseList(Function<String, T> elementParser, String str) {
+ if ("".equals(str)) return new ArrayList<>();
+
+ List<T> ret = new ArrayList<>();
+
+ for (String element : str.split(",")) {
+ ret.add(elementParser.apply(element));
+ }
+
+ return ret;
+ }
+
+ private static <T extends Enum<T>> List<T> tryParseEnumList(Class<T> enumType, String str) {
+ if ("".equals(str)) return new ArrayList<>();
+
+ List<T> ret = new ArrayList<>();
+
+ for (String element : str.split(",")) {
+ ret.add(tryParseEnum(enumType, element));
+ }
+
+ return ret;
+ }
+
+ private static <T> String formatList(List<T> list) {
+ StringJoiner joiner = new StringJoiner(",");
+
+ for (T element : list) {
+ joiner.add(element == null ? "" : element.toString());
+ }
+
+ return joiner.toString();
+ }
+
+ private static <T extends Enum<T>> String formatEnumList(List<T> list, Function<T, String> elementFormatter) {
+ StringJoiner joiner = new StringJoiner(",");
+
+ for (T element : list) {
+ joiner.add(element == null ? "" : elementFormatter.apply(element));
+ }
+
+ return joiner.toString();
+ }
+
+ public static Optional<Integer> test_int() {
+ String value = SystemProperties.get("vendor.test_int");
+ return Optional.ofNullable(tryParseInteger(value));
+ }
+
+ public static Optional<String> test_string() {
+ String value = SystemProperties.get("vendor.test.string");
+ return Optional.ofNullable(tryParseString(value));
+ }
+
+ public static Optional<Boolean> test_BOOLeaN() {
+ String value = SystemProperties.get("ro.vendor.test.b");
+ return Optional.ofNullable(tryParseBoolean(value));
+ }
+
+ public static Optional<Long> vendor_os_test_long() {
+ String value = SystemProperties.get("vendor.vendor.os_test-long");
+ return Optional.ofNullable(tryParseLong(value));
+ }
+
+ public static List<Integer> test_list_int() {
+ String value = SystemProperties.get("vendor.test_list_int");
+ return tryParseList(v -> tryParseInteger(v), value);
+ }
+
+ @Deprecated
+ public static List<String> test_strlist() {
+ String value = SystemProperties.get("vendor.test.strlist");
+ return tryParseList(v -> tryParseString(v), value);
+ }
+}
+)";
+
+constexpr const char* kExpectedInternalOutput =
R"(// Generated by the sysprop generator. DO NOT EDIT!
package com.somecompany;
-import android.annotation.SystemApi;
import android.os.SystemProperties;
import java.util.ArrayList;
@@ -116,8 +259,6 @@ import java.util.Optional;
import java.util.StringJoiner;
import java.util.stream.Collectors;
-/** @hide */
-@SystemApi
public final class TestProperties {
private TestProperties () {}
@@ -214,13 +355,11 @@ public final class TestProperties {
return joiner.toString();
}
- /** @hide */
public static Optional<Double> 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 == null ? "" : value.toString());
}
@@ -230,7 +369,6 @@ public final class TestProperties {
return Optional.ofNullable(tryParseInteger(value));
}
- /** @hide */
public static void test_int(Integer value) {
SystemProperties.set("vendor.test_int", value == null ? "" : value.toString());
}
@@ -240,12 +378,10 @@ public final class TestProperties {
return Optional.ofNullable(tryParseString(value));
}
- /** @hide */
public static void test_string(String value) {
SystemProperties.set("vendor.test.string", value == null ? "" : value.toString());
}
- /** @hide */
public static enum test_enum_values {
A("a"),
B("b"),
@@ -263,13 +399,11 @@ public final class TestProperties {
}
}
- /** @hide */
public static Optional<test_enum_values> 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("vendor.test.enum", value == null ? "" : value.getPropValue());
}
@@ -279,7 +413,6 @@ public final class TestProperties {
return Optional.ofNullable(tryParseBoolean(value));
}
- /** @hide */
public static void test_BOOLeaN(Boolean value) {
SystemProperties.set("ro.vendor.test.b", value == null ? "" : value.toString());
}
@@ -289,18 +422,15 @@ public final class TestProperties {
return Optional.ofNullable(tryParseLong(value));
}
- /** @hide */
public static void vendor_os_test_long(Long value) {
SystemProperties.set("vendor.vendor.os_test-long", value == null ? "" : value.toString());
}
- /** @hide */
public static List<Double> test_double_list() {
String value = SystemProperties.get("vendor.test_double_list");
return tryParseList(v -> tryParseDouble(v), value);
}
- /** @hide */
public static void test_double_list(List<Double> value) {
SystemProperties.set("vendor.test_double_list", value == null ? "" : formatList(value));
}
@@ -310,7 +440,6 @@ public final class TestProperties {
return tryParseList(v -> tryParseInteger(v), value);
}
- /** @hide */
public static void test_list_int(List<Integer> value) {
SystemProperties.set("vendor.test_list_int", value == null ? "" : formatList(value));
}
@@ -321,13 +450,11 @@ public final class TestProperties {
return tryParseList(v -> tryParseString(v), value);
}
- /** @hide */
@Deprecated
public static void test_strlist(List<String> value) {
SystemProperties.set("vendor.test.strlist", value == null ? "" : formatList(value));
}
- /** @hide */
public static enum el_values {
ENU("enu"),
MVA("mva"),
@@ -341,14 +468,12 @@ public final class TestProperties {
}
}
- /** @hide */
@Deprecated
public static List<el_values> el() {
String value = SystemProperties.get("vendor.el");
return tryParseEnumList(el_values.class, value);
}
- /** @hide */
@Deprecated
public static void el(List<el_values> value) {
SystemProperties.set("vendor.el", value == null ? "" : formatEnumList(value, el_values::getPropValue));
@@ -371,17 +496,24 @@ TEST(SyspropTest, JavaGenTest) {
TemporaryDir temp_dir;
- ASSERT_TRUE(GenerateJavaLibrary(temp_file.path, temp_dir.path));
+ std::pair<sysprop::Scope, const char*> tests[] = {
+ {sysprop::Scope::Internal, kExpectedInternalOutput},
+ {sysprop::Scope::Public, kExpectedPublicOutput},
+ };
+
+ for (auto [scope, expected_output] : tests) {
+ ASSERT_TRUE(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_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, kExpectedJavaOutput);
+ 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());
+ unlink(java_output_path.c_str());
+ rmdir((temp_dir.path + "/com/somecompany"s).c_str());
+ rmdir((temp_dir.path + "/com"s).c_str());
+ }
}