Snap for 6198741 from 6d02280c6bc1b17a1d5d51f33eceae2bf9d872b5 to sdk-release
Change-Id: I9bc711c0a5b415185389cdb9429534eb74233f08
diff --git a/Android.bp b/Android.bp
index fa5d485..0c2b367 100644
--- a/Android.bp
+++ b/Android.bp
@@ -47,7 +47,7 @@
srcs: [
"aidl.cpp",
- "aidl_apicheck.cpp",
+ "aidl_checkapi.cpp",
"aidl_const_expressions.cpp",
"aidl_language.cpp",
"aidl_language_l.ll",
@@ -84,17 +84,20 @@
static_libs: [
"libaidl-common",
"libbase",
+ "liblog",
],
}
-// aidl-cpp executable
+// aidl-cpp legacy executable, please use 'aidl' instead
cc_binary_host {
name: "aidl-cpp",
defaults: ["aidl_defaults"],
- srcs: ["main_cpp.cpp"],
+ srcs: ["main.cpp"],
+ cflags: ["-DAIDL_CPP_BUILD"],
static_libs: [
"libaidl-common",
"libbase",
+ "liblog",
],
}
@@ -110,7 +113,6 @@
"-Wextra",
"-Werror",
"-g",
- "-DUNIT_TEST",
],
srcs: [
@@ -134,18 +136,40 @@
"libaidl-common",
"libbase",
"libcutils",
+ "libgmock",
+ "liblog",
],
- target: {
- host: {
- static_libs: ["libgmock_host"],
- },
- android: {
- static_libs: [
- "libgmock",
- "liblog",
- ],
- },
+}
+
+cc_fuzz {
+ name: "aidl_parser_fuzzer",
+ host_supported: true,
+ dictionary: "tests/aidl_parser_fuzzer.dict",
+ corpus: [
+ "tests/corpus/*",
+ ],
+
+ fuzz_config: {
+ cc: [
+ "smoreland@google.com",
+ "jiyong@google.com",
+ "jeongik@google.com",
+ ],
},
+
+ srcs: [
+ "tests/aidl_parser_fuzzer.cpp",
+ "tests/fake_io_delegate.cpp",
+ "tests/test_util.cpp",
+ ],
+ static_libs: [
+ "libaidl-common",
+ "libbase",
+ "libcutils",
+ "liblog",
+ ],
+ // Enable this to show additional information about what is being parsed during fuzzing.
+ // cflags: ["-DFUZZ_LOG"],
}
//
@@ -237,11 +261,16 @@
resource_dirs: ["tests/java_app/resources"],
srcs: [
"tests/android/aidl/tests/*.aidl",
+ "tests/android/aidl/tests/generic/*.aidl",
+ "tests/android/aidl/tests/map/*.aidl",
+ "tests/java_app/src/android/aidl/tests/GenericTests.java",
+ "tests/java_app/src/android/aidl/tests/MapTests.java",
"tests/java_app/src/android/aidl/tests/NullableTests.java",
"tests/java_app/src/android/aidl/tests/SimpleParcelable.java",
"tests/java_app/src/android/aidl/tests/TestFailException.java",
"tests/java_app/src/android/aidl/tests/TestLogger.java",
"tests/java_app/src/android/aidl/tests/TestServiceClient.java",
+ "tests/java_app/src/android/aidl/tests/generic/Pair.java",
],
aidl: {
include_dirs: [
@@ -262,19 +291,6 @@
gen_log: true,
},
ndk: {
- enabled: false,
- },
- },
-}
-
-aidl_interface {
- name: "aidl_test_loggable_interface_ndk",
- local_include_dir: "tests",
- srcs: [
- "tests/android/aidl/loggable/ILoggableInterfaceNdk.aidl",
- ],
- backend: {
- ndk: {
gen_log: true,
},
},
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 26cbca8..a54f480 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -5,6 +5,9 @@
},
{
"name": "CtsNdkBinderTestCases"
+ },
+ {
+ "name": "aidl_lazy_test"
}
]
}
diff --git a/aidl.cpp b/aidl.cpp
index 90b6e45..083d520 100644
--- a/aidl.cpp
+++ b/aidl.cpp
@@ -75,6 +75,7 @@
// are auto-implemented by the AIDL compiler.
const int kFirstMetaMethodId = kLastCallTransaction - kFirstCallTransaction;
const int kGetInterfaceVersionId = kFirstMetaMethodId;
+const int kGetInterfaceHashId = kFirstMetaMethodId - 1;
// Additional meta transactions implemented by AIDL should use
// kFirstMetaMethodId -1, -2, ...and so on.
@@ -357,7 +358,7 @@
}
string line;
- unsigned lineno = 1;
+ int lineno = 1;
for ( ; line_reader->ReadLine(&line); ++lineno) {
if (line.empty() || line.compare(0, 2, "//") == 0) {
// skip comments and empty lines
@@ -449,10 +450,10 @@
ImportResolver import_resolver{io_delegate, input_file_name, options.ImportDirs(),
options.InputFiles()};
- set<string> type_from_import_statements;
+ vector<string> type_from_import_statements;
for (const auto& import : main_parser->GetImports()) {
if (!AidlTypenames::IsBuiltinTypename(import->GetNeededClass())) {
- type_from_import_statements.emplace(import->GetNeededClass());
+ type_from_import_statements.emplace_back(import->GetNeededClass());
}
}
@@ -467,8 +468,9 @@
unresolved_types.emplace(type->GetName());
}
}
- set<string> import_candidates(type_from_import_statements);
- import_candidates.insert(unresolved_types.begin(), unresolved_types.end());
+ vector<string> import_candidates(type_from_import_statements);
+ import_candidates.insert(import_candidates.end(), unresolved_types.begin(),
+ unresolved_types.end());
for (const auto& import : import_candidates) {
if (typenames->IsIgnorableImport(import)) {
// There are places in the Android tree where an import doesn't resolve,
@@ -478,7 +480,16 @@
}
string import_path = import_resolver.FindImportFile(import);
if (import_path.empty()) {
- if (type_from_import_statements.find(import) != type_from_import_statements.end()) {
+ if (typenames->ResolveTypename(import).second) {
+ // Couldn't find the *.aidl file for the type from the include paths, but we
+ // have the type already resolved. This could happen when the type is
+ // from the preprocessed aidl file. In that case, use the type from the
+ // preprocessed aidl file as a last resort.
+ continue;
+ }
+
+ if (std::find(type_from_import_statements.begin(), type_from_import_statements.end(),
+ import) != type_from_import_statements.end()) {
// Complain only when the import from the import statement has failed.
AIDL_ERROR(import) << "couldn't find import for class " << import;
err = AidlError::BAD_IMPORT;
@@ -538,11 +549,14 @@
enum_decl->SetBackingType(std::move(byte_type));
}
- // TODO(b/139877950): Support autofilling enumerators, and ensure that
- // autofilling does not cause any enumerators to have a value larger than
- // allowed by the backing type.
+ if (!enum_decl->Autofill()) {
+ err = AidlError::BAD_TYPE;
+ }
}
});
+ if (err != AidlError::OK) {
+ return err;
+ }
//////////////////////////////////////////////////////////////////////////
// Validation phase
@@ -568,7 +582,7 @@
if (!unstructuredParcelable->CheckValid(*typenames)) {
return AidlError::BAD_TYPE;
}
- bool isStable = unstructuredParcelable->IsStableParcelable();
+ bool isStable = unstructuredParcelable->IsStableApiParcelable(options.TargetLanguage());
if (options.IsStructured() && !isStable) {
AIDL_ERROR(unstructuredParcelable)
<< "Cannot declared parcelable in a --structured interface. Parcelable must be defined "
@@ -626,6 +640,16 @@
kGetInterfaceVersionId, false /* is_user_defined */);
interface->GetMutableMethods().emplace_back(method);
}
+ // add the meta-method 'string getInterfaceHash()' if hash is specified.
+ if (!options.Hash().empty()) {
+ AidlTypeSpecifier* ret =
+ new AidlTypeSpecifier(AIDL_LOCATION_HERE, "String", false, nullptr, "");
+ ret->Resolve(*typenames);
+ vector<unique_ptr<AidlArgument>>* args = new vector<unique_ptr<AidlArgument>>();
+ AidlMethod* method = new AidlMethod(AIDL_LOCATION_HERE, false, ret, kGetInterfaceHash, args,
+ "", kGetInterfaceHashId, false /* is_user_defined */);
+ interface->GetMutableMethods().emplace_back(method);
+ }
if (!check_and_assign_method_ids(interface->GetMethods())) {
return AidlError::BAD_METHOD_ID;
}
@@ -660,7 +684,7 @@
typenames->IterateTypes([&](const AidlDefinedType& type) {
if (options.IsStructured() && type.AsUnstructuredParcelable() != nullptr &&
- !type.AsUnstructuredParcelable()->IsStableParcelable()) {
+ !type.AsUnstructuredParcelable()->IsStableApiParcelable(options.TargetLanguage())) {
err = AidlError::NOT_STRUCTURED;
LOG(ERROR) << type.GetCanonicalName()
<< " is not structured, but this is a structured interface.";
@@ -734,8 +758,13 @@
ndk::GenerateNdk(output_file_name, options, typenames, *defined_type, io_delegate);
success = true;
} else if (lang == Options::Language::JAVA) {
- success =
- java::generate_java(output_file_name, defined_type, typenames, io_delegate, options);
+ if (defined_type->AsUnstructuredParcelable() != nullptr) {
+ // Legacy behavior. For parcelable declarations in Java, don't generate output file.
+ success = true;
+ } else {
+ success =
+ java::generate_java(output_file_name, defined_type, typenames, io_delegate, options);
+ }
} else {
LOG(FATAL) << "Should not reach here" << endl;
return 1;
@@ -813,7 +842,7 @@
if (!type->GetPackage().empty()) {
(*writer) << "package " << type->GetPackage() << ";\n";
}
- type->Write(writer.get());
+ type->Dump(writer.get());
}
} else {
return false;
diff --git a/aidl.h b/aidl.h
index 955b280..94eaf54 100644
--- a/aidl.h
+++ b/aidl.h
@@ -51,6 +51,7 @@
bool dump_mappings(const Options& options, const IoDelegate& io_delegate);
const string kGetInterfaceVersion("getInterfaceVersion");
+const string kGetInterfaceHash("getInterfaceHash");
namespace internals {
diff --git a/aidl_apicheck.cpp b/aidl_checkapi.cpp
similarity index 100%
rename from aidl_apicheck.cpp
rename to aidl_checkapi.cpp
diff --git a/aidl_apicheck.h b/aidl_checkapi.h
similarity index 100%
rename from aidl_apicheck.h
rename to aidl_checkapi.h
diff --git a/aidl_const_expressions.cpp b/aidl_const_expressions.cpp
index b5450a7..6194d7d 100644
--- a/aidl_const_expressions.cpp
+++ b/aidl_const_expressions.cpp
@@ -15,6 +15,7 @@
*/
#include "aidl_language.h"
+#include "aidl_typenames.h"
#include "logging.h"
#include <stdlib.h>
@@ -195,11 +196,12 @@
}
AidlConstantValue* AidlConstantValue::Character(const AidlLocation& location, char value) {
+ const std::string explicit_value = string("'") + value + "'";
if (!isValidLiteralChar(value)) {
AIDL_ERROR(location) << "Invalid character literal " << value;
- return new AidlConstantValue(location, Type::ERROR, "");
+ return new AidlConstantValue(location, Type::ERROR, explicit_value);
}
- return new AidlConstantValue(location, Type::CHARACTER, string("'") + value + "'");
+ return new AidlConstantValue(location, Type::CHARACTER, explicit_value);
}
AidlConstantValue* AidlConstantValue::Floating(const AidlLocation& location,
@@ -299,13 +301,33 @@
if (!isValidLiteralChar(value[i])) {
AIDL_ERROR(location) << "Found invalid character at index " << i << " in string constant '"
<< value << "'";
- return new AidlConstantValue(location, Type::ERROR, "");
+ return new AidlConstantValue(location, Type::ERROR, value);
}
}
return new AidlConstantValue(location, Type::STRING, value);
}
+AidlConstantValue* AidlConstantValue::ShallowIntegralCopy(const AidlConstantValue& other) {
+ // TODO(b/141313220) Perform a full copy instead of parsing+unparsing
+ AidlTypeSpecifier type = AidlTypeSpecifier(AIDL_LOCATION_HERE, "long", false, nullptr, "");
+ // TODO(b/142722772) CheckValid() should be called before ValueString()
+ if (!other.CheckValid() || !other.evaluate(type)) {
+ AIDL_ERROR(other) << "Failed to parse expression as integer: " << other.value_;
+ return nullptr;
+ }
+ const std::string& value = other.ValueString(type, AidlConstantValueDecorator);
+ if (value.empty()) {
+ return nullptr; // error already logged
+ }
+
+ AidlConstantValue* result = Integral(AIDL_LOCATION_HERE, value);
+ if (result == nullptr) {
+ AIDL_FATAL(other) << "Unable to perform ShallowIntegralCopy.";
+ }
+ return result;
+}
+
string AidlConstantValue::ValueString(const AidlTypeSpecifier& type,
const ConstantValueDecorator& decorator) const {
if (type.IsGeneric()) {
@@ -443,6 +465,8 @@
case Type::BINARY:
is_valid_ = true;
break;
+ case Type::ERROR:
+ return false;
default:
AIDL_FATAL(this) << "Unrecognized constant value type: " << ToString(type_);
return false;
@@ -529,27 +553,29 @@
string AidlConstantValue::ToString(Type type) {
switch (type) {
- case Type::ARRAY:
- return "a literal array";
case Type::BOOLEAN:
return "a literal boolean";
- case Type::CHARACTER:
- return "a literal char";
case Type::INT8:
return "an int8 literal";
case Type::INT32:
return "an int32 literal";
case Type::INT64:
return "an int64 literal";
+ case Type::ARRAY:
+ return "a literal array";
+ case Type::CHARACTER:
+ return "a literal char";
case Type::STRING:
return "a literal string";
- case Type::ERROR:
- LOG(FATAL) << "aidl internal error: error type failed to halt program";
- return "";
+ case Type::FLOATING:
+ return "a literal float";
case Type::UNARY:
return "a unary expression";
case Type::BINARY:
return "a binary expression";
+ case Type::ERROR:
+ LOG(FATAL) << "aidl internal error: error type failed to halt program";
+ return "";
default:
LOG(FATAL) << "aidl internal error: unknown constant type: " << static_cast<int>(type);
return ""; // not reached
@@ -566,7 +592,7 @@
return false;
}
- return true;
+ return AidlConstantValue::CheckValid();
}
bool AidlUnaryConstExpression::evaluate(const AidlTypeSpecifier& type) const {
@@ -606,29 +632,6 @@
is_valid_ = false; return false;)
}
-string AidlUnaryConstExpression::ValueString(const AidlTypeSpecifier& type,
- const ConstantValueDecorator& decorator) const {
- if (type.IsGeneric()) {
- AIDL_ERROR(type) << "Generic type cannot be specified with a constant literal.";
- return "";
- }
- if (!is_evaluated_) {
- // TODO(b/142722772) CheckValid() should be called before ValueString()
- bool success = CheckValid();
- success &= evaluate(type);
- if (!success) {
- // A detailed error message shall be printed in evaluate()
- return "";
- }
- }
- if (!is_valid_) {
- AIDL_ERROR(this) << "Invalid constant unary expression";
- return "";
- }
-
- return AidlConstantValue::ValueString(type, decorator);
-}
-
bool AidlBinaryConstExpression::CheckValid() const {
bool success = false;
if (is_evaluated_) return is_valid_;
@@ -653,7 +656,7 @@
}
is_valid_ = true;
- return true;
+ return AidlConstantValue::CheckValid();
}
bool AidlBinaryConstExpression::evaluate(const AidlTypeSpecifier& type) const {
@@ -721,10 +724,10 @@
// CASE: + - * / % | ^ & < > <= >= == !=
if (isArithmeticOrBitflip || OP_IS_BIN_COMP) {
- if (op_ == "/" && right_val_->final_value_ == 0) {
+ if ((op_ == "/" || op_ == "%") && right_val_->final_value_ == 0) {
final_type_ = Type::ERROR;
is_valid_ = false;
- AIDL_ERROR(this) << "Divide by 0! const_expr: " + value_;
+ AIDL_ERROR(this) << "Cannot do division operation with zero for expression: " + value_;
return false;
}
@@ -752,7 +755,7 @@
// instead of promoting rval, simply casting it to int64 should also be good.
int64_t numBits = right_val_->cast<int64_t>();
if (numBits < 0) {
- // shifting with negative number of bits is undefined in C. In HIDL it
+ // shifting with negative number of bits is undefined in C. In AIDL it
// is defined as shifting into the other direction.
newOp = OPEQ("<<") ? ">>" : "<<";
numBits = -numBits;
@@ -779,36 +782,11 @@
return false;
}
-string AidlBinaryConstExpression::ValueString(const AidlTypeSpecifier& type,
- const ConstantValueDecorator& decorator) const {
- if (type.IsGeneric()) {
- AIDL_ERROR(type) << "Generic type cannot be specified with a constant literal.";
- return "";
- }
- if (!is_evaluated_) {
- // TODO(b/142722772) CheckValid() should be called before ValueString()
- bool success = CheckValid();
- success &= evaluate(type);
- if (!success) {
- AIDL_ERROR(this) << "Invalid constant binary expression";
- return "";
- }
- }
- if (!is_valid_) {
- AIDL_ERROR(this) << "Invalid constant binary expression";
- return "";
- }
-
- return AidlConstantValue::ValueString(type, decorator);
-}
-
AidlConstantValue::AidlConstantValue(const AidlLocation& location, Type parsed_type,
int64_t parsed_value, const string& checked_value)
: AidlNode(location),
type_(parsed_type),
value_(checked_value),
- is_valid_(true),
- is_evaluated_(true),
final_type_(parsed_type),
final_value_(parsed_value) {
CHECK(!value_.empty() || type_ == Type::ERROR);
@@ -820,8 +798,6 @@
: AidlNode(location),
type_(type),
value_(checked_value),
- is_valid_(false),
- is_evaluated_(false),
final_type_(type) {
CHECK(!value_.empty() || type_ == Type::ERROR);
switch (type_) {
diff --git a/aidl_language.cpp b/aidl_language.cpp
index 336f97d..c591c45 100644
--- a/aidl_language.cpp
+++ b/aidl_language.cpp
@@ -31,7 +31,7 @@
#include <android-base/parseint.h>
#include <android-base/strings.h>
-#include "aidl_language_y.h"
+#include "aidl_language_y-module.h"
#include "logging.h"
#include "aidl.h"
@@ -55,7 +55,7 @@
using std::vector;
namespace {
-bool is_java_keyword(const char* str) {
+bool IsJavaKeyword(const char* str) {
static const std::vector<std::string> kJavaKeywords{
"abstract", "assert", "boolean", "break", "byte", "case", "catch",
"char", "class", "const", "continue", "default", "do", "double",
@@ -68,6 +68,14 @@
};
return std::find(kJavaKeywords.begin(), kJavaKeywords.end(), str) != kJavaKeywords.end();
}
+
+void AddHideComment(CodeWriter* writer) {
+ writer->Write("/* @hide */\n");
+}
+
+inline bool HasHideComment(const std::string& comment) {
+ return std::regex_search(comment, std::regex("@hide\\b"));
+}
} // namespace
void yylex_init(void **);
@@ -95,22 +103,33 @@
AidlNode::AidlNode(const AidlLocation& location) : location_(location) {}
-std::string AidlNode::PrintLocation() const {
+std::string AidlNode::PrintLine() const {
std::stringstream ss;
ss << location_.file_ << ":" << location_.begin_.line;
return ss.str();
}
+std::string AidlNode::PrintLocation() const {
+ std::stringstream ss;
+ ss << location_.file_ << ":" << location_.begin_.line << ":" << location_.begin_.column << ":"
+ << location_.end_.line << ":" << location_.end_.column;
+ return ss.str();
+}
+
AidlError::AidlError(bool fatal) : os_(std::cerr), fatal_(fatal) {
+ sHadError = true;
+
os_ << "ERROR: ";
}
+bool AidlError::sHadError = false;
+
static const string kNullable("nullable");
static const string kUtf8InCpp("utf8InCpp");
static const string kVintfStability("VintfStability");
static const string kUnsupportedAppUsage("UnsupportedAppUsage");
static const string kSystemApi("SystemApi");
-static const string kStableParcelable("JavaOnlyStableParcelable");
+static const string kJavaStableParcelable("JavaOnlyStableParcelable");
static const string kBacking("Backing");
static const std::map<string, std::map<std::string, std::string>> kAnnotationParameters{
@@ -124,7 +143,7 @@
{"publicAlternatives", "String"},
{"trackingBug", "long"}}},
{kSystemApi, {}},
- {kStableParcelable, {}},
+ {kJavaStableParcelable, {}},
{kBacking, {{"type", "String"}}}};
AidlAnnotation* AidlAnnotation::Parse(
@@ -285,8 +304,8 @@
return HasAnnotation(annotations_, kSystemApi);
}
-bool AidlAnnotatable::IsStableParcelable() const {
- return HasAnnotation(annotations_, kStableParcelable);
+bool AidlAnnotatable::IsStableApiParcelable(Options::Language lang) const {
+ return HasAnnotation(annotations_, kJavaStableParcelable) && lang == Options::Language::JAVA;
}
bool AidlAnnotatable::CheckValidAnnotations() const {
@@ -313,18 +332,24 @@
vector<unique_ptr<AidlTypeSpecifier>>* type_params,
const string& comments)
: AidlAnnotatable(location),
+ AidlParameterizable<unique_ptr<AidlTypeSpecifier>>(type_params),
unresolved_name_(unresolved_name),
is_array_(is_array),
- type_params_(type_params),
comments_(comments),
split_name_(Split(unresolved_name, ".")) {}
AidlTypeSpecifier AidlTypeSpecifier::ArrayBase() const {
AIDL_FATAL_IF(!is_array_, this);
+ // Declaring array of generic type cannot happen, it is grammar error.
+ AIDL_FATAL_IF(IsGeneric(), this);
- AidlTypeSpecifier arrayBase = *this;
- arrayBase.is_array_ = false;
- return arrayBase;
+ AidlTypeSpecifier array_base = *this;
+ array_base.is_array_ = false;
+ return array_base;
+}
+
+bool AidlTypeSpecifier::IsHidden() const {
+ return HasHideComment(GetComments());
}
string AidlTypeSpecifier::ToString() const {
@@ -367,22 +392,64 @@
}
if (IsGeneric()) {
const string& type_name = GetName();
- const int num = GetTypeParameters().size();
+
+ auto& types = GetTypeParameters();
+ // TODO(b/136048684) Disallow to use primitive types only if it is List or Map.
+ if (type_name == "List" || type_name == "Map") {
+ if (std::any_of(types.begin(), types.end(), [](auto& type_ptr) {
+ return AidlTypenames::IsPrimitiveTypename(type_ptr->GetName());
+ })) {
+ AIDL_ERROR(this) << "A generic type cannot has any primitive type parameters.";
+ return false;
+ }
+ }
+ const auto defined_type = typenames.TryGetDefinedType(type_name);
+ const auto parameterizable =
+ defined_type != nullptr ? defined_type->AsParameterizable() : nullptr;
+ const bool is_user_defined_generic_type =
+ parameterizable != nullptr && parameterizable->IsGeneric();
+ const size_t num_params = GetTypeParameters().size();
if (type_name == "List") {
- if (num > 1) {
+ if (num_params > 1) {
AIDL_ERROR(this) << " List cannot have type parameters more than one, but got "
<< "'" << ToString() << "'";
return false;
}
} else if (type_name == "Map") {
- if (num != 0 && num != 2) {
+ if (num_params != 0 && num_params != 2) {
AIDL_ERROR(this) << "Map must have 0 or 2 type parameters, but got "
<< "'" << ToString() << "'";
return false;
}
+ if (num_params == 2) {
+ const string& key_type = GetTypeParameters()[0]->GetName();
+ if (key_type != "String") {
+ AIDL_ERROR(this) << "The type of key in map must be String, but it is "
+ << "'" << key_type << "'";
+ return false;
+ }
+ }
+ } else if (is_user_defined_generic_type) {
+ const size_t allowed = parameterizable->GetTypeParameters().size();
+ if (num_params != allowed) {
+ AIDL_ERROR(this) << type_name << " must have " << allowed << " type parameters, but got "
+ << num_params;
+ return false;
+ }
+ } else {
+ AIDL_ERROR(this) << type_name << " is not a generic type.";
+ return false;
}
}
+ const bool is_generic_string_list = GetName() == "List" && IsGeneric() &&
+ GetTypeParameters().size() == 1 &&
+ GetTypeParameters()[0]->GetName() == "String";
+ if (IsUtf8InCpp() && (GetName() != "String" && !is_generic_string_list)) {
+ AIDL_ERROR(this) << "@utf8InCpp can only be used on String, String[], and List<String>.";
+ return false;
+ }
+
if (GetName() == "void") {
if (IsArray() || IsNullable() || IsUtf8InCpp()) {
AIDL_ERROR(this) << "void type cannot be an array or nullable or utf8 string";
@@ -391,8 +458,8 @@
}
if (IsArray()) {
- const auto definedType = typenames.TryGetDefinedType(GetName());
- if (definedType != nullptr && definedType->AsInterface() != nullptr) {
+ const auto defined_type = typenames.TryGetDefinedType(GetName());
+ if (defined_type != nullptr && defined_type->AsInterface() != nullptr) {
AIDL_ERROR(this) << "Binder type cannot be an array";
return false;
}
@@ -403,8 +470,8 @@
AIDL_ERROR(this) << "Primitive type cannot get nullable annotation";
return false;
}
- const auto definedType = typenames.TryGetDefinedType(GetName());
- if (definedType != nullptr && definedType->AsEnumDeclaration() != nullptr && !IsArray()) {
+ const auto defined_type = typenames.TryGetDefinedType(GetName());
+ if (defined_type != nullptr && defined_type->AsEnumDeclaration() != nullptr && !IsArray()) {
AIDL_ERROR(this) << "Enum type cannot get nullable annotation";
return false;
}
@@ -430,6 +497,12 @@
bool valid = true;
valid &= type_->CheckValid(typenames);
+ if (type_->GetName() == "void") {
+ AIDL_ERROR(this) << "Declaration " << name_
+ << " is void, but declarations cannot be of void type.";
+ valid = false;
+ }
+
if (default_value_ == nullptr) return valid;
valid &= default_value_->CheckValid();
@@ -559,6 +632,9 @@
}
}
+bool AidlMethod::IsHidden() const {
+ return HasHideComment(GetComments());
+}
string AidlMethod::Signature() const {
vector<string> arg_signatures;
@@ -590,6 +666,10 @@
return Join(package_, '.');
}
+bool AidlDefinedType::IsHidden() const {
+ return HasHideComment(GetComments());
+}
+
std::string AidlDefinedType::GetCanonicalName() const {
if (package_.empty()) {
return GetName();
@@ -599,8 +679,9 @@
AidlParcelable::AidlParcelable(const AidlLocation& location, AidlQualifiedName* name,
const std::vector<std::string>& package, const std::string& comments,
- const std::string& cpp_header)
+ const std::string& cpp_header, std::vector<std::string>* type_params)
: AidlDefinedType(location, name->GetDotName(), comments, package),
+ AidlParameterizable<std::string>(type_params),
name_(name),
cpp_header_(cpp_header) {
// Strip off quotation marks if we actually have a cpp header.
@@ -608,12 +689,40 @@
cpp_header_ = cpp_header_.substr(1, cpp_header_.length() - 2);
}
}
+template <typename T>
+AidlParameterizable<T>::AidlParameterizable(const AidlParameterizable& other) {
+ // Copying is not supported if it has type parameters.
+ // It doesn't make a problem because only ArrayBase() makes a copy,
+ // and it can be called only if a type is not generic.
+ CHECK(!other.IsGeneric());
+}
+
+template <typename T>
+bool AidlParameterizable<T>::CheckValid() const {
+ return true;
+};
+
+template <>
+bool AidlParameterizable<std::string>::CheckValid() const {
+ if (!IsGeneric()) {
+ return true;
+ }
+ std::unordered_set<std::string> set(GetTypeParameters().begin(), GetTypeParameters().end());
+ if (set.size() != GetTypeParameters().size()) {
+ AIDL_ERROR(this->AsAidlNode()) << "Every type parameter should be unique.";
+ return false;
+ }
+ return true;
+}
bool AidlParcelable::CheckValid(const AidlTypenames&) const {
- static const std::set<string> allowed{kStableParcelable};
+ static const std::set<string> allowed{kJavaStableParcelable};
if (!CheckValidAnnotations()) {
return false;
}
+ if (!AidlParameterizable<std::string>::CheckValid()) {
+ return false;
+ }
for (const auto& v : GetAnnotations()) {
if (allowed.find(v.GetName()) == allowed.end()) {
std::ostringstream stream;
@@ -630,7 +739,7 @@
return true;
}
-void AidlParcelable::Write(CodeWriter* writer) const {
+void AidlParcelable::Dump(CodeWriter* writer) const {
writer->Write("parcelable %s ;\n", GetName().c_str());
}
@@ -640,10 +749,16 @@
: AidlParcelable(location, name, package, comments, "" /*cpp_header*/),
variables_(std::move(*variables)) {}
-void AidlStructuredParcelable::Write(CodeWriter* writer) const {
+void AidlStructuredParcelable::Dump(CodeWriter* writer) const {
+ if (this->IsHidden()) {
+ AddHideComment(writer);
+ }
writer->Write("parcelable %s {\n", GetName().c_str());
writer->Indent();
for (const auto& field : GetFields()) {
+ if (field->GetType().IsHidden()) {
+ AddHideComment(writer);
+ }
writer->Write("%s;\n", field->ToString().c_str());
}
writer->Dedent();
@@ -660,12 +775,16 @@
// TODO: we should treat every backend all the same in future.
bool AidlTypeSpecifier::LanguageSpecificCheckValid(Options::Language lang) const {
- if (lang == Options::Language::CPP) {
+ if (lang != Options::Language::JAVA) {
if (this->GetName() == "List" && !this->IsGeneric()) {
- AIDL_ERROR(this) << "List without type isn't supported in cpp.";
+ AIDL_ERROR(this) << "Currently, only the Java backend supports non-generic List.";
return false;
}
}
+ if (this->GetName() == "FileDescriptor" && lang == Options::Language::NDK) {
+ AIDL_ERROR(this) << "FileDescriptor isn't supported with the NDK.";
+ return false;
+ }
if (this->IsGeneric()) {
if (this->GetName() == "List") {
if (this->GetTypeParameters().size() != 1) {
@@ -678,32 +797,45 @@
AIDL_ERROR(this) << "List in cpp supports only string and IBinder for now.";
return false;
}
- } else if (lang == Options::Language::NDK) {
- AIDL_ERROR(this) << "NDK backend does not support List yet.";
- return false;
+ } else if (lang == Options::Language::JAVA) {
+ const string& contained_type = this->GetTypeParameters()[0]->GetName();
+ if (AidlTypenames::IsBuiltinTypename(contained_type)) {
+ if (contained_type != "String" && contained_type != "IBinder" &&
+ contained_type != "ParcelFileDescriptor") {
+ AIDL_ERROR(this) << "List<" << contained_type << "> isn't supported in Java";
+ return false;
+ }
+ }
}
-
- } else if (this->GetName() == "Map") {
- if (lang != Options::Language::JAVA) {
- AIDL_ERROR(this) << "Currently, only Java backend supports Map.";
+ }
+ }
+ if (this->GetName() == "Map" || this->GetName() == "CharSequence") {
+ if (lang != Options::Language::JAVA) {
+ AIDL_ERROR(this) << "Currently, only Java backend supports " << this->GetName() << ".";
+ return false;
+ }
+ }
+ if (lang == Options::Language::JAVA) {
+ const string name = this->GetName();
+ // List[], Map[], CharSequence[] are not supported.
+ if (AidlTypenames::IsBuiltinTypename(name) && this->IsArray()) {
+ if (name == "List" || name == "Map" || name == "CharSequence") {
+ AIDL_ERROR(this) << "List[], Map[], CharSequence[] are not supported.";
return false;
}
}
}
+
return true;
}
// TODO: we should treat every backend all the same in future.
bool AidlParcelable::LanguageSpecificCheckValid(Options::Language lang) const {
if (lang != Options::Language::JAVA) {
- if (this->IsStableParcelable()) {
- AIDL_ERROR(this) << "@JavaOnlyStableParcelable supports only Java target.";
- return false;
- }
- const AidlParcelable* unstructuredParcelable = this->AsUnstructuredParcelable();
- if (unstructuredParcelable != nullptr) {
- if (unstructuredParcelable->GetCppHeader().empty()) {
- AIDL_ERROR(unstructuredParcelable)
+ const AidlParcelable* unstructured_parcelable = this->AsUnstructuredParcelable();
+ if (unstructured_parcelable != nullptr) {
+ if (unstructured_parcelable->GetCppHeader().empty()) {
+ AIDL_ERROR(unstructured_parcelable)
<< "Unstructured parcelable must have C++ header defined.";
return false;
}
@@ -758,6 +890,30 @@
backing_type_ = std::move(type);
}
+bool AidlEnumDeclaration::Autofill() {
+ const AidlEnumerator* previous = nullptr;
+ for (const auto& enumerator : enumerators_) {
+ if (enumerator->GetValue() == nullptr) {
+ if (previous == nullptr) {
+ enumerator->SetValue(std::unique_ptr<AidlConstantValue>(
+ AidlConstantValue::Integral(AIDL_LOCATION_HERE, "0")));
+ } else {
+ auto prev_value = std::unique_ptr<AidlConstantValue>(
+ AidlConstantValue::ShallowIntegralCopy(*previous->GetValue()));
+ if (prev_value == nullptr) {
+ return false;
+ }
+ enumerator->SetValue(std::make_unique<AidlBinaryConstExpression>(
+ AIDL_LOCATION_HERE, std::move(prev_value), "+",
+ std::unique_ptr<AidlConstantValue>(
+ AidlConstantValue::Integral(AIDL_LOCATION_HERE, "1"))));
+ }
+ }
+ previous = enumerator.get();
+ }
+ return true;
+}
+
bool AidlEnumDeclaration::CheckValid(const AidlTypenames&) const {
if (backing_type_ == nullptr) {
AIDL_ERROR(this) << "Enum declaration missing backing type.";
@@ -770,13 +926,11 @@
return success;
}
-void AidlEnumDeclaration::Write(CodeWriter* writer) const {
+void AidlEnumDeclaration::Dump(CodeWriter* writer) const {
writer->Write("%s\n", AidlAnnotatable::ToString().c_str());
writer->Write("enum %s {\n", GetName().c_str());
writer->Indent();
for (const auto& enumerator : GetEnumerators()) {
- // TODO(b/123321528): After autofilling is supported, determine if we want
- // to leave out the assigned value for enumerators that were autofilled.
writer->Write("%s = %s,\n", enumerator->GetName().c_str(),
enumerator->ValueString(GetBackingType(), AidlConstantValueDecorator).c_str());
}
@@ -824,13 +978,22 @@
delete members;
}
-void AidlInterface::Write(CodeWriter* writer) const {
+void AidlInterface::Dump(CodeWriter* writer) const {
+ if (this->IsHidden()) {
+ AddHideComment(writer);
+ }
writer->Write("interface %s {\n", GetName().c_str());
writer->Indent();
for (const auto& method : GetMethods()) {
+ if (method->IsHidden()) {
+ AddHideComment(writer);
+ }
writer->Write("%s;\n", method->ToString().c_str());
}
for (const auto& constdecl : GetConstantDeclarations()) {
+ if (constdecl->GetType().IsHidden()) {
+ AddHideComment(writer);
+ }
writer->Write("%s;\n", constdecl->ToString().c_str());
}
writer->Dedent();
@@ -884,7 +1047,7 @@
}
// check that the name doesn't match a keyword
- if (is_java_keyword(arg->GetName().c_str())) {
+ if (IsJavaKeyword(arg->GetName().c_str())) {
AIDL_ERROR(arg) << "Argument name is a Java or aidl keyword";
return false;
}
@@ -906,7 +1069,7 @@
return false;
}
- static set<string> reserved_methods{"asBinder()", "getInterfaceVersion()",
+ static set<string> reserved_methods{"asBinder()", "getInterfaceHash()", "getInterfaceVersion()",
"getTransactionName(int)"};
if (reserved_methods.find(m->Signature()) != reserved_methods.end()) {
@@ -977,8 +1140,13 @@
return package_->GetTerms();
}
-void Parser::AddImport(AidlImport* import) {
- imports_.emplace_back(import);
+void Parser::AddImport(std::unique_ptr<AidlImport>&& import) {
+ for (const auto& i : imports_) {
+ if (i->GetNeededClass() == import->GetNeededClass()) {
+ return;
+ }
+ }
+ imports_.emplace_back(std::move(import));
}
bool Parser::Resolve() {
diff --git a/aidl_language.h b/aidl_language.h
index cb380bd..b8ee857 100644
--- a/aidl_language.h
+++ b/aidl_language.h
@@ -22,7 +22,9 @@
#include "options.h"
#include <memory>
+#include <regex>
#include <string>
+#include <unordered_set>
#include <vector>
#include <android-base/macros.h>
@@ -45,6 +47,9 @@
namespace mappings {
std::string dump_location(const AidlNode& method);
} // namespace mappings
+namespace java {
+std::string dump_location(const AidlNode& method);
+} // namespace java
} // namespace aidl
} // namespace android
@@ -65,8 +70,8 @@
class AidlLocation {
public:
struct Point {
- unsigned int line;
- unsigned int column;
+ int line;
+ int column;
};
AidlLocation(const std::string& file, Point begin, Point end);
@@ -103,8 +108,10 @@
// To be able to print AidlLocation (nothing else should use this information)
friend class AidlError;
friend std::string android::aidl::mappings::dump_location(const AidlNode&);
+ friend std::string android::aidl::java::dump_location(const AidlNode&);
private:
+ std::string PrintLine() const;
std::string PrintLocation() const;
const AidlLocation location_;
};
@@ -128,11 +135,15 @@
std::ostream& os_;
+ static bool hadError() { return sHadError; }
+
private:
AidlError(bool fatal);
bool fatal_;
+ static bool sHadError;
+
DISALLOW_COPY_AND_ASSIGN(AidlError);
};
@@ -144,12 +155,35 @@
namespace android {
namespace aidl {
-class ValidatableType;
class AidlTypenames;
} // namespace aidl
} // namespace android
+// unique_ptr<AidlTypeSpecifier> for type arugment,
+// std::string for type parameter(T, U, and so on).
+template <typename T>
+class AidlParameterizable {
+ public:
+ AidlParameterizable(std::vector<T>* type_params) : type_params_(type_params) {}
+ virtual ~AidlParameterizable() = default;
+ bool IsGeneric() const { return type_params_ != nullptr; }
+ const std::vector<T>& GetTypeParameters() const { return *type_params_; }
+ bool CheckValid() const;
+
+ virtual const AidlNode& AsAidlNode() const = 0;
+
+ protected:
+ AidlParameterizable(const AidlParameterizable&);
+
+ private:
+ const unique_ptr<std::vector<T>> type_params_;
+ static_assert(std::is_same<T, unique_ptr<AidlTypeSpecifier>>::value ||
+ std::is_same<T, std::string>::value);
+};
+template <>
+bool AidlParameterizable<std::string>::CheckValid() const;
+
class AidlConstantValue;
class AidlConstantDeclaration;
@@ -209,7 +243,7 @@
bool IsUtf8InCpp() const;
bool IsVintfStability() const;
bool IsSystemApi() const;
- bool IsStableParcelable() const;
+ bool IsStableApiParcelable(Options::Language lang) const;
const AidlAnnotation* UnsupportedAppUsage() const;
const AidlTypeSpecifier* BackingType(const AidlTypenames& typenames) const;
@@ -226,7 +260,8 @@
// AidlTypeSpecifier represents a reference to either a built-in type,
// a defined type, or a variant (e.g., array of generic) of a type.
-class AidlTypeSpecifier final : public AidlAnnotatable {
+class AidlTypeSpecifier final : public AidlAnnotatable,
+ public AidlParameterizable<unique_ptr<AidlTypeSpecifier>> {
public:
AidlTypeSpecifier(const AidlLocation& location, const string& unresolved_name, bool is_array,
vector<unique_ptr<AidlTypeSpecifier>>* type_params, const string& comments);
@@ -256,6 +291,8 @@
const string& GetUnresolvedName() const { return unresolved_name_; }
+ bool IsHidden() const;
+
const string& GetComments() const { return comments_; }
const std::vector<std::string> GetSplitName() const { return split_name_; }
@@ -266,38 +303,24 @@
bool IsArray() const { return is_array_; }
- bool IsGeneric() const { return type_params_ != nullptr; }
-
- const vector<unique_ptr<AidlTypeSpecifier>>& GetTypeParameters() const { return *type_params_; }
-
// Resolve the base type name to a fully-qualified name. Return false if the
// resolution fails.
bool Resolve(const AidlTypenames& typenames);
bool CheckValid(const AidlTypenames& typenames) const;
bool LanguageSpecificCheckValid(Options::Language lang) const;
+ const AidlNode& AsAidlNode() const override { return *this; }
- void SetLanguageType(const android::aidl::ValidatableType* language_type) {
- language_type_ = language_type;
- }
-
- template<typename T>
- const T* GetLanguageType() const {
- return reinterpret_cast<const T*>(language_type_);
- }
private:
AidlTypeSpecifier(const AidlTypeSpecifier&) = default;
const string unresolved_name_;
string fully_qualified_name_;
bool is_array_;
- const shared_ptr<vector<unique_ptr<AidlTypeSpecifier>>> type_params_;
string comments_;
- const android::aidl::ValidatableType* language_type_ = nullptr;
vector<string> split_name_;
};
-
// Returns the universal value unaltered.
std::string AidlConstantValueDecorator(const AidlTypeSpecifier& type, const std::string& raw_value);
@@ -409,13 +432,17 @@
// example: "\"asdf\""
static AidlConstantValue* String(const AidlLocation& location, const string& value);
+ // Construct an AidlConstantValue by evaluating the other integral constant's
+ // value string. This does not preserve the structure of the copied constant.
+ // Returns nullptr and logs if value cannot be copied.
+ static AidlConstantValue* ShallowIntegralCopy(const AidlConstantValue& other);
+
Type GetType() const { return final_type_; }
virtual bool CheckValid() const;
// Raw value of type (currently valid in C++ and Java). Empty string on error.
- virtual string ValueString(const AidlTypeSpecifier& type,
- const ConstantValueDecorator& decorator) const;
+ string ValueString(const AidlTypeSpecifier& type, const ConstantValueDecorator& decorator) const;
private:
AidlConstantValue(const AidlLocation& location, Type parsed_type, int64_t parsed_value,
@@ -426,6 +453,7 @@
static string ToString(Type type);
static bool ParseIntegral(const string& value, int64_t* parsed_value, Type* parsed_type);
static bool IsHex(const string& value);
+
virtual bool evaluate(const AidlTypeSpecifier& type) const;
const Type type_ = Type::ERROR;
@@ -433,8 +461,8 @@
const string value_; // otherwise
// State for tracking evaluation of expressions
- mutable bool is_valid_;
- mutable bool is_evaluated_;
+ mutable bool is_valid_ = false; // cache of CheckValid, but may be marked false in evaluate
+ mutable bool is_evaluated_ = false; // whether evaluate has been called
mutable Type final_type_;
mutable int64_t final_value_;
mutable string final_string_value_ = "";
@@ -452,9 +480,6 @@
static bool IsCompatibleType(Type type, const string& op);
bool CheckValid() const override;
- string ValueString(const AidlTypeSpecifier& type,
- const ConstantValueDecorator& decorator) const override;
-
private:
bool evaluate(const AidlTypeSpecifier& type) const override;
@@ -468,8 +493,6 @@
const string& op, std::unique_ptr<AidlConstantValue> rval);
bool CheckValid() const override;
- string ValueString(const AidlTypeSpecifier& type,
- const ConstantValueDecorator& decorator) const override;
static bool AreCompatibleTypes(Type t1, Type t2);
// Returns the promoted kind for both operands
@@ -528,7 +551,7 @@
virtual ~AidlMethod() = default;
AidlMethod* AsMethod() override { return this; }
-
+ bool IsHidden() const;
const string& GetComments() const { return comments_; }
const AidlTypeSpecifier& GetType() const { return *type_; }
AidlTypeSpecifier* GetMutableType() { return type_.get(); }
@@ -617,6 +640,7 @@
virtual ~AidlDefinedType() = default;
const std::string& GetName() const { return name_; };
+ bool IsHidden() const;
const std::string& GetComments() const { return comments_; }
void SetComments(const std::string comments) { comments_ = comments; }
@@ -632,6 +656,7 @@
virtual const AidlParcelable* AsParcelable() const { return nullptr; }
virtual const AidlEnumDeclaration* AsEnumDeclaration() const { return nullptr; }
virtual const AidlInterface* AsInterface() const { return nullptr; }
+ virtual const AidlParameterizable<std::string>* AsParameterizable() const { return nullptr; }
virtual bool CheckValid(const AidlTypenames&) const { return CheckValidAnnotations(); }
virtual bool LanguageSpecificCheckValid(Options::Language lang) const = 0;
AidlStructuredParcelable* AsStructuredParcelable() {
@@ -649,6 +674,11 @@
return const_cast<AidlInterface*>(const_cast<const AidlDefinedType*>(this)->AsInterface());
}
+ AidlParameterizable<std::string>* AsParameterizable() {
+ return const_cast<AidlParameterizable<std::string>*>(
+ const_cast<const AidlDefinedType*>(this)->AsParameterizable());
+ }
+
const AidlParcelable* AsUnstructuredParcelable() const {
if (this->AsStructuredParcelable() != nullptr) return nullptr;
return this->AsParcelable();
@@ -658,31 +688,22 @@
const_cast<const AidlDefinedType*>(this)->AsUnstructuredParcelable());
}
- void SetLanguageType(const android::aidl::ValidatableType* language_type) {
- language_type_ = language_type;
- }
-
- template <typename T>
- const T* GetLanguageType() const {
- return reinterpret_cast<const T*>(language_type_);
- }
-
- virtual void Write(CodeWriter* writer) const = 0;
+ virtual void Dump(CodeWriter* writer) const = 0;
private:
std::string name_;
std::string comments_;
- const android::aidl::ValidatableType* language_type_ = nullptr;
const std::vector<std::string> package_;
DISALLOW_COPY_AND_ASSIGN(AidlDefinedType);
};
-class AidlParcelable : public AidlDefinedType {
+class AidlParcelable : public AidlDefinedType, public AidlParameterizable<std::string> {
public:
AidlParcelable(const AidlLocation& location, AidlQualifiedName* name,
const std::vector<std::string>& package, const std::string& comments,
- const std::string& cpp_header = "");
+ const std::string& cpp_header = "",
+ std::vector<std::string>* type_params = nullptr);
virtual ~AidlParcelable() = default;
// C++ uses "::" instead of "." to refer to a inner class.
@@ -692,9 +713,11 @@
bool CheckValid(const AidlTypenames& typenames) const override;
bool LanguageSpecificCheckValid(Options::Language lang) const override;
const AidlParcelable* AsParcelable() const override { return this; }
+ const AidlParameterizable<std::string>* AsParameterizable() const override { return this; }
+ const AidlNode& AsAidlNode() const override { return *this; }
std::string GetPreprocessDeclarationName() const override { return "parcelable"; }
- void Write(CodeWriter* writer) const override;
+ void Dump(CodeWriter* writer) const override;
private:
std::unique_ptr<AidlQualifiedName> name_;
@@ -716,7 +739,7 @@
const AidlStructuredParcelable* AsStructuredParcelable() const override { return this; }
std::string GetPreprocessDeclarationName() const override { return "structured_parcelable"; }
- void Write(CodeWriter* writer) const override;
+ void Dump(CodeWriter* writer) const override;
bool CheckValid(const AidlTypenames& typenames) const override;
bool LanguageSpecificCheckValid(Options::Language lang) const override;
@@ -741,6 +764,8 @@
string ValueString(const AidlTypeSpecifier& backing_type,
const ConstantValueDecorator& decorator) const;
+ void SetValue(std::unique_ptr<AidlConstantValue> value) { value_ = std::move(value); }
+
private:
const std::string name_;
unique_ptr<AidlConstantValue> value_;
@@ -761,10 +786,11 @@
const std::vector<std::unique_ptr<AidlEnumerator>>& GetEnumerators() const {
return enumerators_;
}
+ bool Autofill();
bool CheckValid(const AidlTypenames& typenames) const override;
bool LanguageSpecificCheckValid(Options::Language) const override { return true; }
std::string GetPreprocessDeclarationName() const override { return "enum"; }
- void Write(CodeWriter*) const override;
+ void Dump(CodeWriter* writer) const override;
const AidlEnumDeclaration* AsEnumDeclaration() const override { return this; }
@@ -793,7 +819,7 @@
const AidlInterface* AsInterface() const override { return this; }
std::string GetPreprocessDeclarationName() const override { return "interface"; }
- void Write(CodeWriter* writer) const override;
+ void Dump(CodeWriter* writer) const override;
bool CheckValid(const AidlTypenames& typenames) const override;
bool LanguageSpecificCheckValid(Options::Language lang) const override;
@@ -835,14 +861,10 @@
const std::string& FileName() const { return filename_; }
void* Scanner() const { return scanner_; }
- void AddImport(AidlImport* import);
+ void AddImport(std::unique_ptr<AidlImport>&& import);
const std::vector<std::unique_ptr<AidlImport>>& GetImports() {
return imports_;
}
- void ReleaseImports(std::vector<std::unique_ptr<AidlImport>>* ret) {
- *ret = std::move(imports_);
- imports_.clear();
- }
void SetPackage(unique_ptr<AidlQualifiedName> name) { package_ = std::move(name); }
std::vector<std::string> Package() const;
diff --git a/aidl_language_l.ll b/aidl_language_l.ll
index 658b906..4813da6 100644
--- a/aidl_language_l.ll
+++ b/aidl_language_l.ll
@@ -19,7 +19,7 @@
#include <stdlib.h>
#include "aidl_language.h"
-#include "aidl_language_y.h"
+#include "aidl_language_y-module.h"
#define YY_USER_ACTION yylloc->columns(yyleng);
%}
@@ -32,7 +32,7 @@
%option bison-bridge
%option bison-locations
-%x COPYING LONG_COMMENT
+%x LONG_COMMENT
identifier [_a-zA-Z][_a-zA-Z0-9]*
whitespace ([ \t\r]+)
@@ -47,12 +47,6 @@
yylloc->step();
%}
-
-\%\%\{ { extra_text += "/**"; BEGIN(COPYING); }
-<COPYING>\}\%\% { extra_text += "**/"; yylloc->step(); BEGIN(INITIAL); }
-<COPYING>.* { extra_text += yytext; }
-<COPYING>\n+ { extra_text += yytext; yylloc->lines(yyleng); }
-
\/\* { extra_text += yytext; BEGIN(LONG_COMMENT); }
<LONG_COMMENT>\*+\/ { extra_text += yytext; yylloc->step(); BEGIN(INITIAL); }
<LONG_COMMENT>\*+ { extra_text += yytext; }
@@ -100,9 +94,6 @@
">=" { return(yy::parser::token::GEQ); }
"==" { return(yy::parser::token::EQUALITY); }
"!=" { return(yy::parser::token::NEQ); }
-"?" { return('?'); }
-"@" { return('@'); }
-"#" { return('#'); }
/* annotations */
@{identifier} { yylval->token = new AidlToken(yytext + 1, extra_text);
@@ -119,7 +110,8 @@
out { return yy::parser::token::OUT; }
inout { return yy::parser::token::INOUT; }
cpp_header { return yy::parser::token::CPP_HEADER; }
-const { return yy::parser::token::CONST; }
+const { yylval->token = new AidlToken("const", extra_text);
+ return yy::parser::token::CONST; }
true { return yy::parser::token::TRUE_LITERAL; }
false { return yy::parser::token::FALSE_LITERAL; }
diff --git a/aidl_language_y-module.h b/aidl_language_y-module.h
new file mode 100644
index 0000000..34b1620
--- /dev/null
+++ b/aidl_language_y-module.h
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+// generated code prints std::string* which is disallowed
+// by android-base/logging.h
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wuser-defined-warnings"
+#include <aidl_language_y.h>
+#pragma clang diagnostic pop
diff --git a/aidl_language_y.yy b/aidl_language_y.yy
index ba7de96..67a7b11 100644
--- a/aidl_language_y.yy
+++ b/aidl_language_y.yy
@@ -16,8 +16,9 @@
%{
#include "aidl_language.h"
-#include "aidl_language_y.h"
+#include "aidl_language_y-module.h"
#include "logging.h"
+#include <android-base/parseint.h>
#include <set>
#include <map>
#include <stdio.h>
@@ -26,17 +27,23 @@
int yylex(yy::parser::semantic_type *, yy::parser::location_type *, void *);
+AidlLocation loc(const yy::parser::location_type& begin, const yy::parser::location_type& end) {
+ CHECK(begin.begin.filename == begin.end.filename);
+ CHECK(begin.end.filename == end.begin.filename);
+ CHECK(end.begin.filename == end.end.filename);
+ AidlLocation::Point begin_point {
+ .line = begin.begin.line,
+ .column = begin.begin.column,
+ };
+ AidlLocation::Point end_point {
+ .line = end.end.line,
+ .column = end.end.column,
+ };
+ return AidlLocation(*begin.begin.filename, begin_point, end_point);
+}
+
AidlLocation loc(const yy::parser::location_type& l) {
- CHECK(l.begin.filename == l.end.filename);
- AidlLocation::Point begin {
- .line = l.begin.line,
- .column = l.begin.column,
- };
- AidlLocation::Point end {
- .line = l.end.line,
- .column = l.end.column,
- };
- return AidlLocation(*l.begin.filename, begin, end);
+ return loc(l, l);
}
#define lex_scanner ps->Scanner()
@@ -51,13 +58,13 @@
%parse-param { Parser* ps }
%lex-param { void *lex_scanner }
-%pure-parser
%glr-parser
%skeleton "glr.cc"
%expect-rr 0
-%error-verbose
+%define parse.error verbose
+%locations
%union {
AidlToken* token;
@@ -86,6 +93,7 @@
AidlParcelable* parcelable;
AidlDefinedType* declaration;
std::vector<std::unique_ptr<AidlTypeSpecifier>>* type_args;
+ std::vector<std::string>* type_params;
}
%destructor { } <character>
@@ -99,6 +107,7 @@
%token<token> PARCELABLE "parcelable"
%token<token> ONEWAY "oneway"
%token<token> ENUM "enum"
+%token<token> CONST "const"
%token<character> CHARVALUE "char literal"
%token<token> FLOATVALUE "float literal"
@@ -106,7 +115,6 @@
%token<token> INTVALUE "int literal"
%token '(' ')' ',' '=' '[' ']' '.' '{' '}' ';'
-%token CONST "const"
%token UNKNOWN "unrecognized character"
%token CPP_HEADER "cpp_header"
%token IMPORT "import"
@@ -158,10 +166,11 @@
%type<annotation_list>annotation_list
%type<type> type
%type<type> unannotated_type
-%type<arg_list> arg_list
+%type<arg_list> arg_list arg_non_empty_list
%type<arg> arg
%type<direction> direction
%type<type_args> type_args
+%type<type_params> type_params
%type<qname> qualified_name
%type<const_expr> const_expr
%type<constant_value_list> constant_value_list
@@ -195,7 +204,7 @@
import
: IMPORT qualified_name ';'
- { ps->AddImport(new AidlImport(loc(@2), $2->GetDotName()));
+ { ps->AddImport(std::make_unique<AidlImport>(loc(@2), $2->GetDotName()));
delete $2;
};
@@ -212,10 +221,14 @@
decls
: decl {
- ps->AddDefinedType(unique_ptr<AidlDefinedType>($1));
+ if ($1 != nullptr) {
+ ps->AddDefinedType(unique_ptr<AidlDefinedType>($1));
+ }
}
| decls decl {
- ps->AddDefinedType(unique_ptr<AidlDefinedType>($2));
+ if ($2 != nullptr) {
+ ps->AddDefinedType(unique_ptr<AidlDefinedType>($2));
+ }
};
decl
@@ -223,12 +236,12 @@
{
$$ = $2;
- if ($1->size() > 0) {
+ if ($1->size() > 0 && $$ != nullptr) {
// copy comments from annotation to decl
- $2->SetComments($1->begin()->GetComments());
+ $$->SetComments($1->begin()->GetComments());
+ $$->Annotate(std::move(*$1));
}
- $$->Annotate(std::move(*$1));
delete $1;
}
;
@@ -242,11 +255,28 @@
{ $$ = $1; }
;
+type_params
+ : identifier {
+ $$ = new std::vector<std::string>();
+ $$->emplace_back($1->GetText());
+ delete $1;
+ }
+ | type_params ',' identifier {
+ $1->emplace_back($3->GetText());
+ $$ = $1;
+ delete $3;
+ };
+
+
parcelable_decl
: PARCELABLE qualified_name ';' {
$$ = new AidlParcelable(loc(@2), $2, ps->Package(), $1->GetComments());
delete $1;
}
+ | PARCELABLE qualified_name '<' type_params '>' ';' {
+ $$ = new AidlParcelable(loc(@2), $2, ps->Package(), $1->GetComments(), "", $4);
+ delete $1;
+ }
| PARCELABLE qualified_name CPP_HEADER C_STR ';' {
$$ = new AidlParcelable(loc(@2), $2, ps->Package(), $1->GetComments(), $4->GetText());
delete $1;
@@ -307,7 +337,6 @@
ps->AddError();
$$ = nullptr;
delete $1;
- delete $2;
delete $4;
};
@@ -430,10 +459,9 @@
}
| '(' error ')'
{
- std::cerr << "ERROR: invalid const expression within parenthesis: "
- << $2->GetText() << " at " << @1 << ".\n";
- // to avoid segfaults
+ std::cerr << "ERROR: invalid const expression within parenthesis at " << @1 << ".\n";
ps->AddError();
+ // to avoid segfaults
$$ = AidlConstantValue::Integral(loc(@1), "0");
}
;
@@ -460,17 +488,22 @@
constant_decl
: CONST type identifier '=' const_expr ';' {
+ $2->SetComments($1->GetComments());
$$ = new AidlConstantDeclaration(loc(@3), $2, $3->GetText(), $5);
+ delete $1;
delete $3;
}
;
-// TODO(b/139877950): Support autofilling enumerator values
enumerator
: identifier '=' const_expr {
$$ = new AidlEnumerator(loc(@1), $1->GetText(), $3, $1->GetComments());
delete $1;
}
+ | identifier {
+ $$ = new AidlEnumerator(loc(@1), $1->GetText(), nullptr, $1->GetComments());
+ delete $1;
+ }
;
enumerators
@@ -512,13 +545,23 @@
delete $4;
}
| type identifier '(' arg_list ')' '=' INTVALUE ';' {
- $$ = new AidlMethod(loc(@2), false, $1, $2->GetText(), $4, $1->GetComments(), std::stoi($7->GetText()));
+ int32_t serial = 0;
+ if (!android::base::ParseInt($7->GetText(), &serial)) {
+ AIDL_ERROR(loc(@7)) << "Could not parse int value: " << $7->GetText();
+ ps->AddError();
+ }
+ $$ = new AidlMethod(loc(@2), false, $1, $2->GetText(), $4, $1->GetComments(), serial);
delete $2;
delete $7;
}
| annotation_list ONEWAY type identifier '(' arg_list ')' '=' INTVALUE ';' {
const std::string& comments = ($1->size() > 0) ? $1->begin()->GetComments() : $2->GetComments();
- $$ = new AidlMethod(loc(@4), true, $3, $4->GetText(), $6, comments, std::stoi($9->GetText()));
+ int32_t serial = 0;
+ if (!android::base::ParseInt($9->GetText(), &serial)) {
+ AIDL_ERROR(loc(@9)) << "Could not parse int value: " << $9->GetText();
+ ps->AddError();
+ }
+ $$ = new AidlMethod(loc(@4), true, $3, $4->GetText(), $6, comments, serial);
$3->Annotate(std::move(*$1));
delete $1;
delete $2;
@@ -526,18 +569,22 @@
delete $9;
};
-arg_list
- :
- { $$ = new std::vector<std::unique_ptr<AidlArgument>>(); }
- | arg {
+arg_non_empty_list
+ : arg {
$$ = new std::vector<std::unique_ptr<AidlArgument>>();
$$->push_back(std::unique_ptr<AidlArgument>($1));
}
- | arg_list ',' arg {
+ | arg_non_empty_list ',' arg {
$$ = $1;
$$->push_back(std::unique_ptr<AidlArgument>($3));
};
+arg_list
+ : /*empty*/
+ { $$ = new std::vector<std::unique_ptr<AidlArgument>>(); }
+ | arg_non_empty_list { $$ = $1; }
+ ;
+
arg
: direction type identifier {
$$ = new AidlArgument(loc(@3), $1, $2, $3->GetText());
@@ -633,18 +680,20 @@
: ANNOTATION
{
$$ = AidlAnnotation::Parse(loc(@1), $1->GetText(), nullptr);
- if ($$ == nullptr) {
+ if ($$) {
+ $$->SetComments($1->GetComments());
+ } else {
ps->AddError();
}
- $$->SetComments($1->GetComments());
delete $1;
};
| ANNOTATION '(' parameter_list ')' {
- $$ = AidlAnnotation::Parse(loc(@1), $1->GetText(), $3);
- if ($$ == nullptr) {
+ $$ = AidlAnnotation::Parse(loc(@1, @4), $1->GetText(), $3);
+ if ($$) {
+ $$->SetComments($1->GetComments());
+ } else {
ps->AddError();
}
- $$->SetComments($1->GetComments());
delete $1;
delete $3;
}
diff --git a/aidl_to_cpp.cpp b/aidl_to_cpp.cpp
index f5fa3f8..9f2963e 100644
--- a/aidl_to_cpp.cpp
+++ b/aidl_to_cpp.cpp
@@ -219,6 +219,18 @@
return GetCppName(type, typenames);
}
+bool IsNonCopyableType(const AidlTypeSpecifier& type, const AidlTypenames& typenames) {
+ if (type.IsArray() || type.IsGeneric()) {
+ return false;
+ }
+
+ const std::string cpp_name = GetCppName(type, typenames);
+ if (cpp_name == "::android::base::unique_fd") {
+ return true;
+ }
+ return false;
+}
+
std::string ParcelReadMethodOf(const AidlTypeSpecifier& type, const AidlTypenames& typenames) {
return "read" + RawParcelMethod(type, typenames, true /* readMethod */);
}
diff --git a/aidl_to_cpp.h b/aidl_to_cpp.h
index 3bf608c..5816b2b 100644
--- a/aidl_to_cpp.h
+++ b/aidl_to_cpp.h
@@ -39,6 +39,8 @@
std::string CppNameOf(const AidlTypeSpecifier& type, const AidlTypenames& typenames);
+bool IsNonCopyableType(const AidlTypeSpecifier& type, const AidlTypenames& typenames);
+
// Returns the name of the Parcel method suitable for reading data of the
// given type.
std::string ParcelReadMethodOf(const AidlTypeSpecifier& type, const AidlTypenames& typenames);
diff --git a/aidl_to_cpp_common.cpp b/aidl_to_cpp_common.cpp
index 8978311..d1aeb2e 100644
--- a/aidl_to_cpp_common.cpp
+++ b/aidl_to_cpp_common.cpp
@@ -13,13 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include "aidl_to_cpp_common.h"
+#include <android-base/strings.h>
#include <unordered_map>
-#include "aidl_to_cpp_common.h"
+#include "ast_cpp.h"
#include "logging.h"
#include "os.h"
+using ::android::base::Join;
+
namespace android {
namespace aidl {
namespace cpp {
@@ -352,6 +356,26 @@
return code;
}
+std::string GenerateEnumValues(const AidlEnumDeclaration& enum_decl,
+ const std::vector<std::string>& enclosing_namespaces_of_enum_decl) {
+ const auto fq_name =
+ Join(Append(enclosing_namespaces_of_enum_decl, enum_decl.GetSplitPackage()), "::") +
+ "::" + enum_decl.GetName();
+ const auto size = enum_decl.GetEnumerators().size();
+ std::ostringstream code;
+ code << "#pragma clang diagnostic push\n";
+ code << "#pragma clang diagnostic ignored \"-Wc++17-extensions\"\n";
+ code << "template <>\n";
+ code << "constexpr inline std::array<" << fq_name << ", " << size << "> enum_values<" << fq_name
+ << "> = {\n";
+ for (const auto& enumerator : enum_decl.GetEnumerators()) {
+ code << " " << fq_name << "::" << enumerator->GetName() << ",\n";
+ }
+ code << "};\n";
+ code << "#pragma clang diagnostic pop\n";
+ return code.str();
+}
+
} // namespace cpp
} // namespace aidl
} // namespace android
diff --git a/aidl_to_cpp_common.h b/aidl_to_cpp_common.h
index 381a8c9..a5c7896 100644
--- a/aidl_to_cpp_common.h
+++ b/aidl_to_cpp_common.h
@@ -17,6 +17,7 @@
#pragma once
#include <string>
+#include <type_traits>
#include "aidl_language.h"
@@ -54,6 +55,24 @@
const string GenLogAfterExecute(const string className, const AidlInterface& interface,
const AidlMethod& method, const string& statusVarName,
const string& returnVarName, bool isServer, bool isNdk);
+
+template <typename T, typename = std::enable_if_t<std::is_copy_constructible_v<T>>>
+std::vector<T> Append(std::vector<T> as, const std::vector<T>& bs) {
+ as.insert(as.end(), bs.begin(), bs.end());
+ return as;
+}
+
+template <typename T>
+std::vector<T> Append(std::vector<T>&& as, std::vector<T>&& bs) {
+ std::vector<T> appended = std::move(as);
+ std::copy(std::move_iterator(bs.begin()), std::move_iterator(bs.end()),
+ std::back_inserter(appended));
+ return appended;
+}
+
+std::string GenerateEnumValues(const AidlEnumDeclaration& enum_decl,
+ const std::vector<std::string>& enclosing_namespaces_of_enum_decl);
+
} // namespace cpp
} // namespace aidl
} // namespace android
diff --git a/aidl_to_java.cpp b/aidl_to_java.cpp
index ec7909d..8910365 100644
--- a/aidl_to_java.cpp
+++ b/aidl_to_java.cpp
@@ -48,7 +48,7 @@
};
const string& JavaNameOf(const AidlTypeSpecifier& aidl, const AidlTypenames& typenames,
- bool instantiable = false) {
+ bool instantiable = false, bool boxing = false) {
CHECK(aidl.IsResolved()) << aidl.ToString();
if (instantiable) {
@@ -87,6 +87,12 @@
{"ParcelFileDescriptor", "android.os.ParcelFileDescriptor"},
};
+ // map from primitive types to the corresponding boxing types
+ static map<string, string> boxing_types = {
+ {"void", "Void"}, {"boolean", "Boolean"}, {"byte", "Byte"}, {"char", "Character"},
+ {"int", "Integer"}, {"long", "Long"}, {"float", "Float"}, {"double", "Double"},
+ };
+
// Enums in Java are represented by their backing type when
// referenced in parcelables, methods, etc.
if (const AidlEnumDeclaration* enum_decl = typenames.GetEnumDeclaration(aidl);
@@ -98,6 +104,11 @@
}
const string& aidl_name = aidl.GetName();
+ if (boxing && AidlTypenames::IsPrimitiveTypename(aidl_name)) {
+ // Every primitive type must have the corresponding boxing type
+ CHECK(boxing_types.find(aidl_name) != m.end());
+ return boxing_types[aidl_name];
+ }
if (m.find(aidl_name) != m.end()) {
CHECK(AidlTypenames::IsBuiltinTypename(aidl_name));
return m[aidl_name];
@@ -108,13 +119,15 @@
}
namespace {
-string JavaSignatureOfInternal(const AidlTypeSpecifier& aidl, const AidlTypenames& typenames,
- bool instantiable, bool omit_array) {
- string ret = JavaNameOf(aidl, typenames, instantiable);
+string JavaSignatureOfInternal(
+ const AidlTypeSpecifier& aidl, const AidlTypenames& typenames, bool instantiable,
+ bool omit_array, bool boxing = false /* boxing can be true only if it is a type parameter */) {
+ string ret = JavaNameOf(aidl, typenames, instantiable, boxing && !aidl.IsArray());
if (aidl.IsGeneric()) {
vector<string> arg_names;
for (const auto& ta : aidl.GetTypeParameters()) {
- arg_names.emplace_back(JavaSignatureOfInternal(*ta, typenames, false, false));
+ arg_names.emplace_back(
+ JavaSignatureOfInternal(*ta, typenames, false, false, true /* boxing */));
}
ret += "<" + Join(arg_names, ",") + ">";
}
@@ -169,51 +182,6 @@
}
}
-// These are supported by AIDL syntax, but are unsupported by the AIDL compiler
-static bool IsMarshallingUnsupportedFor(const AidlTypeSpecifier& aidl,
- const AidlTypenames& typenames) {
- const string name = aidl.GetName();
-
- // List<T> is support only for String, Binder, ParcelFileDescriptor and Parcelable.
- if (name == "List" && aidl.IsGeneric()) {
- const string& contained_type = aidl.GetTypeParameters().at(0)->GetName();
- if (AidlTypenames::IsBuiltinTypename(contained_type)) {
- if (contained_type != "String" && contained_type != "IBinder" &&
- contained_type != "ParcelFileDescriptor") {
- return true;
- }
- } else {
- const AidlDefinedType* t = typenames.TryGetDefinedType(contained_type);
- if (t != nullptr && t->AsInterface() != nullptr) {
- return true;
- }
- }
- }
-
- // List[], Map[], CharSequence[] are not supported.
- if (AidlTypenames::IsBuiltinTypename(name) && aidl.IsArray()) {
- if (name == "List" || name == "Map" || name == "CharSequence") {
- return true;
- }
- }
-
- // T[] is not supported for interfaces
- const AidlDefinedType* t = typenames.TryGetDefinedType(name);
- if (aidl.IsArray() && t != nullptr && t->AsInterface() != nullptr) {
- return true;
- }
-
- return false;
-}
-
-static bool EnsureCodegenIsSupported(const CodeGeneratorContext& c) {
- if (IsMarshallingUnsupportedFor(c.type, c.typenames)) {
- AIDL_ERROR(c.type) << c.type.ToString() << "' is not yet supported.";
- return false;
- }
- return true;
-}
-
static string GetFlagFor(const CodeGeneratorContext& c) {
if (c.is_return_value) {
return "android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE";
@@ -223,9 +191,6 @@
}
bool WriteToParcelFor(const CodeGeneratorContext& c) {
- if (!EnsureCodegenIsSupported(c)) {
- return false;
- }
static map<string, function<void(const CodeGeneratorContext&)>> method_map{
{"boolean",
[](const CodeGeneratorContext& c) {
@@ -314,7 +279,37 @@
}},
{"Map",
[](const CodeGeneratorContext& c) {
- c.writer << c.parcel << ".writeMap(" << c.var << ");\n";
+ if (c.type.IsGeneric()) {
+ c.writer << "if (" << c.var << " == null) {\n";
+ c.writer.Indent();
+ c.writer << c.parcel << ".writeInt(-1);\n";
+ c.writer.Dedent();
+ c.writer << "} else {\n";
+ c.writer.Indent();
+ c.writer << c.parcel << ".writeInt(" << c.var << ".size());\n";
+ c.writer << c.var << ".forEach((k, v) -> {\n";
+ c.writer.Indent();
+ c.writer << c.parcel << ".writeString(k);\n";
+
+ CodeGeneratorContext value_context{
+ c.writer,
+ c.typenames,
+ *c.type.GetTypeParameters()[1].get(),
+ c.parcel,
+ "v",
+ c.is_return_value,
+ c.is_classloader_created,
+ c.filename,
+ };
+ WriteToParcelFor(value_context);
+ c.writer.Dedent();
+ c.writer << "});\n";
+
+ c.writer.Dedent();
+ c.writer << "}\n";
+ } else {
+ c.writer << c.parcel << ".writeMap(" << c.var << ");\n";
+ }
}},
{"IBinder",
[](const CodeGeneratorContext& c) {
@@ -420,9 +415,6 @@
}
bool CreateFromParcelFor(const CodeGeneratorContext& c) {
- if (!EnsureCodegenIsSupported(c)) {
- return false;
- }
static map<string, function<void(const CodeGeneratorContext&)>> method_map{
{"boolean",
[](const CodeGeneratorContext& c) {
@@ -514,8 +506,39 @@
}},
{"Map",
[](const CodeGeneratorContext& c) {
- const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
- c.writer << c.var << " = " << c.parcel << ".readHashMap(" << classloader << ");\n";
+ if (c.type.IsGeneric()) {
+ c.writer << "{\n";
+ c.writer.Indent();
+ c.writer << "int N = " << c.parcel << ".readInt();\n";
+ c.writer << c.var << " = N < 0 ? null : new java.util.HashMap<>();\n";
+
+ auto creator = JavaNameOf(*(c.type.GetTypeParameters().at(1)), c.typenames) + ".CREATOR";
+ c.writer << "java.util.stream.IntStream.range(0, N).forEach(i -> {\n";
+ c.writer.Indent();
+ c.writer << "String k = " << c.parcel << ".readString();\n";
+ c.writer << JavaNameOf(*(c.type.GetTypeParameters().at(1)), c.typenames) << " v;\n";
+ CodeGeneratorContext value_context{
+ c.writer,
+ c.typenames,
+ *c.type.GetTypeParameters()[1].get(),
+ c.parcel,
+ "v",
+ c.is_return_value,
+ c.is_classloader_created,
+ c.filename,
+ };
+ CreateFromParcelFor(value_context);
+ c.writer << c.var << ".put(k, v);\n";
+
+ c.writer.Dedent();
+ c.writer << "});\n";
+
+ c.writer.Dedent();
+ c.writer << "}\n";
+ } else {
+ const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
+ c.writer << c.var << " = " << c.parcel << ".readHashMap(" << classloader << ");\n";
+ }
}},
{"IBinder",
[](const CodeGeneratorContext& c) {
@@ -606,9 +629,6 @@
}
bool ReadFromParcelFor(const CodeGeneratorContext& c) {
- if (!EnsureCodegenIsSupported(c)) {
- return false;
- }
static map<string, function<void(const CodeGeneratorContext&)>> method_map{
{"boolean[]",
[](const CodeGeneratorContext& c) {
@@ -668,8 +688,35 @@
}},
{"Map",
[](const CodeGeneratorContext& c) {
- const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
- c.writer << c.var << " = " << c.parcel << ".readHashMap(" << classloader << ");\n";
+ if (c.type.IsGeneric()) {
+ c.writer << "if (" << c.var << " != null) " << c.var << ".clear();\n";
+ c.writer << "java.util.stream.IntStream.range(0, " << c.parcel
+ << ".readInt()).forEach(i -> {\n";
+ c.writer.Indent();
+ c.writer << "String k = " << c.parcel << ".readString();\n";
+ c.writer << JavaNameOf(*(c.type.GetTypeParameters().at(1)), c.typenames) << " v;\n";
+ CodeGeneratorContext value_context{
+ c.writer,
+ c.typenames,
+ *c.type.GetTypeParameters()[1].get(),
+ c.parcel,
+ "v",
+ c.is_return_value,
+ c.is_classloader_created,
+ c.filename,
+ };
+ CreateFromParcelFor(value_context);
+ c.writer << c.var << ".put(k, v);\n";
+
+ c.writer.Dedent();
+ c.writer << "});\n";
+
+ c.writer.Dedent();
+ c.writer << "}\n";
+ } else {
+ const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
+ c.writer << c.var << " = " << c.parcel << ".readHashMap(" << classloader << ");\n";
+ }
}},
{"IBinder[]",
[](const CodeGeneratorContext& c) {
diff --git a/aidl_to_ndk.cpp b/aidl_to_ndk.cpp
index 5379564..663a17e 100644
--- a/aidl_to_ndk.cpp
+++ b/aidl_to_ndk.cpp
@@ -120,7 +120,6 @@
TypeInfo::Aspect{
.cpp_name = "std::shared_ptr<" + clazz + ">",
.value_is_cheap = false,
- // TODO(b/111445392): these should be non-null
.read_func = StandardRead(clazz + "::readFromParcel"),
.write_func = StandardWrite(clazz + "::writeToParcel"),
},
@@ -136,21 +135,15 @@
}
TypeInfo ParcelableTypeInfo(const AidlParcelable& type) {
- const std::string clazz = NdkFullClassName(type, cpp::ClassNames::BASE);
+ const std::string clazz = NdkFullClassName(type, cpp::ClassNames::RAW);
return TypeInfo{
.raw =
TypeInfo::Aspect{
.cpp_name = clazz,
.value_is_cheap = false,
- .read_func =
- [](const CodeGeneratorContext& c) {
- c.writer << "(" << c.var << ")->readFromParcel(" << c.parcel << ")";
- },
- .write_func =
- [](const CodeGeneratorContext& c) {
- c.writer << "(" << c.var << ").writeToParcel(" << c.parcel << ")";
- },
+ .read_func = StandardRead("::ndk::AParcel_readParcelable"),
+ .write_func = StandardWrite("::ndk::AParcel_writeParcelable"),
},
.array = std::shared_ptr<TypeInfo::Aspect>(new TypeInfo::Aspect{
.cpp_name = "std::vector<" + clazz + ">",
@@ -158,13 +151,18 @@
.read_func = StandardRead("::ndk::AParcel_readVector"),
.write_func = StandardWrite("::ndk::AParcel_writeVector"),
}),
- .nullable = nullptr,
+ .nullable = std::shared_ptr<TypeInfo::Aspect>(new TypeInfo::Aspect{
+ .cpp_name = "std::optional<" + clazz + ">",
+ .value_is_cheap = false,
+ .read_func = StandardRead("::ndk::AParcel_readNullableParcelable"),
+ .write_func = StandardWrite("::ndk::AParcel_writeNullableParcelable"),
+ }),
.nullable_array = nullptr,
};
}
TypeInfo EnumDeclarationTypeInfo(const AidlEnumDeclaration& enum_decl) {
- const std::string clazz = NdkFullClassName(enum_decl, cpp::ClassNames::BASE);
+ const std::string clazz = NdkFullClassName(enum_decl, cpp::ClassNames::RAW);
static map<std::string, std::string> kAParcelTypeNameMap = {
{"byte", "Byte"},
@@ -271,8 +269,7 @@
.write_func = StandardWrite("::ndk::AParcel_writeVector"),
}),
}},
- // TODO(b/111445392) {"List", ""},
- // TODO(b/111445392) {"Map", ""},
+ // TODO(b/136048684) {"Map", ""},
{"IBinder",
TypeInfo{
.raw =
@@ -291,7 +288,6 @@
}),
.nullable_array = nullptr,
}},
- // TODO(b/111445392) {"FileDescriptor", ""},
{"ParcelFileDescriptor",
TypeInfo{
.raw =
@@ -301,7 +297,12 @@
.read_func = StandardRead("::ndk::AParcel_readRequiredParcelFileDescriptor"),
.write_func = StandardRead("::ndk::AParcel_writeRequiredParcelFileDescriptor"),
},
- .array = nullptr,
+ .array = std::shared_ptr<TypeInfo::Aspect>(new TypeInfo::Aspect{
+ .cpp_name = "std::vector<::ndk::ScopedFileDescriptor>",
+ .value_is_cheap = false,
+ .read_func = StandardRead("::ndk::AParcel_readVector"),
+ .write_func = StandardWrite("::ndk::AParcel_writeVector"),
+ }),
.nullable = std::shared_ptr<TypeInfo::Aspect>(new TypeInfo::Aspect{
.cpp_name = "::ndk::ScopedFileDescriptor",
.value_is_cheap = false,
@@ -310,18 +311,35 @@
}),
.nullable_array = nullptr,
}},
- // TODO(b/111445392) {"CharSequence", ""},
};
static TypeInfo::Aspect GetTypeAspect(const AidlTypenames& types, const AidlTypeSpecifier& aidl) {
CHECK(aidl.IsResolved()) << aidl.ToString();
-
- const string aidl_name = aidl.GetName();
-
- // TODO(b/112664205): this is okay for some types
- AIDL_FATAL_IF(aidl.IsGeneric(), aidl) << aidl.ToString();
+ auto& aidl_name = aidl.GetName();
TypeInfo info;
+
+ // TODO(b/136048684): For now, List<T> is converted to T[].(Both are using vector<T>)
+ if (aidl_name == "List") {
+ AIDL_FATAL_IF(!aidl.IsGeneric(), aidl) << "List must be generic type.";
+ AIDL_FATAL_IF(aidl.GetTypeParameters().size() != 1, aidl)
+ << "List can accept only one type parameter.";
+ auto& type_param = aidl.GetTypeParameters()[0];
+ // TODO(b/136048684) AIDL doesn't support nested type parameter yet.
+ AIDL_FATAL_IF(type_param->IsGeneric(), aidl) << "AIDL doesn't support nested type parameter";
+
+ AidlTypeSpecifier array_type =
+ AidlTypeSpecifier(AIDL_LOCATION_HERE, type_param->GetUnresolvedName(), true /* isArray */,
+ nullptr /* type_params */, aidl.GetComments());
+ if (!(array_type.Resolve(types) && array_type.CheckValid(types))) {
+ AIDL_FATAL(aidl) << "The type parameter is wrong.";
+ }
+ return GetTypeAspect(types, array_type);
+ }
+
+ // All generic types should be handled above.
+ AIDL_FATAL_IF(aidl.IsGeneric(), aidl);
+
if (AidlTypenames::IsBuiltinTypename(aidl_name)) {
auto it = kNdkTypeInfoMap.find(aidl_name);
CHECK(it != kNdkTypeInfoMap.end());
diff --git a/aidl_typenames.cpp b/aidl_typenames.cpp
index 6f9b590..ad89c1c 100644
--- a/aidl_typenames.cpp
+++ b/aidl_typenames.cpp
@@ -85,7 +85,13 @@
static set<string> ignore_import = {"android.os.IInterface", "android.os.IBinder",
"android.os.Parcelable", "android.os.Parcel",
"android.content.Context", "java.lang.String"};
- return ResolveTypename(import).second || ignore_import.find(import) != ignore_import.end();
+ // these known built-in types don't need to be imported
+ const bool in_ignore_import = ignore_import.find(import) != ignore_import.end();
+ // an already defined type doesn't need to be imported again unless it is from
+ // the preprocessed file
+ auto ret = TryGetDefinedTypeImpl(import);
+ const bool defined_type_not_from_preprocessed = ret.type != nullptr && !ret.from_preprocessed;
+ return in_ignore_import || defined_type_not_from_preprocessed;
}
bool AidlTypenames::AddDefinedType(unique_ptr<AidlDefinedType> type) {
@@ -122,32 +128,37 @@
}
const AidlDefinedType* AidlTypenames::TryGetDefinedType(const string& type_name) const {
+ return TryGetDefinedTypeImpl(type_name).type;
+}
+
+AidlTypenames::DefinedImplResult AidlTypenames::TryGetDefinedTypeImpl(
+ const string& type_name) const {
// Do the exact match first.
auto found_def = defined_types_.find(type_name);
if (found_def != defined_types_.end()) {
- return found_def->second.get();
+ return DefinedImplResult(found_def->second.get(), false);
}
auto found_prep = preprocessed_types_.find(type_name);
if (found_prep != preprocessed_types_.end()) {
- return found_prep->second.get();
+ return DefinedImplResult(found_prep->second.get(), true);
}
// Then match with the class name. Defined types has higher priority than
// types from the preprocessed file.
for (auto it = defined_types_.begin(); it != defined_types_.end(); it++) {
if (it->second->GetName() == type_name) {
- return it->second.get();
+ return DefinedImplResult(it->second.get(), false);
}
}
for (auto it = preprocessed_types_.begin(); it != preprocessed_types_.end(); it++) {
if (it->second->GetName() == type_name) {
- return it->second.get();
+ return DefinedImplResult(it->second.get(), true);
}
}
- return nullptr;
+ return DefinedImplResult(nullptr, false);
}
pair<string, bool> AidlTypenames::ResolveTypename(const string& type_name) const {
diff --git a/aidl_typenames.h b/aidl_typenames.h
index c2ea12e..78b996f 100644
--- a/aidl_typenames.h
+++ b/aidl_typenames.h
@@ -71,6 +71,13 @@
void IterateTypes(const std::function<void(const AidlDefinedType&)>& body) const;
private:
+ struct DefinedImplResult {
+ DefinedImplResult(const AidlDefinedType* type, const bool from_preprocessed)
+ : type(type), from_preprocessed(from_preprocessed) {}
+ const AidlDefinedType* type;
+ const bool from_preprocessed;
+ };
+ DefinedImplResult TryGetDefinedTypeImpl(const string& type_name) const;
map<string, unique_ptr<AidlDefinedType>> defined_types_;
map<string, unique_ptr<AidlDefinedType>> preprocessed_types_;
};
diff --git a/aidl_unittest.cpp b/aidl_unittest.cpp
index 38cac7e..58e391e 100644
--- a/aidl_unittest.cpp
+++ b/aidl_unittest.cpp
@@ -23,7 +23,7 @@
#include <gtest/gtest.h>
#include "aidl.h"
-#include "aidl_apicheck.h"
+#include "aidl_checkapi.h"
#include "aidl_language.h"
#include "aidl_to_cpp.h"
#include "aidl_to_java.h"
@@ -82,7 +82,7 @@
@android.annotation.SystemApi
public int x = 5;
- @dalvik.annotation.compat.UnsupportedAppUsage(expectedSignature = "dummy", implicitMember = "dummy", maxTargetSdk = 28, publicAlternatives = "dummy", trackingBug = 42L)
+ @android.compat.annotation.UnsupportedAppUsage(expectedSignature = "dummy", implicitMember = "dummy", maxTargetSdk = 28, publicAlternatives = "dummy", trackingBug = 42L, overrideSourcePosition="Rect.aidl:7:1:10:14")
@android.annotation.SystemApi
public int y;
@@ -362,15 +362,19 @@
TEST_F(AidlTest, ParsesJavaOnlyStableParcelable) {
Options java_options = Options::From("aidl -o out --structured a/Foo.aidl");
- Options cpp_options =
+ Options cpp_options = Options::From("aidl --lang=cpp -o out -h out/include a/Foo.aidl");
+ Options cpp_structured_options =
Options::From("aidl --lang=cpp --structured -o out -h out/include a/Foo.aidl");
io_delegate_.SetFileContents(
- "a/Foo.aidl", StringPrintf("package a; @JavaOnlyStableParcelable parcelable Foo;"));
+ "a/Foo.aidl",
+ StringPrintf("package a; @JavaOnlyStableParcelable parcelable Foo cpp_header \"Foo.h\" ;"));
EXPECT_EQ(0, ::android::aidl::compile_aidl(java_options, io_delegate_));
+ EXPECT_EQ(0, ::android::aidl::compile_aidl(cpp_options, io_delegate_));
AddExpectedStderr(
- "ERROR: a/Foo.aidl:1.48-52: @JavaOnlyStableParcelable supports only Java target.\n");
- EXPECT_NE(0, ::android::aidl::compile_aidl(cpp_options, io_delegate_));
+ "ERROR: a/Foo.aidl:1.48-52: Cannot declared parcelable in a --structured interface. "
+ "Parcelable must be defined in AIDL directly.\n");
+ EXPECT_NE(0, ::android::aidl::compile_aidl(cpp_structured_options, io_delegate_));
}
TEST_F(AidlTest, AcceptsOneway) {
@@ -448,6 +452,28 @@
EXPECT_EQ("one.IBar", ambiguous_type.GetName());
}
+// Special case of PreferImportToPreprocessed. Imported type should be preferred
+// even when the preprocessed file already has the same type.
+TEST_F(AidlTest, B147918827) {
+ io_delegate_.SetFileContents("preprocessed", "interface another.IBar;\ninterface one.IBar;");
+ io_delegate_.SetFileContents("one/IBar.aidl",
+ "package one; "
+ "interface IBar {}");
+ preprocessed_files_.push_back("preprocessed");
+ import_paths_.emplace("");
+ auto parse_result = Parse("p/IFoo.aidl", "package p; import one.IBar; interface IFoo {}",
+ typenames_, Options::Language::JAVA);
+ EXPECT_NE(nullptr, parse_result);
+
+ // We expect to know about both kinds of IBar
+ EXPECT_TRUE(typenames_.ResolveTypename("one.IBar").second);
+ EXPECT_TRUE(typenames_.ResolveTypename("another.IBar").second);
+ // But if we request just "IBar" we should get our imported one.
+ AidlTypeSpecifier ambiguous_type(AIDL_LOCATION_HERE, "IBar", false, nullptr, "");
+ ambiguous_type.Resolve(typenames_);
+ EXPECT_EQ("one.IBar", ambiguous_type.GetName());
+}
+
TEST_F(AidlTest, WritePreprocessedFile) {
io_delegate_.SetFileContents("p/Outer.aidl",
"package p; parcelable Outer.Inner;");
@@ -755,6 +781,19 @@
EXPECT_EQ(actual_dep_file_contents, kExpectedStructuredParcelableDepFileContents);
}
+TEST_F(AidlTest, NoJavaOutputForParcelableDeclaration) {
+ vector<string> args = {
+ "aidl",
+ "--lang=java",
+ "-o place/for/output",
+ "p/Foo.aidl"};
+ Options options = Options::From(args);
+ io_delegate_.SetFileContents(options.InputFiles().front(), "package p; parcelable Foo;");
+ EXPECT_EQ(0, ::android::aidl::compile_aidl(options, io_delegate_));
+ string output_file_contents;
+ EXPECT_FALSE(io_delegate_.GetWrittenContents(options.OutputFile(), &output_file_contents));
+}
+
/* not working until type_namespace.h is fixed
TEST_F(AidlTest, AcceptsNestedContainerType) {
string nested_in_iface = "package a; interface IFoo {\n"
@@ -769,45 +808,75 @@
}
*/
+// TODO(b/136048684)
+TEST_F(AidlTest, PrimitiveList) {
+ string primitive_interface =
+ "package a; interface IFoo {\n"
+ " List<int> foo(); }";
+ string primitive_parcelable =
+ "package a; parcelable IData {\n"
+ " List<int> foo;}";
+ EXPECT_EQ(nullptr,
+ Parse("a/IFoo.aidl", primitive_interface, typenames_, Options::Language::JAVA));
+ EXPECT_EQ(nullptr, Parse("a/IFoo.aidl", primitive_interface, typenames_, Options::Language::CPP));
+ EXPECT_EQ(nullptr, Parse("a/IFoo.aidl", primitive_interface, typenames_, Options::Language::NDK));
+ EXPECT_EQ(nullptr,
+ Parse("a/IFoo.aidl", primitive_parcelable, typenames_, Options::Language::JAVA));
+ EXPECT_EQ(nullptr,
+ Parse("a/IFoo.aidl", primitive_parcelable, typenames_, Options::Language::CPP));
+ EXPECT_EQ(nullptr,
+ Parse("a/IFoo.aidl", primitive_parcelable, typenames_, Options::Language::NDK));
+}
+
TEST_F(AidlTest, ApiDump) {
io_delegate_.SetFileContents(
"foo/bar/IFoo.aidl",
"package foo.bar;\n"
"import foo.bar.Data;\n"
- "// comment\n"
+ "// comment @hide\n"
"interface IFoo {\n"
+ " /* @hide */\n"
" int foo(out int[] a, String b, boolean c, inout List<String> d);\n"
" int foo2(@utf8InCpp String x, inout List<String> y);\n"
" IFoo foo3(IFoo foo);\n"
" Data getData();\n"
+ " // @hide\n"
" const int A = 1;\n"
" const String STR = \"Hello\";\n"
"}\n");
io_delegate_.SetFileContents("foo/bar/Data.aidl",
"package foo.bar;\n"
"import foo.bar.IFoo;\n"
+ "/* @hide*/\n"
"parcelable Data {\n"
+ " // @hide\n"
" int x = 10;\n"
+ " // @hide\n"
" int y;\n"
" IFoo foo;\n"
" List<IFoo> a;\n"
+ " /*@hide2*/\n"
" List<foo.bar.IFoo> b;\n"
+ " // It should be @hide property\n"
" @nullable String[] c;\n"
"}\n");
io_delegate_.SetFileContents("api.aidl", "");
- vector<string> args = {"aidl", "--dumpapi", "--out=dump", "foo/bar/IFoo.aidl",
- "foo/bar/Data.aidl"};
+ vector<string> args = {"aidl", "--dumpapi", "--out=dump", "--include=.",
+ "foo/bar/IFoo.aidl", "foo/bar/Data.aidl"};
Options options = Options::From(args);
bool result = dump_api(options, io_delegate_);
ASSERT_TRUE(result);
string actual;
EXPECT_TRUE(io_delegate_.GetWrittenContents("dump/foo/bar/IFoo.aidl", &actual));
EXPECT_EQ(actual, R"(package foo.bar;
+/* @hide */
interface IFoo {
+ /* @hide */
int foo(out int[] a, String b, boolean c, inout List<String> d);
int foo2(@utf8InCpp String x, inout List<String> y);
foo.bar.IFoo foo3(foo.bar.IFoo foo);
foo.bar.Data getData();
+ /* @hide */
const int A = 1;
const String STR = "Hello";
}
@@ -815,12 +884,16 @@
EXPECT_TRUE(io_delegate_.GetWrittenContents("dump/foo/bar/Data.aidl", &actual));
EXPECT_EQ(actual, R"(package foo.bar;
+/* @hide */
parcelable Data {
+ /* @hide */
int x = 10;
+ /* @hide */
int y;
foo.bar.IFoo foo;
List<foo.bar.IFoo> a;
List<foo.bar.IFoo> b;
+ /* @hide */
@nullable String[] c;
}
)");
@@ -890,6 +963,70 @@
EXPECT_NE(0, ::android::aidl::compile_aidl(options2, io_delegate_));
}
+TEST_F(AidlTest, CheckTypeParameterInMapType) {
+ Options options = Options::From("aidl -I p p/IFoo.aidl");
+ io_delegate_.SetFileContents("p/Bar.aidl", "package p; parcelable Bar { String s; }");
+
+ io_delegate_.SetFileContents("p/IFoo.aidl",
+ "package p; interface IFoo {"
+ "Map<String, Bar> foo();}");
+ EXPECT_EQ(0, ::android::aidl::compile_aidl(options, io_delegate_));
+
+ io_delegate_.SetFileContents("p/IFoo.aidl",
+ "package p; interface IFoo {"
+ "Map<Bar, Bar> foo();}");
+ EXPECT_NE(0, ::android::aidl::compile_aidl(options, io_delegate_));
+
+ io_delegate_.SetFileContents("p/IFoo.aidl",
+ "package p; interface IFoo {"
+ "Map<String, String> foo();}");
+ EXPECT_EQ(0, ::android::aidl::compile_aidl(options, io_delegate_));
+
+ io_delegate_.SetFileContents("p/IFoo.aidl",
+ "package p; interface IFoo {"
+ "Map<String, ParcelFileDescriptor> foo();}");
+ EXPECT_EQ(0, ::android::aidl::compile_aidl(options, io_delegate_));
+}
+
+TEST_F(AidlTest, WrongGenericType) {
+ Options options = Options::From("aidl p/IFoo.aidl IFoo.java");
+ io_delegate_.SetFileContents(options.InputFiles().front(),
+ "package p; interface IFoo {"
+ "String<String> foo(); }");
+ EXPECT_NE(0, ::android::aidl::compile_aidl(options, io_delegate_));
+}
+
+TEST_F(AidlTest, UserDefinedUnstructuredGenericParcelableType) {
+ Options optionsForParcelable = Options::From("aidl -I p p/Bar.aidl");
+ io_delegate_.SetFileContents("p/Bar.aidl", "package p; parcelable Bar<T, T>;");
+ EXPECT_NE(0, ::android::aidl::compile_aidl(optionsForParcelable, io_delegate_));
+
+ Options options = Options::From("aidl -I p p/IFoo.aidl");
+ io_delegate_.SetFileContents("p/Bar.aidl", "package p; parcelable Bar;");
+ io_delegate_.SetFileContents("p/IFoo.aidl",
+ "package p; interface IFoo {"
+ "Bar<String, String> foo();}");
+ EXPECT_NE(0, ::android::aidl::compile_aidl(options, io_delegate_));
+ io_delegate_.SetFileContents("p/Bar.aidl", "package p; parcelable Bar<T>;");
+ EXPECT_NE(0, ::android::aidl::compile_aidl(options, io_delegate_));
+ io_delegate_.SetFileContents("p/Bar.aidl", "package p; parcelable Bar<T, V>;");
+ EXPECT_EQ(0, ::android::aidl::compile_aidl(options, io_delegate_));
+ io_delegate_.SetFileContents("p/IFoo.aidl",
+ "package p; interface IFoo {"
+ "Bar<String, ParcelFileDescriptor> foo();}");
+ EXPECT_EQ(0, ::android::aidl::compile_aidl(options, io_delegate_));
+
+ io_delegate_.SetFileContents("p/IFoo.aidl",
+ "package p; interface IFoo {"
+ "Bar<int, long> foo();}");
+
+ io_delegate_.SetFileContents("p/IFoo.aidl",
+ "package p; interface IFoo {"
+ "Bar<int[], long[]> foo();}");
+
+ EXPECT_EQ(0, ::android::aidl::compile_aidl(options, io_delegate_));
+}
+
TEST_F(AidlTest, FailOnMultipleTypesInSingleFile) {
std::vector<std::string> rawOptions{"aidl --lang=java -o out foo/bar/Foo.aidl",
"aidl --lang=cpp -o out -h out/include foo/bar/Foo.aidl"};
@@ -922,7 +1059,7 @@
TEST_F(AidlTest, MultipleInputFiles) {
Options options = Options::From(
- "aidl --lang=java -o out foo/bar/IFoo.aidl foo/bar/Data.aidl");
+ "aidl --lang=java -o out -I . foo/bar/IFoo.aidl foo/bar/Data.aidl");
io_delegate_.SetFileContents(options.InputFiles().at(0),
"package foo.bar;\n"
@@ -947,7 +1084,7 @@
TEST_F(AidlTest, MultipleInputFilesCpp) {
Options options = Options::From("aidl --lang=cpp -o out -h out/include "
- "foo/bar/IFoo.aidl foo/bar/Data.aidl");
+ "-I . foo/bar/IFoo.aidl foo/bar/Data.aidl");
io_delegate_.SetFileContents(options.InputFiles().at(0),
"package foo.bar;\n"
@@ -1600,5 +1737,19 @@
EXPECT_EQ(AidlError::PARSE_ERROR, reported_error);
}
+TEST_F(AidlTest, FailOnOutOfBoundsAutofilledEnum) {
+ AidlError reported_error;
+ EXPECT_EQ(nullptr, Parse("p/TestEnum.aidl",
+ R"(package p;
+ @Backing(type="byte")
+ enum TestEnum {
+ FOO = 127,
+ BAR,
+ }
+ )",
+ typenames_, Options::Language::CPP, &reported_error));
+ EXPECT_EQ(AidlError::BAD_TYPE, reported_error);
+}
+
} // namespace aidl
} // namespace android
diff --git a/build/Android.bp b/build/Android.bp
index 9cf4125..df9ea80 100644
--- a/build/Android.bp
+++ b/build/Android.bp
@@ -28,9 +28,17 @@
"aidl_interface.go",
"properties.go",
],
+ testSrcs: [
+ "aidl_test.go",
+ ],
pluginFor: ["soong_build"],
}
+aidl_interfaces_metadata {
+ name: "aidl_metadata_json",
+ visibility: ["//system/tools/aidl:__subpackages__"],
+}
+
// These configurations are inherited by all aidl-gen modules
cc_defaults {
@@ -79,8 +87,11 @@
"tests_1/some_package/Thing.aidl",
"tests_1/some_package/sub_package/*.aidl", // testing glob w/o filegroup
],
- api_dir: "api/test-piece-1",
- versions: ["1"],
+ versions: [
+ "1",
+ "2",
+ "3",
+ ],
}
aidl_interface {
@@ -93,7 +104,6 @@
imports: [
"test-piece-1",
],
- api_dir: "api/test-piece-2",
versions: ["1"],
}
@@ -106,7 +116,6 @@
imports: [
"test-piece-2",
],
- api_dir: "api/test-piece-3",
versions: ["1"],
}
@@ -119,7 +128,6 @@
imports: [
"test-piece-1",
],
- api_dir: "api/test-piece-4",
versions: ["1"],
}
@@ -131,5 +139,8 @@
imports: [
"test-piece-2",
],
- versions: ["1", "2"],
+ versions: [
+ "1",
+ "2",
+ ],
}
diff --git a/build/OWNERS b/build/OWNERS
new file mode 100644
index 0000000..4cac0f5
--- /dev/null
+++ b/build/OWNERS
@@ -0,0 +1 @@
+include platform/build/soong:/OWNERS
diff --git a/build/api/test-piece-1/1/some_package/IFoo.aidl b/build/aidl_api/test-piece-1/1/some_package/IFoo.aidl
similarity index 100%
rename from build/api/test-piece-1/1/some_package/IFoo.aidl
rename to build/aidl_api/test-piece-1/1/some_package/IFoo.aidl
diff --git a/build/api/test-piece-1/1/some_package/Thing.aidl b/build/aidl_api/test-piece-1/1/some_package/Thing.aidl
similarity index 100%
rename from build/api/test-piece-1/1/some_package/Thing.aidl
rename to build/aidl_api/test-piece-1/1/some_package/Thing.aidl
diff --git a/build/api/test-piece-1/1/some_package/sub_package/IFoo.aidl b/build/aidl_api/test-piece-1/1/some_package/sub_package/IFoo.aidl
similarity index 100%
rename from build/api/test-piece-1/1/some_package/sub_package/IFoo.aidl
rename to build/aidl_api/test-piece-1/1/some_package/sub_package/IFoo.aidl
diff --git a/build/api/test-piece-1/1/some_package/sub_package/SubThing.aidl b/build/aidl_api/test-piece-1/1/some_package/sub_package/SubThing.aidl
similarity index 100%
rename from build/api/test-piece-1/1/some_package/sub_package/SubThing.aidl
rename to build/aidl_api/test-piece-1/1/some_package/sub_package/SubThing.aidl
diff --git a/build/aidl_api/test-piece-1/2/.hash b/build/aidl_api/test-piece-1/2/.hash
new file mode 100644
index 0000000..c28f432
--- /dev/null
+++ b/build/aidl_api/test-piece-1/2/.hash
@@ -0,0 +1 @@
+09c794283cac3ff37406d1e5593f8a4f4940562f -
diff --git a/build/aidl_api/test-piece-1/2/some_package/IFoo.aidl b/build/aidl_api/test-piece-1/2/some_package/IFoo.aidl
new file mode 100644
index 0000000..e50ee27
--- /dev/null
+++ b/build/aidl_api/test-piece-1/2/some_package/IFoo.aidl
@@ -0,0 +1,24 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
+// try to edit this file. It looks like you are doing that because you have
+// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
+// function from an interface or a field from a parcelable and it broke the
+// build. That breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package some_package;
+interface IFoo {
+ /* @hide */
+ void CanYouDealWithThisThing(inout some_package.Thing parcel);
+ /* @hide */
+ void CanYouDealWithThisSubThing(inout some_package.sub_package.SubThing parcel);
+}
diff --git a/build/aidl_api/test-piece-1/2/some_package/Thing.aidl b/build/aidl_api/test-piece-1/2/some_package/Thing.aidl
new file mode 100644
index 0000000..3a30cc5
--- /dev/null
+++ b/build/aidl_api/test-piece-1/2/some_package/Thing.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
+// try to edit this file. It looks like you are doing that because you have
+// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
+// function from an interface or a field from a parcelable and it broke the
+// build. That breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package some_package;
+parcelable Thing {
+ int a;
+ int b;
+}
diff --git a/build/aidl_api/test-piece-1/2/some_package/sub_package/IFoo.aidl b/build/aidl_api/test-piece-1/2/some_package/sub_package/IFoo.aidl
new file mode 100644
index 0000000..f45996d
--- /dev/null
+++ b/build/aidl_api/test-piece-1/2/some_package/sub_package/IFoo.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
+// try to edit this file. It looks like you are doing that because you have
+// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
+// function from an interface or a field from a parcelable and it broke the
+// build. That breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package some_package.sub_package;
+interface IFoo {
+ void CanYouDealWithThisThing(inout some_package.Thing parcel);
+ void CanYouDealWithThisSubThing(inout some_package.sub_package.SubThing parcel);
+}
diff --git a/build/aidl_api/test-piece-1/2/some_package/sub_package/SubThing.aidl b/build/aidl_api/test-piece-1/2/some_package/sub_package/SubThing.aidl
new file mode 100644
index 0000000..adad68f
--- /dev/null
+++ b/build/aidl_api/test-piece-1/2/some_package/sub_package/SubThing.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
+// try to edit this file. It looks like you are doing that because you have
+// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
+// function from an interface or a field from a parcelable and it broke the
+// build. That breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package some_package.sub_package;
+parcelable SubThing {
+ int a;
+ int b;
+}
diff --git a/build/aidl_api/test-piece-1/3/.hash b/build/aidl_api/test-piece-1/3/.hash
new file mode 100644
index 0000000..24d0448
--- /dev/null
+++ b/build/aidl_api/test-piece-1/3/.hash
@@ -0,0 +1 @@
+09b8aace9633aa35010b28a424d647dfcb4db5ea -
diff --git a/build/aidl_api/test-piece-1/3/some_package/IFoo.aidl b/build/aidl_api/test-piece-1/3/some_package/IFoo.aidl
new file mode 100644
index 0000000..686805c
--- /dev/null
+++ b/build/aidl_api/test-piece-1/3/some_package/IFoo.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
+// try to edit this file. It looks like you are doing that because you have
+// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
+// function from an interface or a field from a parcelable and it broke the
+// build. That breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package some_package;
+interface IFoo {
+ void CanYouDealWithThisThing(inout some_package.Thing parcel);
+ void CanYouDealWithThisSubThing(inout some_package.sub_package.SubThing parcel);
+}
diff --git a/build/aidl_api/test-piece-1/3/some_package/Thing.aidl b/build/aidl_api/test-piece-1/3/some_package/Thing.aidl
new file mode 100644
index 0000000..3a30cc5
--- /dev/null
+++ b/build/aidl_api/test-piece-1/3/some_package/Thing.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
+// try to edit this file. It looks like you are doing that because you have
+// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
+// function from an interface or a field from a parcelable and it broke the
+// build. That breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package some_package;
+parcelable Thing {
+ int a;
+ int b;
+}
diff --git a/build/aidl_api/test-piece-1/3/some_package/sub_package/IFoo.aidl b/build/aidl_api/test-piece-1/3/some_package/sub_package/IFoo.aidl
new file mode 100644
index 0000000..f45996d
--- /dev/null
+++ b/build/aidl_api/test-piece-1/3/some_package/sub_package/IFoo.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
+// try to edit this file. It looks like you are doing that because you have
+// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
+// function from an interface or a field from a parcelable and it broke the
+// build. That breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package some_package.sub_package;
+interface IFoo {
+ void CanYouDealWithThisThing(inout some_package.Thing parcel);
+ void CanYouDealWithThisSubThing(inout some_package.sub_package.SubThing parcel);
+}
diff --git a/build/aidl_api/test-piece-1/3/some_package/sub_package/SubThing.aidl b/build/aidl_api/test-piece-1/3/some_package/sub_package/SubThing.aidl
new file mode 100644
index 0000000..adad68f
--- /dev/null
+++ b/build/aidl_api/test-piece-1/3/some_package/sub_package/SubThing.aidl
@@ -0,0 +1,22 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not
+// try to edit this file. It looks like you are doing that because you have
+// modified an AIDL interface in a backward-incompatible way, e.g., deleting a
+// function from an interface or a field from a parcelable and it broke the
+// build. That breakage is intended.
+//
+// You must not make a backward incompatible changes to the AIDL files built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package some_package.sub_package;
+parcelable SubThing {
+ int a;
+ int b;
+}
diff --git a/build/api/test-piece-2/1/INoPackage.aidl b/build/aidl_api/test-piece-2/1/INoPackage.aidl
similarity index 100%
rename from build/api/test-piece-2/1/INoPackage.aidl
rename to build/aidl_api/test-piece-2/1/INoPackage.aidl
diff --git a/build/api/test-piece-2/1/some_package/IBar.aidl b/build/aidl_api/test-piece-2/1/some_package/IBar.aidl
similarity index 100%
rename from build/api/test-piece-2/1/some_package/IBar.aidl
rename to build/aidl_api/test-piece-2/1/some_package/IBar.aidl
diff --git a/build/api/test-piece-3/1/other_package/IBaz.aidl b/build/aidl_api/test-piece-3/1/other_package/IBaz.aidl
similarity index 100%
rename from build/api/test-piece-3/1/other_package/IBaz.aidl
rename to build/aidl_api/test-piece-3/1/other_package/IBaz.aidl
diff --git a/build/api/test-piece-4/1/another_package/IFaz.aidl b/build/aidl_api/test-piece-4/1/another_package/IFaz.aidl
similarity index 100%
rename from build/api/test-piece-4/1/another_package/IFaz.aidl
rename to build/aidl_api/test-piece-4/1/another_package/IFaz.aidl
diff --git a/build/api/1/test_package/IBaz.aidl b/build/aidl_api/test-root-package/1/test_package/IBaz.aidl
similarity index 100%
rename from build/api/1/test_package/IBaz.aidl
rename to build/aidl_api/test-root-package/1/test_package/IBaz.aidl
diff --git a/build/api/2/test_package/IBaz.aidl b/build/aidl_api/test-root-package/2/test_package/IBaz.aidl
similarity index 100%
rename from build/api/2/test_package/IBaz.aidl
rename to build/aidl_api/test-root-package/2/test_package/IBaz.aidl
diff --git a/build/aidl_interface.go b/build/aidl_interface.go
index 6cca62b..1a4f3ea 100644
--- a/build/aidl_interface.go
+++ b/build/aidl_interface.go
@@ -34,18 +34,20 @@
)
var (
- aidlInterfaceSuffix = "_interface"
- aidlApiSuffix = "-api"
- langCpp = "cpp"
- langJava = "java"
- langNdk = "ndk"
- langNdkPlatform = "ndk_platform"
+ aidlInterfaceSuffix = "_interface"
+ aidlMetadataSingletonName = "aidl_metadata_json"
+ aidlApiDir = "aidl_api"
+ aidlApiSuffix = "-api"
+ langCpp = "cpp"
+ langJava = "java"
+ langNdk = "ndk"
+ langNdkPlatform = "ndk_platform"
pctx = android.NewPackageContext("android/aidl")
aidlDirPrepareRule = pctx.StaticRule("aidlDirPrepareRule", blueprint.RuleParams{
Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` +
- `touch ${out}`,
+ `touch ${out} # ${in}`,
Description: "create ${out}",
}, "outDir")
@@ -71,10 +73,21 @@
aidlDumpApiRule = pctx.StaticRule("aidlDumpApiRule", blueprint.RuleParams{
Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` +
`${aidlCmd} --dumpapi --structured ${imports} --out ${outDir} ${in} && ` +
- `(cd ${outDir} && find ./ -name "*.aidl" -exec sha1sum {} ';' && echo ${latestVersion}) | sha1sum > ${hashFile} `,
+ `(cd ${outDir} && find ./ -name "*.aidl" -print0 | LC_ALL=C sort -z | xargs -0 sha1sum && echo ${latestVersion}) | sha1sum > ${hashFile} `,
CommandDeps: []string{"${aidlCmd}"},
}, "imports", "outDir", "hashFile", "latestVersion")
+ aidlMetadataRule = pctx.StaticRule("aidlMetadataRule", blueprint.RuleParams{
+ Command: `rm -f ${out} && { ` +
+ `echo '{' && ` +
+ `echo "\"name\": \"${name}\"," && ` +
+ `echo "\"stability\": \"${stability}\"," && ` +
+ `echo "\"types\": [${types}]" && ` +
+ `echo '}' ` +
+ `;} >> ${out}`,
+ Description: "AIDL metadata: ${out}",
+ }, "name", "stability", "types")
+
aidlDumpMappingsRule = pctx.StaticRule("aidlDumpMappingsRule", blueprint.RuleParams{
Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` +
`${aidlCmd} --apimapping ${outDir}/intermediate.txt ${in} ${imports} && ` +
@@ -87,8 +100,8 @@
Command: `mkdir -p ${to} && rm -rf ${to}/* && ` +
`${bpmodifyCmd} -w -m ${name} -parameter versions -a ${version} ${bp} && ` +
`cp -rf ${apiDir}/. ${to} && ` +
- `find ${to} -type f -exec bash -c ` +
- `"cat ${apiPreamble} {} > {}.temp; mv {}.temp {}" \; && ` +
+ `find ${to} -type f -name "*.aidl" | xargs -n 1 bash -c ` +
+ `'cat ${apiPreamble} $$0 > $$0.temp && mv $$0.temp $$0' && ` +
`touch ${out}`,
CommandDeps: []string{"${bpmodifyCmd}"},
}, "to", "name", "version", "bp", "apiDir", "apiPreamble")
@@ -105,6 +118,21 @@
`(cat ${messageFile} && exit 1)`,
Description: "Check equality of ${new} and ${old}",
}, "old", "new", "messageFile", "oldHashFile", "newHashFile")
+
+ joinJsonObjectsToArrayRule = pctx.StaticRule("joinJsonObjectsToArrayRule", blueprint.RuleParams{
+ Rspfile: "$out.rsp",
+ RspfileContent: "$files",
+ Command: "rm -rf ${out} && " +
+ // Start the output array with an opening bracket.
+ "echo '[' >> ${out} && " +
+ // Append each input file and a comma to the output.
+ "for file in $$(cat ${out}.rsp); do " +
+ "cat $$file >> ${out}; echo ',' >> ${out}; " +
+ "done && " +
+ // Remove the last comma, replacing it with the closing bracket.
+ "sed -i '$$d' ${out} && echo ']' >> ${out}",
+ Description: "Joining JSON objects into array ${out}",
+ }, "files")
)
func init() {
@@ -114,6 +142,7 @@
android.RegisterModuleType("aidl_interface", aidlInterfaceFactory)
android.RegisterModuleType("aidl_mapping", aidlMappingFactory)
android.RegisterMakeVarsProvider(pctx, allAidlInterfacesMakeVars)
+ android.RegisterModuleType("aidl_interfaces_metadata", aidlInterfacesMetadataSingletonFactory)
}
// wrap(p, a, s) = [p + v + s for v in a]
@@ -134,22 +163,25 @@
return ret
}
-func checkAndUpdateSources(ctx android.ModuleContext, rawSrcs []string, inDir string) android.Paths {
+func getPaths(ctx android.ModuleContext, rawSrcs []string) (paths android.Paths, imports []string) {
srcs := android.PathsForModuleSrc(ctx, rawSrcs)
- srcs = android.PathsWithModuleSrcSubDir(ctx, srcs, inDir)
if len(srcs) == 0 {
ctx.PropertyErrorf("srcs", "No sources provided.")
}
- for _, source := range srcs {
- if source.Ext() != ".aidl" {
- ctx.PropertyErrorf("srcs", "Source must be a .aidl file: "+source.String())
+ for _, src := range srcs {
+ if src.Ext() != ".aidl" {
+ // Silently ignore non-aidl files as some filegroups have both java and aidl files together
continue
}
+ baseDir := strings.TrimSuffix(src.String(), src.Rel())
+ if baseDir != "" && !android.InList(baseDir, imports) {
+ imports = append(imports, baseDir)
+ }
}
- return srcs
+ return srcs, imports
}
func isRelativePath(path string) bool {
@@ -189,7 +221,7 @@
var _ genrule.SourceFileGenerator = (*aidlGenRule)(nil)
func (g *aidlGenRule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- srcs := checkAndUpdateSources(ctx, g.properties.Srcs, g.properties.AidlRoot)
+ srcs, imports := getPaths(ctx, g.properties.Srcs)
if ctx.Failed() {
return
@@ -199,6 +231,7 @@
g.implicitInputs = append(g.implicitInputs, genDirTimestamp)
var importPaths []string
+ importPaths = append(importPaths, imports...)
ctx.VisitDirectDeps(func(dep android.Module) {
if importedAidl, ok := dep.(*aidlInterface); ok {
importPaths = append(importPaths, importedAidl.properties.Full_import_paths...)
@@ -222,26 +255,60 @@
// This is to clean genOutDir before generating any file
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
- Rule: aidlDirPrepareRule,
- Implicits: srcs,
- Output: genDirTimestamp,
+ Rule: aidlDirPrepareRule,
+ Inputs: srcs,
+ Output: genDirTimestamp,
Args: map[string]string{
"outDir": g.genOutDir.String(),
},
})
}
-func (g *aidlGenRule) generateBuildActionsForSingleAidl(ctx android.ModuleContext, src android.Path) (android.WritablePath, android.Paths) {
- var outFile android.WritablePath
- if g.properties.Lang == langJava {
- outFile = android.PathForModuleGen(ctx, pathtools.ReplaceExtension(src.Rel(), "java"))
- } else {
- outFile = android.PathForModuleGen(ctx, pathtools.ReplaceExtension(src.Rel(), "cpp"))
+// baseDir is the directory where the package name starts. e.g. For an AIDL fil
+// mymodule/aidl_src/com/android/IFoo.aidl, baseDir is mymodule/aidl_src given that the package name is
+// com.android. The build system however don't know the package name without actually reading the AIDL file.
+// Therefore, we rely on the user to correctly set the base directory via following two methods:
+// 1) via the 'path' property of filegroup or
+// 2) via `local_include_dir' of the aidl_interface module.
+func getBaseDir(ctx android.ModuleContext, src android.Path, aidlRoot android.Path) string {
+ // By default, we try to get 1) by reading Rel() of the input path.
+ baseDir := strings.TrimSuffix(src.String(), src.Rel())
+ // However, if 2) is set and it's more specific (i.e. deeper) than 1), we use 2).
+ if strings.HasPrefix(aidlRoot.String(), baseDir) {
+ baseDir = aidlRoot.String()
}
+ return baseDir
+}
+
+func (g *aidlGenRule) generateBuildActionsForSingleAidl(ctx android.ModuleContext, src android.Path) (android.WritablePath, android.Paths) {
+ baseDir := getBaseDir(ctx, src, android.PathForModuleSrc(ctx, g.properties.AidlRoot))
+
+ var ext string
+ if g.properties.Lang == langJava {
+ ext = "java"
+ } else {
+ ext = "cpp"
+ }
+ relPath, _ := filepath.Rel(baseDir, src.String())
+ outFile := android.PathForModuleGen(ctx, pathtools.ReplaceExtension(relPath, ext))
+ implicits := g.implicitInputs
var optionalFlags []string
if g.properties.Version != "" {
optionalFlags = append(optionalFlags, "--version "+g.properties.Version)
+
+ hash := "notfrozen"
+ if !strings.HasPrefix(baseDir, ctx.Config().BuildDir()) {
+ hashFile := android.ExistentPathForSource(ctx, baseDir, ".hash")
+ if hashFile.Valid() {
+ hash = "$$(read -r <" + hashFile.Path().String() + " hash extra; printf '%s' \"$$hash\")"
+ implicits = append(implicits, hashFile.Path())
+ }
+ }
+ optionalFlags = append(optionalFlags, "--hash "+hash)
+ }
+ if g.properties.Stability != nil {
+ optionalFlags = append(optionalFlags, "--stability", *g.properties.Stability)
}
var headers android.WritablePaths
@@ -249,7 +316,7 @@
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlJavaRule,
Input: src,
- Implicits: g.implicitInputs,
+ Implicits: implicits,
Output: outFile,
Args: map[string]string{
"imports": g.importFlags,
@@ -258,8 +325,8 @@
},
})
} else {
- typeName := strings.TrimSuffix(filepath.Base(src.Rel()), ".aidl")
- packagePath := filepath.Dir(src.Rel())
+ typeName := strings.TrimSuffix(filepath.Base(relPath), ".aidl")
+ packagePath := filepath.Dir(relPath)
baseName := typeName
// TODO(b/111362593): aidl_to_cpp_common.cpp uses heuristics to figure out if
// an interface name has a leading I. Those same heuristics have been
@@ -285,10 +352,6 @@
optionalFlags = append(optionalFlags, "--log")
}
- if g.properties.Stability != nil {
- optionalFlags = append(optionalFlags, "--stability", *g.properties.Stability)
- }
-
aidlLang := g.properties.Lang
if aidlLang == langNdkPlatform {
aidlLang = "ndk"
@@ -297,7 +360,7 @@
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlCppRule,
Input: src,
- Implicits: g.implicitInputs,
+ Implicits: implicits,
Output: outFile,
ImplicitOutputs: headers,
Args: map[string]string{
@@ -344,10 +407,9 @@
type aidlApiProperties struct {
BaseName string
Srcs []string `android:"path"`
+ AidlRoot string // base directory for the input aidl file
Imports []string
- Api_dir *string
Versions []string
- AidlRoot string // base directory for the input aidl file
}
type aidlApi struct {
@@ -363,11 +425,7 @@
}
func (m *aidlApi) apiDir() string {
- if m.properties.Api_dir != nil {
- return *(m.properties.Api_dir)
- } else {
- return "api"
- }
+ return filepath.Join(aidlApiDir, m.properties.BaseName)
}
// Version of the interface at ToT if it is frozen
@@ -388,13 +446,14 @@
}
func (m *aidlApi) createApiDumpFromSource(ctx android.ModuleContext) (apiDir android.WritablePath, apiFiles android.WritablePaths, hashFile android.WritablePath) {
- srcs := checkAndUpdateSources(ctx, m.properties.Srcs, m.properties.AidlRoot)
+ srcs, imports := getPaths(ctx, m.properties.Srcs)
if ctx.Failed() {
return
}
var importPaths []string
+ importPaths = append(importPaths, imports...)
ctx.VisitDirectDeps(func(dep android.Module) {
if importedAidl, ok := dep.(*aidlInterface); ok {
importPaths = append(importPaths, importedAidl.properties.Full_import_paths...)
@@ -402,21 +461,24 @@
})
apiDir = android.PathForModuleOut(ctx, "dump")
+ aidlRoot := android.PathForModuleSrc(ctx, m.properties.AidlRoot)
for _, src := range srcs {
- apiFiles = append(apiFiles, android.PathForModuleOut(ctx, "dump", src.Rel()))
+ baseDir := getBaseDir(ctx, src, aidlRoot)
+ relPath, _ := filepath.Rel(baseDir, src.String())
+ outFile := android.PathForModuleOut(ctx, "dump", relPath)
+ apiFiles = append(apiFiles, outFile)
}
hashFile = android.PathForModuleOut(ctx, "dump", ".hash")
latestVersion := "latest-version"
if len(m.properties.Versions) >= 1 {
latestVersion = m.properties.Versions[len(m.properties.Versions)-1]
}
- imports := strings.Join(wrap("-I", importPaths, ""), " ")
ctx.ModuleBuild(pctx, android.ModuleBuildParams{
Rule: aidlDumpApiRule,
Outputs: append(apiFiles, hashFile),
Inputs: srcs,
Args: map[string]string{
- "imports": imports,
+ "imports": strings.Join(wrap("-I", importPaths, ""), " "),
"outDir": apiDir.String(),
"hashFile": hashFile.String(),
"latestVersion": latestVersion,
@@ -571,8 +633,24 @@
return m
}
+type CommonBackendProperties struct {
+ // Whether to generate code in the corresponding backend.
+ // Default: true
+ Enabled *bool
+}
+
+type CommonNativeBackendProperties struct {
+ // Whether to generate additional code for gathering information
+ // about the transactions.
+ // Default: false
+ Gen_log *bool
+
+ // VNDK properties for correspdoning backend.
+ cc.VndkProperties
+}
+
type aidlInterfaceProperties struct {
- // Vndk properties for interface library only.
+ // Vndk properties for C++/NDK libraries only (preferred to use backend-specific settings)
cc.VndkProperties
// Whether the library can be installed on the vendor image.
@@ -585,7 +663,6 @@
// TODO(b/128940869): remove it if aidl_interface can depend on framework.aidl
Include_dirs []string
// Relative path for includes. By default assumes AIDL path is relative to current directory.
- // TODO(b/111117220): automatically compute by letting AIDL parse multiple files simultaneously
Local_include_dir string
// List of .aidl files which compose this interface.
@@ -598,9 +675,6 @@
// Used by gen dependency to fill out aidl include path
Full_import_paths []string `blueprint:"mutated"`
- // Directory where API dumps are. Default is "api".
- Api_dir *string
-
// Stability promise. Currently only supports "vintf".
// If this is unset, this corresponds to an interface with stability within
// this compilation context (so an interface loaded here can only be used
@@ -614,31 +688,27 @@
Versions []string
Backend struct {
+ // Backend of the compiler generating code for Java clients.
Java struct {
- // Whether to generate Java code using Java binder APIs
- // Default: true
- Enabled *bool
+ CommonBackendProperties
// Set to the version of the sdk to compile against
// Default: system_current
Sdk_version *string
+ // Whether to compile against platform APIs instead of
+ // an SDK.
+ Platform_apis *bool
}
+ // Backend of the compiler generating code for C++ clients using
+ // libbinder (unstable C++ interface)
Cpp struct {
- // Whether to generate C++ code using C++ binder APIs
- // Default: true
- Enabled *bool
- // Whether to generate additional code for gathering information
- // about the transactions
- // Default: false
- Gen_log *bool
+ CommonBackendProperties
+ CommonNativeBackendProperties
}
+ // Backend of the compiler generating code for C++ clients using
+ // libbinder_ndk (stable C interface to system's libbinder)
Ndk struct {
- // Whether to generate C++ code using NDK binder APIs
- // Default: true
- Enabled *bool
- // Whether to generate additional code for gathering information
- // about the transactions
- // Default: false
- Gen_log *bool
+ CommonBackendProperties
+ CommonNativeBackendProperties
}
}
}
@@ -647,6 +717,8 @@
android.ModuleBase
properties aidlInterfaceProperties
+
+ computedTypes []string
}
func (i *aidlInterface) shouldGenerateJavaBackend() bool {
@@ -664,9 +736,16 @@
return i.properties.Backend.Ndk.Enabled == nil || *i.properties.Backend.Ndk.Enabled
}
-func (i *aidlInterface) checkImports(mctx android.LoadHookContext) {
+func (i *aidlInterface) gatherInterface(mctx android.LoadHookContext) {
+ aidlInterfaces := aidlInterfaces(mctx.Config())
+ aidlInterfaceMutex.Lock()
+ defer aidlInterfaceMutex.Unlock()
+ *aidlInterfaces = append(*aidlInterfaces, i)
+}
+
+func (i *aidlInterface) checkImports(mctx android.BaseModuleContext) {
for _, anImport := range i.properties.Imports {
- other := lookupInterface(anImport)
+ other := lookupInterface(anImport, mctx.Config())
if other == nil {
mctx.PropertyErrorf("imports", "Import does not exist: "+anImport)
@@ -694,31 +773,18 @@
return
}
- if i.shouldGenerateJavaBackend() {
- mctx.PropertyErrorf("stability", "Java backend does not yet support stability.")
- }
-
// TODO(b/136027762): should we allow more types of stability (e.g. for APEX) or
// should we switch this flag to be something like "vintf { enabled: true }"
if *i.properties.Stability != "vintf" {
mctx.PropertyErrorf("stability", "must be empty or \"vintf\"")
}
-
- // TODO(b/136027762): need some global way to understand AOSP interfaces. Also,
- // need the implementation for vendor extensions to be merged. For now, restrict
- // where this can be defined
- if !filepath.HasPrefix(mctx.ModuleDir(), "hardware/interfaces/") {
- mctx.PropertyErrorf("stability", "can only be set in hardware/interfaces")
- }
}
-func (i *aidlInterface) currentVersion(ctx android.BaseModuleContext) string {
- if len(i.properties.Versions) == 0 {
+func (i *aidlInterface) currentVersion(ctx android.LoadHookContext) string {
+ if !i.hasVersion() {
return ""
} else {
- latestVersion := i.properties.Versions[len(i.properties.Versions)-1]
-
- i, err := strconv.Atoi(latestVersion)
+ i, err := strconv.Atoi(i.latestVersion())
if err != nil {
ctx.PropertyErrorf("versions", "must be integers")
return ""
@@ -728,30 +794,73 @@
}
}
-func (i *aidlInterface) isCurrentVersion(ctx android.BaseModuleContext, version string) bool {
+func (i *aidlInterface) latestVersion() string {
+ if !i.hasVersion() {
+ return "0"
+ }
+ return i.properties.Versions[len(i.properties.Versions)-1]
+}
+func (i *aidlInterface) isLatestVersion(version string) bool {
+ if !i.hasVersion() {
+ return true
+ }
+ return version == i.latestVersion()
+}
+func (i *aidlInterface) hasVersion() bool {
+ return len(i.properties.Versions) > 0
+}
+
+func (i *aidlInterface) isCurrentVersion(ctx android.LoadHookContext, version string) bool {
return version == i.currentVersion(ctx)
}
-func (i *aidlInterface) versionedName(ctx android.BaseModuleContext, version string) string {
+// This function returns module name with version. Assume that there is foo of which latest version is 2
+// Version -> Module name
+// "1"->foo-V1
+// "2"->foo-V2
+// "3"(unfrozen)->foo-unstable
+// ""-> foo
+func (i *aidlInterface) versionedName(ctx android.LoadHookContext, version string) string {
name := i.ModuleBase.Name()
- if !i.isCurrentVersion(ctx, version) {
- name = name + "-V" + version
+ if version == "" {
+ return name
}
- return name
+ if i.isCurrentVersion(ctx, version) {
+ return name + "-unstable"
+ }
+ return name + "-V" + version
}
-func (i *aidlInterface) srcsForVersion(mctx android.LoadHookContext, version string) (srcs []string, base string) {
+// This function returns C++ artifact's name. Mostly, it returns same as versionedName(),
+// But, it returns different value only if it is the case below.
+// Assume that there is foo of which latest version is 2
+// foo-unstable -> foo-V3
+// foo -> foo-V2 (latest frozen version)
+// Assume that there is bar of which version hasn't been defined yet.
+// bar -> bar-V1
+func (i *aidlInterface) cppOutputName(version string) string {
+ name := i.ModuleBase.Name()
+ // Even if the module doesn't have version, it returns with version(-V1)
+ if !i.hasVersion() {
+ // latestVersion() always returns "0"
+ i, err := strconv.Atoi(i.latestVersion())
+ if err != nil {
+ panic(err)
+ }
+ return name + "-V" + strconv.Itoa(i+1)
+ }
+ if version == "" {
+ version = i.latestVersion()
+ }
+ return name + "-V" + version
+}
+
+func (i *aidlInterface) srcsForVersion(mctx android.LoadHookContext, version string) (srcs []string, aidlRoot string) {
if i.isCurrentVersion(mctx, version) {
return i.properties.Srcs, i.properties.Local_include_dir
} else {
- var apiDir string
- if i.properties.Api_dir != nil {
- apiDir = *(i.properties.Api_dir)
- } else {
- apiDir = "api"
- }
- base = filepath.Join(apiDir, version)
- full_paths, err := mctx.GlobWithDeps(filepath.Join(mctx.ModuleDir(), base, "**/*.aidl"), nil)
+ aidlRoot = filepath.Join(aidlApiDir, i.ModuleBase.Name(), version)
+ full_paths, err := mctx.GlobWithDeps(filepath.Join(mctx.ModuleDir(), aidlRoot, "**/*.aidl"), nil)
if err != nil {
panic(err)
}
@@ -759,7 +868,7 @@
// Here, we need path local to the module
srcs = append(srcs, strings.TrimPrefix(path, mctx.ModuleDir()+"/"))
}
- return srcs, base
+ return srcs, aidlRoot
}
}
@@ -773,7 +882,7 @@
i.properties.Full_import_paths = importPaths
- i.checkImports(mctx)
+ i.gatherInterface(mctx)
i.checkStability(mctx)
if mctx.Failed() {
@@ -784,31 +893,40 @@
currentVersion := i.currentVersion(mctx)
+ versionsForCpp := make([]string, len(i.properties.Versions))
+ copy(versionsForCpp, i.properties.Versions)
+ if i.hasVersion() {
+ // In C++ library, AIDL doesn't create the module of which name is with latest version,
+ // instead of it, there is a module without version.
+ versionsForCpp[len(i.properties.Versions)-1] = ""
+ }
if i.shouldGenerateCppBackend() {
libs = append(libs, addCppLibrary(mctx, i, currentVersion, langCpp))
- for _, version := range i.properties.Versions {
+ for _, version := range versionsForCpp {
addCppLibrary(mctx, i, version, langCpp)
}
}
if i.shouldGenerateNdkBackend() {
- // TODO(b/119771576): inherit properties and export 'is vendor' computation from cc.go
if !proptools.Bool(i.properties.Vendor_available) {
libs = append(libs, addCppLibrary(mctx, i, currentVersion, langNdk))
- for _, version := range i.properties.Versions {
+ for _, version := range versionsForCpp {
addCppLibrary(mctx, i, version, langNdk)
}
}
// TODO(b/121157555): combine with '-ndk' variant
libs = append(libs, addCppLibrary(mctx, i, currentVersion, langNdkPlatform))
- for _, version := range i.properties.Versions {
+ for _, version := range versionsForCpp {
addCppLibrary(mctx, i, version, langNdkPlatform)
}
}
-
+ versionsForJava := i.properties.Versions
+ if i.hasVersion() {
+ versionsForJava = append(i.properties.Versions, "")
+ }
if i.shouldGenerateJavaBackend() {
libs = append(libs, addJavaLibrary(mctx, i, currentVersion))
- for _, version := range i.properties.Versions {
+ for _, version := range versionsForJava {
addJavaLibrary(mctx, i, version)
}
}
@@ -825,8 +943,11 @@
func addCppLibrary(mctx android.LoadHookContext, i *aidlInterface, version string, lang string) string {
cppSourceGen := i.versionedName(mctx, version) + "-" + lang + "-source"
cppModuleGen := i.versionedName(mctx, version) + "-" + lang
-
- srcs, base := i.srcsForVersion(mctx, version)
+ cppOutputGen := i.cppOutputName(version) + "-" + lang
+ if i.hasVersion() && version == "" {
+ version = i.latestVersion()
+ }
+ srcs, aidlRoot := i.srcsForVersion(mctx, version)
if len(srcs) == 0 {
// This can happen when the version is about to be frozen; the version
// directory is created but API dump hasn't been copied there.
@@ -834,18 +955,20 @@
return ""
}
- genLog := false
+ var commonProperties *CommonNativeBackendProperties
if lang == langCpp {
- genLog = proptools.Bool(i.properties.Backend.Cpp.Gen_log)
+ commonProperties = &i.properties.Backend.Cpp.CommonNativeBackendProperties
} else if lang == langNdk || lang == langNdkPlatform {
- genLog = proptools.Bool(i.properties.Backend.Ndk.Gen_log)
+ commonProperties = &i.properties.Backend.Ndk.CommonNativeBackendProperties
}
+ genLog := proptools.Bool(commonProperties.Gen_log)
+
mctx.CreateModule(aidlGenFactory, &nameProperties{
Name: proptools.StringPtr(cppSourceGen),
}, &aidlGenProperties{
Srcs: srcs,
- AidlRoot: base,
+ AidlRoot: aidlRoot,
Imports: concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
Stability: i.properties.Stability,
Lang: lang,
@@ -904,7 +1027,8 @@
Stl: stl,
Cpp_std: cpp_std,
Cflags: append(addCflags, "-Wextra", "-Wall", "-Werror"),
- }, &i.properties.VndkProperties)
+ Stem: proptools.StringPtr(cppOutputGen),
+ }, &i.properties.VndkProperties, &commonProperties.VndkProperties)
return cppModuleGen
}
@@ -912,8 +1036,10 @@
func addJavaLibrary(mctx android.LoadHookContext, i *aidlInterface, version string) string {
javaSourceGen := i.versionedName(mctx, version) + "-java-source"
javaModuleGen := i.versionedName(mctx, version) + "-java"
-
- srcs, base := i.srcsForVersion(mctx, version)
+ if i.hasVersion() && version == "" {
+ version = i.latestVersion()
+ }
+ srcs, aidlRoot := i.srcsForVersion(mctx, version)
if len(srcs) == 0 {
// This can happen when the version is about to be frozen; the version
// directory is created but API dump hasn't been copied there.
@@ -921,13 +1047,17 @@
return ""
}
- sdkVersion := proptools.StringDefault(i.properties.Backend.Java.Sdk_version, "system_current")
+ sdkVersion := i.properties.Backend.Java.Sdk_version
+ if !proptools.Bool(i.properties.Backend.Java.Platform_apis) && sdkVersion == nil {
+ // platform apis requires no default
+ sdkVersion = proptools.StringPtr("system_current")
+ }
mctx.CreateModule(aidlGenFactory, &nameProperties{
Name: proptools.StringPtr(javaSourceGen),
}, &aidlGenProperties{
Srcs: srcs,
- AidlRoot: base,
+ AidlRoot: aidlRoot,
Imports: concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
Stability: i.properties.Stability,
Lang: langJava,
@@ -936,12 +1066,13 @@
})
mctx.CreateModule(java.LibraryFactory, &javaProperties{
- Name: proptools.StringPtr(javaModuleGen),
- Installable: proptools.BoolPtr(true),
- Defaults: []string{"aidl-java-module-defaults"},
- Sdk_version: proptools.StringPtr(sdkVersion),
- Static_libs: wrap("", i.properties.Imports, "-java"),
- Srcs: []string{":" + javaSourceGen},
+ Name: proptools.StringPtr(javaModuleGen),
+ Installable: proptools.BoolPtr(true),
+ Defaults: []string{"aidl-java-module-defaults"},
+ Sdk_version: sdkVersion,
+ Platform_apis: i.properties.Backend.Java.Platform_apis,
+ Static_libs: wrap("", i.properties.Imports, "-java"),
+ Srcs: []string{":" + javaSourceGen},
})
return javaModuleGen
@@ -949,14 +1080,14 @@
func addApiModule(mctx android.LoadHookContext, i *aidlInterface) string {
apiModule := i.ModuleBase.Name() + aidlApiSuffix
+ srcs, aidlRoot := i.srcsForVersion(mctx, i.currentVersion(mctx))
mctx.CreateModule(aidlApiFactory, &nameProperties{
Name: proptools.StringPtr(apiModule),
}, &aidlApiProperties{
BaseName: i.ModuleBase.Name(),
- Srcs: i.properties.Srcs,
+ Srcs: srcs,
+ AidlRoot: aidlRoot,
Imports: concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
- Api_dir: i.properties.Api_dir,
- AidlRoot: i.properties.Local_include_dir,
Versions: i.properties.Versions,
})
return apiModule
@@ -966,28 +1097,41 @@
return i.ModuleBase.Name() + aidlInterfaceSuffix
}
func (i *aidlInterface) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ aidlRoot := android.PathForModuleSrc(ctx, i.properties.Local_include_dir)
+ for _, src := range android.PathsForModuleSrc(ctx, i.properties.Srcs) {
+ baseDir := getBaseDir(ctx, src, aidlRoot)
+ relPath, _ := filepath.Rel(baseDir, src.String())
+ computedType := strings.TrimSuffix(strings.ReplaceAll(relPath, "/", "."), ".aidl")
+ i.computedTypes = append(i.computedTypes, computedType)
+ }
}
func (i *aidlInterface) DepsMutator(ctx android.BottomUpMutatorContext) {
+ i.checkImports(ctx)
+
+ ctx.AddReverseDependency(ctx.Module(), nil, aidlMetadataSingletonName)
}
-var aidlInterfaceMutex sync.Mutex
-var aidlInterfaces []*aidlInterface
+var (
+ aidlInterfacesKey = android.NewOnceKey("aidlInterfaces")
+ aidlInterfaceMutex sync.Mutex
+)
+
+func aidlInterfaces(config android.Config) *[]*aidlInterface {
+ return config.Once(aidlInterfacesKey, func() interface{} {
+ return &[]*aidlInterface{}
+ }).(*[]*aidlInterface)
+}
func aidlInterfaceFactory() android.Module {
i := &aidlInterface{}
i.AddProperties(&i.properties)
android.InitAndroidModule(i)
android.AddLoadHook(i, func(ctx android.LoadHookContext) { aidlInterfaceHook(ctx, i) })
-
- aidlInterfaceMutex.Lock()
- aidlInterfaces = append(aidlInterfaces, i)
- aidlInterfaceMutex.Unlock()
-
return i
}
-func lookupInterface(name string) *aidlInterface {
- for _, i := range aidlInterfaces {
+func lookupInterface(name string, config android.Config) *aidlInterface {
+ for _, i := range *aidlInterfaces(config) {
if i.ModuleBase.Name() == name {
return i
}
@@ -995,6 +1139,66 @@
return nil
}
+func aidlInterfacesMetadataSingletonFactory() android.Module {
+ i := &aidlInterfacesMetadataSingleton{}
+ android.InitAndroidModule(i)
+ return i
+}
+
+type aidlInterfacesMetadataSingleton struct {
+ android.ModuleBase
+
+ metadataPath android.OutputPath
+}
+
+var _ android.OutputFileProducer = (*aidlInterfacesMetadataSingleton)(nil)
+
+func (m *aidlInterfacesMetadataSingleton) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ if m.Name() != aidlMetadataSingletonName {
+ ctx.PropertyErrorf("name", "must be %s", aidlMetadataSingletonName)
+ return
+ }
+
+ var metadataOutputs android.Paths
+ ctx.VisitDirectDeps(func(m android.Module) {
+ if !m.ExportedToMake() {
+ return
+ }
+ if t, ok := m.(*aidlInterface); ok {
+ metadataPath := android.PathForModuleOut(ctx, "metadata_"+m.Name())
+ ctx.Build(pctx, android.BuildParams{
+ Rule: aidlMetadataRule,
+ Output: metadataPath,
+ Args: map[string]string{
+ "name": t.Name(),
+ "stability": proptools.StringDefault(t.properties.Stability, ""),
+ "types": strings.Join(wrap(`\"`, t.computedTypes, `\"`), ", "),
+ },
+ })
+ metadataOutputs = append(metadataOutputs, metadataPath)
+ }
+ })
+
+ m.metadataPath = android.PathForModuleOut(ctx, "aidl_metadata.json").OutputPath
+
+ ctx.Build(pctx, android.BuildParams{
+ Rule: joinJsonObjectsToArrayRule,
+ Inputs: metadataOutputs,
+ Output: m.metadataPath,
+ Args: map[string]string{
+ "files": strings.Join(metadataOutputs.Strings(), " "),
+ },
+ })
+}
+
+func (m *aidlInterfacesMetadataSingleton) OutputFiles(tag string) (android.Paths, error) {
+ if tag != "" {
+ return nil, fmt.Errorf("unsupported tag %q", tag)
+ }
+
+ return android.Paths{m.metadataPath}, nil
+}
+
type aidlMappingProperties struct {
// Source file of this prebuilt.
Srcs []string `android:"path"`
@@ -1011,21 +1215,8 @@
}
func (s *aidlMapping) GenerateAndroidBuildActions(ctx android.ModuleContext) {
- var aidlSrcs android.Paths
- var importDirs []string
+ srcs, imports := getPaths(ctx, s.properties.Srcs)
- srcs := android.PathsForModuleSrc(ctx, s.properties.Srcs)
- for _, file := range srcs {
- if file.Ext() == ".aidl" {
- aidlSrcs = append(aidlSrcs, file)
- baseDir := strings.TrimSuffix(file.String(), file.Rel())
- if baseDir != "" && !android.InList(baseDir, importDirs) {
- importDirs = append(importDirs, baseDir)
- }
- }
- }
-
- imports := android.JoinWithPrefix(importDirs, " -I")
s.outputFilePath = android.PathForModuleOut(ctx, s.properties.Output)
outDir := android.PathForModuleGen(ctx)
ctx.Build(pctx, android.BuildParams{
@@ -1033,7 +1224,7 @@
Inputs: srcs,
Output: s.outputFilePath,
Args: map[string]string{
- "imports": imports,
+ "imports": android.JoinWithPrefix(imports, " -I"),
"outDir": outDir.String(),
},
})
diff --git a/build/aidl_test.go b/build/aidl_test.go
new file mode 100644
index 0000000..7a9ff92
--- /dev/null
+++ b/build/aidl_test.go
@@ -0,0 +1,483 @@
+// 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 aidl
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/google/blueprint"
+
+ "android/soong/android"
+ "android/soong/cc"
+ "android/soong/java"
+)
+
+var buildDir string
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "soong_aidl_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
+
+type testCustomizer func(fs map[string][]byte, config android.Config)
+
+func withFiles(files map[string][]byte) testCustomizer {
+ return func(fs map[string][]byte, config android.Config) {
+ for k, v := range files {
+ fs[k] = v
+ }
+ }
+}
+
+func _testAidl(t *testing.T, bp string, customizers ...testCustomizer) (*android.TestContext, android.Config) {
+ t.Helper()
+
+ bp = bp + java.GatherRequiredDepsForTest()
+ bp = bp + cc.GatherRequiredDepsForTest(android.Android)
+ bp = bp + `
+ java_defaults {
+ name: "aidl-java-module-defaults",
+ }
+ cc_defaults {
+ name: "aidl-cpp-module-defaults",
+ }
+ cc_library {
+ name: "libbinder",
+ }
+ cc_library {
+ name: "libutils",
+ }
+ cc_library {
+ name: "libbinder_ndk",
+ }
+ ndk_library {
+ name: "libbinder_ndk",
+ symbol_file: "libbinder_ndk.map.txt",
+ first_version: "29",
+ }
+ ndk_prebuilt_shared_stl {
+ name: "ndk_libc++_shared",
+ }
+ ndk_prebuilt_static_stl {
+ name: "ndk_libunwind",
+ }
+ ndk_prebuilt_object {
+ name: "ndk_crtbegin_dynamic.27",
+ sdk_version: "27",
+ }
+ ndk_prebuilt_object {
+ name: "ndk_crtbegin_so.27",
+ sdk_version: "27",
+ }
+ ndk_prebuilt_object {
+ name: "ndk_crtbegin_static.27",
+ sdk_version: "27",
+ }
+ ndk_prebuilt_object {
+ name: "ndk_crtend_android.27",
+ sdk_version: "27",
+ }
+ ndk_prebuilt_object {
+ name: "ndk_crtend_so.27",
+ sdk_version: "27",
+ }
+ ndk_library {
+ name: "libc",
+ symbol_file: "libc.map.txt",
+ first_version: "9",
+ }
+ ndk_library {
+ name: "libm",
+ symbol_file: "libm.map.txt",
+ first_version: "9",
+ }
+ ndk_library {
+ name: "libdl",
+ symbol_file: "libdl.map.txt",
+ first_version: "9",
+ }
+ aidl_interfaces_metadata {
+ name: "aidl_metadata_json",
+ }
+ `
+ fs := map[string][]byte{
+ "a.java": nil,
+ "AndroidManifest.xml": nil,
+ "build/make/target/product/security/testkey": nil,
+ "framework/aidl/a.aidl": nil,
+ "IFoo.aidl": nil,
+ "libbinder_ndk.map.txt": nil,
+ "libc.map.txt": nil,
+ "libdl.map.txt": nil,
+ "libm.map.txt": nil,
+ "prebuilts/ndk/current/platforms/android-27/arch-arm/usr/lib/ndk_crtbegin_so.so": nil,
+ "prebuilts/ndk/current/platforms/android-27/arch-arm64/usr/lib/ndk_crtbegin_dynamic.o": nil,
+ "prebuilts/ndk/current/platforms/android-27/arch-arm64/usr/lib/ndk_crtbegin_so.so": nil,
+ "prebuilts/ndk/current/platforms/android-27/arch-arm64/usr/lib/ndk_crtbegin_static.a": nil,
+ "prebuilts/ndk/current/platforms/android-27/arch-arm64/usr/lib/ndk_crtend.so": nil,
+ "prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs/ndk_libc++_shared.so": nil,
+ "system/tools/aidl/build/api_preamble.txt": nil,
+ "system/tools/aidl/build/message_check_compatibility.txt": nil,
+ }
+
+ for _, c := range customizers {
+ // The fs now needs to be populated before creating the config, call customizers twice
+ // for now, once to get any fs changes, and later after the config was created to
+ // set product variables or targets.
+ tempConfig := android.TestArchConfig(buildDir, nil, bp, fs)
+ c(fs, tempConfig)
+ }
+
+ config := android.TestArchConfig(buildDir, nil, bp, fs)
+
+ for _, c := range customizers {
+ // The fs now needs to be populated before creating the config, call customizers twice
+ // for now, earlier to get any fs changes, and now after the config was created to
+ // set product variables or targets.
+ tempFS := map[string][]byte{}
+ c(tempFS, config)
+ }
+
+ ctx := android.NewTestArchContext()
+ cc.RegisterRequiredBuildComponentsForTest(ctx)
+ ctx.RegisterModuleType("aidl_interface", aidlInterfaceFactory)
+ ctx.RegisterModuleType("aidl_interfaces_metadata", aidlInterfacesMetadataSingletonFactory)
+ ctx.RegisterModuleType("android_app", java.AndroidAppFactory)
+ ctx.RegisterModuleType("java_defaults", func() android.Module {
+ return java.DefaultsFactory()
+ })
+ ctx.RegisterModuleType("java_library_static", java.LibraryStaticFactory)
+ ctx.RegisterModuleType("java_library", java.LibraryFactory)
+ ctx.RegisterModuleType("java_system_modules", java.SystemModulesFactory)
+ ctx.RegisterModuleType("ndk_library", cc.NdkLibraryFactory)
+ ctx.RegisterModuleType("ndk_prebuilt_object", cc.NdkPrebuiltObjectFactory)
+ ctx.RegisterModuleType("ndk_prebuilt_shared_stl", cc.NdkPrebuiltSharedStlFactory)
+ ctx.RegisterModuleType("ndk_prebuilt_static_stl", cc.NdkPrebuiltStaticStlFactory)
+
+ ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+ ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
+
+ ctx.Register(config)
+
+ return ctx, config
+}
+
+func testAidl(t *testing.T, bp string, customizers ...testCustomizer) (*android.TestContext, android.Config) {
+ t.Helper()
+ ctx, config := _testAidl(t, bp, customizers...)
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ android.FailIfErrored(t, errs)
+ _, errs = ctx.PrepareBuildActions(config)
+ android.FailIfErrored(t, errs)
+ return ctx, config
+}
+
+func testAidlError(t *testing.T, pattern, bp string, customizers ...testCustomizer) {
+ t.Helper()
+ ctx, config := _testAidl(t, bp, customizers...)
+ _, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+ if len(errs) > 0 {
+ android.FailIfNoMatchingErrors(t, pattern, errs)
+ return
+ }
+ _, errs = ctx.PrepareBuildActions(config)
+ if len(errs) > 0 {
+ android.FailIfNoMatchingErrors(t, pattern, errs)
+ return
+ }
+ t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+}
+
+// asserts that there are expected module regardless of variants
+func assertModulesExists(t *testing.T, ctx *android.TestContext, names ...string) {
+ missing := []string{}
+ for _, name := range names {
+ variants := ctx.ModuleVariantsForTests(name)
+ if len(variants) == 0 {
+ missing = append(missing, name)
+ }
+ }
+ if len(missing) > 0 {
+ // find all the modules that do exist
+ allModuleNames := make(map[string]bool)
+ ctx.VisitAllModules(func(m blueprint.Module) {
+ allModuleNames[ctx.ModuleName(m)] = true
+ })
+ t.Errorf("expected modules(%v) not found. all modules: %v", missing, android.SortedStringKeys(allModuleNames))
+ }
+}
+
+func TestCreatesModulesWithNoVersions(t *testing.T) {
+ ctx, _ := testAidl(t, `
+ aidl_interface {
+ name: "foo",
+ srcs: [
+ "IFoo.aidl",
+ ],
+ }
+ `)
+
+ assertModulesExists(t, ctx, "foo-java", "foo-cpp", "foo-ndk", "foo-ndk_platform")
+}
+
+func TestCreatesModulesWithFrozenVersions(t *testing.T) {
+ // Each version should be under aidl_api/<name>/<ver>
+ testAidlError(t, `aidl_api/foo/1`, `
+ aidl_interface {
+ name: "foo",
+ srcs: [
+ "IFoo.aidl",
+ ],
+ versions: [
+ "1",
+ ],
+ }
+ `)
+
+ ctx, _ := testAidl(t, `
+ aidl_interface {
+ name: "foo",
+ srcs: [
+ "IFoo.aidl",
+ ],
+ versions: [
+ "1",
+ ],
+ }
+ `, withFiles(map[string][]byte{
+ "aidl_api/foo/1/foo.1.aidl": nil,
+ }))
+
+ // For alias for the latest frozen version (=1)
+ assertModulesExists(t, ctx, "foo-java", "foo-cpp", "foo-ndk", "foo-ndk_platform")
+
+ // For frozen version "1"
+ // Note that it is not yet implemented to generate native modules for latest frozen version
+ assertModulesExists(t, ctx, "foo-V1-java")
+
+ // For ToT (current)
+ assertModulesExists(t, ctx, "foo-unstable-java", "foo-unstable-cpp", "foo-unstable-ndk", "foo-unstable-ndk_platform")
+}
+
+const (
+ androidVariant = "android_common"
+ nativeVariant = "android_arm_armv7-a-neon_shared"
+)
+
+func TestNativeOutputIsAlwaysVersioned(t *testing.T) {
+ var ctx *android.TestContext
+ assertOutput := func(moduleName, variant, outputFilename string) {
+ t.Helper()
+ producer, ok := ctx.ModuleForTests(moduleName, variant).Module().(android.OutputFileProducer)
+ if !ok {
+ t.Errorf("%s(%s): should be OutputFileProducer.", moduleName, variant)
+ }
+ paths, err := producer.OutputFiles("")
+ if err != nil {
+ t.Errorf("%s(%s): failed to get OutputFiles: %v", moduleName, variant, err)
+ }
+ if len(paths) != 1 || paths[0].Base() != outputFilename {
+ t.Errorf("%s(%s): expected output %q, but got %v", moduleName, variant, outputFilename, paths)
+ }
+ }
+
+ // No versions
+ ctx, _ = testAidl(t, `
+ aidl_interface {
+ name: "foo",
+ srcs: [
+ "IFoo.aidl",
+ ],
+ }
+ `)
+
+ assertOutput("foo-java", androidVariant, "foo-java.jar")
+ assertOutput("foo-cpp", nativeVariant, "foo-V1-cpp.so")
+
+ // With versions: "1", "2"
+ ctx, _ = testAidl(t, `
+ aidl_interface {
+ name: "foo",
+ srcs: [
+ "IFoo.aidl",
+ ],
+ versions: [
+ "1", "2",
+ ],
+ }
+ `, withFiles(map[string][]byte{
+ "aidl_api/foo/1/foo.1.aidl": nil,
+ "aidl_api/foo/2/foo.2.aidl": nil,
+ }))
+
+ // alias for the latest frozen version (=2)
+ assertOutput("foo-java", androidVariant, "foo-java.jar")
+ assertOutput("foo-cpp", nativeVariant, "foo-V2-cpp.so")
+
+ // frozen "1"
+ assertOutput("foo-V1-java", androidVariant, "foo-V1-java.jar")
+ assertOutput("foo-V1-cpp", nativeVariant, "foo-V1-cpp.so")
+
+ // tot
+ assertOutput("foo-unstable-java", androidVariant, "foo-unstable-java.jar")
+ assertOutput("foo-unstable-cpp", nativeVariant, "foo-V3-cpp.so")
+
+ // skip ndk/ndk_platform since they follow the same rule with cpp
+}
+
+func TestGenLogForNativeBackendRequiresJson(t *testing.T) {
+ testAidlError(t, `"foo-cpp" depends on .*"libjsoncpp"`, `
+ aidl_interface {
+ name: "foo",
+ srcs: [
+ "IFoo.aidl",
+ ],
+ backend: {
+ cpp: {
+ gen_log: true,
+ },
+ },
+ }
+ `)
+ testAidl(t, `
+ aidl_interface {
+ name: "foo",
+ srcs: [
+ "IFoo.aidl",
+ ],
+ backend: {
+ cpp: {
+ gen_log: true,
+ },
+ },
+ }
+ cc_library {
+ name: "libjsoncpp",
+ }
+ `)
+}
+
+func TestImports(t *testing.T) {
+ testAidlError(t, `Import does not exist:`, `
+ aidl_interface {
+ name: "foo",
+ srcs: [
+ "IFoo.aidl",
+ ],
+ imports: [
+ "bar",
+ ]
+ }
+ `)
+
+ testAidlError(t, `backend.java.enabled: Java backend not enabled in the imported AIDL interface "bar"`, `
+ aidl_interface {
+ name: "foo",
+ srcs: [
+ "IFoo.aidl",
+ ],
+ imports: [
+ "bar",
+ ]
+ }
+ aidl_interface {
+ name: "bar",
+ srcs: [
+ "IBar.aidl",
+ ],
+ backend: {
+ java: {
+ enabled: false,
+ },
+ },
+ }
+ `, withFiles(map[string][]byte{
+ "IBar.aidl": nil,
+ }))
+
+ testAidlError(t, `backend.cpp.enabled: C\+\+ backend not enabled in the imported AIDL interface "bar"`, `
+ aidl_interface {
+ name: "foo",
+ srcs: [
+ "IFoo.aidl",
+ ],
+ imports: [
+ "bar",
+ ]
+ }
+ aidl_interface {
+ name: "bar",
+ srcs: [
+ "IBar.aidl",
+ ],
+ backend: {
+ cpp: {
+ enabled: false,
+ },
+ },
+ }
+ `, withFiles(map[string][]byte{
+ "IBar.aidl": nil,
+ }))
+
+ ctx, _ := testAidl(t, `
+ aidl_interface {
+ name: "foo",
+ srcs: [
+ "IFoo.aidl",
+ ],
+ imports: [
+ "bar",
+ ]
+ }
+ aidl_interface {
+ name: "bar",
+ srcs: [
+ "IBar.aidl",
+ ],
+ }
+ `, withFiles(map[string][]byte{
+ "IBar.aidl": nil,
+ }))
+
+ ldRule := ctx.ModuleForTests("foo-cpp", nativeVariant).Rule("ld")
+ libFlags := ldRule.Args["libFlags"]
+ libBar := filepath.Join("bar-cpp", nativeVariant, "bar-V1-cpp.so")
+ if !strings.Contains(libFlags, libBar) {
+ t.Errorf("%q is not found in %q", libBar, libFlags)
+ }
+}
diff --git a/build/go.mod b/build/go.mod
new file mode 100644
index 0000000..3ef7a49
--- /dev/null
+++ b/build/go.mod
@@ -0,0 +1,14 @@
+module android/soong/aidl
+
+require (
+ android/soong v0.0.0
+ github.com/google/blueprint v0.0.0
+)
+
+replace android/soong v0.0.0 => ../../../../build/soong
+
+replace github.com/golang/protobuf v0.0.0 => ../../../../external/golang-protobuf
+
+replace github.com/google/blueprint v0.0.0 => ../../../../build/blueprint
+
+go 1.13
diff --git a/build/properties.go b/build/properties.go
index b59db61..62c6a1a 100644
--- a/build/properties.go
+++ b/build/properties.go
@@ -43,16 +43,18 @@
Stl *string
Cpp_std *string
Cflags []string
+ Stem *string
}
type javaProperties struct {
- Name *string
- Owner *string
- Defaults []string
- Installable *bool
- Sdk_version *string
- Srcs []string
- Static_libs []string
+ Name *string
+ Owner *string
+ Defaults []string
+ Installable *bool
+ Sdk_version *string
+ Platform_apis *bool
+ Srcs []string
+ Static_libs []string
}
type phonyProperties struct {
diff --git a/build/test_package/IBaz.aidl b/build/test_package/IBaz.aidl
index 6a63292..357dc50 100644
--- a/build/test_package/IBaz.aidl
+++ b/build/test_package/IBaz.aidl
@@ -26,9 +26,7 @@
void writePFD(in ParcelFileDescriptor fd);
void readWritePFD(inout ParcelFileDescriptor fd);
- // TODO(b/112664205) uncomment these when we have the support for array type in
- // the ndk backend
- //ParcelFileDescriptor[] readPFDArray();
- //void writePFDArray(in ParcelFileDescriptor[] fds);
- //void readWritePFDArray(inout ParcelFileDescriptor[] fds);
+ ParcelFileDescriptor[] readPFDArray();
+ void writePFDArray(in ParcelFileDescriptor[] fds);
+ void readWritePFDArray(inout ParcelFileDescriptor[] fds);
}
diff --git a/code_writer.cpp b/code_writer.cpp
index 6d54d40..096c895 100644
--- a/code_writer.cpp
+++ b/code_writer.cpp
@@ -89,12 +89,12 @@
}
CodeWriter& CodeWriter::operator<<(const char* s) {
- Write(s);
+ Write("%s", s);
return *this;
}
CodeWriter& CodeWriter::operator<<(const std::string& str) {
- Write(str.c_str());
+ Write("%s", str.c_str());
return *this;
}
diff --git a/code_writer.h b/code_writer.h
index 77e9385..d890d52 100644
--- a/code_writer.h
+++ b/code_writer.h
@@ -41,7 +41,7 @@
static CodeWriterPtr ForString(std::string* buf);
// Write a formatted string to this writer in the usual printf sense.
// Returns false on error.
- virtual bool Write(const char* format, ...);
+ virtual bool Write(const char* format, ...) __attribute__((format(printf, 2, 3)));
void Indent();
void Dedent();
virtual bool Close();
diff --git a/generate_aidl_mappings.cpp b/generate_aidl_mappings.cpp
index 1624ee6..994d6a3 100644
--- a/generate_aidl_mappings.cpp
+++ b/generate_aidl_mappings.cpp
@@ -24,7 +24,7 @@
namespace mappings {
std::string dump_location(const AidlNode& method) {
- return method.PrintLocation();
+ return method.PrintLine();
}
SignatureMap generate_mappings(const AidlDefinedType* defined_type,
diff --git a/generate_cpp.cpp b/generate_cpp.cpp
index 70b9acb..b016b68 100644
--- a/generate_cpp.cpp
+++ b/generate_cpp.cpp
@@ -99,6 +99,11 @@
vector<string> method_arguments;
for (const unique_ptr<AidlArgument>& a : method.GetArguments()) {
string literal;
+ // b/144943748: CppNameOf FileDescriptor is unique_fd. Don't pass it by
+ // const reference but by value to make it easier for the user to keep
+ // it beyond the scope of the call. unique_fd is a thin wrapper for an
+ // int (fd) so passing by value is not expensive.
+ const bool nonCopyable = IsNonCopyableType(a->GetType(), typenames);
if (for_declaration) {
// Method declarations need typenames, pointers to out params, and variable
// names that match the .aidl specification.
@@ -114,7 +119,7 @@
// We pass in parameters that are not primitives by const reference.
// Arrays of primitives are not primitives.
- if (!(isPrimitive || isEnum) || a->GetType().IsArray()) {
+ if (!(isPrimitive || isEnum || nonCopyable) || a->GetType().IsArray()) {
literal = "const " + literal + "&";
}
}
@@ -122,8 +127,14 @@
literal += " " + a->GetName();
}
} else {
- if (a->IsOut()) { literal = "&"; }
- literal += BuildVarName(*a);
+ std::string varName = BuildVarName(*a);
+ if (a->IsOut()) {
+ literal = "&" + varName;
+ } else if (nonCopyable) {
+ literal = "std::move(" + varName + ")";
+ } else {
+ literal = varName;
+ }
}
method_arguments.push_back(literal);
}
@@ -131,8 +142,10 @@
if (method.GetType().GetName() != "void") {
string literal;
if (for_declaration) {
- literal = StringPrintf("%s* %s", CppNameOf(method.GetType(), typenames).c_str(),
- type_name_only ? "" : kReturnVarName);
+ literal = CppNameOf(method.GetType(), typenames) + "*";
+ if (!type_name_only) {
+ literal += " " + string(kReturnVarName);
+ }
} else {
literal = string{"&"} + kReturnVarName;
}
@@ -173,6 +186,19 @@
}
return unique_ptr<Declaration>(new LiteralDecl(code.str()));
}
+ if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ std::ostringstream code;
+ if (for_interface) {
+ code << "virtual ";
+ }
+ code << "std::string " << kGetInterfaceHash << "()";
+ if (for_interface) {
+ code << " = 0;\n";
+ } else {
+ code << " override;\n";
+ }
+ return unique_ptr<Declaration>(new LiteralDecl(code.str()));
+ }
return nullptr;
}
@@ -306,7 +332,11 @@
// default implementation, if provided.
vector<string> arg_names;
for (const auto& a : method.GetArguments()) {
- arg_names.emplace_back(a->GetName());
+ if (IsNonCopyableType(a->GetType(), typenames)) {
+ arg_names.emplace_back(StringPrintf("std::move(%s)", a->GetName().c_str()));
+ } else {
+ arg_names.emplace_back(a->GetName());
+ }
}
if (method.GetType().GetName() != "void") {
arg_names.emplace_back(kReturnVarName);
@@ -416,6 +446,30 @@
<< "}\n";
return unique_ptr<Declaration>(new LiteralDecl(code.str()));
}
+ if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ const string iface = ClassName(interface, ClassNames::INTERFACE);
+ const string proxy = ClassName(interface, ClassNames::CLIENT);
+ std::ostringstream code;
+ code << "std::string " << proxy << "::" << kGetInterfaceHash << "() {\n"
+ << " std::lock_guard<std::mutex> lockGuard(cached_hash_mutex_);\n"
+ << " if (cached_hash_ == \"-1\") {\n"
+ << " ::android::Parcel data;\n"
+ << " ::android::Parcel reply;\n"
+ << " data.writeInterfaceToken(getInterfaceDescriptor());\n"
+ << " ::android::status_t err = remote()->transact(" << GetTransactionIdFor(method)
+ << ", data, &reply);\n"
+ << " if (err == ::android::OK) {\n"
+ << " ::android::binder::Status _aidl_status;\n"
+ << " err = _aidl_status.readFromParcel(reply);\n"
+ << " if (err == ::android::OK && _aidl_status.isOk()) {\n"
+ << " cached_hash_ = reply.readString8().c_str();\n"
+ << " }\n"
+ << " }\n"
+ << " }\n"
+ << " return cached_hash_;\n"
+ << "}\n";
+ return unique_ptr<Declaration>(new LiteralDecl(code.str()));
+ }
return nullptr;
}
@@ -605,6 +659,15 @@
b->AddLiteral(code.str());
return true;
}
+ if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ std::ostringstream code;
+ code << "_aidl_data.checkInterface(this);\n"
+ << "_aidl_reply->writeNoException();\n"
+ << "_aidl_reply->writeString8(android::String8("
+ << ClassName(interface, ClassNames::INTERFACE) << "::HASH.c_str()))";
+ b->AddLiteral(code.str());
+ return true;
+ }
return false;
}
@@ -702,6 +765,13 @@
<< "}\n";
decls.emplace_back(new LiteralDecl(code.str()));
}
+ if (!options.Hash().empty()) {
+ std::ostringstream code;
+ code << "std::string " << bn_name << "::" << kGetInterfaceHash << "() {\n"
+ << " return " << ClassName(interface, ClassNames::INTERFACE) << "::HASH;\n"
+ << "}\n";
+ decls.emplace_back(new LiteralDecl(code.str()));
+ }
if (options.GenLog()) {
string code;
@@ -717,7 +787,8 @@
}
unique_ptr<Document> BuildInterfaceSource(const AidlTypenames& typenames,
- const AidlInterface& interface, const Options& options) {
+ const AidlInterface& interface,
+ [[maybe_unused]] const Options& options) {
vector<string> include_list{
HeaderFile(interface, ClassNames::RAW, false),
HeaderFile(interface, ClassNames::CLIENT, false),
@@ -731,9 +802,8 @@
vector<unique_ptr<Declaration>> decls;
unique_ptr<MacroDecl> meta_if{new MacroDecl{
- "IMPLEMENT_META_INTERFACE",
- ArgList{vector<string>{ClassName(interface, ClassNames::BASE),
- '"' + fq_name + '"'}}}};
+ "DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE",
+ ArgList{vector<string>{ClassName(interface, ClassNames::BASE), '"' + fq_name + '"'}}}};
decls.push_back(std::move(meta_if));
for (const auto& constant : interface.GetConstantDeclarations()) {
@@ -751,39 +821,6 @@
decls.push_back(std::move(getter));
}
- // Implement the default impl class.
- // onAsBinder returns nullptr as this interface is not associated with a
- // real binder.
- const string default_impl(ClassName(interface, ClassNames::DEFAULT_IMPL));
- decls.emplace_back(
- new LiteralDecl(StringPrintf("::android::IBinder* %s::onAsBinder() {\n"
- " return nullptr;\n"
- "}\n",
- default_impl.c_str())));
- // Each interface method by default returns UNKNOWN_TRANSACTION with is
- // the same status that is returned by transact() when the method is
- // not implemented in the server side. In other words, these default
- // methods do nothing; they only exist to aid making a real default
- // impl class without having to override all methods in an interface.
- for (const auto& method : interface.GetMethods()) {
- if (method->IsUserDefined()) {
- std::ostringstream code;
- code << "::android::binder::Status " << default_impl << "::" << method->GetName()
- << BuildArgList(typenames, *method, true, true).ToString() << " {\n"
- << " return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);\n"
- << "}\n";
- decls.emplace_back(new LiteralDecl(code.str()));
- } else {
- if (method->GetName() == kGetInterfaceVersion && options.Version() > 0) {
- std::ostringstream code;
- code << "int32_t " << default_impl << "::" << kGetInterfaceVersion << "() {\n"
- << " return 0;\n"
- << "}\n";
- decls.emplace_back(new LiteralDecl(code.str()));
- }
- }
- }
-
return unique_ptr<Document>{new CppSource{
include_list,
NestInNamespaces(std::move(decls), interface.GetSplitPackage())}};
@@ -833,6 +870,10 @@
if (options.Version() > 0) {
privates.emplace_back(new LiteralDecl("int32_t cached_version_ = -1;\n"));
}
+ if (!options.Hash().empty()) {
+ privates.emplace_back(new LiteralDecl("std::string cached_hash_ = \"-1\";\n"));
+ privates.emplace_back(new LiteralDecl("std::mutex cached_hash_mutex_;\n"));
+ }
unique_ptr<ClassDecl> bp_class{new ClassDecl{
bp_name,
@@ -874,6 +915,11 @@
code << "int32_t " << kGetInterfaceVersion << "() final override;\n";
publics.emplace_back(new LiteralDecl(code.str()));
}
+ if (!options.Hash().empty()) {
+ std::ostringstream code;
+ code << "std::string " << kGetInterfaceHash << "();\n";
+ publics.emplace_back(new LiteralDecl(code.str()));
+ }
if (options.GenLog()) {
includes.emplace_back("chrono"); // for std::chrono::steady_clock
@@ -918,6 +964,12 @@
if_class->AddPublic(unique_ptr<Declaration>(new LiteralDecl(code.str())));
}
+ if (!options.Hash().empty()) {
+ std::ostringstream code;
+ code << "const std::string HASH = \"" << options.Hash() << "\";\n";
+
+ if_class->AddPublic(unique_ptr<Declaration>(new LiteralDecl(code.str())));
+ }
std::vector<std::unique_ptr<Declaration>> string_constants;
unique_ptr<Enum> int_constant_enum{new Enum{"", "int32_t", false}};
@@ -970,28 +1022,49 @@
}
}
- vector<unique_ptr<Declaration>> decls;
- decls.emplace_back(std::move(if_class));
-
- // Base class for the default implementation.
- vector<string> method_decls;
+ // Implement the default impl class.
+ vector<unique_ptr<Declaration>> method_decls;
+ // onAsBinder returns nullptr as this interface is not associated with a
+ // real binder.
+ method_decls.emplace_back(
+ new LiteralDecl("::android::IBinder* onAsBinder() override {\n"
+ " return nullptr;\n"
+ "}\n"));
+ // Each interface method by default returns UNKNOWN_TRANSACTION with is
+ // the same status that is returned by transact() when the method is
+ // not implemented in the server side. In other words, these default
+ // methods do nothing; they only exist to aid making a real default
+ // impl class without having to override all methods in an interface.
for (const auto& method : interface.GetMethods()) {
if (method->IsUserDefined()) {
- method_decls.emplace_back(BuildMethodDecl(*method, typenames, false)->ToString());
+ std::ostringstream code;
+ code << "::android::binder::Status " << method->GetName()
+ << BuildArgList(typenames, *method, true, true).ToString() << " override {\n"
+ << " return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);\n"
+ << "}\n";
+ method_decls.emplace_back(new LiteralDecl(code.str()));
} else {
- method_decls.emplace_back(
- BuildMetaMethodDecl(*method, typenames, options, false)->ToString());
+ if (method->GetName() == kGetInterfaceVersion && options.Version() > 0) {
+ std::ostringstream code;
+ code << "int32_t " << kGetInterfaceVersion << "() override {\n"
+ << " return 0;\n"
+ << "}\n";
+ method_decls.emplace_back(new LiteralDecl(code.str()));
+ }
+ if (method->GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ std::ostringstream code;
+ code << "std::string " << kGetInterfaceHash << "() override {\n"
+ << " return \"\";\n"
+ << "}\n";
+ method_decls.emplace_back(new LiteralDecl(code.str()));
+ }
}
}
- decls.emplace_back(new LiteralDecl(
- android::base::StringPrintf("class %s : public %s {\n"
- "public:\n"
- " ::android::IBinder* onAsBinder() override;\n"
- " %s\n"
- "};\n",
- ClassName(interface, ClassNames::DEFAULT_IMPL).c_str(),
- i_name.c_str(), Join(method_decls, " ").c_str())));
+ vector<unique_ptr<Declaration>> decls;
+ decls.emplace_back(std::move(if_class));
+ decls.emplace_back(new ClassDecl{
+ ClassName(interface, ClassNames::DEFAULT_IMPL), i_name, std::move(method_decls), {}});
return unique_ptr<Document>{
new CppHeader{BuildHeaderGuard(interface, ClassNames::INTERFACE),
@@ -1054,7 +1127,7 @@
parcel_class->AddPublic(std::move(write));
return unique_ptr<Document>{new CppHeader{
- BuildHeaderGuard(parcel, ClassNames::BASE), vector<string>(includes.begin(), includes.end()),
+ BuildHeaderGuard(parcel, ClassNames::RAW), vector<string>(includes.begin(), includes.end()),
NestInNamespaces(std::move(parcel_class), parcel.GetSplitPackage())}};
}
std::unique_ptr<Document> BuildParcelSource(const AidlTypenames& typenames,
@@ -1129,9 +1202,35 @@
NestInNamespaces(std::move(file_decls), parcel.GetSplitPackage())}};
}
+std::string GenerateEnumToString(const AidlTypenames& typenames,
+ const AidlEnumDeclaration& enum_decl) {
+ std::ostringstream code;
+ code << "static inline std::string toString(" << enum_decl.GetName() << " val) {\n";
+ code << " switch(val) {\n";
+ std::set<std::string> unique_cases;
+ for (const auto& enumerator : enum_decl.GetEnumerators()) {
+ std::string c = enumerator->ValueString(enum_decl.GetBackingType(), ConstantValueDecorator);
+ // Only add a case if its value has not yet been used in the switch
+ // statement. C++ does not allow multiple cases with the same value, but
+ // enums does allow this. In this scenario, the first declared
+ // enumerator with the given value is printed.
+ if (unique_cases.count(c) == 0) {
+ unique_cases.insert(c);
+ code << " case " << enum_decl.GetName() << "::" << enumerator->GetName() << ":\n";
+ code << " return \"" << enumerator->GetName() << "\";\n";
+ }
+ }
+ code << " default:\n";
+ code << " return std::to_string(static_cast<"
+ << CppNameOf(enum_decl.GetBackingType(), typenames) << ">(val));\n";
+ code << " }\n";
+ code << "}\n";
+ return code.str();
+}
+
std::unique_ptr<Document> BuildEnumHeader(const AidlTypenames& typenames,
const AidlEnumDeclaration& enum_decl) {
- unique_ptr<Enum> generated_enum{
+ std::unique_ptr<Enum> generated_enum{
new Enum{enum_decl.GetName(), CppNameOf(enum_decl.GetBackingType(), typenames), true}};
for (const auto& enumerator : enum_decl.GetEnumerators()) {
generated_enum->AddValue(
@@ -1139,13 +1238,25 @@
enumerator->ValueString(enum_decl.GetBackingType(), ConstantValueDecorator));
}
- set<string> includes = {};
+ std::set<std::string> includes = {
+ "array",
+ "binder/Enums.h",
+ "string",
+ };
AddHeaders(enum_decl.GetBackingType(), typenames, includes);
+ std::vector<std::unique_ptr<Declaration>> decls1;
+ decls1.push_back(std::move(generated_enum));
+ decls1.push_back(std::make_unique<LiteralDecl>(GenerateEnumToString(typenames, enum_decl)));
+
+ std::vector<std::unique_ptr<Declaration>> decls2;
+ decls2.push_back(std::make_unique<LiteralDecl>(GenerateEnumValues(enum_decl, {""})));
+
return unique_ptr<Document>{
- new CppHeader{BuildHeaderGuard(enum_decl, ClassNames::BASE),
+ new CppHeader{BuildHeaderGuard(enum_decl, ClassNames::RAW),
vector<string>(includes.begin(), includes.end()),
- NestInNamespaces(std::move(generated_enum), enum_decl.GetSplitPackage())}};
+ Append(NestInNamespaces(std::move(decls1), enum_decl.GetSplitPackage()),
+ NestInNamespaces(std::move(decls2), {"android", "internal"}))}};
}
bool WriteHeader(const Options& options, const AidlTypenames& typenames,
diff --git a/generate_cpp_unittest.cpp b/generate_cpp_unittest.cpp
index 3ec8b16..b5e39bd 100644
--- a/generate_cpp_unittest.cpp
+++ b/generate_cpp_unittest.cpp
@@ -76,7 +76,7 @@
::android::binder::Status NullableBinder(::android::sp<::foo::IFooType>* _aidl_return) override;
::android::binder::Status StringListMethod(const ::std::vector<::android::String16>& input, ::std::vector<::android::String16>* output, ::std::vector<::android::String16>* _aidl_return) override;
::android::binder::Status BinderListMethod(const ::std::vector<::android::sp<::android::IBinder>>& input, ::std::vector<::android::sp<::android::IBinder>>* output, ::std::vector<::android::sp<::android::IBinder>>* _aidl_return) override;
- ::android::binder::Status TakesAFileDescriptor(const ::android::base::unique_fd& f, ::android::base::unique_fd* _aidl_return) override;
+ ::android::binder::Status TakesAFileDescriptor(::android::base::unique_fd f, ::android::base::unique_fd* _aidl_return) override;
::android::binder::Status TakesAFileDescriptorArray(const ::std::vector<::android::base::unique_fd>& f, ::std::vector<::android::base::unique_fd>* _aidl_return) override;
}; // class BpComplexTypeInterface
@@ -325,7 +325,7 @@
return _aidl_status;
}
-::android::binder::Status BpComplexTypeInterface::TakesAFileDescriptor(const ::android::base::unique_fd& f, ::android::base::unique_fd* _aidl_return) {
+::android::binder::Status BpComplexTypeInterface::TakesAFileDescriptor(::android::base::unique_fd f, ::android::base::unique_fd* _aidl_return) {
::android::Parcel _aidl_data;
::android::Parcel _aidl_reply;
::android::status_t _aidl_ret_status = ::android::OK;
@@ -340,7 +340,7 @@
}
_aidl_ret_status = remote()->transact(::android::IBinder::FIRST_CALL_TRANSACTION + 6 /* TakesAFileDescriptor */, _aidl_data, &_aidl_reply);
if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IComplexTypeInterface::getDefaultImpl())) {
- return IComplexTypeInterface::getDefaultImpl()->TakesAFileDescriptor(f, _aidl_return);
+ return IComplexTypeInterface::getDefaultImpl()->TakesAFileDescriptor(std::move(f), _aidl_return);
}
if (((_aidl_ret_status) != (::android::OK))) {
goto _aidl_error;
@@ -646,7 +646,7 @@
return _aidl_status;
}
-::android::binder::Status BpComplexTypeInterface::TakesAFileDescriptor(const ::android::base::unique_fd& f, ::android::base::unique_fd* _aidl_return) {
+::android::binder::Status BpComplexTypeInterface::TakesAFileDescriptor(::android::base::unique_fd f, ::android::base::unique_fd* _aidl_return) {
::android::Parcel _aidl_data;
::android::Parcel _aidl_reply;
::android::status_t _aidl_ret_status = ::android::OK;
@@ -662,7 +662,7 @@
}
_aidl_ret_status = remote()->transact(::android::IBinder::FIRST_CALL_TRANSACTION + 6 /* TakesAFileDescriptor */, _aidl_data, &_aidl_reply);
if (UNLIKELY(_aidl_ret_status == ::android::UNKNOWN_TRANSACTION && IComplexTypeInterface::getDefaultImpl())) {
- return IComplexTypeInterface::getDefaultImpl()->TakesAFileDescriptor(f, _aidl_return);
+ return IComplexTypeInterface::getDefaultImpl()->TakesAFileDescriptor(std::move(f), _aidl_return);
}
if (((_aidl_ret_status) != (::android::OK))) {
goto _aidl_error;
@@ -945,7 +945,7 @@
if (((_aidl_ret_status) != (::android::OK))) {
break;
}
- ::android::binder::Status _aidl_status(TakesAFileDescriptor(in_f, &_aidl_return));
+ ::android::binder::Status _aidl_status(TakesAFileDescriptor(std::move(in_f), &_aidl_return));
_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
if (((_aidl_ret_status) != (::android::OK))) {
break;
@@ -1211,7 +1211,7 @@
break;
}
atrace_begin(ATRACE_TAG_AIDL, "IComplexTypeInterface::TakesAFileDescriptor::cppServer");
- ::android::binder::Status _aidl_status(TakesAFileDescriptor(in_f, &_aidl_return));
+ ::android::binder::Status _aidl_status(TakesAFileDescriptor(std::move(in_f), &_aidl_return));
atrace_end(ATRACE_TAG_AIDL);
_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
if (((_aidl_ret_status) != (::android::OK))) {
@@ -1302,23 +1302,40 @@
virtual ::android::binder::Status NullableBinder(::android::sp<::foo::IFooType>* _aidl_return) = 0;
virtual ::android::binder::Status StringListMethod(const ::std::vector<::android::String16>& input, ::std::vector<::android::String16>* output, ::std::vector<::android::String16>* _aidl_return) = 0;
virtual ::android::binder::Status BinderListMethod(const ::std::vector<::android::sp<::android::IBinder>>& input, ::std::vector<::android::sp<::android::IBinder>>* output, ::std::vector<::android::sp<::android::IBinder>>* _aidl_return) = 0;
- virtual ::android::binder::Status TakesAFileDescriptor(const ::android::base::unique_fd& f, ::android::base::unique_fd* _aidl_return) = 0;
+ virtual ::android::binder::Status TakesAFileDescriptor(::android::base::unique_fd f, ::android::base::unique_fd* _aidl_return) = 0;
virtual ::android::binder::Status TakesAFileDescriptorArray(const ::std::vector<::android::base::unique_fd>& f, ::std::vector<::android::base::unique_fd>* _aidl_return) = 0;
}; // class IComplexTypeInterface
class IComplexTypeInterfaceDefault : public IComplexTypeInterface {
public:
- ::android::IBinder* onAsBinder() override;
- ::android::binder::Status Send(const ::std::unique_ptr<::std::vector<int32_t>>& goes_in, ::std::vector<double>* goes_in_and_out, ::std::vector<bool>* goes_out, ::std::vector<int32_t>* _aidl_return) override;
- ::android::binder::Status Piff(int32_t times) override;
- ::android::binder::Status TakesABinder(const ::android::sp<::foo::IFooType>& f, ::android::sp<::foo::IFooType>* _aidl_return) override;
- ::android::binder::Status NullableBinder(::android::sp<::foo::IFooType>* _aidl_return) override;
- ::android::binder::Status StringListMethod(const ::std::vector<::android::String16>& input, ::std::vector<::android::String16>* output, ::std::vector<::android::String16>* _aidl_return) override;
- ::android::binder::Status BinderListMethod(const ::std::vector<::android::sp<::android::IBinder>>& input, ::std::vector<::android::sp<::android::IBinder>>* output, ::std::vector<::android::sp<::android::IBinder>>* _aidl_return) override;
- ::android::binder::Status TakesAFileDescriptor(const ::android::base::unique_fd& f, ::android::base::unique_fd* _aidl_return) override;
- ::android::binder::Status TakesAFileDescriptorArray(const ::std::vector<::android::base::unique_fd>& f, ::std::vector<::android::base::unique_fd>* _aidl_return) override;
-
-};
+ ::android::IBinder* onAsBinder() override {
+ return nullptr;
+ }
+ ::android::binder::Status Send(const ::std::unique_ptr<::std::vector<int32_t>>&, ::std::vector<double>*, ::std::vector<bool>*, ::std::vector<int32_t>*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status Piff(int32_t) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status TakesABinder(const ::android::sp<::foo::IFooType>&, ::android::sp<::foo::IFooType>*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status NullableBinder(::android::sp<::foo::IFooType>*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status StringListMethod(const ::std::vector<::android::String16>&, ::std::vector<::android::String16>*, ::std::vector<::android::String16>*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status BinderListMethod(const ::std::vector<::android::sp<::android::IBinder>>&, ::std::vector<::android::sp<::android::IBinder>>*, ::std::vector<::android::sp<::android::IBinder>>*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status TakesAFileDescriptor(::android::base::unique_fd, ::android::base::unique_fd*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status TakesAFileDescriptorArray(const ::std::vector<::android::base::unique_fd>&, ::std::vector<::android::base::unique_fd>*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+}; // class IComplexTypeInterfaceDefault
} // namespace os
@@ -1335,43 +1352,7 @@
namespace os {
-IMPLEMENT_META_INTERFACE(ComplexTypeInterface, "android.os.IComplexTypeInterface")
-
-::android::IBinder* IComplexTypeInterfaceDefault::onAsBinder() {
- return nullptr;
-}
-
-::android::binder::Status IComplexTypeInterfaceDefault::Send(const ::std::unique_ptr<::std::vector<int32_t>>&, ::std::vector<double>*, ::std::vector<bool>*, ::std::vector<int32_t>* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IComplexTypeInterfaceDefault::Piff(int32_t) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IComplexTypeInterfaceDefault::TakesABinder(const ::android::sp<::foo::IFooType>&, ::android::sp<::foo::IFooType>* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IComplexTypeInterfaceDefault::NullableBinder(::android::sp<::foo::IFooType>* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IComplexTypeInterfaceDefault::StringListMethod(const ::std::vector<::android::String16>&, ::std::vector<::android::String16>*, ::std::vector<::android::String16>* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IComplexTypeInterfaceDefault::BinderListMethod(const ::std::vector<::android::sp<::android::IBinder>>&, ::std::vector<::android::sp<::android::IBinder>>*, ::std::vector<::android::sp<::android::IBinder>>* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IComplexTypeInterfaceDefault::TakesAFileDescriptor(const ::android::base::unique_fd&, ::android::base::unique_fd* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IComplexTypeInterfaceDefault::TakesAFileDescriptorArray(const ::std::vector<::android::base::unique_fd>&, ::std::vector<::android::base::unique_fd>* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
+DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(ComplexTypeInterface, "android.os.IComplexTypeInterface")
} // namespace os
@@ -1380,31 +1361,103 @@
const string kEnumAIDL = R"(package android.os;
enum TestEnum {
- FOO = 1,
- BAR = 2,
+ ZERO,
+ ONE,
+ THREE = 3,
+ FOUR = 3 + 1,
+ FIVE,
+ SIX,
+ SEVEN,
+ EIGHT = 16 / 2,
+ NINE,
+ TEN,
})";
+// clang-format off
const char kExpectedEnumHeaderOutput[] =
R"(#ifndef AIDL_GENERATED_ANDROID_OS_TEST_ENUM_H_
#define AIDL_GENERATED_ANDROID_OS_TEST_ENUM_H_
+#include <array>
+#include <binder/Enums.h>
#include <cstdint>
+#include <string>
namespace android {
namespace os {
enum class TestEnum : int8_t {
- FOO = 1,
- BAR = 2,
+ ZERO = 0,
+ ONE = 1,
+ THREE = 3,
+ FOUR = 4,
+ FIVE = 5,
+ SIX = 6,
+ SEVEN = 7,
+ EIGHT = 8,
+ NINE = 9,
+ TEN = 10,
};
+static inline std::string toString(TestEnum val) {
+ switch(val) {
+ case TestEnum::ZERO:
+ return "ZERO";
+ case TestEnum::ONE:
+ return "ONE";
+ case TestEnum::THREE:
+ return "THREE";
+ case TestEnum::FOUR:
+ return "FOUR";
+ case TestEnum::FIVE:
+ return "FIVE";
+ case TestEnum::SIX:
+ return "SIX";
+ case TestEnum::SEVEN:
+ return "SEVEN";
+ case TestEnum::EIGHT:
+ return "EIGHT";
+ case TestEnum::NINE:
+ return "NINE";
+ case TestEnum::TEN:
+ return "TEN";
+ default:
+ return std::to_string(static_cast<int8_t>(val));
+ }
+}
+
} // namespace os
} // namespace android
+namespace android {
+
+namespace internal {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<::android::os::TestEnum, 10> enum_values<::android::os::TestEnum> = {
+ ::android::os::TestEnum::ZERO,
+ ::android::os::TestEnum::ONE,
+ ::android::os::TestEnum::THREE,
+ ::android::os::TestEnum::FOUR,
+ ::android::os::TestEnum::FIVE,
+ ::android::os::TestEnum::SIX,
+ ::android::os::TestEnum::SEVEN,
+ ::android::os::TestEnum::EIGHT,
+ ::android::os::TestEnum::NINE,
+ ::android::os::TestEnum::TEN,
+};
+#pragma clang diagnostic pop
+
+} // namespace internal
+
+} // namespace android
#endif // AIDL_GENERATED_ANDROID_OS_TEST_ENUM_H_
)";
+// clang-format on
const string kEnumWithBackingTypeAIDL = R"(package android.os;
@Backing(type="long")
@@ -1413,11 +1466,15 @@
BAR = 2,
})";
+// clang-format off
const char kExpectedEnumWithBackingTypeHeaderOutput[] =
R"(#ifndef AIDL_GENERATED_ANDROID_OS_TEST_ENUM_H_
#define AIDL_GENERATED_ANDROID_OS_TEST_ENUM_H_
+#include <array>
+#include <binder/Enums.h>
#include <cstdint>
+#include <string>
namespace android {
@@ -1428,12 +1485,40 @@
BAR = 2L,
};
+static inline std::string toString(TestEnum val) {
+ switch(val) {
+ case TestEnum::FOO:
+ return "FOO";
+ case TestEnum::BAR:
+ return "BAR";
+ default:
+ return std::to_string(static_cast<int64_t>(val));
+ }
+}
+
} // namespace os
} // namespace android
+namespace android {
+
+namespace internal {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++17-extensions"
+template <>
+constexpr inline std::array<::android::os::TestEnum, 2> enum_values<::android::os::TestEnum> = {
+ ::android::os::TestEnum::FOO,
+ ::android::os::TestEnum::BAR,
+};
+#pragma clang diagnostic pop
+
+} // namespace internal
+
+} // namespace android
#endif // AIDL_GENERATED_ANDROID_OS_TEST_ENUM_H_
)";
+// clang-format on
} // namespace
diff --git a/generate_java.cpp b/generate_java.cpp
index ea23016..f87ce79 100644
--- a/generate_java.cpp
+++ b/generate_java.cpp
@@ -64,14 +64,6 @@
return true;
}
-bool generate_java_parcel_declaration(const std::string& filename, const IoDelegate& io_delegate) {
- CodeWriterPtr code_writer = io_delegate.GetCodeWriter(filename);
- *code_writer
- << "// This file is intentionally left blank as placeholder for parcel declaration.\n";
-
- return true;
-}
-
bool generate_java_enum_declaration(const std::string& filename,
const AidlEnumDeclaration* enum_decl,
const AidlTypenames& typenames, const IoDelegate& io_delegate) {
@@ -88,11 +80,6 @@
return generate_java_parcel(filename, parcelable, typenames, io_delegate);
}
- if (const AidlParcelable* parcelable_decl = defined_type->AsParcelable();
- parcelable_decl != nullptr) {
- return generate_java_parcel_declaration(filename, io_delegate);
- }
-
if (const AidlEnumDeclaration* enum_decl = defined_type->AsEnumDeclaration();
enum_decl != nullptr) {
return generate_java_enum_declaration(filename, enum_decl, typenames, io_delegate);
@@ -261,12 +248,12 @@
code_writer->Write("package %s;\n", enum_decl->GetPackage().c_str());
code_writer->Write("%s\n", enum_decl->GetComments().c_str());
for (const std::string& annotation : generate_java_annotations(*enum_decl)) {
- code_writer->Write(annotation.c_str());
+ code_writer->Write("%s", annotation.c_str());
}
code_writer->Write("public @interface %s {\n", enum_decl->GetName().c_str());
code_writer->Indent();
for (const auto& enumerator : enum_decl->GetEnumerators()) {
- code_writer->Write(enumerator->GetComments().c_str());
+ code_writer->Write("%s", enumerator->GetComments().c_str());
code_writer->Write(
"public static final %s %s = %s;\n",
JavaSignatureOf(enum_decl->GetBackingType(), typenames).c_str(),
@@ -277,17 +264,19 @@
code_writer->Write("}\n");
}
-std::string generate_java_annotation_parameters(const AidlAnnotation& a) {
+std::string dump_location(const AidlNode& method) {
+ return method.PrintLocation();
+}
+
+std::string generate_java_unsupportedappusage_parameters(const AidlAnnotation& a) {
const std::map<std::string, std::string> params = a.AnnotationParams(ConstantValueDecorator);
- if (params.empty()) {
- return "";
- }
std::vector<string> parameters_decl;
for (const auto& name_and_param : params) {
const std::string& param_name = name_and_param.first;
const std::string& param_value = name_and_param.second;
parameters_decl.push_back(param_name + " = " + param_value);
}
+ parameters_decl.push_back("overrideSourcePosition=\"" + dump_location(a) + "\"");
return "(" + base::Join(parameters_decl, ", ") + ")";
}
@@ -295,8 +284,8 @@
std::vector<std::string> result;
const AidlAnnotation* unsupported_app_usage = a.UnsupportedAppUsage();
if (unsupported_app_usage != nullptr) {
- result.emplace_back("@dalvik.annotation.compat.UnsupportedAppUsage" +
- generate_java_annotation_parameters(*unsupported_app_usage));
+ result.emplace_back("@android.compat.annotation.UnsupportedAppUsage" +
+ generate_java_unsupportedappusage_parameters(*unsupported_app_usage));
}
if (a.IsSystemApi()) {
result.emplace_back("@android.annotation.SystemApi");
diff --git a/generate_java_binder.cpp b/generate_java_binder.cpp
index b791241..296e023 100644
--- a/generate_java_binder.cpp
+++ b/generate_java_binder.cpp
@@ -129,6 +129,10 @@
"interface. */";
ctor->name = "Stub";
ctor->statements = std::make_shared<StatementBlock>();
+ if (interfaceType->IsVintfStability()) {
+ auto stability = std::make_shared<LiteralStatement>("this.markVintfStability();\n");
+ ctor->statements->Add(stability);
+ }
auto attach = std::make_shared<MethodCall>(
THIS_VALUE, "attachInterface",
std::vector<std::shared_ptr<Expression>>{THIS_VALUE,
@@ -332,6 +336,11 @@
code << "private int mCachedVersion = -1;\n";
this->elements.emplace_back(std::make_shared<LiteralClassElement>(code.str()));
}
+ if (!options.Hash().empty()) {
+ std::ostringstream code;
+ code << "private String mCachedHash = \"-1\";\n";
+ this->elements.emplace_back(std::make_shared<LiteralClassElement>(code.str()));
+ }
// IBinder asBinder()
auto asBinder = std::make_shared<Method>();
@@ -781,6 +790,12 @@
<< "throws android.os.RemoteException;\n";
decl = std::make_shared<LiteralClassElement>(code.str());
}
+ if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ std::ostringstream code;
+ code << "public String " << kGetInterfaceHash << "() "
+ << "throws android.os.RemoteException;\n";
+ decl = std::make_shared<LiteralClassElement>(code.str());
+ }
}
interface->elements.push_back(decl);
@@ -805,6 +820,16 @@
c->statements->Add(std::make_shared<LiteralStatement>(code.str()));
stubClass->transact_switch->cases.push_back(c);
}
+ if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ auto c = std::make_shared<Case>(transactCodeName);
+ std::ostringstream code;
+ code << "data.enforceInterface(descriptor);\n"
+ << "reply.writeNoException();\n"
+ << "reply.writeString(" << kGetInterfaceHash << "());\n"
+ << "return true;\n";
+ c->statements->Add(std::make_shared<LiteralStatement>(code.str()));
+ stubClass->transact_switch->cases.push_back(c);
+ }
}
// == the proxy method ===================================================
@@ -843,6 +868,35 @@
<< "}\n";
proxy = std::make_shared<LiteralClassElement>(code.str());
}
+ if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ std::ostringstream code;
+ code << "@Override\n"
+ << "public synchronized String " << kGetInterfaceHash << "()"
+ << " throws "
+ << "android.os.RemoteException {\n"
+ << " if (mCachedHash == \"-1\") {\n"
+ << " android.os.Parcel data = android.os.Parcel.obtain();\n"
+ << " android.os.Parcel reply = android.os.Parcel.obtain();\n"
+ << " try {\n"
+ << " data.writeInterfaceToken(DESCRIPTOR);\n"
+ << " boolean _status = mRemote.transact(Stub." << transactCodeName << ", "
+ << "data, reply, 0);\n"
+ << " if (!_status) {\n"
+ << " if (getDefaultImpl() != null) {\n"
+ << " return getDefaultImpl().getInterfaceHash();\n"
+ << " }\n"
+ << " }\n"
+ << " reply.readException();\n"
+ << " mCachedHash = reply.readString();\n"
+ << " } finally {\n"
+ << " reply.recycle();\n"
+ << " data.recycle();\n"
+ << " }\n"
+ << " }\n"
+ << " return mCachedHash;\n"
+ << "}\n";
+ proxy = std::make_shared<LiteralClassElement>(code.str());
+ }
}
if (proxy != nullptr) {
proxyClass->elements.push_back(proxy);
@@ -943,17 +997,25 @@
if (m->IsUserDefined()) {
default_class->elements.emplace_back(generate_default_impl_method(*m.get(), typenames));
} else {
+ // These are called only when the remote side does not implement these
+ // methods, which is normally impossible, because these methods are
+ // automatically declared in the interface class and not implementing
+ // them on the remote side causes a compilation error. But if the remote
+ // side somehow managed to not implement it, that's an error and we
+ // report the case by returning an invalid value here.
if (m->GetName() == kGetInterfaceVersion && options.Version() > 0) {
- // This is called only when the remote side is not implementing this
- // method, which is impossible in normal case, because this method is
- // automatically declared in the interface class and not implementing
- // it in the remote side is causing compilation error. But if the remote
- // side somehow managed to not implement it, that's an error and we
- // report the case by returning -1 here.
std::ostringstream code;
code << "@Override\n"
<< "public int " << kGetInterfaceVersion << "() {\n"
- << " return -1;\n"
+ << " return 0;\n"
+ << "}\n";
+ default_class->elements.emplace_back(std::make_shared<LiteralClassElement>(code.str()));
+ }
+ if (m->GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ std::ostringstream code;
+ code << "@Override\n"
+ << "public String " << kGetInterfaceHash << "() {\n"
+ << " return \"\";\n"
<< "}\n";
default_class->elements.emplace_back(std::make_shared<LiteralClassElement>(code.str()));
}
@@ -992,6 +1054,11 @@
<< "public static final int VERSION = " << options.Version() << ";\n";
interface->elements.emplace_back(std::make_shared<LiteralClassElement>(code.str()));
}
+ if (!options.Hash().empty()) {
+ std::ostringstream code;
+ code << "public static final String HASH = \"" << options.Hash() << "\";\n";
+ interface->elements.emplace_back(std::make_shared<LiteralClassElement>(code.str()));
+ }
// the default impl class
auto default_impl = generate_default_impl_class(*iface, typenames, options);
@@ -1016,7 +1083,11 @@
// all the declared constants of the interface
for (const auto& constant : iface->GetConstantDeclarations()) {
const AidlConstantValue& value = constant->GetValue();
-
+ auto comment = constant->GetType().GetComments();
+ if (comment.length() != 0) {
+ auto code = StringPrintf("%s\n", comment.c_str());
+ interface->elements.push_back(std::make_shared<LiteralClassElement>(code));
+ }
switch (value.GetType()) {
case AidlConstantValue::Type::STRING: {
generate_string_constant(interface.get(), constant->GetName(),
diff --git a/generate_ndk.cpp b/generate_ndk.cpp
index 60c93d6..fd824bf 100644
--- a/generate_ndk.cpp
+++ b/generate_ndk.cpp
@@ -30,7 +30,10 @@
static constexpr const char* kClazz = "_g_aidl_clazz";
static constexpr const char* kDescriptor = "descriptor";
static constexpr const char* kVersion = "version";
-static constexpr const char* kCacheVariable = "_aidl_cached_value";
+static constexpr const char* kHash = "hash";
+static constexpr const char* kCachedVersion = "_aidl_cached_version";
+static constexpr const char* kCachedHash = "_aidl_cached_hash";
+static constexpr const char* kCachedHashMutex = "_aidl_cached_hash_mutex";
using namespace internals;
using cpp::ClassNames;
@@ -170,7 +173,11 @@
static void GenerateHeaderIncludes(CodeWriter& out, const AidlTypenames& types,
const AidlDefinedType& defined_type) {
- out << "#include <android/binder_parcel_utils.h>\n";
+ out << "#include <cstdint>\n";
+ out << "#include <memory>\n";
+ out << "#include <optional>\n";
+ out << "#include <string>\n";
+ out << "#include <vector>\n";
out << "#ifdef BINDER_STABILITY_SUPPORT\n";
out << "#include <android/binder_stability.h>\n";
out << "#endif // BINDER_STABILITY_SUPPORT\n";
@@ -183,12 +190,12 @@
<< NdkHeaderFile(other_defined_type, ClassNames::RAW, false /*use_os_sep*/) << ">\n";
} else if (other_defined_type.AsStructuredParcelable() != nullptr) {
out << "#include <"
- << NdkHeaderFile(other_defined_type, ClassNames::BASE, false /*use_os_sep*/) << ">\n";
+ << NdkHeaderFile(other_defined_type, ClassNames::RAW, false /*use_os_sep*/) << ">\n";
} else if (other_defined_type.AsParcelable() != nullptr) {
out << "#include \"" << other_defined_type.AsParcelable()->GetCppHeader() << "\"\n";
} else if (other_defined_type.AsEnumDeclaration() != nullptr) {
out << "#include <"
- << NdkHeaderFile(other_defined_type, ClassNames::BASE, false /*use_os_sep*/) << ">\n";
+ << NdkHeaderFile(other_defined_type, ClassNames::RAW, false /*use_os_sep*/) << ">\n";
} else {
AIDL_FATAL(defined_type) << "Unrecognized type.";
}
@@ -196,6 +203,8 @@
}
static void GenerateSourceIncludes(CodeWriter& out, const AidlTypenames& types,
const AidlDefinedType& /*defined_type*/) {
+ out << "#include <android/binder_parcel_utils.h>\n";
+
types.IterateTypes([&](const AidlDefinedType& a_defined_type) {
if (a_defined_type.AsInterface() != nullptr) {
out << "#include <" << NdkHeaderFile(a_defined_type, ClassNames::CLIENT, false /*use_os_sep*/)
@@ -282,7 +291,6 @@
static void GenerateClientMethodDefinition(CodeWriter& out, const AidlTypenames& types,
const AidlInterface& defined_type,
const AidlMethod& method,
- const std::optional<std::string> return_value_cached_to,
const Options& options) {
const std::string clazz = ClassName(defined_type, ClassNames::CLIENT);
@@ -291,10 +299,19 @@
out << "binder_status_t _aidl_ret_status = STATUS_OK;\n";
out << "::ndk::ScopedAStatus _aidl_status;\n";
- if (return_value_cached_to) {
- out << "if (" << *return_value_cached_to << " != -1) {\n";
+ if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ out << "const std::lock_guard<std::mutex> lock(" << kCachedHashMutex << ");\n";
+ out << "if (" << kCachedHash << " != \"-1\") {\n";
out.Indent();
- out << "*_aidl_return = " << *return_value_cached_to << ";\n"
+ out << "*_aidl_return = " << kCachedHash << ";\n"
+ << "_aidl_status.set(AStatus_fromStatus(_aidl_ret_status));\n"
+ << "return _aidl_status;\n";
+ out.Dedent();
+ out << "}\n";
+ } else if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
+ out << "if (" << kCachedVersion << " != -1) {\n";
+ out.Indent();
+ out << "*_aidl_return = " << kCachedVersion << ";\n"
<< "_aidl_status.set(AStatus_fromStatus(_aidl_ret_status));\n"
<< "return _aidl_status;\n";
out.Dedent();
@@ -364,8 +381,10 @@
ReadFromParcelFor({out, types, method.GetType(), "_aidl_out.get()", "_aidl_return"});
out << ";\n";
StatusCheckGoto(out);
- if (return_value_cached_to) {
- out << *return_value_cached_to << " = *_aidl_return;\n";
+ if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ out << kCachedHash << " = *_aidl_return;\n";
+ } else if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
+ out << kCachedVersion << " = *_aidl_return;\n";
}
}
for (const AidlArgument* arg : method.GetOutArguments()) {
@@ -500,13 +519,7 @@
}
out << "\n";
for (const auto& method : defined_type.GetMethods()) {
- // Only getInterfaceVersion can use cache.
- const bool cacheable = !method->IsUserDefined() && method->GetName() == kGetInterfaceVersion &&
- options.Version() > 0;
- const auto return_value_cached_to =
- cacheable ? std::make_optional<std::string>(kCacheVariable) : std::nullopt;
- GenerateClientMethodDefinition(out, types, defined_type, *method, return_value_cached_to,
- options);
+ GenerateClientMethodDefinition(out, types, defined_type, *method, options);
}
}
void GenerateServerSource(CodeWriter& out, const AidlTypenames& types,
@@ -549,6 +562,14 @@
out.Dedent();
out << "}\n";
}
+ if (method->GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ out << NdkMethodDecl(types, *method, clazz) << " {\n";
+ out.Indent();
+ out << "*_aidl_return = " << iface << "::" << kHash << ";\n";
+ out << "return ::ndk::ScopedAStatus(AStatus_newOk());\n";
+ out.Dedent();
+ out << "}\n";
+ }
}
}
void GenerateInterfaceSource(CodeWriter& out, const AidlTypenames& types,
@@ -644,6 +665,15 @@
out.Dedent();
out << "}\n";
}
+ if (method->GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ out << "::ndk::ScopedAStatus " << defaultClazz << "::" << method->GetName() << "("
+ << "std::string* _aidl_return) {\n";
+ out.Indent();
+ out << "*_aidl_return = \"\";\n";
+ out << "return ::ndk::ScopedAStatus(AStatus_newOk());\n";
+ out.Dedent();
+ out << "}\n";
+ }
}
}
@@ -689,7 +719,12 @@
}
if (options.Version() > 0) {
- out << "int32_t " << kCacheVariable << " = -1;\n";
+ out << "int32_t " << kCachedVersion << " = -1;\n";
+ }
+
+ if (!options.Hash().empty()) {
+ out << "std::string " << kCachedHash << " = \"-1\";\n";
+ out << "std::mutex " << kCachedHashMutex << ";\n";
}
if (options.GenLog()) {
out << "static std::function<void(const Json::Value&)> logFunc;\n";
@@ -723,6 +758,8 @@
}
if (method->GetName() == kGetInterfaceVersion && options.Version() > 0) {
out << NdkMethodDecl(types, *method) << " final override;\n";
+ } else if (method->GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ out << NdkMethodDecl(types, *method) << " final override;\n";
} else {
AIDL_FATAL(defined_type) << "Meta method '" << method->GetName() << "' is unimplemented.";
}
@@ -771,6 +808,9 @@
out << "static const int32_t " << kVersion << " = " << std::to_string(options.Version())
<< ";\n";
}
+ if (!options.Hash().empty()) {
+ out << "static inline const std::string " << kHash << " = \"" << options.Hash() << "\";\n";
+ }
out << "\n";
out << "static std::shared_ptr<" << clazz << "> fromBinder(const ::ndk::SpAIBinder& binder);\n";
out << "static binder_status_t writeToParcel(AParcel* parcel, const std::shared_ptr<" << clazz
@@ -803,6 +843,8 @@
out << NdkMethodDecl(types, *method) << " override;\n";
} else if (method->GetName() == kGetInterfaceVersion && options.Version() > 0) {
out << NdkMethodDecl(types, *method) << " override;\n";
+ } else if (method->GetName() == kGetInterfaceHash && !options.Hash().empty()) {
+ out << NdkMethodDecl(types, *method) << " override;\n";
}
}
out << "::ndk::SpAIBinder asBinder() override;\n";
@@ -815,7 +857,7 @@
void GenerateParcelHeader(CodeWriter& out, const AidlTypenames& types,
const AidlStructuredParcelable& defined_type,
const Options& /*options*/) {
- const std::string clazz = ClassName(defined_type, ClassNames::BASE);
+ const std::string clazz = ClassName(defined_type, ClassNames::RAW);
out << "#pragma once\n";
out << "#include <android/binder_interface_utils.h>\n";
@@ -846,7 +888,7 @@
void GenerateParcelSource(CodeWriter& out, const AidlTypenames& types,
const AidlStructuredParcelable& defined_type,
const Options& /*options*/) {
- const std::string clazz = ClassName(defined_type, ClassNames::BASE);
+ const std::string clazz = ClassName(defined_type, ClassNames::RAW);
out << "#include \"" << NdkHeaderFile(defined_type, ClassNames::RAW, false /*use_os_sep*/)
<< "\"\n";
@@ -860,22 +902,12 @@
out << "binder_status_t " << clazz << "::readFromParcel(const AParcel* parcel) {\n";
out.Indent();
- out << "std::string _aidl_descriptor;\n";
- out << "binder_status_t _aidl_ret_status;\n";
-
- out << "int32_t _aidl_null;\n";
out << "int32_t _aidl_parcelable_size;\n";
- out << "int32_t _aidl_start_pos;\n";
- out << "_aidl_ret_status = AParcel_readInt32(parcel, &_aidl_null);\n";
- StatusCheckReturn(out);
- out << "_aidl_start_pos = AParcel_getDataPosition(parcel);\n";
- out << "_aidl_ret_status = AParcel_readInt32(parcel, &_aidl_parcelable_size);\n";
+ out << "int32_t _aidl_start_pos = AParcel_getDataPosition(parcel);\n";
+ out << "binder_status_t _aidl_ret_status = AParcel_readInt32(parcel, &_aidl_parcelable_size);\n";
out << "if (_aidl_parcelable_size < 0) return STATUS_BAD_VALUE;\n";
StatusCheckReturn(out);
- // TODO(b/117281836)
- out << "if (_aidl_null == 0) return STATUS_UNEXPECTED_NULL;\n\n";
-
for (const auto& variable : defined_type.GetFields()) {
out << "_aidl_ret_status = ";
ReadFromParcelFor({out, types, variable->GetType(), "parcel", "&" + variable->GetName()});
@@ -895,9 +927,6 @@
out.Indent();
out << "binder_status_t _aidl_ret_status;\n";
- // non-null
- out << "_aidl_ret_status = AParcel_writeInt32(parcel, 1);\n";
- StatusCheckReturn(out);
out << "size_t _aidl_start_pos = AParcel_getDataPosition(parcel);\n";
out << "_aidl_ret_status = AParcel_writeInt32(parcel, 0);\n";
StatusCheckReturn(out);
@@ -920,12 +949,41 @@
LeaveNdkNamespace(out, defined_type);
}
+std::string GenerateEnumToString(const AidlTypenames& typenames,
+ const AidlEnumDeclaration& enum_decl) {
+ std::ostringstream code;
+ code << "static inline std::string toString(" << enum_decl.GetName() << " val) {\n";
+ code << " switch(val) {\n";
+ std::set<std::string> unique_cases;
+ for (const auto& enumerator : enum_decl.GetEnumerators()) {
+ std::string c = enumerator->ValueString(enum_decl.GetBackingType(), ConstantValueDecorator);
+ // Only add a case if its value has not yet been used in the switch
+ // statement. C++ does not allow multiple cases with the same value, but
+ // enums does allow this. In this scenario, the first declared
+ // enumerator with the given value is printed.
+ if (unique_cases.count(c) == 0) {
+ unique_cases.insert(c);
+ code << " case " << enum_decl.GetName() << "::" << enumerator->GetName() << ":\n";
+ code << " return \"" << enumerator->GetName() << "\";\n";
+ }
+ }
+ code << " default:\n";
+ code << " return std::to_string(static_cast<"
+ << NdkNameOf(typenames, enum_decl.GetBackingType(), StorageMode::STACK) << ">(val));\n";
+ code << " }\n";
+ code << "}\n";
+ return code.str();
+}
+
void GenerateEnumHeader(CodeWriter& out, const AidlTypenames& types,
const AidlEnumDeclaration& enum_decl, const Options& /*options*/) {
out << "#pragma once\n";
out << "\n";
GenerateHeaderIncludes(out, types, enum_decl);
+ // enum specific headers
+ out << "#include <array>\n";
+ out << "#include <android/binder_enums.h>\n";
EnterNdkNamespace(out, enum_decl);
out << "enum class " << enum_decl.GetName() << " : "
@@ -937,7 +995,15 @@
}
out.Dedent();
out << "};\n";
+ out << "\n";
+ out << GenerateEnumToString(types, enum_decl);
LeaveNdkNamespace(out, enum_decl);
+
+ out << "namespace ndk {\n";
+ out << "namespace internal {\n";
+ out << cpp::GenerateEnumValues(enum_decl, {"aidl"});
+ out << "} // namespace internal\n";
+ out << "} // namespace android\n";
}
} // namespace internals
diff --git a/import_resolver.cpp b/import_resolver.cpp
index 0500cf8..10cc4e4 100644
--- a/import_resolver.cpp
+++ b/import_resolver.cpp
@@ -74,13 +74,6 @@
int num_found = found_paths.size();
if (num_found == 0) {
- // If not found from the import paths, try to find from the input files
- relative_path.insert(0, 1, OS_PATH_SEPARATOR);
- for (string input_file : input_files_) {
- if (android::base::EndsWith(input_file, relative_path)) {
- return input_file;
- }
- }
return "";
} else if (num_found == 1) {
return found_paths.front();
diff --git a/main.cpp b/main.cpp
index 1199579..10b9274 100644
--- a/main.cpp
+++ b/main.cpp
@@ -15,26 +15,22 @@
*/
#include "aidl.h"
-#include "aidl_apicheck.h"
+#include "aidl_checkapi.h"
#include "io_delegate.h"
#include "logging.h"
#include "options.h"
#include <iostream>
-#include <memory>
+
+#ifdef AIDL_CPP_BUILD
+constexpr Options::Language kDefaultLang = Options::Language::CPP;
+#else
+constexpr Options::Language kDefaultLang = Options::Language::JAVA;
+#endif
using android::aidl::Options;
-int main(int argc, char* argv[]) {
- android::base::InitLogging(argv);
- LOG(DEBUG) << "aidl starting";
- Options options(argc, argv, Options::Language::JAVA);
- if (!options.Ok()) {
- std::cerr << options.GetErrorMessage();
- std::cerr << options.GetUsage();
- return 1;
- }
-
+int process_options(const Options& options) {
android::aidl::IoDelegate io_delegate;
switch (options.GetTask()) {
case Options::Task::COMPILE:
@@ -52,3 +48,27 @@
return 1;
}
}
+
+int main(int argc, char* argv[]) {
+ android::base::InitLogging(argv);
+ LOG(DEBUG) << "aidl starting";
+
+ Options options(argc, argv, kDefaultLang);
+ if (!options.Ok()) {
+ std::cerr << options.GetErrorMessage();
+ std::cerr << options.GetUsage();
+ return 1;
+ }
+
+ int ret = process_options(options);
+
+ // compiler invariants
+
+ // once AIDL_ERROR/AIDL_FATAL are used everywhere instead of std::cerr/LOG, we
+ // can make this assertion in both directions.
+ if (ret == 0) {
+ AIDL_FATAL_IF(AidlError::hadError(), "Compiler success, but error emitted");
+ }
+
+ return ret;
+}
diff --git a/main_cpp.cpp b/main_cpp.cpp
deleted file mode 100644
index 5cb778c..0000000
--- a/main_cpp.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2015, 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 "aidl.h"
-#include "io_delegate.h"
-#include "logging.h"
-#include "options.h"
-
-#include <iostream>
-
-using android::aidl::Options;
-
-int main(int argc, char* argv[]) {
- android::base::InitLogging(argv);
- LOG(DEBUG) << "aidl starting";
-
- Options options(argc, argv, Options::Language::CPP);
- if (!options.Ok()) {
- std::cerr << options.GetErrorMessage();
- std::cerr << options.GetUsage();
- return 1;
- }
-
- android::aidl::IoDelegate io_delegate;
- return android::aidl::compile_aidl(options, io_delegate);
-}
diff --git a/metadata/Android.bp b/metadata/Android.bp
new file mode 100644
index 0000000..245c366
--- /dev/null
+++ b/metadata/Android.bp
@@ -0,0 +1,31 @@
+// build time C++ available list of all AIDL interfaces in the tree
+cc_library {
+ name: "libaidlmetadata",
+ host_supported: true,
+ srcs: [":aidl_metadata_in_cpp"],
+ export_include_dirs: ["include"],
+
+ cflags: ["-O0"],
+}
+
+// private impl below
+
+cc_binary {
+ name: "aidl_metadata_parser",
+ host_supported: true,
+ srcs: ["parser.cpp"],
+ shared_libs: ["libjsoncpp"],
+ visibility: [":__subpackages__"],
+}
+
+cc_genrule {
+ name: "aidl_metadata_in_cpp",
+ host_supported: true,
+ cmd: "$(location aidl_metadata_parser) $(in) > $(genDir)/metadata.cpp",
+ srcs: [
+ ":aidl_metadata_json",
+ ],
+ tools: ["aidl_metadata_parser"],
+ visibility: [":__subpackages__"],
+ out: ["metadata.cpp"],
+}
diff --git a/metadata/include/aidl/metadata.h b/metadata/include/aidl/metadata.h
new file mode 100644
index 0000000..8a1caa2
--- /dev/null
+++ b/metadata/include/aidl/metadata.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace android {
+
+struct AidlInterfaceMetadata {
+ // name of module defining package
+ std::string name;
+
+ // stability of interface (e.g. "vintf")
+ std::string stability;
+
+ // list of types e.g. android.hardware.foo::IFoo
+ std::vector<std::string> types;
+
+ static std::vector<AidlInterfaceMetadata> all();
+};
+
+} // namespace android
diff --git a/metadata/parser.cpp b/metadata/parser.cpp
new file mode 100644
index 0000000..ba0aeb8
--- /dev/null
+++ b/metadata/parser.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#include <fstream>
+#include <iostream>
+
+#include <json/json.h>
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ std::cerr << "Usage: aidl_metadata_parser *.json" << std::endl;
+ return EXIT_FAILURE;
+ }
+ const std::string path = argv[1];
+
+ Json::Value root;
+ Json::Reader reader;
+
+ std::ifstream stream(path);
+ if (!reader.parse(stream, root)) {
+ std::cerr << "Failed to read interface metadata file: " << path << std::endl
+ << reader.getFormattedErrorMessages() << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ std::cout << "#include <aidl/metadata.h>" << std::endl;
+ std::cout << "namespace android {" << std::endl;
+ std::cout << "std::vector<AidlInterfaceMetadata> AidlInterfaceMetadata::all() {" << std::endl;
+ std::cout << "return std::vector<AidlInterfaceMetadata>{" << std::endl;
+ for (const Json::Value& entry : root) {
+ std::cout << "AidlInterfaceMetadata{" << std::endl;
+ // AIDL interface characters guaranteed to be accepted in C++ string
+ std::cout << "std::string(\"" << entry["name"].asString() << "\")," << std::endl;
+ std::cout << "std::string(\"" << entry["stability"].asString() << "\")," << std::endl;
+ std::cout << "std::vector<std::string>{" << std::endl;
+ for (const Json::Value& intf : entry["types"]) {
+ std::cout << "std::string(\"" << intf.asString() << "\")," << std::endl;
+ }
+ std::cout << "}," << std::endl;
+ std::cout << "}," << std::endl;
+ }
+ std::cout << "};" << std::endl;
+ std::cout << "}" << std::endl;
+ std::cout << "} // namespace android" << std::endl;
+ return EXIT_SUCCESS;
+}
diff --git a/options.cpp b/options.cpp
index 8bd2887..b79c361 100644
--- a/options.cpp
+++ b/options.cpp
@@ -39,7 +39,7 @@
string Options::GetUsage() const {
std::ostringstream sstr;
sstr << "usage:" << endl
- << myname_ << " --lang={java|cpp} [OPTION]... INPUT..." << endl
+ << myname_ << " --lang={java|cpp|ndk} [OPTION]... INPUT..." << endl
<< " Generate Java or C++ files for AIDL file(s)." << endl
<< endl
<< myname_ << " --preprocess OUTPUT INPUT..." << endl
@@ -109,6 +109,8 @@
<< " -v VER, --version=VER" << endl
<< " Set the version of the interface and parcelable to VER." << endl
<< " VER must be an interger greater than 0." << endl
+ << " --hash=HASH" << endl
+ << " Set the interface hash to HASH." << endl
<< " --log" << endl
<< " Information about the transaction, e.g., method name, argument" << endl
<< " values, execution time, etc., is provided via callback." << endl
@@ -188,6 +190,7 @@
{"version", required_argument, 0, 'v'},
{"log", no_argument, 0, 'L'},
{"parcelable-to-string", no_argument, 0, 'P'},
+ {"hash", required_argument, 0, 'H'},
{"help", no_argument, 0, 'e'},
{0, 0, 0, 0},
};
@@ -306,6 +309,9 @@
}
break;
}
+ case 'H':
+ hash_ = Trim(optarg);
+ break;
case 'L':
gen_log_ = true;
break;
diff --git a/options.h b/options.h
index 43334a7..73d33bb 100644
--- a/options.h
+++ b/options.h
@@ -121,6 +121,8 @@
int Version() const { return version_; }
+ string Hash() const { return hash_; }
+
bool GenLog() const { return gen_log_; }
bool GenParcelableToString() const { return gen_parcelable_to_string_; }
@@ -161,6 +163,7 @@
vector<string> input_files_;
string output_file_;
int version_ = 0;
+ string hash_ = "";
bool gen_log_ = false;
bool gen_parcelable_to_string_ = false;
ErrorMessage error_message_;
diff --git a/tests/aidl_parser_fuzzer.cpp b/tests/aidl_parser_fuzzer.cpp
new file mode 100644
index 0000000..43da866
--- /dev/null
+++ b/tests/aidl_parser_fuzzer.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#include "aidl.h"
+#include "fake_io_delegate.h"
+#include "options.h"
+
+#include <iostream>
+
+#ifdef FUZZ_LOG
+constexpr bool kFuzzLog = true;
+#else
+constexpr bool kFuzzLog = false;
+#endif
+
+using android::aidl::test::FakeIoDelegate;
+
+void fuzz(const std::string& langOpt, const std::string& content) {
+ // TODO: fuzz multiple files
+ // TODO: fuzz arguments
+ FakeIoDelegate io;
+ io.SetFileContents("a/path/Foo.aidl", content);
+
+ std::vector<std::string> args;
+ args.emplace_back("aidl");
+ args.emplace_back("--lang=" + langOpt);
+ args.emplace_back("-b");
+ args.emplace_back("-I .");
+ args.emplace_back("-o out");
+ // corresponding items also in aidl_parser_fuzzer.dict
+ args.emplace_back("a/path/Foo.aidl");
+
+ if (kFuzzLog) {
+ std::cout << "lang: " << langOpt << " content: " << content << std::endl;
+ }
+
+ int ret = android::aidl::compile_aidl(Options::From(args), io);
+ if (ret != 0) return;
+
+ if (kFuzzLog) {
+ for (const std::string& f : io.ListOutputFiles()) {
+ std::string output;
+ if (io.GetWrittenContents(f, &output)) {
+ std::cout << "OUTPUT " << f << ": " << std::endl;
+ std::cout << output << std::endl;
+ }
+ }
+ }
+}
+
+void fuzz(uint8_t options, const std::string& content) {
+ // keeping a byte of options we can use for various flags in the future (do
+ // not remove or add unless absolutely necessary in order to preserve the
+ // corpus).
+ (void)options;
+
+ // Process for each backend.
+ //
+ // This is unfortunate because we are parsing multiple times, but we want to
+ // check generation of content for each backend. If output fails in one
+ // backend, it's likely to fail in another.
+ fuzz("ndk", content);
+ fuzz("cpp", content);
+ fuzz("java", content);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (size <= 1) return 0; // no use
+
+ // b/145447540, large nested expressions sometimes hit the stack depth limit.
+ // Fuzzing things of this size don't provide any additional meaningful
+ // coverage. This is an approximate value which should allow us to explore all
+ // of the language w/o hitting a stack overflow.
+ if (size > 2000) return 0;
+
+ uint8_t options = *data;
+ data++;
+ size--;
+
+ std::string content(reinterpret_cast<const char*>(data), size);
+ fuzz(options, content);
+
+ return 0;
+}
diff --git a/tests/aidl_parser_fuzzer.dict b/tests/aidl_parser_fuzzer.dict
new file mode 100644
index 0000000..1f882a0
--- /dev/null
+++ b/tests/aidl_parser_fuzzer.dict
@@ -0,0 +1,62 @@
+# keywords
+" parcelable "
+" import "
+" package "
+" in "
+" out "
+" inout "
+" cpp_header "
+" const "
+" true "
+" false "
+" interface "
+" oneway "
+" enum "
+
+# types
+" void "
+" boolean "
+" byte "
+" char "
+" int "
+" long "
+" float "
+" double "
+" String "
+" List "
+" Map "
+" IBinder "
+" FileDescriptor "
+" CharSequence "
+" ParcelFileDescriptor "
+" java.util.List "
+" java.util.Map "
+" android.os.ParcelFileDescriptor "
+
+# annotations
+" @nullable "
+" @utf8InCpp "
+" @VintfStability "
+" @UnsupportedAppUsage "
+" @SystemApi "
+" @JavaOnlyStableParcelable "
+" @Backing "
+" expectedSignature "
+" implicitMember "
+" maxTargetSdk "
+" publicAlternatives "
+" trackingBug "
+" type "
+
+# ignorable imports
+" android.os.IInterface "
+" android.os.IBinder "
+" android.os.Parcelable "
+" android.os.Parcel "
+" android.content.Context "
+" java.lang.String "
+
+# specific to fuzzer
+" a.path "
+" Foo "
+
diff --git a/tests/aidl_test_client_file_descriptors.cpp b/tests/aidl_test_client_file_descriptors.cpp
index e2d59a9..6e51869 100644
--- a/tests/aidl_test_client_file_descriptors.cpp
+++ b/tests/aidl_test_client_file_descriptors.cpp
@@ -119,7 +119,7 @@
return false;
}
- status = s->RepeatFileDescriptor(write_fd, &return_fd);
+ status = s->RepeatFileDescriptor(std::move(write_fd), &return_fd);
if (!status.isOk()) {
cerr << "Could not repeat file descriptors." << endl;
diff --git a/tests/aidl_test_client_primitives.cpp b/tests/aidl_test_client_primitives.cpp
index 2733800..beadf96 100644
--- a/tests/aidl_test_client_primitives.cpp
+++ b/tests/aidl_test_client_primitives.cpp
@@ -17,6 +17,7 @@
#include "aidl_test_client_primitives.h"
#include <iostream>
+#include <iterator>
#include <vector>
#include <utils/String16.h>
@@ -117,6 +118,9 @@
{String16{"f"}, String16{"a"}, String16{"b"}}) ||
!ReverseArray(s, &ITestService::ReverseByteEnum,
{ByteEnum::FOO, ByteEnum::BAR, ByteEnum::BAR}) ||
+ !ReverseArray(s, &ITestService::ReverseByteEnum,
+ {std::begin(::android::enum_range<ByteEnum>()),
+ std::end(::android::enum_range<ByteEnum>())}) ||
!ReverseArray(s, &ITestService::ReverseIntEnum, {IntEnum::FOO, IntEnum::BAR, IntEnum::BAR}) ||
!ReverseArray(s, &ITestService::ReverseLongEnum,
{LongEnum::FOO, LongEnum::BAR, LongEnum::BAR})) {
diff --git a/tests/aidl_test_service.cpp b/tests/aidl_test_service.cpp
index 3b0deed..113d1f5 100644
--- a/tests/aidl_test_service.cpp
+++ b/tests/aidl_test_service.cpp
@@ -163,17 +163,17 @@
return Status::ok();
}
Status RepeatByteEnum(ByteEnum token, ByteEnum* _aidl_return) override {
- ALOGI("Repeating ByteEnum token %d", static_cast<int8_t>(token));
+ ALOGI("Repeating ByteEnum token %s", toString(token).c_str());
*_aidl_return = token;
return Status::ok();
}
Status RepeatIntEnum(IntEnum token, IntEnum* _aidl_return) override {
- ALOGI("Repeating IntEnum token %d", static_cast<int8_t>(token));
+ ALOGI("Repeating IntEnum token %s", toString(token).c_str());
*_aidl_return = token;
return Status::ok();
}
Status RepeatLongEnum(LongEnum token, LongEnum* _aidl_return) override {
- ALOGI("Repeating LongEnum token %d", static_cast<int8_t>(token));
+ ALOGI("Repeating LongEnum token %s", toString(token).c_str());
*_aidl_return = token;
return Status::ok();
}
@@ -317,7 +317,7 @@
return ReverseArray(input, repeated, _aidl_return);
}
- Status RepeatFileDescriptor(const unique_fd& read,
+ Status RepeatFileDescriptor(unique_fd read,
unique_fd* _aidl_return) override {
ALOGE("Repeating file descriptor");
*_aidl_return = unique_fd(dup(read.get()));
diff --git a/tests/android/aidl/loggable/ILoggableInterfaceNdk.aidl b/tests/android/aidl/loggable/ILoggableInterfaceNdk.aidl
deleted file mode 100644
index b3d2de6..0000000
--- a/tests/android/aidl/loggable/ILoggableInterfaceNdk.aidl
+++ /dev/null
@@ -1,14 +0,0 @@
-package android.aidl.loggable;
-
-interface ILoggableInterfaceNdk {
- String[] LogThis(boolean boolValue, inout boolean[] boolArray,
- byte byteValue, inout byte[] byteArray,
- char charValue, inout char[] charArray,
- int intValue, inout int[] intArray,
- long longValue, inout long[] longArray,
- float floatValue, inout float[] floatArray,
- double doubleValue, inout double[] doubleArray,
- String stringValue, inout String[] stringArray,
- IBinder binderValue,
- inout ParcelFileDescriptor pfdValue);
-}
diff --git a/tests/android/aidl/tests/ByteEnum.aidl b/tests/android/aidl/tests/ByteEnum.aidl
index 31ca9e0..503ae8a 100644
--- a/tests/android/aidl/tests/ByteEnum.aidl
+++ b/tests/android/aidl/tests/ByteEnum.aidl
@@ -24,5 +24,6 @@
// Comment about FOO.
FOO = 1,
BAR = 2,
+ BAZ,
}
diff --git a/tests/android/aidl/tests/IntEnum.aidl b/tests/android/aidl/tests/IntEnum.aidl
index 5f10f58..d3769ef 100644
--- a/tests/android/aidl/tests/IntEnum.aidl
+++ b/tests/android/aidl/tests/IntEnum.aidl
@@ -20,5 +20,6 @@
enum IntEnum {
FOO = 1000,
BAR = 2000,
+ BAZ,
}
diff --git a/tests/android/aidl/tests/LongEnum.aidl b/tests/android/aidl/tests/LongEnum.aidl
index e7caa1b..9d7610b 100644
--- a/tests/android/aidl/tests/LongEnum.aidl
+++ b/tests/android/aidl/tests/LongEnum.aidl
@@ -20,5 +20,6 @@
enum LongEnum {
FOO = 100000000000,
BAR = 200000000000,
+ BAZ,
}
diff --git a/tests/android/aidl/tests/generic/Baz.aidl b/tests/android/aidl/tests/generic/Baz.aidl
new file mode 100644
index 0000000..2244d98
--- /dev/null
+++ b/tests/android/aidl/tests/generic/Baz.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.aidl.tests.generic;
+
+parcelable Baz {
+ int a;
+}
\ No newline at end of file
diff --git a/tests/android/aidl/tests/generic/IFaz.aidl b/tests/android/aidl/tests/generic/IFaz.aidl
new file mode 100644
index 0000000..127f3b7
--- /dev/null
+++ b/tests/android/aidl/tests/generic/IFaz.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.aidl.tests.generic;
+
+import android.aidl.tests.generic.Baz;
+import android.aidl.tests.generic.Pair;
+
+interface IFaz {
+ Pair<int, String> getPair();
+ Pair<Baz, Baz> getPair2();
+}
\ No newline at end of file
diff --git a/tests/android/aidl/tests/generic/Pair.aidl b/tests/android/aidl/tests/generic/Pair.aidl
new file mode 100644
index 0000000..69e5ed9
--- /dev/null
+++ b/tests/android/aidl/tests/generic/Pair.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.aidl.tests.generic;
+
+parcelable Pair<A, B>;
\ No newline at end of file
diff --git a/tests/android/aidl/tests/map/Bar.aidl b/tests/android/aidl/tests/map/Bar.aidl
new file mode 100644
index 0000000..cbd95ef
--- /dev/null
+++ b/tests/android/aidl/tests/map/Bar.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.aidl.tests.map;
+
+parcelable Bar {
+ int a;
+ String b;
+}
\ No newline at end of file
diff --git a/tests/android/aidl/tests/map/Foo.aidl b/tests/android/aidl/tests/map/Foo.aidl
new file mode 100644
index 0000000..4bc3e91
--- /dev/null
+++ b/tests/android/aidl/tests/map/Foo.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.aidl.tests.map;
+
+import android.aidl.tests.map.Bar;
+import android.aidl.tests.map.IEmpty;
+
+parcelable Foo {
+ Map<String, Bar> barMap;
+ Map<String, String> stringMap;
+ Map<String, IEmpty> interfaceMap;
+ Map<String, IBinder> ibinderMap;
+}
diff --git a/tests/android/aidl/tests/map/IEmpty.aidl b/tests/android/aidl/tests/map/IEmpty.aidl
new file mode 100644
index 0000000..c7f3f12
--- /dev/null
+++ b/tests/android/aidl/tests/map/IEmpty.aidl
@@ -0,0 +1,20 @@
+/*
+ * 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.aidl.tests.map;
+
+interface IEmpty {
+}
diff --git a/tests/corpus/char_seq_leak b/tests/corpus/char_seq_leak
new file mode 100644
index 0000000..688d6d8
--- /dev/null
+++ b/tests/corpus/char_seq_leak
@@ -0,0 +1 @@
+1enum F{U}parcelable e{CharSequence e;}
diff --git a/tests/corpus/double_include b/tests/corpus/double_include
new file mode 100644
index 0000000..e2e4a3d
--- /dev/null
+++ b/tests/corpus/double_include
@@ -0,0 +1 @@
+;import d;import d;
\ No newline at end of file
diff --git a/tests/corpus/map_leak b/tests/corpus/map_leak
new file mode 100644
index 0000000..11cc670
--- /dev/null
+++ b/tests/corpus/map_leak
@@ -0,0 +1 @@
+qparcelable a{Map e;}enum n{p}
diff --git a/tests/corpus/ndk_file_descriptor b/tests/corpus/ndk_file_descriptor
new file mode 100644
index 0000000..3769da0
--- /dev/null
+++ b/tests/corpus/ndk_file_descriptor
@@ -0,0 +1 @@
+&enum F{p}parcelable e{FileDescriptor e;}
\ No newline at end of file
diff --git a/tests/corpus/nongeneric_list b/tests/corpus/nongeneric_list
new file mode 100644
index 0000000..96e9e97
--- /dev/null
+++ b/tests/corpus/nongeneric_list
@@ -0,0 +1 @@
+²enum F{t}parcelable e{List e;}
\ No newline at end of file
diff --git a/tests/corpus/type_param_leak b/tests/corpus/type_param_leak
new file mode 100644
index 0000000..36eb684
--- /dev/null
+++ b/tests/corpus/type_param_leak
@@ -0,0 +1 @@
+;parcelable p<g
\ No newline at end of file
diff --git a/tests/corpus/utf8_non_string b/tests/corpus/utf8_non_string
new file mode 100644
index 0000000..f7a4e3a
--- /dev/null
+++ b/tests/corpus/utf8_non_string
@@ -0,0 +1 @@
+Uenum o{r}parcelable e{@utf8InCpp byte[]y;}
\ No newline at end of file
diff --git a/tests/end_to_end_tests.cpp b/tests/end_to_end_tests.cpp
index 601c76f..24458d6 100644
--- a/tests/end_to_end_tests.cpp
+++ b/tests/end_to_end_tests.cpp
@@ -170,7 +170,7 @@
CheckFileContents(options.DependencyFile(), kExpectedJavaDepsOutput);
}
-TEST_F(EndToEndTest, IExampleInterface_WithVersion) {
+TEST_F(EndToEndTest, IExampleInterface_WithVersionAndHash) {
using namespace ::android::aidl::test_data::example_interface;
vector<string> args = {
@@ -179,6 +179,7 @@
"-I .",
"-d an/arbitrary/path/to/dep.P",
"--version=10",
+ "--hash=abcdefg",
CanonicalNameToPath(kCanonicalName, ".aidl"),
kJavaOutputPath};
Options options = Options::From(args);
@@ -193,7 +194,7 @@
// Check that we parse correctly.
EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
- CheckFileContents(kJavaOutputPath, kExpectedJavaOutputWithVersion);
+ CheckFileContents(kJavaOutputPath, kExpectedJavaOutputWithVersionAndHash);
CheckFileContents(options.DependencyFile(), kExpectedJavaDepsOutput);
}
@@ -223,7 +224,7 @@
CheckFileContents(options.DependencyFile(), kExpectedCppDepsOutput);
}
-TEST_F(EndToEndTest, IPingResponderCpp_WithVersion) {
+TEST_F(EndToEndTest, IPingResponderCpp_WithVersionAndHash) {
using namespace ::android::aidl::test_data::ping_responder;
vector<string> args = {
@@ -231,6 +232,7 @@
"-d deps.P",
"-I .",
"--version=10",
+ "--hash=abcdefg",
CanonicalNameToPath(kCanonicalName, ".aidl"),
kGenHeaderDir,
kCppOutputPath};
@@ -242,10 +244,10 @@
// Check that we parse and generate code correctly.
EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
- CheckFileContents(kCppOutputPath, kExpectedCppOutputWithVersion);
- CheckFileContents(kGenInterfaceHeaderPath, kExpectedIHeaderOutputWithVersion);
- CheckFileContents(kGenClientHeaderPath, kExpectedBpHeaderOutputWithVersion);
- CheckFileContents(kGenServerHeaderPath, kExpectedBnHeaderOutputWithVersion);
+ CheckFileContents(kCppOutputPath, kExpectedCppOutputWithVersionAndHash);
+ CheckFileContents(kGenInterfaceHeaderPath, kExpectedIHeaderOutputWithVersionAndHash);
+ CheckFileContents(kGenClientHeaderPath, kExpectedBpHeaderOutputWithVersionAndHash);
+ CheckFileContents(kGenServerHeaderPath, kExpectedBnHeaderOutputWithVersionAndHash);
CheckFileContents(options.DependencyFile(), kExpectedCppDepsOutput);
}
@@ -286,12 +288,13 @@
CheckFileContents(kJavaOutputPath, kExpectedJavaOutput);
}
-TEST_F(EndToEndTest, StringConstantsInCpp_WithVersion) {
+TEST_F(EndToEndTest, StringConstantsInCpp_WithVersionAndHash) {
using namespace ::android::aidl::test_data::string_constants;
vector<string> args = {
"aidl-cpp",
"--version=10",
+ "--hash=abcdefg",
CanonicalNameToPath(kCanonicalName, ".aidl"),
kGenHeaderDir,
kCppOutputPath};
@@ -302,17 +305,18 @@
// Check that we parse and generate code correctly.
EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
- CheckFileContents(kCppOutputPath, kExpectedCppOutputWithVersion);
- CheckFileContents(kGenInterfaceHeaderPath, kExpectedIHeaderOutputWithVersion);
+ CheckFileContents(kCppOutputPath, kExpectedCppOutputWithVersionAndHash);
+ CheckFileContents(kGenInterfaceHeaderPath, kExpectedIHeaderOutputWithVersionAndHash);
}
-TEST_F(EndToEndTest, StringConstantsInJava_WithVersion) {
+TEST_F(EndToEndTest, StringConstantsInJava_WithVersionAndHash) {
using namespace ::android::aidl::test_data::string_constants;
vector<string> args = {
"aidl",
"-b",
"--version=10",
+ "--hash=abcdefg",
CanonicalNameToPath(kCanonicalName, ".aidl"),
kJavaOutputPath};
Options options = Options::From(args);
@@ -322,7 +326,7 @@
// Check that we parse correctly.
EXPECT_EQ(android::aidl::compile_aidl(options, io_delegate_), 0);
- CheckFileContents(kJavaOutputPath, kExpectedJavaOutputWithVersion);
+ CheckFileContents(kJavaOutputPath, kExpectedJavaOutputWithVersionAndHash);
}
} // namespace aidl
diff --git a/tests/fake_io_delegate.cpp b/tests/fake_io_delegate.cpp
index 682ea57..dc35c4d 100644
--- a/tests/fake_io_delegate.cpp
+++ b/tests/fake_io_delegate.cpp
@@ -151,6 +151,14 @@
return true;
}
+std::vector<std::string> FakeIoDelegate::ListOutputFiles() {
+ std::vector<std::string> out;
+ for (const auto& [file, contents] : written_file_contents_) {
+ out.push_back(file);
+ }
+ return out;
+}
+
bool FakeIoDelegate::PathWasRemoved(const std::string& path) {
if (removed_files_.count(path) > 0) {
return true;
diff --git a/tests/fake_io_delegate.h b/tests/fake_io_delegate.h
index 68a9626..f077c0f 100644
--- a/tests/fake_io_delegate.h
+++ b/tests/fake_io_delegate.h
@@ -60,6 +60,7 @@
// Returns true iff we've previously written to |path|.
// When we return true, we'll set *contents to the written string.
bool GetWrittenContents(const std::string& path, std::string* content);
+ std::vector<std::string> ListOutputFiles();
bool PathWasRemoved(const std::string& path);
diff --git a/tests/java_app/src/android/aidl/tests/GenericTests.java b/tests/java_app/src/android/aidl/tests/GenericTests.java
new file mode 100644
index 0000000..85f09ab
--- /dev/null
+++ b/tests/java_app/src/android/aidl/tests/GenericTests.java
@@ -0,0 +1,60 @@
+/*
+ * 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.aidl.tests;
+
+import android.aidl.tests.generic.Baz;
+import android.aidl.tests.generic.IFaz;
+import android.aidl.tests.generic.Pair;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+class GenericTests {
+ private TestLogger mLog;
+
+ public GenericTests(TestLogger logger) { mLog = logger; }
+
+ public void checkGeneric() throws TestFailException {
+ mLog.log("Checking generic feature.");
+ IFaz.Stub ifaz = new IFaz.Stub() {
+ public Pair<Integer, String> getPair() {
+ Pair<Integer, String> ret = new Pair<Integer, String>();
+ ret.mFirst = 15;
+ ret.mSecond = "My";
+ return ret;
+ }
+ public Pair<Baz, Baz> getPair2() {
+ Pair<Baz, Baz> ret = new Pair<Baz, Baz>();
+ ret.mFirst = new Baz();
+ ret.mSecond = new Baz();
+ return ret;
+ }
+ };
+ try {
+ IFaz service = IFaz.Stub.asInterface(ifaz);
+ if (service.getPair().mFirst != 15) {
+ mLog.logAndThrow("mFirst must be 15, but it is " + service.getPair().mFirst);
+ }
+ if (!"My".equals(service.getPair().mSecond)) {
+ mLog.logAndThrow("mSecond must be \"My\", but it is " + service.getPair().mSecond);
+ }
+ } catch (RemoteException e) {
+ mLog.logAndThrow("This test is local, so the exception is not expected: " + e);
+ }
+ }
+
+ public void runTests() throws TestFailException { checkGeneric(); }
+}
diff --git a/tests/java_app/src/android/aidl/tests/MapTests.java b/tests/java_app/src/android/aidl/tests/MapTests.java
new file mode 100644
index 0000000..46d1657
--- /dev/null
+++ b/tests/java_app/src/android/aidl/tests/MapTests.java
@@ -0,0 +1,96 @@
+/*
+ * 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.aidl.tests;
+
+import android.aidl.tests.map.Bar;
+import android.aidl.tests.map.Foo;
+import android.aidl.tests.map.IEmpty;
+import android.os.IBinder;
+import android.os.Parcel;
+import java.util.HashMap;
+
+class MapTests {
+ private TestLogger mLog;
+
+ public MapTests(TestLogger logger) { mLog = logger; }
+
+ public void checkMap() throws TestFailException {
+ mLog.log("Checking if data in a Map object is transferred well.");
+ Parcel parcel = Parcel.obtain();
+ IEmpty intf = new IEmpty.Stub() {};
+ {
+ Foo foo = new Foo();
+ Bar bar = new Bar();
+ bar.a = 42;
+ bar.b = "Bar";
+ foo.barMap = new HashMap<>();
+ foo.barMap.put("Foo", bar);
+
+ foo.stringMap = new HashMap<>();
+ foo.stringMap.put("Foo", "Bar");
+
+ foo.interfaceMap = new HashMap<>();
+ foo.interfaceMap.put("Foo", intf);
+
+ foo.ibinderMap = new HashMap<>();
+ foo.ibinderMap.put("Foo", intf.asBinder());
+
+ foo.writeToParcel(parcel, 0);
+ }
+ parcel.setDataPosition(0);
+ {
+ Foo foo = new Foo();
+ foo.readFromParcel(parcel);
+ if (!foo.barMap.containsKey("Foo")) {
+ mLog.logAndThrow("Map foo.a must have the element of which key is \"Foo\"");
+ }
+ if (foo.barMap.size() != 1) {
+ mLog.logAndThrow("The size of map a is expected to be 1.");
+ }
+ Bar bar = foo.barMap.get("Foo");
+ if (bar.a != 42 || !"Bar".equals(bar.b)) {
+ mLog.logAndThrow("The content of bar is expected to be {a: 42, b: \"Bar\"}.");
+ }
+
+ if (foo.stringMap.size() != 1) {
+ mLog.logAndThrow("The size of map a is expected to be 1.");
+ }
+ String string = foo.stringMap.get("Foo");
+ if (!"Bar".equals(string)) {
+ mLog.logAndThrow("The content of string is expected to be \"Bar\".");
+ }
+
+ if (foo.interfaceMap.size() != 1) {
+ mLog.logAndThrow("The size of map a is expected to be 1.");
+ }
+
+ if (!intf.equals(foo.interfaceMap.get("Foo"))) {
+ mLog.logAndThrow("The content of service is expected to be same.");
+ }
+
+ if (foo.ibinderMap.size() != 1) {
+ mLog.logAndThrow("The size of map a is expected to be 1.");
+ }
+ IBinder ibinder = foo.ibinderMap.get("Foo");
+ if (!intf.asBinder().equals(ibinder)) {
+ mLog.logAndThrow("The content of IBinder is expected to be same.");
+ }
+ }
+ }
+
+ public void runTests() throws TestFailException { checkMap(); }
+}
diff --git a/tests/java_app/src/android/aidl/tests/TestServiceClient.java b/tests/java_app/src/android/aidl/tests/TestServiceClient.java
index ef5c651..01df091 100644
--- a/tests/java_app/src/android/aidl/tests/TestServiceClient.java
+++ b/tests/java_app/src/android/aidl/tests/TestServiceClient.java
@@ -972,6 +972,8 @@
checkUtf8Strings(service);
checkStructuredParcelable(service);
new NullableTests(service, mLog).runTests();
+ new MapTests(mLog).runTests();
+ new GenericTests(mLog).runTests();
checkDefaultImpl(service);
mLog.log(mSuccessSentinel);
diff --git a/tests/java_app/src/android/aidl/tests/generic/Pair.java b/tests/java_app/src/android/aidl/tests/generic/Pair.java
new file mode 100644
index 0000000..a0631df
--- /dev/null
+++ b/tests/java_app/src/android/aidl/tests/generic/Pair.java
@@ -0,0 +1,46 @@
+/*
+ * 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.aidl.tests.generic;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class Pair<A, B> implements Parcelable {
+ public A mFirst;
+ public B mSecond;
+
+ public Pair() {}
+ public Pair(Parcel source) { readFromParcel(source); }
+
+ public int describeContents() { return 0; }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeValue(mFirst);
+ dest.writeValue(mSecond);
+ }
+
+ public void readFromParcel(Parcel source) {
+ mFirst = (A) source.readValue(null);
+ mSecond = (B) source.readValue(null);
+ }
+
+ public static final Parcelable.Creator<Pair> CREATOR = new Parcelable.Creator<Pair>() {
+ public Pair createFromParcel(Parcel source) { return new Pair(source); }
+
+ public Pair[] newArray(int size) { return new Pair[size]; }
+ };
+}
diff --git a/tests/lazy_test/Android.bp b/tests/lazy_test/Android.bp
new file mode 100644
index 0000000..47914ca
--- /dev/null
+++ b/tests/lazy_test/Android.bp
@@ -0,0 +1,26 @@
+cc_test {
+ name: "aidl_lazy_test",
+ srcs: ["main.cpp"],
+ test_suites: ["general-tests"],
+ require_root: true,
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libutils",
+ "libbinder",
+ ],
+}
+
+cc_binary {
+ name: "aidl_lazy_test_server",
+ srcs: ["server.cpp"],
+ init_rc: ["aidl_lazy_test_server.rc"],
+ system_ext_specific: true,
+
+ shared_libs: [
+ "libbinder",
+ "liblog",
+ "libutils",
+ ],
+}
diff --git a/tests/lazy_test/README b/tests/lazy_test/README
new file mode 100644
index 0000000..e05f8f5
--- /dev/null
+++ b/tests/lazy_test/README
@@ -0,0 +1,66 @@
+aidl_lazy_test_server is a simple test service.
+
+Because it represents the bare minimum requirements for implementing a service, it also serves as
+an example of how to add a new service. The required files are as follows:
+
+============================================
+In this directory
+============================================
+--------------------------------------------
+server.cpp
+--------------------------------------------
+The implementation of the server. The only required function is main(), wherein the service must be
+instantiated and added (either to servicemanager as a standard service or to LazyServiceRegistrar
+to be a dynamic service). The server then joins the thread pool.
+
+--------------------------------------------
+aidl_lazy_test_server.rc
+--------------------------------------------
+This file is read by init, which later starts the service. The interface, oneshot, and disabled
+lines are only required for dynamic services.
+
+--------------------------------------------
+Android.bp
+--------------------------------------------
+A cc_binary entry will need to be added for the service with:
+name
+srcs - The source file(s), in this case just server.cpp
+init_rc - The .rc file
+shared_libs - Any shared libraries the source file depends on
+
+============================================
+In system/sepolicy
+============================================
+--------------------------------------------
+private/aidl_lazy_test_server.te
+--------------------------------------------
+Only two lines are required in this file.
+
+--------------------------------------------
+public/aidl_lazy_test_server.te
+--------------------------------------------
+The first two lines establish types for aidl_lazy_test_server and aidl_lazy_test_server_exec.
+binder_use and binder_call allow for basic use of this service.
+add_service allows the service to be registered. Note that an additional service type is required
+as the second argument to this function.
+
+--------------------------------------------
+private/compat/<number>/<number>.ignore.cil
+--------------------------------------------
+aidl_lazy_test_server and aidl_lazy_test_server_exec, and aidl_lazy_test_service need to be added
+to the new objects list.
+
+--------------------------------------------
+private/file_contexts
+--------------------------------------------
+A line is required to map aidl_lazy_test_server to aidl_lazy_test_server_exec.
+
+--------------------------------------------
+private/service_contexts
+--------------------------------------------
+Each interface for the service must be mapped to aidl_lazy_test_service here.
+
+--------------------------------------------
+public/service.te
+--------------------------------------------
+A line is required to define aidl_lazy_test_service.
diff --git a/tests/lazy_test/aidl_lazy_test_server.rc b/tests/lazy_test/aidl_lazy_test_server.rc
new file mode 100644
index 0000000..05c25b4
--- /dev/null
+++ b/tests/lazy_test/aidl_lazy_test_server.rc
@@ -0,0 +1,7 @@
+service aidl_lazy_test /system_ext/bin/aidl_lazy_test_server
+ interface aidl aidl_lazy_test_1
+ interface aidl aidl_lazy_test_2
+ user system
+ group system
+ oneshot
+ disabled
diff --git a/tests/lazy_test/main.cpp b/tests/lazy_test/main.cpp
new file mode 100644
index 0000000..178c2f7
--- /dev/null
+++ b/tests/lazy_test/main.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+#include <cstdlib>
+#include <ctime>
+#include <iostream>
+#include <numeric>
+#include <string>
+#include <thread>
+
+#include <unistd.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <gtest/gtest.h>
+#include <utils/String8.h>
+
+using ::android::IBinder;
+using ::android::IPCThreadState;
+using ::android::IServiceManager;
+using ::android::sp;
+
+std::vector<android::String16> gServiceNames;
+
+sp<IBinder> waitForService(size_t inx) {
+ sp<IServiceManager> manager;
+ manager = android::defaultServiceManager();
+ EXPECT_TRUE(manager != nullptr);
+
+ return manager->waitForService(gServiceNames.at(inx));
+}
+
+class AidlLazyTest : public ::testing::Test {
+ protected:
+ sp<IServiceManager> manager;
+
+ void SetUp() override {
+ manager = android::defaultServiceManager();
+ ASSERT_NE(manager, nullptr);
+
+ for (size_t i = 0; i < gServiceNames.size(); i++) {
+ ASSERT_FALSE(isServiceRunning(i))
+ << "Service '" << android::String8(gServiceNames.at(i)) << "' is already running. "
+ << "Please ensure this is implemented as a lazy service, then kill all "
+ << "clients of this service and try again.";
+ }
+ }
+
+ static constexpr size_t SHUTDOWN_WAIT_TIME = 10;
+ void TearDown() override {
+ std::cout << "Waiting " << SHUTDOWN_WAIT_TIME << " seconds before checking that the "
+ << "service has shut down." << std::endl;
+ IPCThreadState::self()->flushCommands();
+ sleep(SHUTDOWN_WAIT_TIME);
+ for (size_t i = 0; i < gServiceNames.size(); i++) {
+ ASSERT_FALSE(isServiceRunning(i)) << "Service failed to shut down.";
+ }
+ }
+
+ bool isServiceRunning(size_t inx) {
+ auto services = manager->listServices();
+ for (size_t i = 0; i < services.size(); i++) {
+ if (services[i] == gServiceNames.at(inx)) return true;
+ }
+ return false;
+ }
+};
+
+static constexpr size_t NUM_IMMEDIATE_GETS = 100;
+TEST_F(AidlLazyTest, GetRelease) {
+ size_t nServices = gServiceNames.size();
+
+ for (size_t i = 0; i < nServices * NUM_IMMEDIATE_GETS; i++) {
+ IPCThreadState::self()->flushCommands();
+ sp<IBinder> service = waitForService(i % nServices);
+ ASSERT_NE(service.get(), nullptr);
+ EXPECT_TRUE(service->pingBinder() == android::NO_ERROR);
+ }
+}
+
+static std::vector<size_t> waitTimes(size_t numTimes, size_t maxWait) {
+ std::vector<size_t> times(numTimes);
+ for (size_t i = 0; i < numTimes; i++) {
+ times.at(i) = (size_t)(rand() % (maxWait + 1));
+ }
+ return times;
+}
+
+static void testWithTimes(const std::vector<size_t>& waitTimes, bool beforeGet) {
+ size_t nServices = gServiceNames.size();
+ for (size_t i = 0; i < waitTimes.size(); i++) {
+ IPCThreadState::self()->flushCommands();
+ if (beforeGet) {
+ std::cout << "Thread waiting " << waitTimes.at(i) << " while not holding service."
+ << std::endl;
+ sleep(waitTimes.at(i));
+ }
+
+ sp<IBinder> service = waitForService(i % nServices);
+
+ if (!beforeGet) {
+ std::cout << "Thread waiting " << waitTimes.at(i) << " while holding service." << std::endl;
+ sleep(waitTimes.at(i));
+ }
+
+ ASSERT_NE(service.get(), nullptr);
+ ASSERT_TRUE(service->pingBinder() == android::NO_ERROR);
+ }
+}
+
+static constexpr size_t NUM_TIMES_GET_RELEASE = 5;
+static constexpr size_t MAX_WAITING_DURATION = 10;
+static constexpr size_t NUM_CONCURRENT_THREADS = 3;
+static void testConcurrentThreadsWithDelays(bool delayBeforeGet) {
+ size_t nServices = gServiceNames.size();
+ std::vector<std::vector<size_t>> threadWaitTimes(NUM_CONCURRENT_THREADS);
+ int maxWait = 0;
+ for (size_t i = 0; i < threadWaitTimes.size(); i++) {
+ threadWaitTimes.at(i) = waitTimes(NUM_TIMES_GET_RELEASE * nServices, MAX_WAITING_DURATION);
+ int totalWait = std::accumulate(threadWaitTimes.at(i).begin(), threadWaitTimes.at(i).end(), 0);
+ maxWait = std::max(maxWait, totalWait);
+ }
+ std::cout << "Additional runtime expected from sleeps: " << maxWait << " second(s)." << std::endl;
+
+ std::vector<std::thread> threads(NUM_CONCURRENT_THREADS);
+ for (size_t i = 0; i < threads.size(); i++) {
+ threads.at(i) = std::thread(testWithTimes, threadWaitTimes.at(i), delayBeforeGet);
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+}
+
+TEST_F(AidlLazyTest, GetConcurrentWithWaitBefore) {
+ testConcurrentThreadsWithDelays(true);
+}
+
+TEST_F(AidlLazyTest, GetConcurrentWithWaitAfter) {
+ testConcurrentThreadsWithDelays(false);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ srand(time(nullptr));
+
+ if (argc < 2) {
+ // If the user does not specify any service to test, default to these test interfaces
+ gServiceNames.push_back(android::String16("aidl_lazy_test_1"));
+ gServiceNames.push_back(android::String16("aidl_lazy_test_2"));
+ } else {
+ for (int i = 1; i < argc; i++) {
+ gServiceNames.push_back(android::String16(argv[i]));
+ }
+ }
+
+ android::ProcessState::self()->startThreadPool();
+
+ return RUN_ALL_TESTS();
+}
diff --git a/tests/lazy_test/server.cpp b/tests/lazy_test/server.cpp
new file mode 100644
index 0000000..f5fa9ed
--- /dev/null
+++ b/tests/lazy_test/server.cpp
@@ -0,0 +1,25 @@
+#include <binder/IBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/LazyServiceRegistrar.h>
+#include <utils/Log.h>
+
+using android::BBinder;
+using android::IBinder;
+using android::IPCThreadState;
+using android::OK;
+using android::sp;
+using android::binder::LazyServiceRegistrar;
+
+int main() {
+ sp<IBinder> binder1 = new BBinder;
+ sp<IBinder> binder2 = new BBinder;
+
+ auto lazyRegistrar = LazyServiceRegistrar::getInstance();
+ LOG_ALWAYS_FATAL_IF(OK != lazyRegistrar.registerService(binder1, "aidl_lazy_test_1"), "");
+ LOG_ALWAYS_FATAL_IF(OK != lazyRegistrar.registerService(binder2, "aidl_lazy_test_2"), "");
+
+ IPCThreadState::self()->joinThreadPool();
+
+ return 1;
+}
diff --git a/tests/test_data.h b/tests/test_data.h
index b4be6ec..ad428b0 100644
--- a/tests/test_data.h
+++ b/tests/test_data.h
@@ -35,7 +35,7 @@
extern const char kExpectedJavaOutputOutlining[];
extern const char kExpectedJavaOutputWithTransactionNames[];
extern const char kExpectedJavaOutputWithTrace[];
-extern const char kExpectedJavaOutputWithVersion[];
+extern const char kExpectedJavaOutputWithVersionAndHash[];
} // namespace example_interface
@@ -61,10 +61,10 @@
extern const char kExpectedBpHeaderOutput[];
extern const char kExpectedBnHeaderOutput[];
-extern const char kExpectedCppOutputWithVersion[];
-extern const char kExpectedIHeaderOutputWithVersion[];
-extern const char kExpectedBpHeaderOutputWithVersion[];
-extern const char kExpectedBnHeaderOutputWithVersion[];
+extern const char kExpectedCppOutputWithVersionAndHash[];
+extern const char kExpectedIHeaderOutputWithVersionAndHash[];
+extern const char kExpectedBpHeaderOutputWithVersionAndHash[];
+extern const char kExpectedBnHeaderOutputWithVersionAndHash[];
} // namespace ping_responder
@@ -76,7 +76,7 @@
extern const char kJavaOutputPath[];
extern const char kExpectedJavaOutput[];
-extern const char kExpectedJavaOutputWithVersion[];
+extern const char kExpectedJavaOutputWithVersionAndHash[];
extern const char kCppOutputPath[];
extern const char kGenHeaderDir[];
@@ -84,8 +84,8 @@
extern const char kExpectedIHeaderOutput[];
extern const char kExpectedCppOutput[];
-extern const char kExpectedIHeaderOutputWithVersion[];
-extern const char kExpectedCppOutputWithVersion[];
+extern const char kExpectedIHeaderOutputWithVersionAndHash[];
+extern const char kExpectedCppOutputWithVersionAndHash[];
} // namespace string_constants
diff --git a/tests/test_data_example_interface.cpp b/tests/test_data_example_interface.cpp
index 58a0426..7536306 100644
--- a/tests/test_data_example_interface.cpp
+++ b/tests/test_data_example_interface.cpp
@@ -105,15 +105,15 @@
const char kExpectedJavaDepsOutput[] =
R"(some/path/to/output.java : \
android/test/IExampleInterface.aidl \
- ./android/bar/IAuxInterface.aidl \
./android/foo/ExampleParcelable.aidl \
./android/test/CompoundParcelable.aidl \
+ ./android/bar/IAuxInterface.aidl \
./android/test/IAuxInterface2.aidl
android/test/IExampleInterface.aidl :
-./android/bar/IAuxInterface.aidl :
./android/foo/ExampleParcelable.aidl :
./android/test/CompoundParcelable.aidl :
+./android/bar/IAuxInterface.aidl :
./android/test/IAuxInterface2.aidl :
)";
@@ -122,7 +122,7 @@
* This file is auto-generated. DO NOT MODIFY.
*/
package android.test;
-@dalvik.annotation.compat.UnsupportedAppUsage
+@android.compat.annotation.UnsupportedAppUsage(overrideSourcePosition="android/test/IExampleInterface.aidl:10:1:10:21")
@android.annotation.SystemApi
public interface IExampleInterface extends android.os.IInterface
{
@@ -564,7 +564,7 @@
public int getState() throws android.os.RemoteException;
public java.lang.String getAddress() throws android.os.RemoteException;
/* Test long comment */
- @dalvik.annotation.compat.UnsupportedAppUsage
+ @android.compat.annotation.UnsupportedAppUsage(overrideSourcePosition="android/test/IExampleInterface.aidl:18:1:18:25")
@android.annotation.SystemApi
public android.foo.ExampleParcelable[] getParcelables() throws android.os.RemoteException;
// Test short comment
@@ -584,7 +584,7 @@
* This file is auto-generated. DO NOT MODIFY.
*/
package android.test;
-@dalvik.annotation.compat.UnsupportedAppUsage
+@android.compat.annotation.UnsupportedAppUsage(overrideSourcePosition="android/test/IExampleInterface.aidl:10:1:10:21")
@android.annotation.SystemApi
public interface IExampleInterface extends android.os.IInterface
{
@@ -1078,7 +1078,7 @@
public int getState() throws android.os.RemoteException;
public java.lang.String getAddress() throws android.os.RemoteException;
/* Test long comment */
- @dalvik.annotation.compat.UnsupportedAppUsage
+ @android.compat.annotation.UnsupportedAppUsage(overrideSourcePosition="android/test/IExampleInterface.aidl:18:1:18:25")
@android.annotation.SystemApi
public android.foo.ExampleParcelable[] getParcelables() throws android.os.RemoteException;
// Test short comment
@@ -1098,7 +1098,7 @@
* This file is auto-generated. DO NOT MODIFY.
*/
package android.test;
-@dalvik.annotation.compat.UnsupportedAppUsage
+@android.compat.annotation.UnsupportedAppUsage(overrideSourcePosition="android/test/IExampleInterface.aidl:10:1:10:21")
@android.annotation.SystemApi
public interface IExampleInterface extends android.os.IInterface
{
@@ -1620,7 +1620,7 @@
public int getState() throws android.os.RemoteException;
public java.lang.String getAddress() throws android.os.RemoteException;
/* Test long comment */
- @dalvik.annotation.compat.UnsupportedAppUsage
+ @android.compat.annotation.UnsupportedAppUsage(overrideSourcePosition="android/test/IExampleInterface.aidl:18:1:18:25")
@android.annotation.SystemApi
public android.foo.ExampleParcelable[] getParcelables() throws android.os.RemoteException;
// Test short comment
@@ -2123,7 +2123,7 @@
}
)";
-const char kExpectedJavaOutputWithVersion[] =
+const char kExpectedJavaOutputWithVersionAndHash[] =
R"(/*
* This file is auto-generated. DO NOT MODIFY.
*/
@@ -2137,6 +2137,7 @@
* that the remote object is implementing.
*/
public static final int VERSION = 10;
+ public static final String HASH = "abcdefg";
/** Default implementation for IExampleInterface. */
public static class Default implements android.test.IExampleInterface
{
@@ -2182,7 +2183,11 @@
}
@Override
public int getInterfaceVersion() {
- return -1;
+ return 0;
+ }
+ @Override
+ public String getInterfaceHash() {
+ return "";
}
@Override
public android.os.IBinder asBinder() {
@@ -2282,6 +2287,13 @@
reply.writeInt(getInterfaceVersion());
return true;
}
+ case TRANSACTION_getInterfaceHash:
+ {
+ data.enforceInterface(descriptor);
+ reply.writeNoException();
+ reply.writeString(getInterfaceHash());
+ return true;
+ }
default:
{
return super.onTransact(code, data, reply, flags);
@@ -2296,6 +2308,7 @@
mRemote = remote;
}
private int mCachedVersion = -1;
+ private String mCachedHash = "-1";
@Override public android.os.IBinder asBinder()
{
return mRemote;
@@ -2532,6 +2545,28 @@
}
return mCachedVersion;
}
+ @Override
+ public synchronized String getInterfaceHash() throws android.os.RemoteException {
+ if (mCachedHash == "-1") {
+ android.os.Parcel data = android.os.Parcel.obtain();
+ android.os.Parcel reply = android.os.Parcel.obtain();
+ try {
+ data.writeInterfaceToken(DESCRIPTOR);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_getInterfaceHash, data, reply, 0);
+ if (!_status) {
+ if (getDefaultImpl() != null) {
+ return getDefaultImpl().getInterfaceHash();
+ }
+ }
+ reply.readException();
+ mCachedHash = reply.readString();
+ } finally {
+ reply.recycle();
+ data.recycle();
+ }
+ }
+ return mCachedHash;
+ }
public static android.test.IExampleInterface sDefaultImpl;
}
static final int TRANSACTION_isEnabled = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
@@ -2624,6 +2659,7 @@
return true;
}
static final int TRANSACTION_getInterfaceVersion = (android.os.IBinder.FIRST_CALL_TRANSACTION + 16777214);
+ static final int TRANSACTION_getInterfaceHash = (android.os.IBinder.FIRST_CALL_TRANSACTION + 16777213);
public static boolean setDefaultImpl(android.test.IExampleInterface impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
@@ -2651,6 +2687,7 @@
public int takesAnInterface(android.test.IAuxInterface2 arg) throws android.os.RemoteException;
public int takesAParcelable(android.test.CompoundParcelable.Subclass1 arg, android.test.CompoundParcelable.Subclass2 arg2) throws android.os.RemoteException;
public int getInterfaceVersion() throws android.os.RemoteException;
+ public String getInterfaceHash() throws android.os.RemoteException;
}
)";
diff --git a/tests/test_data_ping_responder.cpp b/tests/test_data_ping_responder.cpp
index 130a225..fdc2bd4 100644
--- a/tests/test_data_ping_responder.cpp
+++ b/tests/test_data_ping_responder.cpp
@@ -82,27 +82,7 @@
namespace os {
-IMPLEMENT_META_INTERFACE(PingResponder, "android.os.IPingResponder")
-
-::android::IBinder* IPingResponderDefault::onAsBinder() {
- return nullptr;
-}
-
-::android::binder::Status IPingResponderDefault::Ping(const ::android::String16&, ::android::String16* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IPingResponderDefault::NullablePing(const ::std::unique_ptr<::android::String16>&, ::std::unique_ptr<::android::String16>* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IPingResponderDefault::Utf8Ping(const ::std::string&, ::std::string* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IPingResponderDefault::NullableUtf8Ping(const ::std::unique_ptr<::std::string>&, ::std::unique_ptr<::std::string>* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
+DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(PingResponder, "android.os.IPingResponder")
} // namespace os
@@ -430,13 +410,22 @@
class IPingResponderDefault : public IPingResponder {
public:
- ::android::IBinder* onAsBinder() override;
- ::android::binder::Status Ping(const ::android::String16& input, ::android::String16* _aidl_return) override;
- ::android::binder::Status NullablePing(const ::std::unique_ptr<::android::String16>& input, ::std::unique_ptr<::android::String16>* _aidl_return) override;
- ::android::binder::Status Utf8Ping(const ::std::string& input, ::std::string* _aidl_return) override;
- ::android::binder::Status NullableUtf8Ping(const ::std::unique_ptr<::std::string>& input, ::std::unique_ptr<::std::string>* _aidl_return) override;
-
-};
+ ::android::IBinder* onAsBinder() override {
+ return nullptr;
+ }
+ ::android::binder::Status Ping(const ::android::String16&, ::android::String16*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status NullablePing(const ::std::unique_ptr<::android::String16>&, ::std::unique_ptr<::android::String16>*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status Utf8Ping(const ::std::string&, ::std::string*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status NullableUtf8Ping(const ::std::unique_ptr<::std::string>&, ::std::unique_ptr<::std::string>*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+}; // class IPingResponderDefault
} // namespace os
@@ -499,7 +488,7 @@
#endif // AIDL_GENERATED_ANDROID_OS_BN_PING_RESPONDER_H_
)";
-const char kExpectedCppOutputWithVersion[] =
+const char kExpectedCppOutputWithVersionAndHash[] =
R"(#include <android/os/IPingResponder.h>
#include <android/os/BpPingResponder.h>
@@ -507,31 +496,7 @@
namespace os {
-IMPLEMENT_META_INTERFACE(PingResponder, "android.os.IPingResponder")
-
-::android::IBinder* IPingResponderDefault::onAsBinder() {
- return nullptr;
-}
-
-::android::binder::Status IPingResponderDefault::Ping(const ::android::String16&, ::android::String16* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IPingResponderDefault::NullablePing(const ::std::unique_ptr<::android::String16>&, ::std::unique_ptr<::android::String16>* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IPingResponderDefault::Utf8Ping(const ::std::string&, ::std::string* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-::android::binder::Status IPingResponderDefault::NullableUtf8Ping(const ::std::unique_ptr<::std::string>&, ::std::unique_ptr<::std::string>* ) {
- return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
-}
-
-int32_t IPingResponderDefault::getInterfaceVersion() {
- return 0;
-}
+DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(PingResponder, "android.os.IPingResponder")
} // namespace os
@@ -709,6 +674,24 @@
return cached_version_;
}
+std::string BpPingResponder::getInterfaceHash() {
+ std::lock_guard<std::mutex> lockGuard(cached_hash_mutex_);
+ if (cached_hash_ == "-1") {
+ ::android::Parcel data;
+ ::android::Parcel reply;
+ data.writeInterfaceToken(getInterfaceDescriptor());
+ ::android::status_t err = remote()->transact(::android::IBinder::FIRST_CALL_TRANSACTION + 16777213 /* getInterfaceHash */, data, &reply);
+ if (err == ::android::OK) {
+ ::android::binder::Status _aidl_status;
+ err = _aidl_status.readFromParcel(reply);
+ if (err == ::android::OK && _aidl_status.isOk()) {
+ cached_hash_ = reply.readString8().c_str();
+ }
+ }
+ }
+ return cached_hash_;
+}
+
} // namespace os
} // namespace android
@@ -839,6 +822,13 @@
_aidl_reply->writeInt32(IPingResponder::VERSION);
}
break;
+ case ::android::IBinder::FIRST_CALL_TRANSACTION + 16777213 /* getInterfaceHash */:
+ {
+ _aidl_data.checkInterface(this);
+ _aidl_reply->writeNoException();
+ _aidl_reply->writeString8(android::String8(IPingResponder::HASH.c_str()));
+ }
+ break;
default:
{
_aidl_ret_status = ::android::BBinder::onTransact(_aidl_code, _aidl_data, _aidl_reply, _aidl_flags);
@@ -855,13 +845,17 @@
return IPingResponder::VERSION;
}
+std::string BnPingResponder::getInterfaceHash() {
+ return IPingResponder::HASH;
+}
+
} // namespace os
} // namespace android
)";
-const char kExpectedIHeaderOutputWithVersion[] =
-R"(#ifndef AIDL_GENERATED_ANDROID_OS_I_PING_RESPONDER_H_
+const char kExpectedIHeaderOutputWithVersionAndHash[] =
+ R"(#ifndef AIDL_GENERATED_ANDROID_OS_I_PING_RESPONDER_H_
#define AIDL_GENERATED_ANDROID_OS_I_PING_RESPONDER_H_
#include <binder/IBinder.h>
@@ -881,23 +875,39 @@
public:
DECLARE_META_INTERFACE(PingResponder)
const int32_t VERSION = 10;
+ const std::string HASH = "abcdefg";
virtual ::android::binder::Status Ping(const ::android::String16& input, ::android::String16* _aidl_return) = 0;
virtual ::android::binder::Status NullablePing(const ::std::unique_ptr<::android::String16>& input, ::std::unique_ptr<::android::String16>* _aidl_return) = 0;
virtual ::android::binder::Status Utf8Ping(const ::std::string& input, ::std::string* _aidl_return) = 0;
virtual ::android::binder::Status NullableUtf8Ping(const ::std::unique_ptr<::std::string>& input, ::std::unique_ptr<::std::string>* _aidl_return) = 0;
virtual int32_t getInterfaceVersion() = 0;
+ virtual std::string getInterfaceHash() = 0;
}; // class IPingResponder
class IPingResponderDefault : public IPingResponder {
public:
- ::android::IBinder* onAsBinder() override;
- ::android::binder::Status Ping(const ::android::String16& input, ::android::String16* _aidl_return) override;
- ::android::binder::Status NullablePing(const ::std::unique_ptr<::android::String16>& input, ::std::unique_ptr<::android::String16>* _aidl_return) override;
- ::android::binder::Status Utf8Ping(const ::std::string& input, ::std::string* _aidl_return) override;
- ::android::binder::Status NullableUtf8Ping(const ::std::unique_ptr<::std::string>& input, ::std::unique_ptr<::std::string>* _aidl_return) override;
- int32_t getInterfaceVersion() override;
-
-};
+ ::android::IBinder* onAsBinder() override {
+ return nullptr;
+ }
+ ::android::binder::Status Ping(const ::android::String16&, ::android::String16*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status NullablePing(const ::std::unique_ptr<::android::String16>&, ::std::unique_ptr<::android::String16>*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status Utf8Ping(const ::std::string&, ::std::string*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ ::android::binder::Status NullableUtf8Ping(const ::std::unique_ptr<::std::string>&, ::std::unique_ptr<::std::string>*) override {
+ return ::android::binder::Status::fromStatusT(::android::UNKNOWN_TRANSACTION);
+ }
+ int32_t getInterfaceVersion() override {
+ return 0;
+ }
+ std::string getInterfaceHash() override {
+ return "";
+ }
+}; // class IPingResponderDefault
} // namespace os
@@ -906,7 +916,7 @@
#endif // AIDL_GENERATED_ANDROID_OS_I_PING_RESPONDER_H_
)";
-const char kExpectedBpHeaderOutputWithVersion[] =
+const char kExpectedBpHeaderOutputWithVersionAndHash[] =
R"(#ifndef AIDL_GENERATED_ANDROID_OS_BP_PING_RESPONDER_H_
#define AIDL_GENERATED_ANDROID_OS_BP_PING_RESPONDER_H_
@@ -928,8 +938,11 @@
::android::binder::Status Utf8Ping(const ::std::string& input, ::std::string* _aidl_return) override;
::android::binder::Status NullableUtf8Ping(const ::std::unique_ptr<::std::string>& input, ::std::unique_ptr<::std::string>* _aidl_return) override;
int32_t getInterfaceVersion() override;
+ std::string getInterfaceHash() override;
private:
int32_t cached_version_ = -1;
+ std::string cached_hash_ = "-1";
+ std::mutex cached_hash_mutex_;
}; // class BpPingResponder
} // namespace os
@@ -939,7 +952,7 @@
#endif // AIDL_GENERATED_ANDROID_OS_BP_PING_RESPONDER_H_
)";
-const char kExpectedBnHeaderOutputWithVersion[] =
+const char kExpectedBnHeaderOutputWithVersionAndHash[] =
R"(#ifndef AIDL_GENERATED_ANDROID_OS_BN_PING_RESPONDER_H_
#define AIDL_GENERATED_ANDROID_OS_BN_PING_RESPONDER_H_
@@ -955,6 +968,7 @@
explicit BnPingResponder();
::android::status_t onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) override;
int32_t getInterfaceVersion() final override;
+ std::string getInterfaceHash();
}; // class BnPingResponder
} // namespace os
diff --git a/tests/test_data_string_constants.cpp b/tests/test_data_string_constants.cpp
index d7b4bb5..0bea929 100644
--- a/tests/test_data_string_constants.cpp
+++ b/tests/test_data_string_constants.cpp
@@ -147,9 +147,10 @@
class IStringConstantsDefault : public IStringConstants {
public:
- ::android::IBinder* onAsBinder() override;
-
-};
+ ::android::IBinder* onAsBinder() override {
+ return nullptr;
+ }
+}; // class IStringConstantsDefault
} // namespace os
@@ -166,17 +167,13 @@
namespace os {
-IMPLEMENT_META_INTERFACE(StringConstants, "android.os.IStringConstants")
+DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(StringConstants, "android.os.IStringConstants")
const ::android::String16& IStringConstants::EXAMPLE_CONSTANT() {
static const ::android::String16 value(::android::String16("foo"));
return value;
}
-::android::IBinder* IStringConstantsDefault::onAsBinder() {
- return nullptr;
-}
-
} // namespace os
} // namespace android
@@ -228,7 +225,7 @@
} // namespace android
)";
-const char kExpectedJavaOutputWithVersion[] =
+const char kExpectedJavaOutputWithVersionAndHash[] =
R"(/*
* This file is auto-generated. DO NOT MODIFY.
*/
@@ -242,12 +239,17 @@
* that the remote object is implementing.
*/
public static final int VERSION = 10;
+ public static final String HASH = "abcdefg";
/** Default implementation for IStringConstants. */
public static class Default implements android.os.IStringConstants
{
@Override
public int getInterfaceVersion() {
- return -1;
+ return 0;
+ }
+ @Override
+ public String getInterfaceHash() {
+ return "";
}
@Override
public android.os.IBinder asBinder() {
@@ -299,6 +301,13 @@
reply.writeInt(getInterfaceVersion());
return true;
}
+ case TRANSACTION_getInterfaceHash:
+ {
+ data.enforceInterface(descriptor);
+ reply.writeNoException();
+ reply.writeString(getInterfaceHash());
+ return true;
+ }
default:
{
return super.onTransact(code, data, reply, flags);
@@ -313,6 +322,7 @@
mRemote = remote;
}
private int mCachedVersion = -1;
+ private String mCachedHash = "-1";
@Override public android.os.IBinder asBinder()
{
return mRemote;
@@ -343,9 +353,32 @@
}
return mCachedVersion;
}
+ @Override
+ public synchronized String getInterfaceHash() throws android.os.RemoteException {
+ if (mCachedHash == "-1") {
+ android.os.Parcel data = android.os.Parcel.obtain();
+ android.os.Parcel reply = android.os.Parcel.obtain();
+ try {
+ data.writeInterfaceToken(DESCRIPTOR);
+ boolean _status = mRemote.transact(Stub.TRANSACTION_getInterfaceHash, data, reply, 0);
+ if (!_status) {
+ if (getDefaultImpl() != null) {
+ return getDefaultImpl().getInterfaceHash();
+ }
+ }
+ reply.readException();
+ mCachedHash = reply.readString();
+ } finally {
+ reply.recycle();
+ data.recycle();
+ }
+ }
+ return mCachedHash;
+ }
public static android.os.IStringConstants sDefaultImpl;
}
static final int TRANSACTION_getInterfaceVersion = (android.os.IBinder.FIRST_CALL_TRANSACTION + 16777214);
+ static final int TRANSACTION_getInterfaceHash = (android.os.IBinder.FIRST_CALL_TRANSACTION + 16777213);
public static boolean setDefaultImpl(android.os.IStringConstants impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
@@ -359,11 +392,12 @@
}
public static final String EXAMPLE_CONSTANT = "foo";
public int getInterfaceVersion() throws android.os.RemoteException;
+ public String getInterfaceHash() throws android.os.RemoteException;
}
)";
-const char kExpectedIHeaderOutputWithVersion[] =
-R"(#ifndef AIDL_GENERATED_ANDROID_OS_I_STRING_CONSTANTS_H_
+const char kExpectedIHeaderOutputWithVersionAndHash[] =
+ R"(#ifndef AIDL_GENERATED_ANDROID_OS_I_STRING_CONSTANTS_H_
#define AIDL_GENERATED_ANDROID_OS_I_STRING_CONSTANTS_H_
#include <binder/IBinder.h>
@@ -381,16 +415,24 @@
public:
DECLARE_META_INTERFACE(StringConstants)
const int32_t VERSION = 10;
+ const std::string HASH = "abcdefg";
static const ::android::String16& EXAMPLE_CONSTANT();
virtual int32_t getInterfaceVersion() = 0;
+ virtual std::string getInterfaceHash() = 0;
}; // class IStringConstants
class IStringConstantsDefault : public IStringConstants {
public:
- ::android::IBinder* onAsBinder() override;
- int32_t getInterfaceVersion() override;
-
-};
+ ::android::IBinder* onAsBinder() override {
+ return nullptr;
+ }
+ int32_t getInterfaceVersion() override {
+ return 0;
+ }
+ std::string getInterfaceHash() override {
+ return "";
+ }
+}; // class IStringConstantsDefault
} // namespace os
@@ -399,7 +441,7 @@
#endif // AIDL_GENERATED_ANDROID_OS_I_STRING_CONSTANTS_H_
)";
-const char kExpectedCppOutputWithVersion[] =
+const char kExpectedCppOutputWithVersionAndHash[] =
R"(#include <android/os/IStringConstants.h>
#include <android/os/BpStringConstants.h>
@@ -407,21 +449,13 @@
namespace os {
-IMPLEMENT_META_INTERFACE(StringConstants, "android.os.IStringConstants")
+DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(StringConstants, "android.os.IStringConstants")
const ::android::String16& IStringConstants::EXAMPLE_CONSTANT() {
static const ::android::String16 value(::android::String16("foo"));
return value;
}
-::android::IBinder* IStringConstantsDefault::onAsBinder() {
- return nullptr;
-}
-
-int32_t IStringConstantsDefault::getInterfaceVersion() {
- return 0;
-}
-
} // namespace os
} // namespace android
@@ -454,6 +488,24 @@
return cached_version_;
}
+std::string BpStringConstants::getInterfaceHash() {
+ std::lock_guard<std::mutex> lockGuard(cached_hash_mutex_);
+ if (cached_hash_ == "-1") {
+ ::android::Parcel data;
+ ::android::Parcel reply;
+ data.writeInterfaceToken(getInterfaceDescriptor());
+ ::android::status_t err = remote()->transact(::android::IBinder::FIRST_CALL_TRANSACTION + 16777213 /* getInterfaceHash */, data, &reply);
+ if (err == ::android::OK) {
+ ::android::binder::Status _aidl_status;
+ err = _aidl_status.readFromParcel(reply);
+ if (err == ::android::OK && _aidl_status.isOk()) {
+ cached_hash_ = reply.readString8().c_str();
+ }
+ }
+ }
+ return cached_hash_;
+}
+
} // namespace os
} // namespace android
@@ -480,6 +532,13 @@
_aidl_reply->writeInt32(IStringConstants::VERSION);
}
break;
+ case ::android::IBinder::FIRST_CALL_TRANSACTION + 16777213 /* getInterfaceHash */:
+ {
+ _aidl_data.checkInterface(this);
+ _aidl_reply->writeNoException();
+ _aidl_reply->writeString8(android::String8(IStringConstants::HASH.c_str()));
+ }
+ break;
default:
{
_aidl_ret_status = ::android::BBinder::onTransact(_aidl_code, _aidl_data, _aidl_reply, _aidl_flags);
@@ -496,6 +555,10 @@
return IStringConstants::VERSION;
}
+std::string BnStringConstants::getInterfaceHash() {
+ return IStringConstants::HASH;
+}
+
} // namespace os
} // namespace android