Generate code for AIDL defined string constants
Also validate that constant names are not duplicated between
integer and string constants.
Bug: 28233277
Test: Unittests expanded to reflect this change.
Integration tests expanded to reflect this change.
Change-Id: If46619151cf6ff0146a2dfa90b863b096435a30a
diff --git a/aidl.cpp b/aidl.cpp
index d74c376..3602d1e 100644
--- a/aidl.cpp
+++ b/aidl.cpp
@@ -424,6 +424,32 @@
return 0;
}
+bool validate_constants(const AidlInterface& interface) {
+ bool success = true;
+ set<string> names;
+ for (const std::unique_ptr<AidlIntConstant>& int_constant :
+ interface.GetIntConstants()) {
+ if (names.count(int_constant->GetName()) > 0) {
+ LOG(ERROR) << "Found duplicate constant name '" << int_constant->GetName()
+ << "'";
+ success = false;
+ }
+ names.insert(int_constant->GetName());
+ }
+ for (const std::unique_ptr<AidlStringConstant>& string_constant :
+ interface.GetStringConstants()) {
+ if (names.count(string_constant->GetName()) > 0) {
+ LOG(ERROR) << "Found duplicate constant name '" << string_constant->GetName()
+ << "'";
+ success = false;
+ }
+ names.insert(string_constant->GetName());
+ // We've logged an error message for this on object construction.
+ success = success && string_constant->IsValid();
+ }
+ return success;
+}
+
// TODO: Remove this in favor of using the YACC parser b/25479378
bool ParsePreprocessedLine(const string& line, string* decl,
vector<string>* package, string* class_name) {
@@ -636,6 +662,9 @@
interface->GetMethods()) != 0) {
return AidlError::BAD_METHOD_ID;
}
+ if (!validate_constants(*interface)) {
+ return AidlError::BAD_CONSTANTS;
+ }
if (returned_interface)
*returned_interface = std::move(interface);
diff --git a/aidl.h b/aidl.h
index 29f0569..dffdf14 100644
--- a/aidl.h
+++ b/aidl.h
@@ -40,6 +40,7 @@
BAD_TYPE,
BAD_METHOD_ID,
GENERATION_ERROR,
+ BAD_CONSTANTS,
OK = 0,
};
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index 3375ed9..4677092 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -66,11 +66,12 @@
unique_ptr<AidlInterface> Parse(const string& path,
const string& contents,
- TypeNamespace* types) {
+ TypeNamespace* types,
+ AidlError* error = nullptr) {
io_delegate_.SetFileContents(path, contents);
unique_ptr<AidlInterface> ret;
std::vector<std::unique_ptr<AidlImport>> imports;
- ::android::aidl::internals::load_and_validate_aidl(
+ AidlError actual_error = ::android::aidl::internals::load_and_validate_aidl(
preprocessed_files_,
import_paths_,
path,
@@ -78,6 +79,9 @@
types,
&ret,
&imports);
+ if (error != nullptr) {
+ *error = actual_error;
+ }
return ret;
}
@@ -263,6 +267,21 @@
EXPECT_NE(0, ::android::aidl::compile_aidl_to_java(options, io_delegate_));
}
+TEST_F(AidlTest, FailOnDuplicateConstantNames) {
+ AidlError reported_error;
+ EXPECT_EQ(nullptr,
+ Parse("p/IFoo.aidl",
+ R"(package p;
+ interface IFoo {
+ const String DUPLICATED = "d";
+ const int DUPLICATED = 1;
+ }
+ )",
+ &cpp_types_,
+ &reported_error));
+ EXPECT_EQ(AidlError::BAD_CONSTANTS, reported_error);
+}
+
TEST_F(AidlTest, UnderstandsNativeParcelables) {
io_delegate_.SetFileContents(
"p/Bar.aidl",
diff --git a/ast_cpp.cpp b/ast_cpp.cpp
index 093ff14..783b0f3 100644
--- a/ast_cpp.cpp
+++ b/ast_cpp.cpp
@@ -182,12 +182,16 @@
is_const_(modifiers & IS_CONST),
is_virtual_(modifiers & IS_VIRTUAL),
is_override_(modifiers & IS_OVERRIDE),
- is_pure_virtual_(modifiers & IS_PURE_VIRTUAL) {}
+ is_pure_virtual_(modifiers & IS_PURE_VIRTUAL),
+ is_static_(modifiers & IS_STATIC) {}
void MethodDecl::Write(CodeWriter* to) const {
if (is_virtual_)
to->Write("virtual ");
+ if (is_static_)
+ to->Write("static ");
+
to->Write("%s %s", return_type_.c_str(), name_.c_str());
arguments_.Write(to);
diff --git a/ast_cpp.h b/ast_cpp.h
index 50a9a08..7849857 100644
--- a/ast_cpp.h
+++ b/ast_cpp.h
@@ -162,6 +162,7 @@
IS_VIRTUAL = 1 << 1,
IS_OVERRIDE = 1 << 2,
IS_PURE_VIRTUAL = 1 << 3,
+ IS_STATIC = 1 << 4,
};
MethodDecl(const std::string& return_type,
@@ -183,6 +184,7 @@
bool is_virtual_ = false;
bool is_override_ = false;
bool is_pure_virtual_ = false;
+ bool is_static_ = true;
DISALLOW_COPY_AND_ASSIGN(MethodDecl);
}; // class MethodDecl
diff --git a/ast_java.cpp b/ast_java.cpp
index 44e298d..1ba723c 100644
--- a/ast_java.cpp
+++ b/ast_java.cpp
@@ -417,11 +417,16 @@
}
}
-void Constant::Write(CodeWriter* to) const {
+void IntConstant::Write(CodeWriter* to) const {
WriteModifiers(to, STATIC | FINAL | PUBLIC, ALL_MODIFIERS);
to->Write("int %s = %d;\n", name.c_str(), value);
}
+void StringConstant::Write(CodeWriter* to) const {
+ WriteModifiers(to, STATIC | FINAL | PUBLIC, ALL_MODIFIERS);
+ to->Write("String %s = %s;\n", name.c_str(), value.c_str());
+}
+
void Class::Write(CodeWriter* to) const {
size_t N, i;
diff --git a/ast_java.h b/ast_java.h
index cd318d7..3ec48b7 100644
--- a/ast_java.h
+++ b/ast_java.h
@@ -326,12 +326,24 @@
void Write(CodeWriter* to) const override;
};
-struct Constant : public ClassElement {
- std::string name;
- int value;
+struct IntConstant : public ClassElement {
+ const std::string name;
+ const int value;
- Constant() = default;
- virtual ~Constant() = default;
+ IntConstant(std::string name, int value)
+ : name(name), value(value) {}
+ virtual ~IntConstant() = default;
+
+ void Write(CodeWriter* to) const override;
+};
+
+struct StringConstant : public ClassElement {
+ const std::string name;
+ const std::string value;
+
+ StringConstant(std::string name, std::string value)
+ : name(name), value(value) {}
+ ~StringConstant() override = default;
void Write(CodeWriter* to) const override;
};
diff --git a/generate_cpp.cpp b/generate_cpp.cpp
index 0ab136e..77badc4 100644
--- a/generate_cpp.cpp
+++ b/generate_cpp.cpp
@@ -60,6 +60,7 @@
const char kIInterfaceHeader[] = "binder/IInterface.h";
const char kParcelHeader[] = "binder/Parcel.h";
const char kStatusHeader[] = "binder/Status.h";
+const char kString16Header[] = "utils/String16.h";
const char kStrongPointerHeader[] = "utils/StrongPointer.h";
unique_ptr<AstNode> BreakOnStatusNotOk() {
@@ -572,14 +573,30 @@
fq_name = interface.GetPackage() + "." + fq_name;
}
+ vector<unique_ptr<Declaration>> decls;
+
unique_ptr<MacroDecl> meta_if{new MacroDecl{
"IMPLEMENT_META_INTERFACE",
ArgList{vector<string>{ClassName(interface, ClassNames::BASE),
'"' + fq_name + '"'}}}};
+ decls.push_back(std::move(meta_if));
+
+ for (const auto& constant: interface.GetStringConstants()) {
+ unique_ptr<MethodImpl> getter(new MethodImpl(
+ "const ::android::String16&",
+ ClassName(interface, ClassNames::INTERFACE),
+ constant->GetName(),
+ {}));
+ getter->GetStatementBlock()->AddLiteral(
+ StringPrintf("static const ::android::String16 value(%s)",
+ constant->GetValue().c_str()));
+ getter->GetStatementBlock()->AddLiteral("return value");
+ decls.push_back(std::move(getter));
+ }
return unique_ptr<Document>{new CppSource{
include_list,
- NestInNamespaces(std::move(meta_if), interface.GetSplitPackage())}};
+ NestInNamespaces(std::move(decls), interface.GetSplitPackage())}};
}
unique_ptr<Document> BuildClientHeader(const TypeNamespace& types,
@@ -685,6 +702,16 @@
if_class->AddPublic(std::move(constant_enum));
}
+ if (!interface.GetStringConstants().empty()) {
+ includes.insert(kString16Header);
+ }
+ for (const auto& constant : interface.GetStringConstants()) {
+ unique_ptr<MethodDecl> getter(new MethodDecl(
+ "const ::android::String16&", constant->GetName(),
+ {}, MethodDecl::IS_STATIC));
+ if_class->AddPublic(std::move(getter));
+ }
+
if (!interface.GetMethods().empty()) {
unique_ptr<Enum> call_enum{new Enum{"Call"}};
for (const auto& method : interface.GetMethods()) {
diff --git a/generate_java_binder.cpp b/generate_java_binder.cpp
index 9ec2a23..02a6922 100644
--- a/generate_java_binder.cpp
+++ b/generate_java_binder.cpp
@@ -245,12 +245,16 @@
t->CreateFromParcel(addTo, v, parcel, cl);
}
-static void generate_constant(const AidlIntConstant& constant,
- Class* interface) {
- Constant* decl = new Constant;
- decl->name = constant.GetName();
- decl->value = constant.GetValue();
+static void generate_int_constant(const AidlIntConstant& constant,
+ Class* interface) {
+ IntConstant* decl = new IntConstant(constant.GetName(), constant.GetValue());
+ interface->elements.push_back(decl);
+}
+static void generate_string_constant(const AidlStringConstant& constant,
+ Class* interface) {
+ StringConstant* decl = new StringConstant(constant.GetName(),
+ constant.GetValue());
interface->elements.push_back(decl);
}
@@ -529,7 +533,10 @@
// all the declared constants of the interface
for (const auto& item : iface->GetIntConstants()) {
- generate_constant(*item, interface);
+ generate_int_constant(*item, interface);
+ }
+ for (const auto& item : iface->GetStringConstants()) {
+ generate_string_constant(*item, interface);
}
// all the declared methods of the interface
diff --git a/tests/aidl_test_client_primitives.cpp b/tests/aidl_test_client_primitives.cpp
index 405538e..9142abd 100644
--- a/tests/aidl_test_client_primitives.cpp
+++ b/tests/aidl_test_client_primitives.cpp
@@ -86,6 +86,8 @@
// U+10437: The 'small letter yee' character in the deseret alphabet
// U+20AC: A euro sign
String16("\xD8\x01\xDC\x37\x20\xAC"),
+ ITestService::STRING_TEST_CONSTANT(),
+ ITestService::STRING_TEST_CONSTANT2(),
};
for (const auto& input : inputs) {
String16 reply;
diff --git a/tests/android/aidl/tests/ITestService.aidl b/tests/android/aidl/tests/ITestService.aidl
index 30887d4..b8a3ab7 100644
--- a/tests/android/aidl/tests/ITestService.aidl
+++ b/tests/android/aidl/tests/ITestService.aidl
@@ -31,6 +31,9 @@
const int TEST_CONSTANT7 = +0;
const int TEST_CONSTANT8 = 0;
+ const String STRING_TEST_CONSTANT = "foo";
+ const String STRING_TEST_CONSTANT2 = "bar";
+
// Test that primitives work as parameters and return types.
boolean RepeatBoolean(boolean token);
byte RepeatByte(byte token);
diff --git a/tests/java_app/src/android/aidl/tests/TestServiceClient.java b/tests/java_app/src/android/aidl/tests/TestServiceClient.java
index 04b76f6..4af4aa0 100644
--- a/tests/java_app/src/android/aidl/tests/TestServiceClient.java
+++ b/tests/java_app/src/android/aidl/tests/TestServiceClient.java
@@ -154,7 +154,12 @@
" responded " + response);
}
}
- for (String query : Arrays.asList("not empty", "", "\0")) {
+
+ List<String> queries = Arrays.asList(
+ "not empty", "", "\0",
+ ITestService.STRING_TEST_CONSTANT,
+ ITestService.STRING_TEST_CONSTANT2);
+ for (String query : queries) {
String response = service.RepeatString(query);
if (!query.equals(response)) {
mLog.logAndThrow("Repeat request with '" + query + "'" +
diff --git a/tests/test_data_string_constants.cpp b/tests/test_data_string_constants.cpp
index 40c6547..4674843 100644
--- a/tests/test_data_string_constants.cpp
+++ b/tests/test_data_string_constants.cpp
@@ -96,6 +96,7 @@
}
}
}
+public static final String EXAMPLE_CONSTANT = "foo";
}
)";
@@ -109,6 +110,7 @@
#include <binder/IBinder.h>
#include <binder/IInterface.h>
#include <binder/Status.h>
+#include <utils/String16.h>
#include <utils/StrongPointer.h>
namespace android {
@@ -118,6 +120,7 @@
class IStringConstants : public ::android::IInterface {
public:
DECLARE_META_INTERFACE(StringConstants)
+static const ::android::String16& EXAMPLE_CONSTANT();
}; // class IStringConstants
} // namespace os
@@ -137,6 +140,11 @@
IMPLEMENT_META_INTERFACE(StringConstants, "android.os.IStringConstants")
+const ::android::String16& IStringConstants::EXAMPLE_CONSTANT() {
+static const ::android::String16 value("foo");
+return value;
+}
+
} // namespace os
} // namespace android