Project import
diff --git a/aidl/Android.mk b/aidl/Android.mk
new file mode 100644
index 0000000..3c1463a
--- /dev/null
+++ b/aidl/Android.mk
@@ -0,0 +1,204 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+aidl_cflags := -Wall -Wextra -Werror
+aidl_static_libraries := libbase libcutils
+
+aidl_module_host_os := darwin linux windows
+ifdef BRILLO
+ aidl_module_host_os := darwin linux
+endif
+
+# Logic shared between aidl and its unittests
+include $(CLEAR_VARS)
+LOCAL_MODULE := libaidl-common
+LOCAL_MODULE_HOST_OS := $(aidl_module_host_os)
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libgtest_prod
+LOCAL_CLANG_CFLAGS := $(aidl_cflags)
+# Tragically, the code is riddled with unused parameters.
+LOCAL_CLANG_CFLAGS += -Wno-unused-parameter
+# yacc dumps a lot of code *just in case*.
+LOCAL_CLANG_CFLAGS += -Wno-unused-function
+LOCAL_CLANG_CFLAGS += -Wno-unneeded-internal-declaration
+# yacc is a tool from a more civilized age.
+LOCAL_CLANG_CFLAGS += -Wno-deprecated-register
+# yacc also has a habit of using char* over const char*.
+LOCAL_CLANG_CFLAGS += -Wno-writable-strings
+LOCAL_STATIC_LIBRARIES := $(aidl_static_libraries)
+
+LOCAL_SRC_FILES := \
+ aidl.cpp \
+ aidl_language.cpp \
+ aidl_language_l.ll \
+ aidl_language_y.yy \
+ ast_cpp.cpp \
+ ast_java.cpp \
+ code_writer.cpp \
+ generate_cpp.cpp \
+ generate_java.cpp \
+ generate_java_binder.cpp \
+ import_resolver.cpp \
+ line_reader.cpp \
+ io_delegate.cpp \
+ options.cpp \
+ type_cpp.cpp \
+ type_java.cpp \
+ type_namespace.cpp \
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+# aidl executable
+include $(CLEAR_VARS)
+LOCAL_MODULE := aidl
+
+LOCAL_MODULE_HOST_OS := $(aidl_module_host_os)
+LOCAL_CFLAGS := $(aidl_cflags)
+LOCAL_WHOLE_STATIC_LIBRARIES := libgtest_prod
+LOCAL_SRC_FILES := main_java.cpp
+LOCAL_STATIC_LIBRARIES := libaidl-common $(aidl_static_libraries)
+include $(BUILD_HOST_EXECUTABLE)
+
+# aidl-cpp executable
+include $(CLEAR_VARS)
+LOCAL_MODULE := aidl-cpp
+
+LOCAL_MODULE_HOST_OS := $(aidl_module_host_os)
+LOCAL_CFLAGS := $(aidl_cflags)
+LOCAL_WHOLE_STATIC_LIBRARIES := libgtest_prod
+LOCAL_SRC_FILES := main_cpp.cpp
+LOCAL_STATIC_LIBRARIES := libaidl-common $(aidl_static_libraries)
+include $(BUILD_HOST_EXECUTABLE)
+
+# Unit tests
+include $(CLEAR_VARS)
+LOCAL_MODULE := aidl_unittests
+LOCAL_MODULE_HOST_OS := darwin linux
+
+LOCAL_CFLAGS := $(aidl_cflags) -g -DUNIT_TEST
+# Tragically, the code is riddled with unused parameters.
+LOCAL_CLANG_CFLAGS := -Wno-unused-parameter
+LOCAL_SRC_FILES := \
+ aidl_unittest.cpp \
+ ast_cpp_unittest.cpp \
+ ast_java_unittest.cpp \
+ generate_cpp_unittest.cpp \
+ io_delegate_unittest.cpp \
+ options_unittest.cpp \
+ tests/end_to_end_tests.cpp \
+ tests/fake_io_delegate.cpp \
+ tests/main.cpp \
+ tests/test_data_example_interface.cpp \
+ tests/test_data_ping_responder.cpp \
+ tests/test_data_string_constants.cpp \
+ tests/test_util.cpp \
+ type_cpp_unittest.cpp \
+ type_java_unittest.cpp \
+
+LOCAL_STATIC_LIBRARIES := \
+ libaidl-common \
+ $(aidl_static_libraries) \
+ libgmock_host \
+
+LOCAL_LDLIBS_linux := -lrt
+include $(BUILD_HOST_NATIVE_TEST)
+
+#
+# Everything below here is used for integration testing of generated AIDL code.
+#
+aidl_integration_test_cflags := $(aidl_cflags) -Wunused-parameter
+aidl_integration_test_shared_libs := \
+ libbase \
+ libbinder \
+ liblog \
+ libutils
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libaidl-integration-test
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_CFLAGS := $(aidl_integration_test_cflags)
+LOCAL_SHARED_LIBRARIES := $(aidl_integration_test_shared_libs)
+LOCAL_AIDL_INCLUDES := \
+ system/tools/aidl/tests/ \
+ frameworks/native/aidl/binder
+LOCAL_SRC_FILES := \
+ tests/android/aidl/tests/ITestService.aidl \
+ tests/android/aidl/tests/INamedCallback.aidl \
+ tests/simple_parcelable.cpp
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := aidl_test_service
+LOCAL_CFLAGS := $(aidl_integration_test_cflags)
+LOCAL_SHARED_LIBRARIES := \
+ libaidl-integration-test \
+ $(aidl_integration_test_shared_libs)
+LOCAL_SRC_FILES := \
+ tests/aidl_test_service.cpp
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := aidl_test_client
+LOCAL_CFLAGS := $(aidl_integration_test_cflags)
+LOCAL_SHARED_LIBRARIES := \
+ libaidl-integration-test \
+ $(aidl_integration_test_shared_libs)
+LOCAL_SRC_FILES := \
+ tests/aidl_test_client.cpp \
+ tests/aidl_test_client_file_descriptors.cpp \
+ tests/aidl_test_client_parcelables.cpp \
+ tests/aidl_test_client_nullables.cpp \
+ tests/aidl_test_client_primitives.cpp \
+ tests/aidl_test_client_utf8_strings.cpp \
+ tests/aidl_test_client_service_exceptions.cpp
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := aidl_test_sentinel_searcher
+LOCAL_SRC_FILES := tests/aidl_test_sentinel_searcher.cpp
+LOCAL_CFLAGS := $(aidl_integration_test_cflags)
+include $(BUILD_EXECUTABLE)
+
+
+# aidl on its own doesn't need the framework, but testing native/java
+# compatibility introduces java dependencies.
+ifndef BRILLO
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := aidl_test_services
+# Turn off Java optimization tools to speed up our test iterations.
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+LOCAL_CERTIFICATE := platform
+LOCAL_MANIFEST_FILE := tests/java_app/AndroidManifest.xml
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/tests/java_app/resources
+LOCAL_SRC_FILES := \
+ tests/android/aidl/tests/INamedCallback.aidl \
+ tests/android/aidl/tests/ITestService.aidl \
+ 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
+LOCAL_AIDL_INCLUDES := \
+ system/tools/aidl/tests/ \
+ frameworks/native/aidl/binder
+include $(BUILD_PACKAGE)
+
+endif # not defined BRILLO
diff --git a/aidl/MODULE_LICENSE_APACHE2 b/aidl/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/aidl/MODULE_LICENSE_APACHE2
diff --git a/aidl/NOTICE b/aidl/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/aidl/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, 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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/aidl/aidl.cpp b/aidl/aidl.cpp
new file mode 100644
index 0000000..cd778fa
--- /dev/null
+++ b/aidl/aidl.cpp
@@ -0,0 +1,771 @@
+/*
+ * 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 <fcntl.h>
+#include <iostream>
+#include <map>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef _WIN32
+#include <io.h>
+#include <direct.h>
+#include <sys/stat.h>
+#endif
+
+#include <android-base/strings.h>
+
+#include "aidl_language.h"
+#include "generate_cpp.h"
+#include "generate_java.h"
+#include "import_resolver.h"
+#include "logging.h"
+#include "options.h"
+#include "os.h"
+#include "type_cpp.h"
+#include "type_java.h"
+#include "type_namespace.h"
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+using android::base::Join;
+using android::base::Split;
+using std::cerr;
+using std::endl;
+using std::map;
+using std::set;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace android {
+namespace aidl {
+namespace {
+
+// The following are gotten as the offset from the allowable id's between
+// android.os.IBinder.FIRST_CALL_TRANSACTION=1 and
+// android.os.IBinder.LAST_CALL_TRANSACTION=16777215
+const int kMinUserSetMethodId = 0;
+const int kMaxUserSetMethodId = 16777214;
+
+bool check_filename(const std::string& filename,
+ const std::string& package,
+ const std::string& name,
+ unsigned line) {
+ const char* p;
+ string expected;
+ string fn;
+ size_t len;
+ bool valid = false;
+
+ if (!IoDelegate::GetAbsolutePath(filename, &fn)) {
+ return false;
+ }
+
+ if (!package.empty()) {
+ expected = package;
+ expected += '.';
+ }
+
+ len = expected.length();
+ for (size_t i=0; i<len; i++) {
+ if (expected[i] == '.') {
+ expected[i] = OS_PATH_SEPARATOR;
+ }
+ }
+
+ expected.append(name, 0, name.find('.'));
+
+ expected += ".aidl";
+
+ len = fn.length();
+ valid = (len >= expected.length());
+
+ if (valid) {
+ p = fn.c_str() + (len - expected.length());
+
+#ifdef _WIN32
+ if (OS_PATH_SEPARATOR != '/') {
+ // Input filename under cygwin most likely has / separators
+ // whereas the expected string uses \\ separators. Adjust
+ // them accordingly.
+ for (char *c = const_cast<char *>(p); *c; ++c) {
+ if (*c == '/') *c = OS_PATH_SEPARATOR;
+ }
+ }
+#endif
+
+ // aidl assumes case-insensitivity on Mac Os and Windows.
+#if defined(__linux__)
+ valid = (expected == p);
+#else
+ valid = !strcasecmp(expected.c_str(), p);
+#endif
+ }
+
+ if (!valid) {
+ fprintf(stderr, "%s:%d interface %s should be declared in a file"
+ " called %s.\n",
+ filename.c_str(), line, name.c_str(), expected.c_str());
+ }
+
+ return valid;
+}
+
+bool check_filenames(const std::string& filename, const AidlDocument* doc) {
+ if (!doc)
+ return true;
+
+ const AidlInterface* interface = doc->GetInterface();
+
+ if (interface) {
+ return check_filename(filename, interface->GetPackage(),
+ interface->GetName(), interface->GetLine());
+ }
+
+ bool success = true;
+
+ for (const auto& item : doc->GetParcelables()) {
+ success &= check_filename(filename, item->GetPackage(), item->GetName(),
+ item->GetLine());
+ }
+
+ return success;
+}
+
+bool gather_types(const std::string& filename,
+ const AidlDocument* doc,
+ TypeNamespace* types) {
+ bool success = true;
+
+ const AidlInterface* interface = doc->GetInterface();
+
+ if (interface)
+ return types->AddBinderType(*interface, filename);
+
+ for (const auto& item : doc->GetParcelables()) {
+ success &= types->AddParcelableType(*item, filename);
+ }
+
+ return success;
+}
+
+int check_types(const string& filename,
+ const AidlInterface* c,
+ TypeNamespace* types) {
+ int err = 0;
+
+ if (c->IsUtf8() && c->IsUtf8InCpp()) {
+ cerr << filename << ":" << c->GetLine()
+ << "Interface cannot be marked as both @utf8 and @utf8InCpp";
+ err = 1;
+ }
+
+ // Has to be a pointer due to deleting copy constructor. No idea why.
+ map<string, const AidlMethod*> method_names;
+ for (const auto& m : c->GetMethods()) {
+ bool oneway = m->IsOneway() || c->IsOneway();
+
+ if (!types->MaybeAddContainerType(m->GetType())) {
+ err = 1; // return type is invalid
+ }
+
+ const ValidatableType* return_type =
+ types->GetReturnType(m->GetType(), filename, *c);
+
+ if (!return_type) {
+ err = 1;
+ }
+
+ m->GetMutableType()->SetLanguageType(return_type);
+
+ if (oneway && m->GetType().GetName() != "void") {
+ cerr << filename << ":" << m->GetLine()
+ << " oneway method '" << m->GetName() << "' cannot return a value"
+ << endl;
+ err = 1;
+ }
+
+ int index = 1;
+ for (const auto& arg : m->GetArguments()) {
+ if (!types->MaybeAddContainerType(arg->GetType())) {
+ err = 1;
+ }
+
+ const ValidatableType* arg_type =
+ types->GetArgType(*arg, index, filename, *c);
+
+ if (!arg_type) {
+ err = 1;
+ }
+
+ arg->GetMutableType()->SetLanguageType(arg_type);
+
+ if (oneway && arg->IsOut()) {
+ cerr << filename << ":" << m->GetLine()
+ << " oneway method '" << m->GetName()
+ << "' cannot have out parameters" << endl;
+ err = 1;
+ }
+ }
+
+ auto it = method_names.find(m->GetName());
+ // prevent duplicate methods
+ if (it == method_names.end()) {
+ method_names[m->GetName()] = m.get();
+ } else {
+ cerr << filename << ":" << m->GetLine()
+ << " attempt to redefine method " << m->GetName() << "," << endl
+ << filename << ":" << it->second->GetLine()
+ << " previously defined here." << endl;
+ err = 1;
+ }
+ }
+ return err;
+}
+
+void write_common_dep_file(const string& output_file,
+ const vector<string>& aidl_sources,
+ CodeWriter* writer) {
+ // Encode that the output file depends on aidl input files.
+ writer->Write("%s : \\\n", output_file.c_str());
+ writer->Write(" %s", Join(aidl_sources, " \\\n ").c_str());
+ writer->Write("\n\n");
+
+ // Output "<input_aidl_file>: " so make won't fail if the input .aidl file
+ // has been deleted, moved or renamed in incremental build.
+ for (const auto& src : aidl_sources) {
+ writer->Write("%s :\n", src.c_str());
+ }
+}
+
+bool write_java_dep_file(const JavaOptions& options,
+ const vector<unique_ptr<AidlImport>>& imports,
+ const IoDelegate& io_delegate,
+ const string& output_file_name) {
+ string dep_file_name = options.DependencyFilePath();
+ if (dep_file_name.empty()) {
+ return true; // nothing to do
+ }
+ CodeWriterPtr writer = io_delegate.GetCodeWriter(dep_file_name);
+ if (!writer) {
+ LOG(ERROR) << "Could not open dependency file: " << dep_file_name;
+ return false;
+ }
+
+ vector<string> source_aidl = {options.input_file_name_};
+ for (const auto& import : imports) {
+ if (!import->GetFilename().empty()) {
+ source_aidl.push_back(import->GetFilename());
+ }
+ }
+
+ write_common_dep_file(output_file_name, source_aidl, writer.get());
+
+ return true;
+}
+
+bool write_cpp_dep_file(const CppOptions& options,
+ const AidlInterface& interface,
+ const vector<unique_ptr<AidlImport>>& imports,
+ const IoDelegate& io_delegate) {
+ using ::android::aidl::cpp::HeaderFile;
+ using ::android::aidl::cpp::ClassNames;
+
+ string dep_file_name = options.DependencyFilePath();
+ if (dep_file_name.empty()) {
+ return true; // nothing to do
+ }
+ CodeWriterPtr writer = io_delegate.GetCodeWriter(dep_file_name);
+ if (!writer) {
+ LOG(ERROR) << "Could not open dependency file: " << dep_file_name;
+ return false;
+ }
+
+ vector<string> source_aidl = {options.InputFileName()};
+ for (const auto& import : imports) {
+ if (!import->GetFilename().empty()) {
+ source_aidl.push_back(import->GetFilename());
+ }
+ }
+
+ vector<string> headers;
+ for (ClassNames c : {ClassNames::CLIENT,
+ ClassNames::SERVER,
+ ClassNames::INTERFACE}) {
+ headers.push_back(options.OutputHeaderDir() + '/' +
+ HeaderFile(interface, c, false /* use_os_sep */));
+ }
+
+ write_common_dep_file(options.OutputCppFilePath(), source_aidl, writer.get());
+ writer->Write("\n");
+
+ // Generated headers also depend on the source aidl files.
+ writer->Write("%s : \\\n %s\n", Join(headers, " \\\n ").c_str(),
+ Join(source_aidl, " \\\n ").c_str());
+
+ return true;
+}
+
+string generate_outputFileName(const JavaOptions& options,
+ const AidlInterface& interface) {
+ const string& name = interface.GetName();
+ string package = interface.GetPackage();
+ string result;
+
+ // create the path to the destination folder based on the
+ // interface package name
+ result = options.output_base_folder_;
+ result += OS_PATH_SEPARATOR;
+
+ string packageStr = package;
+ size_t len = packageStr.length();
+ for (size_t i=0; i<len; i++) {
+ if (packageStr[i] == '.') {
+ packageStr[i] = OS_PATH_SEPARATOR;
+ }
+ }
+
+ result += packageStr;
+
+ // add the filename by replacing the .aidl extension to .java
+ result += OS_PATH_SEPARATOR;
+ result.append(name, 0, name.find('.'));
+ result += ".java";
+
+ return result;
+}
+
+int check_and_assign_method_ids(const char * filename,
+ const std::vector<std::unique_ptr<AidlMethod>>& items) {
+ // Check whether there are any methods with manually assigned id's and any that are not.
+ // Either all method id's must be manually assigned or all of them must not.
+ // Also, check for duplicates of user set id's and that the id's are within the proper bounds.
+ set<int> usedIds;
+ bool hasUnassignedIds = false;
+ bool hasAssignedIds = false;
+ for (const auto& item : items) {
+ if (item->HasId()) {
+ hasAssignedIds = true;
+ // Ensure that the user set id is not duplicated.
+ if (usedIds.find(item->GetId()) != usedIds.end()) {
+ // We found a duplicate id, so throw an error.
+ fprintf(stderr,
+ "%s:%d Found duplicate method id (%d) for method: %s\n",
+ filename, item->GetLine(),
+ item->GetId(), item->GetName().c_str());
+ return 1;
+ }
+ // Ensure that the user set id is within the appropriate limits
+ if (item->GetId() < kMinUserSetMethodId ||
+ item->GetId() > kMaxUserSetMethodId) {
+ fprintf(stderr, "%s:%d Found out of bounds id (%d) for method: %s\n",
+ filename, item->GetLine(),
+ item->GetId(), item->GetName().c_str());
+ fprintf(stderr, " Value for id must be between %d and %d inclusive.\n",
+ kMinUserSetMethodId, kMaxUserSetMethodId);
+ return 1;
+ }
+ usedIds.insert(item->GetId());
+ } else {
+ hasUnassignedIds = true;
+ }
+ if (hasAssignedIds && hasUnassignedIds) {
+ fprintf(stderr,
+ "%s: You must either assign id's to all methods or to none of them.\n",
+ filename);
+ return 1;
+ }
+ }
+
+ // In the case that all methods have unassigned id's, set a unique id for them.
+ if (hasUnassignedIds) {
+ int newId = 0;
+ for (const auto& item : items) {
+ item->SetId(newId++);
+ }
+ }
+
+ // success
+ return 0;
+}
+
+bool validate_constants(const AidlInterface& interface) {
+ bool success = true;
+ set<string> names;
+ for (const std::unique_ptr<AidlIntConstant>& int_constant :
+ interface.GetIntConstants()) {
+ if (names.count(int_constant->GetName()) > 0) {
+ LOG(ERROR) << "Found duplicate constant name '" << int_constant->GetName()
+ << "'";
+ success = false;
+ }
+ names.insert(int_constant->GetName());
+ // We've logged an error message for this on object construction.
+ success = success && int_constant->IsValid();
+ }
+ for (const std::unique_ptr<AidlStringConstant>& string_constant :
+ interface.GetStringConstants()) {
+ if (names.count(string_constant->GetName()) > 0) {
+ LOG(ERROR) << "Found duplicate constant name '" << string_constant->GetName()
+ << "'";
+ success = false;
+ }
+ names.insert(string_constant->GetName());
+ // We've logged an error message for this on object construction.
+ success = success && string_constant->IsValid();
+ }
+ return success;
+}
+
+// TODO: Remove this in favor of using the YACC parser b/25479378
+bool ParsePreprocessedLine(const string& line, string* decl,
+ vector<string>* package, string* class_name) {
+ // erase all trailing whitespace and semicolons
+ const size_t end = line.find_last_not_of(" ;\t");
+ if (end == string::npos) {
+ return false;
+ }
+ if (line.rfind(';', end) != string::npos) {
+ return false;
+ }
+
+ decl->clear();
+ string type;
+ vector<string> pieces = Split(line.substr(0, end + 1), " \t");
+ for (const string& piece : pieces) {
+ if (piece.empty()) {
+ continue;
+ }
+ if (decl->empty()) {
+ *decl = std::move(piece);
+ } else if (type.empty()) {
+ type = std::move(piece);
+ } else {
+ return false;
+ }
+ }
+
+ // Note that this logic is absolutely wrong. Given a parcelable
+ // org.some.Foo.Bar, the class name is Foo.Bar, but this code will claim that
+ // the class is just Bar. However, this was the way it was done in the past.
+ //
+ // See b/17415692
+ size_t dot_pos = type.rfind('.');
+ if (dot_pos != string::npos) {
+ *class_name = type.substr(dot_pos + 1);
+ *package = Split(type.substr(0, dot_pos), ".");
+ } else {
+ *class_name = type;
+ package->clear();
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace internals {
+
+bool parse_preprocessed_file(const IoDelegate& io_delegate,
+ const string& filename, TypeNamespace* types) {
+ bool success = true;
+ unique_ptr<LineReader> line_reader = io_delegate.GetLineReader(filename);
+ if (!line_reader) {
+ LOG(ERROR) << "cannot open preprocessed file: " << filename;
+ success = false;
+ return success;
+ }
+
+ string line;
+ unsigned lineno = 1;
+ for ( ; line_reader->ReadLine(&line); ++lineno) {
+ if (line.empty() || line.compare(0, 2, "//") == 0) {
+ // skip comments and empty lines
+ continue;
+ }
+
+ string decl;
+ vector<string> package;
+ string class_name;
+ if (!ParsePreprocessedLine(line, &decl, &package, &class_name)) {
+ success = false;
+ break;
+ }
+
+ if (decl == "parcelable") {
+ AidlParcelable doc(new AidlQualifiedName(class_name, ""),
+ lineno, package);
+ types->AddParcelableType(doc, filename);
+ } else if (decl == "interface") {
+ auto temp = new std::vector<std::unique_ptr<AidlMember>>();
+ AidlInterface doc(class_name, lineno, "", false, temp, package);
+ types->AddBinderType(doc, filename);
+ } else {
+ success = false;
+ break;
+ }
+ }
+ if (!success) {
+ LOG(ERROR) << filename << ':' << lineno
+ << " malformed preprocessed file line: '" << line << "'";
+ }
+
+ return success;
+}
+
+AidlError load_and_validate_aidl(
+ const std::vector<std::string>& preprocessed_files,
+ const std::vector<std::string>& import_paths,
+ const std::string& input_file_name,
+ const IoDelegate& io_delegate,
+ TypeNamespace* types,
+ std::unique_ptr<AidlInterface>* returned_interface,
+ std::vector<std::unique_ptr<AidlImport>>* returned_imports) {
+ AidlError err = AidlError::OK;
+
+ std::map<AidlImport*,std::unique_ptr<AidlDocument>> docs;
+
+ // import the preprocessed file
+ for (const string& s : preprocessed_files) {
+ if (!parse_preprocessed_file(io_delegate, s, types)) {
+ err = AidlError::BAD_PRE_PROCESSED_FILE;
+ }
+ }
+ if (err != AidlError::OK) {
+ return err;
+ }
+
+ // parse the input file
+ Parser p{io_delegate};
+ if (!p.ParseFile(input_file_name)) {
+ return AidlError::PARSE_ERROR;
+ }
+
+ AidlDocument* parsed_doc = p.GetDocument();
+
+ unique_ptr<AidlInterface> interface(parsed_doc->ReleaseInterface());
+
+ if (!interface) {
+ LOG(ERROR) << "refusing to generate code from aidl file defining "
+ "parcelable";
+ return AidlError::FOUND_PARCELABLE;
+ }
+
+ if (!check_filename(input_file_name.c_str(), interface->GetPackage(),
+ interface->GetName(), interface->GetLine()) ||
+ !types->IsValidPackage(interface->GetPackage())) {
+ LOG(ERROR) << "Invalid package declaration '" << interface->GetPackage()
+ << "'";
+ return AidlError::BAD_PACKAGE;
+ }
+
+ // parse the imports of the input file
+ ImportResolver import_resolver{io_delegate, import_paths};
+ for (auto& import : p.GetImports()) {
+ if (types->HasImportType(*import)) {
+ // There are places in the Android tree where an import doesn't resolve,
+ // but we'll pick the type up through the preprocessed types.
+ // This seems like an error, but legacy support demands we support it...
+ continue;
+ }
+ string import_path = import_resolver.FindImportFile(import->GetNeededClass());
+ if (import_path.empty()) {
+ cerr << import->GetFileFrom() << ":" << import->GetLine()
+ << ": couldn't find import for class "
+ << import->GetNeededClass() << endl;
+ err = AidlError::BAD_IMPORT;
+ continue;
+ }
+ import->SetFilename(import_path);
+
+ Parser p{io_delegate};
+ if (!p.ParseFile(import->GetFilename())) {
+ cerr << "error while parsing import for class "
+ << import->GetNeededClass() << endl;
+ err = AidlError::BAD_IMPORT;
+ continue;
+ }
+
+ std::unique_ptr<AidlDocument> document(p.ReleaseDocument());
+ if (!check_filenames(import->GetFilename(), document.get()))
+ err = AidlError::BAD_IMPORT;
+ docs[import.get()] = std::move(document);
+ }
+ if (err != AidlError::OK) {
+ return err;
+ }
+
+ // gather the types that have been declared
+ if (!types->AddBinderType(*interface.get(), input_file_name)) {
+ err = AidlError::BAD_TYPE;
+ }
+
+ interface->SetLanguageType(types->GetInterfaceType(*interface));
+
+ for (const auto& import : p.GetImports()) {
+ // If we skipped an unresolved import above (see comment there) we'll have
+ // an empty bucket here.
+ const auto import_itr = docs.find(import.get());
+ if (import_itr == docs.cend()) {
+ continue;
+ }
+
+ if (!gather_types(import->GetFilename(), import_itr->second.get(), types)) {
+ err = AidlError::BAD_TYPE;
+ }
+ }
+
+ // check the referenced types in parsed_doc to make sure we've imported them
+ if (check_types(input_file_name, interface.get(), types) != 0) {
+ err = AidlError::BAD_TYPE;
+ }
+ if (err != AidlError::OK) {
+ return err;
+ }
+
+
+ // assign method ids and validate.
+ if (check_and_assign_method_ids(input_file_name.c_str(),
+ interface->GetMethods()) != 0) {
+ return AidlError::BAD_METHOD_ID;
+ }
+ if (!validate_constants(*interface)) {
+ return AidlError::BAD_CONSTANTS;
+ }
+
+ if (returned_interface)
+ *returned_interface = std::move(interface);
+
+ if (returned_imports)
+ p.ReleaseImports(returned_imports);
+
+ return AidlError::OK;
+}
+
+} // namespace internals
+
+int compile_aidl_to_cpp(const CppOptions& options,
+ const IoDelegate& io_delegate) {
+ unique_ptr<AidlInterface> interface;
+ std::vector<std::unique_ptr<AidlImport>> imports;
+ unique_ptr<cpp::TypeNamespace> types(new cpp::TypeNamespace());
+ types->Init();
+ AidlError err = internals::load_and_validate_aidl(
+ std::vector<std::string>{}, // no preprocessed files
+ options.ImportPaths(),
+ options.InputFileName(),
+ io_delegate,
+ types.get(),
+ &interface,
+ &imports);
+ if (err != AidlError::OK) {
+ return 1;
+ }
+
+ if (!write_cpp_dep_file(options, *interface, imports, io_delegate)) {
+ return 1;
+ }
+
+ return (cpp::GenerateCpp(options, *types, *interface, io_delegate)) ? 0 : 1;
+}
+
+int compile_aidl_to_java(const JavaOptions& options,
+ const IoDelegate& io_delegate) {
+ unique_ptr<AidlInterface> interface;
+ std::vector<std::unique_ptr<AidlImport>> imports;
+ unique_ptr<java::JavaTypeNamespace> types(new java::JavaTypeNamespace());
+ types->Init();
+ AidlError aidl_err = internals::load_and_validate_aidl(
+ options.preprocessed_files_,
+ options.import_paths_,
+ options.input_file_name_,
+ io_delegate,
+ types.get(),
+ &interface,
+ &imports);
+ if (aidl_err == AidlError::FOUND_PARCELABLE && !options.fail_on_parcelable_) {
+ // We aborted code generation because this file contains parcelables.
+ // However, we were not told to complain if we find parcelables.
+ // Just generate a dep file and exit quietly. The dep file is for a legacy
+ // use case by the SDK.
+ write_java_dep_file(options, imports, io_delegate, "");
+ return 0;
+ }
+ if (aidl_err != AidlError::OK) {
+ return 1;
+ }
+
+ string output_file_name = options.output_file_name_;
+ // if needed, generate the output file name from the base folder
+ if (output_file_name.empty() && !options.output_base_folder_.empty()) {
+ output_file_name = generate_outputFileName(options, *interface);
+ }
+
+ // make sure the folders of the output file all exists
+ if (!io_delegate.CreatePathForFile(output_file_name)) {
+ return 1;
+ }
+
+ if (!write_java_dep_file(options, imports, io_delegate, output_file_name)) {
+ return 1;
+ }
+
+ return generate_java(output_file_name, options.input_file_name_.c_str(),
+ interface.get(), types.get(), io_delegate);
+}
+
+bool preprocess_aidl(const JavaOptions& options,
+ const IoDelegate& io_delegate) {
+ unique_ptr<CodeWriter> writer =
+ io_delegate.GetCodeWriter(options.output_file_name_);
+
+ for (const auto& file : options.files_to_preprocess_) {
+ Parser p{io_delegate};
+ if (!p.ParseFile(file))
+ return false;
+ AidlDocument* doc = p.GetDocument();
+ string line;
+
+ const AidlInterface* interface = doc->GetInterface();
+
+ if (interface != nullptr &&
+ !writer->Write("interface %s;\n",
+ interface->GetCanonicalName().c_str())) {
+ return false;
+ }
+
+ for (const auto& parcelable : doc->GetParcelables()) {
+ if (!writer->Write("parcelable %s;\n",
+ parcelable->GetCanonicalName().c_str())) {
+ return false;
+ }
+ }
+ }
+
+ return writer->Close();
+}
+
+} // namespace android
+} // namespace aidl
diff --git a/aidl/aidl.h b/aidl/aidl.h
new file mode 100644
index 0000000..2e5b0ee
--- /dev/null
+++ b/aidl/aidl.h
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_AIDL_H_
+#define AIDL_AIDL_H_
+
+#include <limits>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "aidl_language.h"
+#include "io_delegate.h"
+#include "options.h"
+#include "type_namespace.h"
+
+namespace android {
+namespace aidl {
+
+enum class AidlError {
+ UNKOWN = std::numeric_limits<int32_t>::min(),
+ BAD_PRE_PROCESSED_FILE,
+ PARSE_ERROR,
+ FOUND_PARCELABLE,
+ BAD_PACKAGE,
+ BAD_IMPORT,
+ BAD_TYPE,
+ BAD_METHOD_ID,
+ GENERATION_ERROR,
+ BAD_CONSTANTS,
+
+ OK = 0,
+};
+
+int compile_aidl_to_cpp(const CppOptions& options,
+ const IoDelegate& io_delegate);
+int compile_aidl_to_java(const JavaOptions& options,
+ const IoDelegate& io_delegate);
+bool preprocess_aidl(const JavaOptions& options,
+ const IoDelegate& io_delegate);
+
+namespace internals {
+
+AidlError load_and_validate_aidl(
+ const std::vector<std::string>& preprocessed_files,
+ const std::vector<std::string>& import_paths,
+ const std::string& input_file_name,
+ const IoDelegate& io_delegate,
+ TypeNamespace* types,
+ std::unique_ptr<AidlInterface>* returned_interface,
+ std::vector<std::unique_ptr<AidlImport>>* returned_imports);
+
+bool parse_preprocessed_file(const IoDelegate& io_delegate,
+ const std::string& filename, TypeNamespace* types);
+
+} // namespace internals
+
+} // namespace android
+} // namespace aidl
+
+#endif // AIDL_AIDL_H_
diff --git a/aidl/aidl_language.cpp b/aidl/aidl_language.cpp
new file mode 100644
index 0000000..bbe305b
--- /dev/null
+++ b/aidl/aidl_language.cpp
@@ -0,0 +1,312 @@
+#include "aidl_language.h"
+
+#include <iostream>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "aidl_language_y.h"
+#include "logging.h"
+
+#ifdef _WIN32
+int isatty(int fd)
+{
+ return (fd == 0);
+}
+#endif
+
+using android::aidl::IoDelegate;
+using android::base::Join;
+using android::base::Split;
+using std::cerr;
+using std::endl;
+using std::string;
+using std::unique_ptr;
+
+void yylex_init(void **);
+void yylex_destroy(void *);
+void yyset_in(FILE *f, void *);
+int yyparse(Parser*);
+YY_BUFFER_STATE yy_scan_buffer(char *, size_t, void *);
+void yy_delete_buffer(YY_BUFFER_STATE, void *);
+
+AidlToken::AidlToken(const std::string& text, const std::string& comments)
+ : text_(text),
+ comments_(comments) {}
+
+AidlType::AidlType(const std::string& name, unsigned line,
+ const std::string& comments, bool is_array)
+ : name_(name),
+ line_(line),
+ is_array_(is_array),
+ comments_(comments) {}
+
+string AidlType::ToString() const {
+ return name_ + (is_array_ ? "[]" : "");
+}
+
+AidlArgument::AidlArgument(AidlArgument::Direction direction, AidlType* type,
+ std::string name, unsigned line)
+ : type_(type),
+ direction_(direction),
+ direction_specified_(true),
+ name_(name),
+ line_(line) {}
+
+AidlArgument::AidlArgument(AidlType* type, std::string name, unsigned line)
+ : type_(type),
+ direction_(AidlArgument::IN_DIR),
+ direction_specified_(false),
+ name_(name),
+ line_(line) {}
+
+string AidlArgument::ToString() const {
+ string ret;
+
+ if (direction_specified_) {
+ switch(direction_) {
+ case AidlArgument::IN_DIR:
+ ret += "in ";
+ break;
+ case AidlArgument::OUT_DIR:
+ ret += "out ";
+ break;
+ case AidlArgument::INOUT_DIR:
+ ret += "inout ";
+ break;
+ }
+ }
+
+ ret += type_->ToString();
+ ret += " ";
+ ret += name_;
+
+ return ret;
+}
+
+AidlIntConstant::AidlIntConstant(std::string name, int32_t value)
+ : name_(name),
+ value_(value),
+ is_valid_(true) {}
+
+AidlIntConstant::AidlIntConstant(std::string name,
+ std::string value,
+ unsigned line_number)
+ : name_(name) {
+ uint32_t unsigned_val;
+ if (!android::base::ParseUint(value.c_str(), &unsigned_val)) {
+ is_valid_ = false;
+ LOG(ERROR) << "Found invalid int value '" << value
+ << "' on line " << line_number;
+ } else {
+ // Converting from unsigned to signed integer.
+ value_ = unsigned_val;
+ is_valid_ = true;
+ }
+}
+
+AidlStringConstant::AidlStringConstant(std::string name,
+ std::string value,
+ unsigned line_number)
+ : name_(name),
+ value_(value) {
+ is_valid_ = true;
+ for (size_t i = 0; i < value_.length(); ++i) {
+ const char& c = value_[i];
+ if (c <= 0x1f || // control characters are < 0x20
+ c >= 0x7f || // DEL is 0x7f
+ c == '\\') { // Disallow backslashes for future proofing.
+ LOG(ERROR) << "Found invalid character at index " << i
+ << " in string constant '" << value_
+ << "' beginning on line " << line_number;
+ is_valid_ = false;
+ break;
+ }
+ }
+}
+
+AidlMethod::AidlMethod(bool oneway, AidlType* type, std::string name,
+ std::vector<std::unique_ptr<AidlArgument>>* args,
+ unsigned line, const std::string& comments, int id)
+ : oneway_(oneway),
+ comments_(comments),
+ type_(type),
+ name_(name),
+ line_(line),
+ arguments_(std::move(*args)),
+ id_(id) {
+ has_id_ = true;
+ delete args;
+ for (const unique_ptr<AidlArgument>& a : arguments_) {
+ if (a->IsIn()) { in_arguments_.push_back(a.get()); }
+ if (a->IsOut()) { out_arguments_.push_back(a.get()); }
+ }
+}
+
+AidlMethod::AidlMethod(bool oneway, AidlType* type, std::string name,
+ std::vector<std::unique_ptr<AidlArgument>>* args,
+ unsigned line, const std::string& comments)
+ : AidlMethod(oneway, type, name, args, line, comments, 0) {
+ has_id_ = false;
+}
+
+Parser::Parser(const IoDelegate& io_delegate)
+ : io_delegate_(io_delegate) {
+ yylex_init(&scanner_);
+}
+
+AidlParcelable::AidlParcelable(AidlQualifiedName* name, unsigned line,
+ const std::vector<std::string>& package,
+ const std::string& cpp_header)
+ : name_(name),
+ line_(line),
+ package_(package),
+ cpp_header_(cpp_header) {
+ // Strip off quotation marks if we actually have a cpp header.
+ if (cpp_header_.length() >= 2) {
+ cpp_header_ = cpp_header_.substr(1, cpp_header_.length() - 2);
+ }
+}
+
+std::string AidlParcelable::GetPackage() const {
+ return Join(package_, '.');
+}
+
+std::string AidlParcelable::GetCanonicalName() const {
+ if (package_.empty()) {
+ return GetName();
+ }
+ return GetPackage() + "." + GetName();
+}
+
+AidlInterface::AidlInterface(const std::string& name, unsigned line,
+ const std::string& comments, bool oneway,
+ std::vector<std::unique_ptr<AidlMember>>* members,
+ const std::vector<std::string>& package)
+ : name_(name),
+ comments_(comments),
+ line_(line),
+ oneway_(oneway),
+ package_(package) {
+ for (auto& member : *members) {
+ AidlMember* local = member.release();
+ AidlMethod* method = local->AsMethod();
+ AidlIntConstant* int_constant = local->AsIntConstant();
+ AidlStringConstant* string_constant = local->AsStringConstant();
+
+ if (method) {
+ methods_.emplace_back(method);
+ } else if (int_constant) {
+ int_constants_.emplace_back(int_constant);
+ } else if (string_constant) {
+ string_constants_.emplace_back(string_constant);
+ } else {
+ LOG(FATAL) << "Member is neither method nor constant!";
+ }
+ }
+
+ delete members;
+}
+
+std::string AidlInterface::GetPackage() const {
+ return Join(package_, '.');
+}
+
+std::string AidlInterface::GetCanonicalName() const {
+ if (package_.empty()) {
+ return GetName();
+ }
+ return GetPackage() + "." + GetName();
+}
+
+AidlDocument::AidlDocument(AidlInterface* interface)
+ : interface_(interface) {}
+
+AidlQualifiedName::AidlQualifiedName(std::string term,
+ std::string comments)
+ : terms_({term}),
+ comments_(comments) {
+ if (term.find('.') != string::npos) {
+ terms_ = Split(term, ".");
+ for (const auto& term: terms_) {
+ if (term.empty()) {
+ LOG(FATAL) << "Malformed qualified identifier: '" << term << "'";
+ }
+ }
+ }
+}
+
+void AidlQualifiedName::AddTerm(const std::string& term) {
+ terms_.push_back(term);
+}
+
+AidlImport::AidlImport(const std::string& from,
+ const std::string& needed_class, unsigned line)
+ : from_(from),
+ needed_class_(needed_class),
+ line_(line) {}
+
+Parser::~Parser() {
+ if (raw_buffer_) {
+ yy_delete_buffer(buffer_, scanner_);
+ raw_buffer_.reset();
+ }
+ yylex_destroy(scanner_);
+}
+
+bool Parser::ParseFile(const string& filename) {
+ // Make sure we can read the file first, before trashing previous state.
+ unique_ptr<string> new_buffer = io_delegate_.GetFileContents(filename);
+ if (!new_buffer) {
+ LOG(ERROR) << "Error while opening file for parsing: '" << filename << "'";
+ return false;
+ }
+
+ // Throw away old parsing state if we have any.
+ if (raw_buffer_) {
+ yy_delete_buffer(buffer_, scanner_);
+ raw_buffer_.reset();
+ }
+
+ raw_buffer_ = std::move(new_buffer);
+ // We're going to scan this buffer in place, and yacc demands we put two
+ // nulls at the end.
+ raw_buffer_->append(2u, '\0');
+ filename_ = filename;
+ package_.reset();
+ error_ = 0;
+ document_.reset();
+
+ buffer_ = yy_scan_buffer(&(*raw_buffer_)[0], raw_buffer_->length(), scanner_);
+
+ if (yy::parser(this).parse() != 0 || error_ != 0) {
+ return false;}
+
+ if (document_.get() != nullptr)
+ return true;
+
+ LOG(ERROR) << "Parser succeeded but yielded no document!";
+ return false;
+}
+
+void Parser::ReportError(const string& err, unsigned line) {
+ cerr << filename_ << ":" << line << ": " << err << endl;
+ error_ = 1;
+}
+
+std::vector<std::string> Parser::Package() const {
+ if (!package_) {
+ return {};
+ }
+ return package_->GetTerms();
+}
+
+void Parser::AddImport(AidlQualifiedName* name, unsigned line) {
+ imports_.emplace_back(new AidlImport(this->FileName(),
+ name->GetDotName(), line));
+ delete name;
+}
diff --git a/aidl/aidl_language.h b/aidl/aidl_language.h
new file mode 100644
index 0000000..e27cd10
--- /dev/null
+++ b/aidl/aidl_language.h
@@ -0,0 +1,428 @@
+#ifndef AIDL_AIDL_LANGUAGE_H_
+#define AIDL_AIDL_LANGUAGE_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/strings.h>
+
+#include <io_delegate.h>
+
+struct yy_buffer_state;
+typedef yy_buffer_state* YY_BUFFER_STATE;
+
+class AidlToken {
+ public:
+ AidlToken(const std::string& text, const std::string& comments);
+
+ const std::string& GetText() const { return text_; }
+ const std::string& GetComments() const { return comments_; }
+
+ private:
+ std::string text_;
+ std::string comments_;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlToken);
+};
+
+class AidlNode {
+ public:
+ AidlNode() = default;
+ virtual ~AidlNode() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AidlNode);
+};
+
+namespace android {
+namespace aidl {
+
+class ValidatableType;
+
+} // namespace aidl
+} // namespace android
+
+class AidlAnnotatable : public AidlNode {
+ public:
+ enum Annotation : uint32_t {
+ AnnotationNone = 0,
+ AnnotationNullable = 1 << 0,
+ AnnotationUtf8 = 1 << 1,
+ AnnotationUtf8InCpp = 1 << 2,
+ };
+
+ AidlAnnotatable() = default;
+ virtual ~AidlAnnotatable() = default;
+
+ void Annotate(AidlAnnotatable::Annotation annotation) {
+ annotations_ =
+ static_cast<AidlAnnotatable::Annotation>(annotations_ | annotation);
+ }
+ bool IsNullable() const {
+ return annotations_ & AnnotationNullable;
+ }
+ bool IsUtf8() const {
+ return annotations_ & AnnotationUtf8;
+ }
+ bool IsUtf8InCpp() const {
+ return annotations_ & AnnotationUtf8InCpp;
+ }
+
+ private:
+ Annotation annotations_ = AnnotationNone;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlAnnotatable);
+};
+
+class AidlType : public AidlAnnotatable {
+ public:
+ AidlType(const std::string& name, unsigned line,
+ const std::string& comments, bool is_array);
+ virtual ~AidlType() = default;
+
+ const std::string& GetName() const { return name_; }
+ unsigned GetLine() const { return line_; }
+ bool IsArray() const { return is_array_; }
+ const std::string& GetComments() const { return comments_; }
+
+ std::string ToString() const;
+
+ 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:
+ std::string name_;
+ unsigned line_;
+ bool is_array_;
+ std::string comments_;
+ const android::aidl::ValidatableType* language_type_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlType);
+};
+
+class AidlArgument : public AidlNode {
+ public:
+ enum Direction { IN_DIR = 1, OUT_DIR = 2, INOUT_DIR = 3 };
+
+ AidlArgument(AidlArgument::Direction direction, AidlType* type,
+ std::string name, unsigned line);
+ AidlArgument(AidlType* type, std::string name, unsigned line);
+ virtual ~AidlArgument() = default;
+
+ Direction GetDirection() const { return direction_; }
+ bool IsOut() const { return direction_ & OUT_DIR; }
+ bool IsIn() const { return direction_ & IN_DIR; }
+ bool DirectionWasSpecified() const { return direction_specified_; }
+
+ std::string GetName() const { return name_; }
+ int GetLine() const { return line_; }
+ const AidlType& GetType() const { return *type_; }
+ AidlType* GetMutableType() { return type_.get(); }
+
+ std::string ToString() const;
+
+ private:
+ std::unique_ptr<AidlType> type_;
+ Direction direction_;
+ bool direction_specified_;
+ std::string name_;
+ unsigned line_;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlArgument);
+};
+
+class AidlMethod;
+class AidlIntConstant;
+class AidlStringConstant;
+class AidlMember : public AidlNode {
+ public:
+ AidlMember() = default;
+ virtual ~AidlMember() = default;
+
+ virtual AidlMethod* AsMethod() { return nullptr; }
+ virtual AidlIntConstant* AsIntConstant() { return nullptr; }
+ virtual AidlStringConstant* AsStringConstant() { return nullptr; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(AidlMember);
+};
+
+class AidlIntConstant : public AidlMember {
+ public:
+ AidlIntConstant(std::string name, int32_t value);
+ AidlIntConstant(std::string name, std::string value, unsigned line_number);
+ virtual ~AidlIntConstant() = default;
+
+ const std::string& GetName() const { return name_; }
+ int GetValue() const { return value_; }
+ bool IsValid() const { return is_valid_; }
+
+ AidlIntConstant* AsIntConstant() override { return this; }
+
+ private:
+ std::string name_;
+ int32_t value_;
+ bool is_valid_;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlIntConstant);
+};
+
+class AidlStringConstant : public AidlMember {
+ public:
+ AidlStringConstant(std::string name, std::string value, unsigned line_number);
+ virtual ~AidlStringConstant() = default;
+
+ const std::string& GetName() const { return name_; }
+ const std::string& GetValue() const { return value_; }
+ bool IsValid() const { return is_valid_; }
+
+ AidlStringConstant* AsStringConstant() override { return this; }
+
+ private:
+ std::string name_;
+ std::string value_;
+ bool is_valid_;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlStringConstant);
+};
+
+class AidlMethod : public AidlMember {
+ public:
+ AidlMethod(bool oneway, AidlType* type, std::string name,
+ std::vector<std::unique_ptr<AidlArgument>>* args,
+ unsigned line, const std::string& comments);
+ AidlMethod(bool oneway, AidlType* type, std::string name,
+ std::vector<std::unique_ptr<AidlArgument>>* args,
+ unsigned line, const std::string& comments, int id);
+ virtual ~AidlMethod() = default;
+
+ AidlMethod* AsMethod() override { return this; }
+
+ const std::string& GetComments() const { return comments_; }
+ const AidlType& GetType() const { return *type_; }
+ AidlType* GetMutableType() { return type_.get(); }
+ bool IsOneway() const { return oneway_; }
+ const std::string& GetName() const { return name_; }
+ unsigned GetLine() const { return line_; }
+ bool HasId() const { return has_id_; }
+ int GetId() { return id_; }
+ void SetId(unsigned id) { id_ = id; }
+
+ const std::vector<std::unique_ptr<AidlArgument>>& GetArguments() const {
+ return arguments_;
+ }
+ // An inout parameter will appear in both GetInArguments()
+ // and GetOutArguments(). AidlMethod retains ownership of the argument
+ // pointers returned in this way.
+ const std::vector<const AidlArgument*>& GetInArguments() const {
+ return in_arguments_;
+ }
+ const std::vector<const AidlArgument*>& GetOutArguments() const {
+ return out_arguments_;
+ }
+
+ private:
+ bool oneway_;
+ std::string comments_;
+ std::unique_ptr<AidlType> type_;
+ std::string name_;
+ unsigned line_;
+ const std::vector<std::unique_ptr<AidlArgument>> arguments_;
+ std::vector<const AidlArgument*> in_arguments_;
+ std::vector<const AidlArgument*> out_arguments_;
+ bool has_id_;
+ int id_;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlMethod);
+};
+
+class AidlParcelable;
+class AidlInterface;
+class AidlDocument : public AidlNode {
+ public:
+ AidlDocument() = default;
+ explicit AidlDocument(AidlInterface* interface);
+ virtual ~AidlDocument() = default;
+
+ const AidlInterface* GetInterface() const { return interface_.get(); }
+ AidlInterface* ReleaseInterface() { return interface_.release(); }
+
+ const std::vector<std::unique_ptr<AidlParcelable>>& GetParcelables() const {
+ return parcelables_;
+ }
+
+ void AddParcelable(AidlParcelable* parcelable) {
+ parcelables_.push_back(std::unique_ptr<AidlParcelable>(parcelable));
+ }
+
+ private:
+ std::vector<std::unique_ptr<AidlParcelable>> parcelables_;
+ std::unique_ptr<AidlInterface> interface_;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlDocument);
+};
+
+class AidlQualifiedName : public AidlNode {
+ public:
+ AidlQualifiedName(std::string term, std::string comments);
+ virtual ~AidlQualifiedName() = default;
+
+ const std::vector<std::string>& GetTerms() const { return terms_; }
+ const std::string& GetComments() const { return comments_; }
+ std::string GetDotName() const { return android::base::Join(terms_, '.'); }
+
+ void AddTerm(const std::string& term);
+
+ private:
+ std::vector<std::string> terms_;
+ std::string comments_;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlQualifiedName);
+};
+
+class AidlParcelable : public AidlNode {
+ public:
+ AidlParcelable(AidlQualifiedName* name, unsigned line,
+ const std::vector<std::string>& package,
+ const std::string& cpp_header = "");
+ virtual ~AidlParcelable() = default;
+
+ std::string GetName() const { return name_->GetDotName(); }
+ unsigned GetLine() const { return line_; }
+ std::string GetPackage() const;
+ const std::vector<std::string>& GetSplitPackage() const { return package_; }
+ std::string GetCppHeader() const { return cpp_header_; }
+ std::string GetCanonicalName() const;
+
+ private:
+ std::unique_ptr<AidlQualifiedName> name_;
+ unsigned line_;
+ const std::vector<std::string> package_;
+ std::string cpp_header_;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlParcelable);
+};
+
+class AidlInterface : public AidlAnnotatable {
+ public:
+ AidlInterface(const std::string& name, unsigned line,
+ const std::string& comments, bool oneway_,
+ std::vector<std::unique_ptr<AidlMember>>* members,
+ const std::vector<std::string>& package);
+ virtual ~AidlInterface() = default;
+
+ const std::string& GetName() const { return name_; }
+ unsigned GetLine() const { return line_; }
+ const std::string& GetComments() const { return comments_; }
+ bool IsOneway() const { return oneway_; }
+ const std::vector<std::unique_ptr<AidlMethod>>& GetMethods() const
+ { return methods_; }
+ const std::vector<std::unique_ptr<AidlIntConstant>>& GetIntConstants() const
+ { return int_constants_; }
+ const std::vector<std::unique_ptr<AidlStringConstant>>&
+ GetStringConstants() const { return string_constants_; }
+ std::string GetPackage() const;
+ std::string GetCanonicalName() const;
+ const std::vector<std::string>& GetSplitPackage() const { return package_; }
+
+ 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:
+ std::string name_;
+ std::string comments_;
+ unsigned line_;
+ bool oneway_;
+ std::vector<std::unique_ptr<AidlMethod>> methods_;
+ std::vector<std::unique_ptr<AidlIntConstant>> int_constants_;
+ std::vector<std::unique_ptr<AidlStringConstant>> string_constants_;
+ std::vector<std::string> package_;
+
+ const android::aidl::ValidatableType* language_type_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlInterface);
+};
+
+class AidlImport : public AidlNode {
+ public:
+ AidlImport(const std::string& from, const std::string& needed_class,
+ unsigned line);
+ virtual ~AidlImport() = default;
+
+ const std::string& GetFileFrom() const { return from_; }
+ const std::string& GetFilename() const { return filename_; }
+ const std::string& GetNeededClass() const { return needed_class_; }
+ unsigned GetLine() const { return line_; }
+
+ void SetFilename(const std::string& filename) { filename_ = filename; }
+
+ private:
+ std::string from_;
+ std::string filename_;
+ std::string needed_class_;
+ unsigned line_;
+
+ DISALLOW_COPY_AND_ASSIGN(AidlImport);
+};
+
+class Parser {
+ public:
+ explicit Parser(const android::aidl::IoDelegate& io_delegate);
+ ~Parser();
+
+ // Parse contents of file |filename|.
+ bool ParseFile(const std::string& filename);
+
+ void ReportError(const std::string& err, unsigned line);
+
+ bool FoundNoErrors() const { return error_ == 0; }
+ const std::string& FileName() const { return filename_; }
+ void* Scanner() const { return scanner_; }
+
+ void SetDocument(AidlDocument* doc) { document_.reset(doc); };
+
+ void AddImport(AidlQualifiedName* name, unsigned line);
+
+ std::vector<std::string> Package() const;
+ void SetPackage(AidlQualifiedName* name) { package_.reset(name); }
+
+ AidlDocument* GetDocument() const { return document_.get(); }
+ AidlDocument* ReleaseDocument() { return document_.release(); }
+ 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();
+ }
+
+ private:
+ const android::aidl::IoDelegate& io_delegate_;
+ int error_ = 0;
+ std::string filename_;
+ std::unique_ptr<AidlQualifiedName> package_;
+ void* scanner_ = nullptr;
+ std::unique_ptr<AidlDocument> document_;
+ std::vector<std::unique_ptr<AidlImport>> imports_;
+ std::unique_ptr<std::string> raw_buffer_;
+ YY_BUFFER_STATE buffer_;
+
+ DISALLOW_COPY_AND_ASSIGN(Parser);
+};
+
+#endif // AIDL_AIDL_LANGUAGE_H_
diff --git a/aidl/aidl_language_l.ll b/aidl/aidl_language_l.ll
new file mode 100644
index 0000000..a56758b
--- /dev/null
+++ b/aidl/aidl_language_l.ll
@@ -0,0 +1,106 @@
+%{
+#include <string.h>
+#include <stdlib.h>
+
+#include "aidl_language.h"
+#include "aidl_language_y.h"
+
+#define YY_USER_ACTION yylloc->columns(yyleng);
+%}
+
+%option yylineno
+%option noyywrap
+%option reentrant
+%option bison-bridge
+%option bison-locations
+
+%x COPYING LONG_COMMENT
+
+identifier [_a-zA-Z][_a-zA-Z0-9]*
+whitespace ([ \t\r]+)
+intvalue [-+]?(0|[1-9][0-9]*)
+hexvalue 0[x|X][0-9a-fA-F]+
+
+%%
+%{
+ /* This happens at every call to yylex (every time we receive one token) */
+ std::string extra_text;
+ 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; }
+<LONG_COMMENT>\n+ { extra_text += yytext; yylloc->lines(yyleng); }
+<LONG_COMMENT>[^*\n]+ { extra_text += yytext; }
+
+\"[^\"]*\" { yylval->token = new AidlToken(yytext, extra_text);
+ return yy::parser::token::C_STR; }
+
+\/\/.*\n { extra_text += yytext; yylloc->lines(1); yylloc->step(); }
+
+\n+ { yylloc->lines(yyleng); yylloc->step(); }
+{whitespace} {}
+<<EOF>> { yyterminate(); }
+
+ /* symbols */
+; { return ';'; }
+\{ { return '{'; }
+\} { return '}'; }
+= { return '='; }
+, { return ','; }
+\. { return '.'; }
+\( { return '('; }
+\) { return ')'; }
+\[ { return '['; }
+\] { return ']'; }
+\< { return '<'; }
+\> { return '>'; }
+
+ /* keywords */
+parcelable { return yy::parser::token::PARCELABLE; }
+import { return yy::parser::token::IMPORT; }
+package { return yy::parser::token::PACKAGE; }
+int { return yy::parser::token::INT; }
+String { return yy::parser::token::STRING; }
+in { return yy::parser::token::IN; }
+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; }
+@nullable { return yy::parser::token::ANNOTATION_NULLABLE; }
+@utf8 { return yy::parser::token::ANNOTATION_UTF8; }
+@utf8InCpp { return yy::parser::token::ANNOTATION_UTF8_CPP; }
+
+interface { yylval->token = new AidlToken("interface", extra_text);
+ return yy::parser::token::INTERFACE;
+ }
+oneway { yylval->token = new AidlToken("oneway", extra_text);
+ return yy::parser::token::ONEWAY;
+ }
+
+ /* scalars */
+{identifier} { yylval->token = new AidlToken(yytext, extra_text);
+ return yy::parser::token::IDENTIFIER;
+ }
+{intvalue} { yylval->integer = std::stoi(yytext);
+ return yy::parser::token::INTVALUE; }
+{hexvalue} { yylval->token = new AidlToken(yytext, extra_text);
+ return yy::parser::token::HEXVALUE; }
+
+ /* syntax error! */
+. { printf("UNKNOWN(%s)", yytext);
+ yylval->token = new AidlToken(yytext, extra_text);
+ return yy::parser::token::IDENTIFIER;
+ }
+
+%%
+
+// comment and whitespace handling
+// ================================================
diff --git a/aidl/aidl_language_y.yy b/aidl/aidl_language_y.yy
new file mode 100644
index 0000000..4ad6754
--- /dev/null
+++ b/aidl/aidl_language_y.yy
@@ -0,0 +1,321 @@
+%{
+#include "aidl_language.h"
+#include "aidl_language_y.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int yylex(yy::parser::semantic_type *, yy::parser::location_type *, void *);
+
+#define lex_scanner ps->Scanner()
+
+%}
+
+%parse-param { Parser* ps }
+%lex-param { void *lex_scanner }
+
+%pure-parser
+%skeleton "glr.cc"
+
+%union {
+ AidlToken* token;
+ int integer;
+ std::string *str;
+ AidlType::Annotation annotation;
+ AidlType::Annotation annotation_list;
+ AidlType* type;
+ AidlType* unannotated_type;
+ AidlArgument* arg;
+ AidlArgument::Direction direction;
+ std::vector<std::unique_ptr<AidlArgument>>* arg_list;
+ AidlMethod* method;
+ AidlMember* constant;
+ std::vector<std::unique_ptr<AidlMember>>* members;
+ AidlQualifiedName* qname;
+ AidlInterface* interface_obj;
+ AidlParcelable* parcelable;
+ AidlDocument* parcelable_list;
+}
+
+%token<token> IDENTIFIER INTERFACE ONEWAY C_STR HEXVALUE
+%token<integer> INTVALUE
+
+%token '(' ')' ',' '=' '[' ']' '<' '>' '.' '{' '}' ';'
+%token IN OUT INOUT PACKAGE IMPORT PARCELABLE CPP_HEADER CONST INT STRING
+%token ANNOTATION_NULLABLE ANNOTATION_UTF8 ANNOTATION_UTF8_CPP
+
+%type<parcelable_list> parcelable_decls
+%type<parcelable> parcelable_decl
+%type<members> members
+%type<interface_obj> interface_decl
+%type<method> method_decl
+%type<constant> constant_decl
+%type<annotation> annotation
+%type<annotation_list>annotation_list
+%type<type> type
+%type<unannotated_type> unannotated_type
+%type<arg_list> arg_list
+%type<arg> arg
+%type<direction> direction
+%type<str> generic_list
+%type<qname> qualified_name
+
+%type<token> identifier error
+%%
+document
+ : package imports parcelable_decls
+ { ps->SetDocument($3); }
+ | package imports interface_decl
+ { ps->SetDocument(new AidlDocument($3)); };
+
+/* A couple of tokens that are keywords elsewhere are identifiers when
+ * occurring in the identifier position. Therefore identifier is a
+ * non-terminal, which is either an IDENTIFIER token, or one of the
+ * aforementioned keyword tokens.
+ */
+identifier
+ : IDENTIFIER
+ { $$ = $1; }
+ | CPP_HEADER
+ { $$ = new AidlToken("cpp_header", ""); }
+ | INT
+ { $$ = new AidlToken("int", ""); }
+ | STRING
+ { $$ = new AidlToken("String", ""); }
+ ;
+
+package
+ : {}
+ | PACKAGE qualified_name ';'
+ { ps->SetPackage($2); };
+
+imports
+ : {}
+ | import imports {};
+
+import
+ : IMPORT qualified_name ';'
+ { ps->AddImport($2, @1.begin.line); };
+
+qualified_name
+ : identifier {
+ $$ = new AidlQualifiedName($1->GetText(), $1->GetComments());
+ delete $1;
+ }
+ | qualified_name '.' identifier
+ { $$ = $1;
+ $$->AddTerm($3->GetText());
+ };
+
+parcelable_decls
+ :
+ { $$ = new AidlDocument(); }
+ | parcelable_decls parcelable_decl {
+ $$ = $1;
+ $$->AddParcelable($2);
+ }
+ | parcelable_decls error {
+ fprintf(stderr, "%s:%d: syntax error don't know what to do with \"%s\"\n",
+ ps->FileName().c_str(),
+ @2.begin.line, $2->GetText().c_str());
+ $$ = $1;
+ };
+
+parcelable_decl
+ : PARCELABLE qualified_name ';' {
+ $$ = new AidlParcelable($2, @2.begin.line, ps->Package());
+ }
+ | PARCELABLE qualified_name CPP_HEADER C_STR ';' {
+ $$ = new AidlParcelable($2, @2.begin.line, ps->Package(), $4->GetText());
+ }
+ | PARCELABLE ';' {
+ fprintf(stderr, "%s:%d syntax error in parcelable declaration. Expected type name.\n",
+ ps->FileName().c_str(), @1.begin.line);
+ $$ = NULL;
+ }
+ | PARCELABLE error ';' {
+ fprintf(stderr, "%s:%d syntax error in parcelable declaration. Expected type name, saw \"%s\".\n",
+ ps->FileName().c_str(), @2.begin.line, $2->GetText().c_str());
+ $$ = NULL;
+ };
+
+interface_decl
+ : annotation_list INTERFACE identifier '{' members '}' {
+ $$ = new AidlInterface($3->GetText(), @2.begin.line, $2->GetComments(),
+ false, $5, ps->Package());
+ $$->Annotate($1);
+ delete $2;
+ delete $3;
+ }
+ | annotation_list ONEWAY INTERFACE identifier '{' members '}' {
+ $$ = new AidlInterface($4->GetText(), @4.begin.line, $2->GetComments(),
+ true, $6, ps->Package());
+ $$->Annotate($1);
+ delete $2;
+ delete $3;
+ delete $4;
+ }
+ | annotation_list INTERFACE error '{' members '}' {
+ fprintf(stderr, "%s:%d: syntax error in interface declaration. Expected "
+ "type name, saw \"%s\"\n",
+ ps->FileName().c_str(), @3.begin.line, $3->GetText().c_str());
+ $$ = NULL;
+ delete $2;
+ delete $3;
+ delete $5;
+ }
+ | annotation_list INTERFACE error '}' {
+ fprintf(stderr, "%s:%d: syntax error in interface declaration. Expected "
+ "type name, saw \"%s\"\n",
+ ps->FileName().c_str(), @3.begin.line, $3->GetText().c_str());
+ $$ = NULL;
+ delete $2;
+ delete $3;
+ };
+
+members
+ :
+ { $$ = new std::vector<std::unique_ptr<AidlMember>>(); }
+ | members method_decl
+ { $1->push_back(std::unique_ptr<AidlMember>($2)); }
+ | members constant_decl
+ { $1->push_back(std::unique_ptr<AidlMember>($2)); }
+ | members error ';' {
+ fprintf(stderr, "%s:%d: syntax error before ';' "
+ "(expected method or constant declaration)\n",
+ ps->FileName().c_str(), @3.begin.line);
+ $$ = $1;
+ };
+
+constant_decl
+ : CONST INT identifier '=' INTVALUE ';' {
+ $$ = new AidlIntConstant($3->GetText(), $5);
+ delete $3;
+ }
+ | CONST INT identifier '=' HEXVALUE ';' {
+ $$ = new AidlIntConstant($3->GetText(), $5->GetText(), @5.begin.line);
+ delete $3;
+ }
+ | CONST STRING identifier '=' C_STR ';' {
+ $$ = new AidlStringConstant($3->GetText(), $5->GetText(), @5.begin.line);
+ delete $3;
+ delete $5;
+ }
+ ;
+
+method_decl
+ : type identifier '(' arg_list ')' ';' {
+ $$ = new AidlMethod(false, $1, $2->GetText(), $4, @2.begin.line,
+ $1->GetComments());
+ delete $2;
+ }
+ | ONEWAY type identifier '(' arg_list ')' ';' {
+ $$ = new AidlMethod(true, $2, $3->GetText(), $5, @3.begin.line,
+ $1->GetComments());
+ delete $1;
+ delete $3;
+ }
+ | type identifier '(' arg_list ')' '=' INTVALUE ';' {
+ $$ = new AidlMethod(false, $1, $2->GetText(), $4, @2.begin.line,
+ $1->GetComments(), $7);
+ delete $2;
+ }
+ | ONEWAY type identifier '(' arg_list ')' '=' INTVALUE ';' {
+ $$ = new AidlMethod(true, $2, $3->GetText(), $5, @3.begin.line,
+ $1->GetComments(), $8);
+ delete $1;
+ delete $3;
+ };
+
+arg_list
+ :
+ { $$ = new std::vector<std::unique_ptr<AidlArgument>>(); }
+ | arg {
+ $$ = new std::vector<std::unique_ptr<AidlArgument>>();
+ $$->push_back(std::unique_ptr<AidlArgument>($1));
+ }
+ | arg_list ',' arg {
+ $$ = $1;
+ $$->push_back(std::unique_ptr<AidlArgument>($3));
+ }
+ | error {
+ fprintf(stderr, "%s:%d: syntax error in parameter list\n",
+ ps->FileName().c_str(), @1.begin.line);
+ $$ = new std::vector<std::unique_ptr<AidlArgument>>();
+ };
+
+arg
+ : direction type identifier {
+ $$ = new AidlArgument($1, $2, $3->GetText(), @3.begin.line);
+ delete $3;
+ };
+ | type identifier {
+ $$ = new AidlArgument($1, $2->GetText(), @2.begin.line);
+ delete $2;
+ };
+
+unannotated_type
+ : qualified_name {
+ $$ = new AidlType($1->GetDotName(), @1.begin.line, $1->GetComments(), false);
+ delete $1;
+ }
+ | qualified_name '[' ']' {
+ $$ = new AidlType($1->GetDotName(), @1.begin.line, $1->GetComments(),
+ true);
+ delete $1;
+ }
+ | qualified_name '<' generic_list '>' {
+ $$ = new AidlType($1->GetDotName() + "<" + *$3 + ">", @1.begin.line,
+ $1->GetComments(), false);
+ delete $1;
+ delete $3;
+ };
+
+type
+ : annotation_list unannotated_type {
+ $$ = $2;
+ $2->Annotate($1);
+ };
+
+generic_list
+ : qualified_name {
+ $$ = new std::string($1->GetDotName());
+ delete $1;
+ }
+ | generic_list ',' qualified_name {
+ $$ = new std::string(*$1 + "," + $3->GetDotName());
+ delete $1;
+ delete $3;
+ };
+
+annotation_list
+ :
+ { $$ = AidlType::AnnotationNone; }
+ | annotation_list annotation
+ { $$ = static_cast<AidlType::Annotation>($1 | $2); };
+
+annotation
+ : ANNOTATION_NULLABLE
+ { $$ = AidlType::AnnotationNullable; }
+ | ANNOTATION_UTF8
+ { $$ = AidlType::AnnotationUtf8; }
+ | ANNOTATION_UTF8_CPP
+ { $$ = AidlType::AnnotationUtf8InCpp; };
+
+direction
+ : IN
+ { $$ = AidlArgument::IN_DIR; }
+ | OUT
+ { $$ = AidlArgument::OUT_DIR; }
+ | INOUT
+ { $$ = AidlArgument::INOUT_DIR; };
+
+%%
+
+#include <ctype.h>
+#include <stdio.h>
+
+void yy::parser::error(const yy::parser::location_type& l,
+ const std::string& errstr) {
+ ps->ReportError(errstr, l.begin.line);
+}
diff --git a/aidl/aidl_unittest.cpp b/aidl/aidl_unittest.cpp
new file mode 100644
index 0000000..0d2ba53
--- /dev/null
+++ b/aidl/aidl_unittest.cpp
@@ -0,0 +1,399 @@
+/*
+ * 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 <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "aidl.h"
+#include "aidl_language.h"
+#include "tests/fake_io_delegate.h"
+#include "type_cpp.h"
+#include "type_java.h"
+#include "type_namespace.h"
+
+using android::aidl::test::FakeIoDelegate;
+using android::base::StringPrintf;
+using std::set;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+using android::aidl::internals::parse_preprocessed_file;
+
+namespace android {
+namespace aidl {
+namespace {
+
+const char kExpectedDepFileContents[] =
+R"(place/for/output/p/IFoo.java : \
+ p/IFoo.aidl
+
+p/IFoo.aidl :
+)";
+
+const char kExpectedParcelableDepFileContents[] =
+R"( : \
+ p/Foo.aidl
+
+p/Foo.aidl :
+)";
+
+} // namespace
+
+class AidlTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ java_types_.Init();
+ cpp_types_.Init();
+ }
+
+ unique_ptr<AidlInterface> Parse(const string& path,
+ const string& contents,
+ TypeNamespace* types,
+ AidlError* error = nullptr) {
+ io_delegate_.SetFileContents(path, contents);
+ unique_ptr<AidlInterface> ret;
+ std::vector<std::unique_ptr<AidlImport>> imports;
+ AidlError actual_error = ::android::aidl::internals::load_and_validate_aidl(
+ preprocessed_files_,
+ import_paths_,
+ path,
+ io_delegate_,
+ types,
+ &ret,
+ &imports);
+ if (error != nullptr) {
+ *error = actual_error;
+ }
+ return ret;
+ }
+
+ FakeIoDelegate io_delegate_;
+ vector<string> preprocessed_files_;
+ vector<string> import_paths_;
+ java::JavaTypeNamespace java_types_;
+ cpp::TypeNamespace cpp_types_;
+};
+
+TEST_F(AidlTest, JavaAcceptsMissingPackage) {
+ EXPECT_NE(nullptr, Parse("IFoo.aidl", "interface IFoo { }", &java_types_));
+}
+
+TEST_F(AidlTest, RejectsArraysOfBinders) {
+ import_paths_.push_back("");
+ io_delegate_.SetFileContents("bar/IBar.aidl",
+ "package bar; interface IBar {}");
+ string path = "foo/IFoo.aidl";
+ string contents = "package foo;\n"
+ "import bar.IBar;\n"
+ "interface IFoo { void f(in IBar[] input); }";
+ EXPECT_EQ(nullptr, Parse(path, contents, &java_types_));
+ EXPECT_EQ(nullptr, Parse(path, contents, &cpp_types_));
+}
+
+TEST_F(AidlTest, CppRejectsMissingPackage) {
+ EXPECT_EQ(nullptr, Parse("IFoo.aidl", "interface IFoo { }", &cpp_types_));
+ EXPECT_NE(nullptr,
+ Parse("a/IFoo.aidl", "package a; interface IFoo { }", &cpp_types_));
+}
+
+TEST_F(AidlTest, RejectsOnewayOutParameters) {
+ string oneway_interface =
+ "package a; oneway interface IFoo { void f(out int bar); }";
+ string oneway_method =
+ "package a; interface IBar { oneway void f(out int bar); }";
+ EXPECT_EQ(nullptr, Parse("a/IFoo.aidl", oneway_interface, &cpp_types_));
+ EXPECT_EQ(nullptr, Parse("a/IFoo.aidl", oneway_interface, &java_types_));
+ EXPECT_EQ(nullptr, Parse("a/IBar.aidl", oneway_method, &cpp_types_));
+ EXPECT_EQ(nullptr, Parse("a/IBar.aidl", oneway_method, &java_types_));
+}
+
+TEST_F(AidlTest, RejectsOnewayNonVoidReturn) {
+ string oneway_method = "package a; interface IFoo { oneway int f(); }";
+ EXPECT_EQ(nullptr, Parse("a/IFoo.aidl", oneway_method, &cpp_types_));
+ EXPECT_EQ(nullptr, Parse("a/IFoo.aidl", oneway_method, &java_types_));
+}
+
+TEST_F(AidlTest, RejectsNullablePrimitive) {
+ string oneway_method = "package a; interface IFoo { @nullable int f(); }";
+ EXPECT_EQ(nullptr, Parse("a/IFoo.aidl", oneway_method, &cpp_types_));
+ EXPECT_EQ(nullptr, Parse("a/IFoo.aidl", oneway_method, &java_types_));
+}
+
+TEST_F(AidlTest, ParsesNullableAnnotation) {
+ for (auto is_nullable: {true, false}) {
+ auto parse_result = Parse(
+ "a/IFoo.aidl",
+ StringPrintf( "package a; interface IFoo {%s String f(); }",
+ (is_nullable) ? "@nullable" : ""),
+ &cpp_types_);
+ ASSERT_NE(nullptr, parse_result);
+ ASSERT_FALSE(parse_result->GetMethods().empty());
+ EXPECT_EQ(parse_result->GetMethods()[0]->GetType().IsNullable(),
+ is_nullable);
+ }
+}
+
+TEST_F(AidlTest, ParsesUtf8Annotations) {
+ for (auto is_utf8: {true, false}) {
+ auto parse_result = Parse(
+ "a/IFoo.aidl",
+ StringPrintf( "package a; interface IFoo {%s String f(); }",
+ (is_utf8) ? "@utf8InCpp" : ""),
+ &cpp_types_);
+ ASSERT_NE(nullptr, parse_result);
+ ASSERT_FALSE(parse_result->GetMethods().empty());
+ EXPECT_EQ(parse_result->GetMethods()[0]->GetType().IsUtf8InCpp(),
+ is_utf8);
+ }
+}
+
+TEST_F(AidlTest, AcceptsOneway) {
+ string oneway_method = "package a; interface IFoo { oneway void f(int a); }";
+ string oneway_interface =
+ "package a; oneway interface IBar { void f(int a); }";
+ EXPECT_NE(nullptr, Parse("a/IFoo.aidl", oneway_method, &cpp_types_));
+ EXPECT_NE(nullptr, Parse("a/IFoo.aidl", oneway_method, &java_types_));
+ EXPECT_NE(nullptr, Parse("a/IBar.aidl", oneway_interface, &cpp_types_));
+ EXPECT_NE(nullptr, Parse("a/IBar.aidl", oneway_interface, &java_types_));
+}
+
+TEST_F(AidlTest, ParsesPreprocessedFile) {
+ string simple_content = "parcelable a.Foo;\ninterface b.IBar;";
+ io_delegate_.SetFileContents("path", simple_content);
+ EXPECT_FALSE(java_types_.HasTypeByCanonicalName("a.Foo"));
+ EXPECT_TRUE(parse_preprocessed_file(io_delegate_, "path", &java_types_));
+ EXPECT_TRUE(java_types_.HasTypeByCanonicalName("a.Foo"));
+ EXPECT_TRUE(java_types_.HasTypeByCanonicalName("b.IBar"));
+}
+
+TEST_F(AidlTest, ParsesPreprocessedFileWithWhitespace) {
+ string simple_content = "parcelable a.Foo;\n interface b.IBar ;\t";
+ io_delegate_.SetFileContents("path", simple_content);
+ EXPECT_FALSE(java_types_.HasTypeByCanonicalName("a.Foo"));
+ EXPECT_TRUE(parse_preprocessed_file(io_delegate_, "path", &java_types_));
+ EXPECT_TRUE(java_types_.HasTypeByCanonicalName("a.Foo"));
+ EXPECT_TRUE(java_types_.HasTypeByCanonicalName("b.IBar"));
+}
+
+TEST_F(AidlTest, PreferImportToPreprocessed) {
+ io_delegate_.SetFileContents("preprocessed", "interface another.IBar;");
+ io_delegate_.SetFileContents("one/IBar.aidl", "package one; "
+ "interface IBar {}");
+ preprocessed_files_.push_back("preprocessed");
+ import_paths_.push_back("");
+ auto parse_result = Parse(
+ "p/IFoo.aidl", "package p; import one.IBar; interface IFoo {}",
+ &java_types_);
+ EXPECT_NE(nullptr, parse_result);
+ // We expect to know about both kinds of IBar
+ EXPECT_TRUE(java_types_.HasTypeByCanonicalName("one.IBar"));
+ EXPECT_TRUE(java_types_.HasTypeByCanonicalName("another.IBar"));
+ // But if we request just "IBar" we should get our imported one.
+ AidlType ambiguous_type("IBar", 0, "", false /* not an array */);
+ const java::Type* type = java_types_.Find(ambiguous_type);
+ ASSERT_TRUE(type);
+ EXPECT_EQ("one.IBar", type->CanonicalName());
+}
+
+TEST_F(AidlTest, WritePreprocessedFile) {
+ io_delegate_.SetFileContents("p/Outer.aidl",
+ "package p; parcelable Outer.Inner;");
+ io_delegate_.SetFileContents("one/IBar.aidl", "package one; import p.Outer;"
+ "interface IBar {}");
+
+ JavaOptions options;
+ options.output_file_name_ = "preprocessed";
+ options.files_to_preprocess_.resize(2);
+ options.files_to_preprocess_[0] = "p/Outer.aidl";
+ options.files_to_preprocess_[1] = "one/IBar.aidl";
+ EXPECT_TRUE(::android::aidl::preprocess_aidl(options, io_delegate_));
+
+ string output;
+ EXPECT_TRUE(io_delegate_.GetWrittenContents("preprocessed", &output));
+ EXPECT_EQ("parcelable p.Outer.Inner;\ninterface one.IBar;\n", output);
+}
+
+TEST_F(AidlTest, RequireOuterClass) {
+ io_delegate_.SetFileContents("p/Outer.aidl",
+ "package p; parcelable Outer.Inner;");
+ import_paths_.push_back("");
+ auto parse_result = Parse(
+ "p/IFoo.aidl",
+ "package p; import p.Outer; interface IFoo { void f(in Inner c); }",
+ &java_types_);
+ EXPECT_EQ(nullptr, parse_result);
+}
+
+TEST_F(AidlTest, ParseCompoundParcelableFromPreprocess) {
+ io_delegate_.SetFileContents("preprocessed",
+ "parcelable p.Outer.Inner;");
+ preprocessed_files_.push_back("preprocessed");
+ auto parse_result = Parse(
+ "p/IFoo.aidl",
+ "package p; interface IFoo { void f(in Inner c); }",
+ &java_types_);
+ // TODO(wiley): This should actually return nullptr because we require
+ // the outer class name. However, for legacy reasons,
+ // this behavior must be maintained. b/17415692
+ EXPECT_NE(nullptr, parse_result);
+}
+
+TEST_F(AidlTest, FailOnParcelable) {
+ JavaOptions options;
+ options.input_file_name_ = "p/IFoo.aidl";
+ io_delegate_.SetFileContents(options.input_file_name_,
+ "package p; parcelable IFoo;");
+ // By default, we shouldn't fail on parcelable.
+ EXPECT_EQ(0, ::android::aidl::compile_aidl_to_java(options, io_delegate_));
+ options.fail_on_parcelable_ = true;
+ EXPECT_NE(0, ::android::aidl::compile_aidl_to_java(options, io_delegate_));
+}
+
+TEST_F(AidlTest, FailOnDuplicateConstantNames) {
+ AidlError reported_error;
+ EXPECT_EQ(nullptr,
+ Parse("p/IFoo.aidl",
+ R"(package p;
+ interface IFoo {
+ const String DUPLICATED = "d";
+ const int DUPLICATED = 1;
+ }
+ )",
+ &cpp_types_,
+ &reported_error));
+ EXPECT_EQ(AidlError::BAD_CONSTANTS, reported_error);
+}
+
+TEST_F(AidlTest, FailOnMalformedConstHexValue) {
+ AidlError reported_error;
+ EXPECT_EQ(nullptr,
+ Parse("p/IFoo.aidl",
+ R"(package p;
+ interface IFoo {
+ const int BAD_HEX_VALUE = 0xffffffffffffffffff;
+ }
+ )",
+ &cpp_types_,
+ &reported_error));
+ EXPECT_EQ(AidlError::BAD_CONSTANTS, reported_error);
+}
+
+TEST_F(AidlTest, ParsePositiveConstHexValue) {
+ AidlError reported_error;
+ auto cpp_parse_result =
+ Parse("p/IFoo.aidl",
+ R"(package p;
+ interface IFoo {
+ const int POSITIVE_HEX_VALUE = 0xf5;
+ }
+ )",
+ &cpp_types_,
+ &reported_error);
+ EXPECT_NE(nullptr, cpp_parse_result);
+ const auto& cpp_int_constants = cpp_parse_result->GetIntConstants();
+ EXPECT_EQ((size_t)1, cpp_int_constants.size());
+ EXPECT_EQ("POSITIVE_HEX_VALUE", cpp_int_constants[0]->GetName());
+ EXPECT_EQ(245, cpp_int_constants[0]->GetValue());
+}
+
+TEST_F(AidlTest, ParseNegativeConstHexValue) {
+ AidlError reported_error;
+ auto cpp_parse_result =
+ Parse("p/IFoo.aidl",
+ R"(package p;
+ interface IFoo {
+ const int NEGATIVE_HEX_VALUE = 0xffffffff;
+ }
+ )",
+ &cpp_types_,
+ &reported_error);
+ EXPECT_NE(nullptr, cpp_parse_result);
+ const auto& cpp_int_constants = cpp_parse_result->GetIntConstants();
+ EXPECT_EQ((size_t)1, cpp_int_constants.size());
+ EXPECT_EQ("NEGATIVE_HEX_VALUE", cpp_int_constants[0]->GetName());
+ EXPECT_EQ(-1, cpp_int_constants[0]->GetValue());
+}
+
+TEST_F(AidlTest, UnderstandsNativeParcelables) {
+ io_delegate_.SetFileContents(
+ "p/Bar.aidl",
+ "package p; parcelable Bar cpp_header \"baz/header\";");
+ import_paths_.push_back("");
+ const string input_path = "p/IFoo.aidl";
+ const string input = "package p; import p.Bar; interface IFoo { }";
+
+ // C++ understands C++ specific stuff
+ auto cpp_parse_result = Parse(input_path, input, &cpp_types_);
+ EXPECT_NE(nullptr, cpp_parse_result);
+ auto cpp_type = cpp_types_.FindTypeByCanonicalName("p.Bar");
+ ASSERT_NE(nullptr, cpp_type);
+ EXPECT_EQ("::p::Bar", cpp_type->CppType());
+ set<string> headers;
+ cpp_type->GetHeaders(&headers);
+ EXPECT_EQ(1u, headers.size());
+ EXPECT_EQ(1u, headers.count("baz/header"));
+
+ // Java ignores C++ specific stuff
+ auto java_parse_result = Parse(input_path, input, &java_types_);
+ EXPECT_NE(nullptr, java_parse_result);
+ auto java_type = java_types_.FindTypeByCanonicalName("p.Bar");
+ ASSERT_NE(nullptr, java_type);
+ EXPECT_EQ("p.Bar", java_type->InstantiableName());
+}
+
+TEST_F(AidlTest, WritesCorrectDependencyFile) {
+ // While the in tree build system always gives us an output file name,
+ // other android tools take advantage of our ability to infer the intended
+ // file name. This test makes sure we handle this correctly.
+ JavaOptions options;
+ options.input_file_name_ = "p/IFoo.aidl";
+ options.output_base_folder_ = "place/for/output";
+ options.dep_file_name_ = "dep/file/path";
+ io_delegate_.SetFileContents(options.input_file_name_,
+ "package p; interface IFoo {}");
+ EXPECT_EQ(0, ::android::aidl::compile_aidl_to_java(options, io_delegate_));
+ string actual_dep_file_contents;
+ EXPECT_TRUE(io_delegate_.GetWrittenContents(options.dep_file_name_,
+ &actual_dep_file_contents));
+ EXPECT_EQ(actual_dep_file_contents, kExpectedDepFileContents);
+}
+
+TEST_F(AidlTest, WritesTrivialDependencyFileForParcelable) {
+ // The SDK uses aidl to decide whether a .aidl file is a parcelable. It does
+ // this by calling aidl with every .aidl file it finds, then parsing the
+ // generated dependency files. Those that reference .java output files are
+ // for interfaces and those that do not are parcelables. However, for both
+ // parcelables and interfaces, we *must* generate a non-empty dependency file.
+ JavaOptions options;
+ options.input_file_name_ = "p/Foo.aidl";
+ options.output_base_folder_ = "place/for/output";
+ options.dep_file_name_ = "dep/file/path";
+ io_delegate_.SetFileContents(options.input_file_name_,
+ "package p; parcelable Foo;");
+ EXPECT_EQ(0, ::android::aidl::compile_aidl_to_java(options, io_delegate_));
+ string actual_dep_file_contents;
+ EXPECT_TRUE(io_delegate_.GetWrittenContents(options.dep_file_name_,
+ &actual_dep_file_contents));
+ EXPECT_EQ(actual_dep_file_contents, kExpectedParcelableDepFileContents);
+}
+
+} // namespace aidl
+} // namespace android
diff --git a/aidl/ast_cpp.cpp b/aidl/ast_cpp.cpp
new file mode 100644
index 0000000..783b0f3
--- /dev/null
+++ b/aidl/ast_cpp.cpp
@@ -0,0 +1,455 @@
+/*
+ * 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 "ast_cpp.h"
+
+#include <algorithm>
+
+#include "code_writer.h"
+#include "logging.h"
+
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace android {
+namespace aidl {
+namespace cpp {
+
+ClassDecl::ClassDecl(const std::string& name, const std::string& parent)
+ : name_(name),
+ parent_(parent) {}
+
+ClassDecl::ClassDecl(const std::string& name, const std::string& parent,
+ std::vector<unique_ptr<Declaration>> public_members,
+ std::vector<unique_ptr<Declaration>> private_members)
+ : name_(name),
+ parent_(parent),
+ public_members_(std::move(public_members)),
+ private_members_(std::move(private_members)) {}
+
+void ClassDecl::Write(CodeWriter* to) const {
+ to->Write("class %s ", name_.c_str());
+
+ if (parent_.length() > 0)
+ to->Write(": public %s ", parent_.c_str());
+
+ to->Write("{\n");
+
+ if (!public_members_.empty())
+ to->Write("public:\n");
+
+ for (const auto& dec : public_members_)
+ dec->Write(to);
+
+ if (!private_members_.empty())
+ to->Write("private:\n");
+
+ for (const auto& dec : private_members_)
+ dec->Write(to);
+
+ to->Write("}; // class %s\n", name_.c_str());
+}
+
+void ClassDecl::AddPublic(std::unique_ptr<Declaration> member) {
+ public_members_.push_back(std::move(member));
+}
+
+void ClassDecl::AddPrivate(std::unique_ptr<Declaration> member) {
+ private_members_.push_back(std::move(member));
+}
+
+Enum::EnumField::EnumField(const string& k, const string& v)
+ : key(k),
+ value(v) {}
+
+Enum::Enum(const string& name, const string& base_type)
+ : enum_name_(name), underlying_type_(base_type) {}
+
+Enum::Enum(const string& name) : Enum(name, "") {}
+
+void Enum::Write(CodeWriter* to) const {
+ if (underlying_type_.empty()) {
+ to->Write("enum %s {\n", enum_name_.c_str());
+ } else {
+ to->Write("enum %s : %s {\n", enum_name_.c_str(), underlying_type_.c_str());
+ }
+ for (const auto& field : fields_) {
+ if (field.value.empty()) {
+ to->Write(" %s,\n", field.key.c_str());
+ } else {
+ to->Write(" %s = %s,\n", field.key.c_str(), field.value.c_str());
+ }
+ }
+ to->Write("};\n");
+}
+
+void Enum::AddValue(const string& key, const string& value) {
+ fields_.emplace_back(key, value);
+}
+
+ArgList::ArgList(const std::string& single_argument)
+ : ArgList(vector<string>{single_argument}) {}
+
+ArgList::ArgList(const std::vector<std::string>& arg_list) {
+ for (const auto& s : arg_list) {
+ arguments_.emplace_back(new LiteralExpression(s));
+ }
+}
+
+ArgList::ArgList(std::vector<std::unique_ptr<AstNode>> arg_list)
+ : arguments_(std::move(arg_list)) {}
+
+ArgList::ArgList(ArgList&& arg_list)
+ : arguments_(std::move(arg_list.arguments_)) {}
+
+void ArgList::Write(CodeWriter* to) const {
+ to->Write("(");
+ bool is_first = true;
+ for (const auto& s : arguments_) {
+ if (!is_first) { to->Write(", "); }
+ is_first = false;
+ s->Write(to);
+ }
+ to->Write(")");
+}
+
+ConstructorDecl::ConstructorDecl(
+ const std::string& name,
+ ArgList&& arg_list)
+ : ConstructorDecl(name, std::move(arg_list), 0u) {}
+
+ConstructorDecl::ConstructorDecl(
+ const std::string& name,
+ ArgList&& arg_list,
+ uint32_t modifiers)
+ : name_(name),
+ arguments_(std::move(arg_list)),
+ modifiers_(modifiers) {}
+
+void ConstructorDecl::Write(CodeWriter* to) const {
+ if (modifiers_ & Modifiers::IS_VIRTUAL)
+ to->Write("virtual ");
+
+ if (modifiers_ & Modifiers::IS_EXPLICIT)
+ to->Write("explicit ");
+
+ to->Write("%s", name_.c_str());
+
+ arguments_.Write(to);
+
+ if (modifiers_ & Modifiers::IS_DEFAULT)
+ to->Write(" = default");
+
+ to->Write(";\n");
+}
+
+MacroDecl::MacroDecl(const std::string& name, ArgList&& arg_list)
+ : name_(name),
+ arguments_(std::move(arg_list)) {}
+
+void MacroDecl::Write(CodeWriter* to) const {
+ to->Write("%s", name_.c_str());
+ arguments_.Write(to);
+ to->Write("\n");
+}
+
+MethodDecl::MethodDecl(const std::string& return_type,
+ const std::string& name,
+ ArgList&& arg_list)
+ : MethodDecl(return_type, name, std::move(arg_list), 0u) {}
+
+MethodDecl::MethodDecl(const std::string& return_type,
+ const std::string& name,
+ ArgList&& arg_list,
+ uint32_t modifiers)
+ : return_type_(return_type),
+ name_(name),
+ arguments_(std::move(arg_list)),
+ is_const_(modifiers & IS_CONST),
+ is_virtual_(modifiers & IS_VIRTUAL),
+ is_override_(modifiers & IS_OVERRIDE),
+ is_pure_virtual_(modifiers & IS_PURE_VIRTUAL),
+ is_static_(modifiers & IS_STATIC) {}
+
+void MethodDecl::Write(CodeWriter* to) const {
+ if (is_virtual_)
+ to->Write("virtual ");
+
+ if (is_static_)
+ to->Write("static ");
+
+ to->Write("%s %s", return_type_.c_str(), name_.c_str());
+
+ arguments_.Write(to);
+
+ if (is_const_)
+ to->Write(" const");
+
+ if (is_override_)
+ to->Write(" override");
+
+ if (is_pure_virtual_)
+ to->Write(" = 0");
+
+ to->Write(";\n");
+}
+
+void StatementBlock::AddStatement(unique_ptr<AstNode> statement) {
+ statements_.push_back(std::move(statement));
+}
+
+void StatementBlock::AddStatement(AstNode* statement) {
+ statements_.emplace_back(statement);
+}
+
+void StatementBlock::AddLiteral(const std::string& expression_str,
+ bool add_semicolon) {
+ if (add_semicolon) {
+ statements_.push_back(unique_ptr<AstNode>(new Statement(expression_str)));
+ } else {
+ statements_.push_back(unique_ptr<AstNode>(
+ new LiteralExpression(expression_str)));
+ }
+}
+
+void StatementBlock::Write(CodeWriter* to) const {
+ to->Write("{\n");
+ for (const auto& statement : statements_) {
+ statement->Write(to);
+ }
+ to->Write("}\n");
+}
+
+ConstructorImpl::ConstructorImpl(const string& class_name,
+ ArgList&& arg_list,
+ const vector<string>& initializer_list)
+ : class_name_(class_name),
+ arguments_(std::move(arg_list)),
+ initializer_list_(initializer_list) {}
+
+void ConstructorImpl::Write(CodeWriter* to) const {
+ to->Write("%s::%s", class_name_.c_str(), class_name_.c_str());
+ arguments_.Write(to);
+ to->Write("\n");
+
+ bool is_first = true;
+ for (const string& i : initializer_list_) {
+ if (is_first) {
+ to->Write(" : %s", i.c_str());
+ } else {
+ to->Write(",\n %s", i.c_str());
+ }
+ is_first = false;
+ }
+
+ body_.Write(to);
+}
+
+MethodImpl::MethodImpl(const string& return_type,
+ const string& class_name,
+ const string& method_name,
+ ArgList&& arg_list,
+ bool is_const_method)
+ : return_type_(return_type),
+ method_name_(method_name),
+ arguments_(std::move(arg_list)),
+ is_const_method_(is_const_method) {
+ if (!class_name.empty()) {
+ method_name_ = class_name + "::" + method_name;
+ }
+}
+
+StatementBlock* MethodImpl::GetStatementBlock() {
+ return &statements_;
+}
+
+void MethodImpl::Write(CodeWriter* to) const {
+ to->Write("%s %s", return_type_.c_str(), method_name_.c_str());
+ arguments_.Write(to);
+ to->Write("%s ", (is_const_method_) ? " const" : "");
+ statements_.Write(to);
+}
+
+SwitchStatement::SwitchStatement(const std::string& expression)
+ : switch_expression_(expression) {}
+
+StatementBlock* SwitchStatement::AddCase(const string& value_expression) {
+ auto it = std::find(case_values_.begin(), case_values_.end(), value_expression);
+ if (it != case_values_.end()) {
+ LOG(ERROR) << "internal error: duplicate switch case labels";
+ return nullptr;
+ }
+ StatementBlock* ret = new StatementBlock();
+ case_values_.push_back(value_expression);
+ case_logic_.push_back(unique_ptr<StatementBlock>{ret});
+ return ret;
+}
+
+void SwitchStatement::Write(CodeWriter* to) const {
+ to->Write("switch (%s) {\n", switch_expression_.c_str());
+ for (size_t i = 0; i < case_values_.size(); ++i) {
+ const string& case_value = case_values_[i];
+ const unique_ptr<StatementBlock>& statements = case_logic_[i];
+ if (case_value.empty()) {
+ to->Write("default:\n");
+ } else {
+ to->Write("case %s:\n", case_value.c_str());
+ }
+ statements->Write(to);
+ to->Write("break;\n");
+ }
+ to->Write("}\n");
+}
+
+
+Assignment::Assignment(const std::string& left, const std::string& right)
+ : Assignment(left, new LiteralExpression{right}) {}
+
+Assignment::Assignment(const std::string& left, AstNode* right)
+ : lhs_(left),
+ rhs_(right) {}
+
+void Assignment::Write(CodeWriter* to) const {
+ to->Write("%s = ", lhs_.c_str());
+ rhs_->Write(to);
+ to->Write(";\n");
+}
+
+MethodCall::MethodCall(const std::string& method_name,
+ const std::string& single_argument)
+ : MethodCall(method_name, ArgList{single_argument}) {}
+
+MethodCall::MethodCall(const std::string& method_name,
+ ArgList&& arg_list)
+ : method_name_(method_name),
+ arguments_{std::move(arg_list)} {}
+
+void MethodCall::Write(CodeWriter* to) const {
+ to->Write("%s", method_name_.c_str());
+ arguments_.Write(to);
+}
+
+IfStatement::IfStatement(AstNode* expression, bool invert_expression)
+ : expression_(expression),
+ invert_expression_(invert_expression) {}
+
+void IfStatement::Write(CodeWriter* to) const {
+ to->Write("if (%s", (invert_expression_) ? "!(" : "");
+ expression_->Write(to);
+ to->Write(")%s ", (invert_expression_) ? ")" : "");
+ on_true_.Write(to);
+
+ if (!on_false_.Empty()) {
+ to->Write("else ");
+ on_false_.Write(to);
+ }
+}
+
+Statement::Statement(unique_ptr<AstNode> expression)
+ : expression_(std::move(expression)) {}
+
+Statement::Statement(AstNode* expression) : expression_(expression) {}
+
+Statement::Statement(const string& expression)
+ : expression_(new LiteralExpression(expression)) {}
+
+void Statement::Write(CodeWriter* to) const {
+ expression_->Write(to);
+ to->Write(";\n");
+}
+
+Comparison::Comparison(AstNode* lhs, const string& comparison, AstNode* rhs)
+ : left_(lhs),
+ right_(rhs),
+ operator_(comparison) {}
+
+void Comparison::Write(CodeWriter* to) const {
+ to->Write("((");
+ left_->Write(to);
+ to->Write(") %s (", operator_.c_str());
+ right_->Write(to);
+ to->Write("))");
+}
+
+LiteralExpression::LiteralExpression(const std::string& expression)
+ : expression_(expression) {}
+
+void LiteralExpression::Write(CodeWriter* to) const {
+ to->Write("%s", expression_.c_str());
+}
+
+CppNamespace::CppNamespace(const std::string& name,
+ std::vector<unique_ptr<Declaration>> declarations)
+ : declarations_(std::move(declarations)),
+ name_(name) {}
+
+CppNamespace::CppNamespace(const std::string& name,
+ unique_ptr<Declaration> declaration)
+ : name_(name) {
+ declarations_.push_back(std::move(declaration));
+}
+CppNamespace::CppNamespace(const std::string& name)
+ : name_(name) {}
+
+void CppNamespace::Write(CodeWriter* to) const {
+ to->Write("namespace %s {\n\n", name_.c_str());
+
+ for (const auto& dec : declarations_) {
+ dec->Write(to);
+ to->Write("\n");
+ }
+
+ to->Write("} // namespace %s\n", name_.c_str());
+}
+
+Document::Document(const std::vector<std::string>& include_list,
+ unique_ptr<CppNamespace> a_namespace)
+ : include_list_(include_list),
+ namespace_(std::move(a_namespace)) {}
+
+void Document::Write(CodeWriter* to) const {
+ for (const auto& include : include_list_) {
+ to->Write("#include <%s>\n", include.c_str());
+ }
+ to->Write("\n");
+
+ namespace_->Write(to);
+}
+
+CppHeader::CppHeader(const std::string& include_guard,
+ const std::vector<std::string>& include_list,
+ unique_ptr<CppNamespace> a_namespace)
+ : Document(include_list, std::move(a_namespace)),
+ include_guard_(include_guard) {}
+
+void CppHeader::Write(CodeWriter* to) const {
+ to->Write("#ifndef %s\n", include_guard_.c_str());
+ to->Write("#define %s\n\n", include_guard_.c_str());
+
+ Document::Write(to);
+ to->Write("\n");
+
+ to->Write("#endif // %s\n", include_guard_.c_str());
+}
+
+CppSource::CppSource(const std::vector<std::string>& include_list,
+ unique_ptr<CppNamespace> a_namespace)
+ : Document(include_list, std::move(a_namespace)) {}
+
+} // namespace cpp
+} // namespace aidl
+} // namespace android
diff --git a/aidl/ast_cpp.h b/aidl/ast_cpp.h
new file mode 100644
index 0000000..bb251ec
--- /dev/null
+++ b/aidl/ast_cpp.h
@@ -0,0 +1,420 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_AST_CPP_H_
+#define AIDL_AST_CPP_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace aidl {
+class CodeWriter;
+} // namespace aidl
+} // namespace android
+
+namespace android {
+namespace aidl {
+namespace cpp {
+
+class AstNode {
+ public:
+ AstNode() = default;
+ virtual ~AstNode() = default;
+ virtual void Write(CodeWriter* to) const = 0;
+}; // class AstNode
+
+class Declaration : public AstNode {
+ public:
+ Declaration() = default;
+ virtual ~Declaration() = default;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Declaration);
+}; // class Declaration
+
+class ClassDecl : public Declaration {
+ public:
+ ClassDecl(const std::string& name,
+ const std::string& parent);
+ ClassDecl(const std::string& name,
+ const std::string& parent,
+ std::vector<std::unique_ptr<Declaration>> public_members,
+ std::vector<std::unique_ptr<Declaration>> private_members);
+ virtual ~ClassDecl() = default;
+
+ void Write(CodeWriter* to) const override;
+
+ void AddPublic(std::unique_ptr<Declaration> member);
+ void AddPrivate(std::unique_ptr<Declaration> member);
+
+ private:
+ std::string name_;
+ std::string parent_;
+ std::vector<std::unique_ptr<Declaration>> public_members_;
+ std::vector<std::unique_ptr<Declaration>> private_members_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClassDecl);
+}; // class ClassDecl
+
+class Enum : public Declaration {
+ public:
+ Enum(const std::string& name, const std::string& base_type);
+ explicit Enum(const std::string& name);
+ virtual ~Enum() = default;
+
+ bool HasValues() const { return !fields_.empty(); }
+ void Write(CodeWriter* to) const override;
+
+ void AddValue(const std::string& key, const std::string& value);
+
+ private:
+ struct EnumField {
+ EnumField(const std::string& k, const std::string& v);
+ const std::string key;
+ const std::string value;
+ };
+
+ std::string enum_name_;
+ std::string underlying_type_;
+ std::vector<EnumField> fields_;
+
+ DISALLOW_COPY_AND_ASSIGN(Enum);
+}; // class Enum
+
+class ArgList : public AstNode {
+ public:
+ ArgList() = default;
+ explicit ArgList(const std::string& single_argument);
+ explicit ArgList(const std::vector<std::string>& arg_list);
+ explicit ArgList(std::vector<std::unique_ptr<AstNode>> arg_list);
+ ArgList(ArgList&& arg_list);
+ virtual ~ArgList() = default;
+
+ void Write(CodeWriter* to) const override;
+
+ private:
+ std::vector<std::unique_ptr<AstNode>> arguments_;
+
+ DISALLOW_COPY_AND_ASSIGN(ArgList);
+}; // class ArgList
+
+class ConstructorDecl : public Declaration {
+ public:
+ enum Modifiers {
+ IS_VIRTUAL = 1 << 0,
+ IS_DEFAULT = 1 << 1,
+ IS_EXPLICIT = 1 << 2,
+ };
+
+ ConstructorDecl(const std::string& name,
+ ArgList&& arg_list);
+ ConstructorDecl(const std::string& name,
+ ArgList&& arg_list,
+ uint32_t modifiers);
+
+ virtual ~ConstructorDecl() = default;
+
+ void Write(CodeWriter* to) const override;
+
+ private:
+ const std::string name_;
+ const ArgList arguments_;
+ const uint32_t modifiers_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConstructorDecl);
+}; // class ConstructorDecl
+
+class MacroDecl : public Declaration {
+ public:
+ MacroDecl(const std::string& name, ArgList&& arg_list);
+ virtual ~MacroDecl() = default;
+
+ void Write(CodeWriter* to) const override;
+
+ private:
+ const std::string name_;
+ const ArgList arguments_;
+
+ DISALLOW_COPY_AND_ASSIGN(MacroDecl);
+}; // class MacroDecl
+
+class MethodDecl : public Declaration {
+ public:
+ enum Modifiers {
+ IS_CONST = 1 << 0,
+ IS_VIRTUAL = 1 << 1,
+ IS_OVERRIDE = 1 << 2,
+ IS_PURE_VIRTUAL = 1 << 3,
+ IS_STATIC = 1 << 4,
+ };
+
+ MethodDecl(const std::string& return_type,
+ const std::string& name,
+ ArgList&& arg_list);
+ MethodDecl(const std::string& return_type,
+ const std::string& name,
+ ArgList&& arg_list,
+ uint32_t modifiers);
+ virtual ~MethodDecl() = default;
+
+ void Write(CodeWriter* to) const override;
+
+ private:
+ const std::string return_type_;
+ const std::string name_;
+ const ArgList arguments_;
+ bool is_const_ = false;
+ bool is_virtual_ = false;
+ bool is_override_ = false;
+ bool is_pure_virtual_ = false;
+ bool is_static_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(MethodDecl);
+}; // class MethodDecl
+
+class StatementBlock : public Declaration {
+ public:
+ StatementBlock() = default;
+ virtual ~StatementBlock() = default;
+
+ void AddStatement(std::unique_ptr<AstNode> statement);
+ void AddStatement(AstNode* statement); // Takes ownership
+ void AddLiteral(const std::string& expression, bool add_semicolon = true);
+ bool Empty() const { return statements_.empty(); }
+
+ void Write(CodeWriter* to) const override;
+
+ private:
+ std::vector<std::unique_ptr<AstNode>> statements_;
+
+ DISALLOW_COPY_AND_ASSIGN(StatementBlock);
+}; // class StatementBlock
+
+class ConstructorImpl : public Declaration {
+ public:
+ ConstructorImpl(const std::string& class_name,
+ ArgList&& arg_list,
+ const std::vector<std::string>& initializer_list);
+ virtual ~ConstructorImpl() = default;
+
+ void Write(CodeWriter* to) const override;
+
+ private:
+ std::string class_name_;
+ ArgList arguments_;
+ std::vector<std::string> initializer_list_;
+ StatementBlock body_;
+
+ DISALLOW_COPY_AND_ASSIGN(ConstructorImpl);
+}; // class ConstructorImpl
+
+class MethodImpl : public Declaration {
+ public:
+ // Passing an empty class name causes the method to be declared as a normal
+ // function (ie. no ClassName:: qualifier).
+ MethodImpl(const std::string& return_type,
+ const std::string& class_name,
+ const std::string& method_name,
+ ArgList&& arg_list,
+ bool is_const_method = false);
+ virtual ~MethodImpl() = default;
+
+ // MethodImpl retains ownership of the statement block.
+ StatementBlock* GetStatementBlock();
+
+ void Write(CodeWriter* to) const override;
+
+ private:
+ std::string return_type_;
+ std::string method_name_;
+ const ArgList arguments_;
+ StatementBlock statements_;
+ bool is_const_method_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(MethodImpl);
+}; // class MethodImpl
+
+class SwitchStatement : public AstNode {
+ public:
+ explicit SwitchStatement(const std::string& expression);
+ virtual ~SwitchStatement() = default;
+
+ // Add a case statement and return a pointer code block corresponding
+ // to the case. The switch statement will add a break statement
+ // after the code block by default to prevent accidental fall-through.
+ // Returns nullptr on duplicate value expressions (by strcmp, not value
+ // equivalence).
+ StatementBlock* AddCase(const std::string& value_expression);
+ void Write(CodeWriter* to) const override;
+
+ private:
+ const std::string switch_expression_;
+ std::vector<std::string> case_values_;
+ std::vector<std::unique_ptr<StatementBlock>> case_logic_;
+
+ DISALLOW_COPY_AND_ASSIGN(SwitchStatement);
+}; // class SwitchStatement
+
+class Assignment : public AstNode {
+ public:
+ Assignment(const std::string& left, const std::string& right);
+ Assignment(const std::string& left, AstNode* right);
+ ~Assignment() = default;
+ void Write(CodeWriter* to) const override;
+
+ private:
+ const std::string lhs_;
+ std::unique_ptr<AstNode> rhs_;
+
+ DISALLOW_COPY_AND_ASSIGN(Assignment);
+}; // class Assignment
+
+class MethodCall : public AstNode {
+ public:
+ MethodCall(const std::string& method_name,
+ const std::string& single_argument);
+ MethodCall(const std::string& method_name, ArgList&& arg_list);
+ ~MethodCall() = default;
+ void Write(CodeWriter* to) const override;
+
+ private:
+ const std::string method_name_;
+ const ArgList arguments_;
+
+ DISALLOW_COPY_AND_ASSIGN(MethodCall);
+}; // class MethodCall
+
+class IfStatement : public AstNode {
+ public:
+ explicit IfStatement(AstNode* expression,
+ bool invert_expression = false);
+ virtual ~IfStatement() = default;
+ StatementBlock* OnTrue() { return &on_true_; }
+ StatementBlock* OnFalse() { return &on_false_; }
+ void Write(CodeWriter* to) const override;
+
+ private:
+ std::unique_ptr<AstNode> expression_;
+ bool invert_expression_ = false;
+ StatementBlock on_true_;
+ StatementBlock on_false_;
+
+ DISALLOW_COPY_AND_ASSIGN(IfStatement);
+}; // class IfStatement
+
+class Statement : public AstNode {
+ public:
+ explicit Statement(std::unique_ptr<AstNode> expression);
+ explicit Statement(AstNode* expression); // Takes possession.
+ explicit Statement(const std::string& expression);
+ ~Statement() = default;
+ void Write(CodeWriter* to) const override;
+
+ private:
+ std::unique_ptr<AstNode> expression_;
+
+ DISALLOW_COPY_AND_ASSIGN(Statement);
+}; // class Statement
+
+class Comparison : public AstNode {
+ public:
+ Comparison(AstNode* lhs, const std::string& comparison, AstNode* rhs);
+ ~Comparison() = default;
+ void Write(CodeWriter* to) const override;
+
+ private:
+ std::unique_ptr<AstNode> left_;
+ std::unique_ptr<AstNode> right_;
+ const std::string operator_;
+
+ DISALLOW_COPY_AND_ASSIGN(Comparison);
+}; // class Comparison
+
+class LiteralExpression : public AstNode {
+ public:
+ explicit LiteralExpression(const std::string& expression);
+ ~LiteralExpression() = default;
+ void Write(CodeWriter* to) const override;
+
+ private:
+ const std::string expression_;
+
+ DISALLOW_COPY_AND_ASSIGN(LiteralExpression);
+}; // class LiteralExpression
+
+class CppNamespace : public Declaration {
+ public:
+ CppNamespace(const std::string& name,
+ std::vector<std::unique_ptr<Declaration>> declarations);
+ CppNamespace(const std::string& name,
+ std::unique_ptr<Declaration> declaration);
+ explicit CppNamespace(const std::string& name);
+ virtual ~CppNamespace() = default;
+
+ void Write(CodeWriter* to) const override;
+
+ private:
+ std::vector<std::unique_ptr<Declaration>> declarations_;
+ std::string name_;
+
+ DISALLOW_COPY_AND_ASSIGN(CppNamespace);
+}; // class CppNamespace
+
+class Document : public AstNode {
+ public:
+ Document(const std::vector<std::string>& include_list,
+ std::unique_ptr<CppNamespace> a_namespace);
+
+ void Write(CodeWriter* to) const override;
+
+ private:
+ std::vector<std::string> include_list_;
+ std::unique_ptr<CppNamespace> namespace_;
+
+ DISALLOW_COPY_AND_ASSIGN(Document);
+}; // class Document
+
+class CppHeader final : public Document {
+ public:
+ CppHeader(const std::string& include_guard,
+ const std::vector<std::string>& include_list,
+ std::unique_ptr<CppNamespace> a_namespace);
+ void Write(CodeWriter* to) const override;
+
+ private:
+ const std::string include_guard_;
+
+ DISALLOW_COPY_AND_ASSIGN(CppHeader);
+}; // class CppHeader
+
+class CppSource final : public Document {
+ public:
+ CppSource(const std::vector<std::string>& include_list,
+ std::unique_ptr<CppNamespace> a_namespace);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CppSource);
+}; // class CppSource
+
+} // namespace cpp
+} // namespace aidl
+} // namespace android
+
+#endif // AIDL_AST_CPP_H_
diff --git a/aidl/ast_cpp_unittest.cpp b/aidl/ast_cpp_unittest.cpp
new file mode 100644
index 0000000..aee23b7
--- /dev/null
+++ b/aidl/ast_cpp_unittest.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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 <string>
+
+#include <gtest/gtest.h>
+
+#include "ast_cpp.h"
+#include "code_writer.h"
+
+using std::string;
+using std::vector;
+using std::unique_ptr;
+
+namespace android {
+namespace aidl {
+namespace cpp {
+namespace {
+
+const char kExpectedHeaderOutput[] =
+R"(#ifndef HEADER_INCLUDE_GUARD_H_
+#define HEADER_INCLUDE_GUARD_H_
+
+#include <string>
+#include <memory>
+
+namespace android {
+
+namespace test {
+
+class TestClass {
+public:
+void NormalMethod(int normalarg, float normal2);
+virtual void SubMethod(int subarg) const;
+}; // class TestClass
+
+class TestSubClass : public TestClass {
+public:
+virtual void SubMethod(int subarg) const;
+}; // class TestSubClass
+
+} // namespace test
+
+} // namespace android
+
+#endif // HEADER_INCLUDE_GUARD_H_
+)";
+
+const char kExpectedEnumOutput[] =
+R"(enum Foo {
+ BAR = 42,
+ BAZ,
+};
+)";
+
+const char kExpectedSwitchOutput[] =
+R"(switch (var) {
+case 2:
+{
+baz;
+}
+break;
+case 1:
+{
+foo;
+bar;
+}
+break;
+}
+)";
+
+const char kExpectedMethodImplOutput[] =
+R"(return_type ClassName::MethodName(arg 1, arg 2, arg 3) const {
+foo;
+bar;
+}
+)";
+} // namespace
+
+class AstCppTests : public ::testing::Test {
+ protected:
+ void CompareGeneratedCode(const AstNode& node,
+ const string& expected_output) {
+ string actual_output;
+ CodeWriterPtr writer = GetStringWriter(&actual_output);
+ node.Write(writer.get());
+ EXPECT_EQ(expected_output, actual_output);
+ }
+}; // class AstCppTests
+
+
+TEST_F(AstCppTests, GeneratesHeader) {
+ unique_ptr<MethodDecl> norm{new MethodDecl(
+ "void", "NormalMethod",
+ ArgList{vector<string>{"int normalarg", "float normal2"}})};
+ unique_ptr<MethodDecl> sub{
+ new MethodDecl("void", "SubMethod",
+ ArgList{ "int subarg" },
+ MethodDecl::IS_CONST | MethodDecl::IS_VIRTUAL)};
+ unique_ptr<MethodDecl> sub2{
+ new MethodDecl("void", "SubMethod",
+ ArgList{ "int subarg" },
+ MethodDecl::IS_CONST | MethodDecl::IS_VIRTUAL)};
+ vector<unique_ptr<Declaration>> test_methods;
+ test_methods.push_back(std::move(norm));
+ test_methods.push_back(std::move(sub));
+
+ vector<unique_ptr<Declaration>> test_sub_methods;
+ test_sub_methods.push_back(std::move(sub2));
+
+ unique_ptr<Declaration> test{new ClassDecl { "TestClass", "",
+ std::move(test_methods), {} }};
+
+ unique_ptr<Declaration> test_sub{new ClassDecl { "TestSubClass",
+ "TestClass", std::move(test_sub_methods), {} }};
+
+ vector<unique_ptr<Declaration>> classes;
+ classes.push_back(std::move(test));
+ classes.push_back(std::move(test_sub));
+
+ unique_ptr<CppNamespace> test_ns{new CppNamespace {"test",
+ std::move(classes)}};
+
+ vector<unique_ptr<Declaration>> test_ns_vec;
+ test_ns_vec.push_back(std::move(test_ns));
+
+ unique_ptr<CppNamespace> android_ns{new CppNamespace {"android",
+ std::move(test_ns_vec) }};
+
+ CppHeader cpp_header{"HEADER_INCLUDE_GUARD_H_", {"string", "memory"},
+ std::move(android_ns) };
+ CompareGeneratedCode(cpp_header, kExpectedHeaderOutput);
+}
+
+TEST_F(AstCppTests, GeneratesEnum) {
+ Enum e("Foo");
+ e.AddValue("BAR", "42");
+ e.AddValue("BAZ", "");
+ CompareGeneratedCode(e, kExpectedEnumOutput);
+}
+
+TEST_F(AstCppTests, GeneratesArgList) {
+ ArgList simple("foo");
+ CompareGeneratedCode(simple, "(foo)");
+ ArgList compound({"foo", "bar", "baz"});
+ CompareGeneratedCode(compound, "(foo, bar, baz)");
+ std::vector<unique_ptr<AstNode>> args;
+ args.emplace_back(new LiteralExpression("foo()"));
+ ArgList nested(std::move(args));
+ CompareGeneratedCode(nested, "(foo())");
+}
+
+TEST_F(AstCppTests, GeneratesStatement) {
+ Statement s(new LiteralExpression("foo"));
+ CompareGeneratedCode(s, "foo;\n");
+}
+
+TEST_F(AstCppTests, GeneratesComparison) {
+ Comparison c(
+ new LiteralExpression("lhs"), "&&", new LiteralExpression("rhs"));
+ CompareGeneratedCode(c, "((lhs) && (rhs))");
+}
+
+TEST_F(AstCppTests, GeneratesStatementBlock) {
+ StatementBlock block;
+ block.AddStatement(unique_ptr<AstNode>(new Statement("foo")));
+ block.AddStatement(unique_ptr<AstNode>(new Statement("bar")));
+ CompareGeneratedCode(block, "{\nfoo;\nbar;\n}\n");
+}
+
+TEST_F(AstCppTests, GeneratesConstructorImpl) {
+ ConstructorImpl c("ClassName", ArgList({"a", "b", "c"}),
+ {"baz_(foo)", "bar_(blah)"});
+ string expected = R"(ClassName::ClassName(a, b, c)
+ : baz_(foo),
+ bar_(blah){
+}
+)";
+ CompareGeneratedCode(c, expected);
+}
+
+TEST_F(AstCppTests, GeneratesAssignment) {
+ Assignment simple("foo", "8");
+ CompareGeneratedCode(simple, "foo = 8;\n");
+ Assignment less_simple("foo", new MethodCall("f", "8"));
+ CompareGeneratedCode(less_simple, "foo = f(8);\n");
+}
+
+TEST_F(AstCppTests, GeneratesMethodCall) {
+ MethodCall single("single", "arg");
+ CompareGeneratedCode(single, "single(arg)");
+ MethodCall multi(
+ "multi",
+ ArgList({"has", "some", "args"}));
+ CompareGeneratedCode(multi, "multi(has, some, args)");
+}
+
+TEST_F(AstCppTests, GeneratesIfStatement) {
+ IfStatement s(new LiteralExpression("foo"));
+ s.OnTrue()->AddLiteral("on true1");
+ s.OnFalse()->AddLiteral("on false");
+ CompareGeneratedCode(s, "if (foo) {\non true1;\n}\nelse {\non false;\n}\n");
+
+ IfStatement s2(new LiteralExpression("bar"));
+ s2.OnTrue()->AddLiteral("on true1");
+ CompareGeneratedCode(s2, "if (bar) {\non true1;\n}\n");
+}
+
+TEST_F(AstCppTests, GeneratesSwitchStatement) {
+ SwitchStatement s("var");
+ // These are intentionally out of alphanumeric order. We're testing
+ // that switch respects case addition order.
+ auto case2 = s.AddCase("2");
+ case2->AddStatement(unique_ptr<AstNode>{new Statement{"baz"}});
+ auto case1 = s.AddCase("1");
+ case1->AddStatement(unique_ptr<AstNode>{new Statement{"foo"}});
+ case1->AddStatement(unique_ptr<AstNode>{new Statement{"bar"}});
+ CompareGeneratedCode(s, kExpectedSwitchOutput);
+}
+
+TEST_F(AstCppTests, GeneratesMethodImpl) {
+ MethodImpl m{"return_type", "ClassName", "MethodName",
+ ArgList{{"arg 1", "arg 2", "arg 3"}},
+ true};
+ auto b = m.GetStatementBlock();
+ b->AddLiteral("foo");
+ b->AddLiteral("bar");
+ CompareGeneratedCode(m, kExpectedMethodImplOutput);
+}
+
+} // namespace cpp
+} // namespace aidl
+} // namespace android
diff --git a/aidl/ast_java.cpp b/aidl/ast_java.cpp
new file mode 100644
index 0000000..1ba723c
--- /dev/null
+++ b/aidl/ast_java.cpp
@@ -0,0 +1,525 @@
+/*
+ * 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 "ast_java.h"
+
+#include "code_writer.h"
+#include "type_java.h"
+
+using std::vector;
+using std::string;
+
+namespace android {
+namespace aidl {
+namespace java {
+
+void WriteModifiers(CodeWriter* to, int mod, int mask) {
+ int m = mod & mask;
+
+ if (m & OVERRIDE) {
+ to->Write("@Override ");
+ }
+
+ if ((m & SCOPE_MASK) == PUBLIC) {
+ to->Write("public ");
+ } else if ((m & SCOPE_MASK) == PRIVATE) {
+ to->Write("private ");
+ } else if ((m & SCOPE_MASK) == PROTECTED) {
+ to->Write("protected ");
+ }
+
+ if (m & STATIC) {
+ to->Write("static ");
+ }
+
+ if (m & FINAL) {
+ to->Write("final ");
+ }
+
+ if (m & ABSTRACT) {
+ to->Write("abstract ");
+ }
+}
+
+void WriteArgumentList(CodeWriter* to, const vector<Expression*>& arguments) {
+ size_t N = arguments.size();
+ for (size_t i = 0; i < N; i++) {
+ arguments[i]->Write(to);
+ if (i != N - 1) {
+ to->Write(", ");
+ }
+ }
+}
+
+Field::Field(int m, Variable* v) : ClassElement(), modifiers(m), variable(v) {}
+
+void Field::Write(CodeWriter* to) const {
+ if (this->comment.length() != 0) {
+ to->Write("%s\n", this->comment.c_str());
+ }
+ WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | FINAL | OVERRIDE);
+ to->Write("%s %s", this->variable->type->JavaType().c_str(),
+ this->variable->name.c_str());
+ if (this->value.length() != 0) {
+ to->Write(" = %s", this->value.c_str());
+ }
+ to->Write(";\n");
+}
+
+LiteralExpression::LiteralExpression(const string& v) : value(v) {}
+
+void LiteralExpression::Write(CodeWriter* to) const {
+ to->Write("%s", this->value.c_str());
+}
+
+StringLiteralExpression::StringLiteralExpression(const string& v) : value(v) {}
+
+void StringLiteralExpression::Write(CodeWriter* to) const {
+ to->Write("\"%s\"", this->value.c_str());
+}
+
+Variable::Variable(const Type* t, const string& n)
+ : type(t), name(n), dimension(0) {}
+
+Variable::Variable(const Type* t, const string& n, int d)
+ : type(t), name(n), dimension(d) {}
+
+void Variable::WriteDeclaration(CodeWriter* to) const {
+ string dim;
+ for (int i = 0; i < this->dimension; i++) {
+ dim += "[]";
+ }
+ to->Write("%s%s %s", this->type->JavaType().c_str(), dim.c_str(),
+ this->name.c_str());
+}
+
+void Variable::Write(CodeWriter* to) const { to->Write("%s", name.c_str()); }
+
+FieldVariable::FieldVariable(Expression* o, const string& n)
+ : object(o), clazz(NULL), name(n) {}
+
+FieldVariable::FieldVariable(const Type* c, const string& n)
+ : object(NULL), clazz(c), name(n) {}
+
+void FieldVariable::Write(CodeWriter* to) const {
+ if (this->object != NULL) {
+ this->object->Write(to);
+ } else if (this->clazz != NULL) {
+ to->Write("%s", this->clazz->JavaType().c_str());
+ }
+ to->Write(".%s", name.c_str());
+}
+
+void StatementBlock::Write(CodeWriter* to) const {
+ to->Write("{\n");
+ int N = this->statements.size();
+ for (int i = 0; i < N; i++) {
+ this->statements[i]->Write(to);
+ }
+ to->Write("}\n");
+}
+
+void StatementBlock::Add(Statement* statement) {
+ this->statements.push_back(statement);
+}
+
+void StatementBlock::Add(Expression* expression) {
+ this->statements.push_back(new ExpressionStatement(expression));
+}
+
+ExpressionStatement::ExpressionStatement(Expression* e) : expression(e) {}
+
+void ExpressionStatement::Write(CodeWriter* to) const {
+ this->expression->Write(to);
+ to->Write(";\n");
+}
+
+Assignment::Assignment(Variable* l, Expression* r)
+ : lvalue(l), rvalue(r), cast(NULL) {}
+
+Assignment::Assignment(Variable* l, Expression* r, const Type* c)
+ : lvalue(l), rvalue(r), cast(c) {}
+
+void Assignment::Write(CodeWriter* to) const {
+ this->lvalue->Write(to);
+ to->Write(" = ");
+ if (this->cast != NULL) {
+ to->Write("(%s)", this->cast->JavaType().c_str());
+ }
+ this->rvalue->Write(to);
+}
+
+MethodCall::MethodCall(const string& n) : name(n) {}
+
+MethodCall::MethodCall(const string& n, int argc = 0, ...) : name(n) {
+ va_list args;
+ va_start(args, argc);
+ init(argc, args);
+ va_end(args);
+}
+
+MethodCall::MethodCall(Expression* o, const string& n) : obj(o), name(n) {}
+
+MethodCall::MethodCall(const Type* t, const string& n) : clazz(t), name(n) {}
+
+MethodCall::MethodCall(Expression* o, const string& n, int argc = 0, ...)
+ : obj(o), name(n) {
+ va_list args;
+ va_start(args, argc);
+ init(argc, args);
+ va_end(args);
+}
+
+MethodCall::MethodCall(const Type* t, const string& n, int argc = 0, ...)
+ : clazz(t), name(n) {
+ va_list args;
+ va_start(args, argc);
+ init(argc, args);
+ va_end(args);
+}
+
+void MethodCall::init(int n, va_list args) {
+ for (int i = 0; i < n; i++) {
+ Expression* expression = (Expression*)va_arg(args, void*);
+ this->arguments.push_back(expression);
+ }
+}
+
+void MethodCall::Write(CodeWriter* to) const {
+ if (this->obj != NULL) {
+ this->obj->Write(to);
+ to->Write(".");
+ } else if (this->clazz != NULL) {
+ to->Write("%s.", this->clazz->JavaType().c_str());
+ }
+ to->Write("%s(", this->name.c_str());
+ WriteArgumentList(to, this->arguments);
+ to->Write(")");
+}
+
+Comparison::Comparison(Expression* l, const string& o, Expression* r)
+ : lvalue(l), op(o), rvalue(r) {}
+
+void Comparison::Write(CodeWriter* to) const {
+ to->Write("(");
+ this->lvalue->Write(to);
+ to->Write("%s", this->op.c_str());
+ this->rvalue->Write(to);
+ to->Write(")");
+}
+
+NewExpression::NewExpression(const Type* t) : type(t) {}
+
+NewExpression::NewExpression(const Type* t, int argc = 0, ...) : type(t) {
+ va_list args;
+ va_start(args, argc);
+ init(argc, args);
+ va_end(args);
+}
+
+void NewExpression::init(int n, va_list args) {
+ for (int i = 0; i < n; i++) {
+ Expression* expression = (Expression*)va_arg(args, void*);
+ this->arguments.push_back(expression);
+ }
+}
+
+void NewExpression::Write(CodeWriter* to) const {
+ to->Write("new %s(", this->type->InstantiableName().c_str());
+ WriteArgumentList(to, this->arguments);
+ to->Write(")");
+}
+
+NewArrayExpression::NewArrayExpression(const Type* t, Expression* s)
+ : type(t), size(s) {}
+
+void NewArrayExpression::Write(CodeWriter* to) const {
+ to->Write("new %s[", this->type->JavaType().c_str());
+ size->Write(to);
+ to->Write("]");
+}
+
+Ternary::Ternary(Expression* a, Expression* b, Expression* c)
+ : condition(a), ifpart(b), elsepart(c) {}
+
+void Ternary::Write(CodeWriter* to) const {
+ to->Write("((");
+ this->condition->Write(to);
+ to->Write(")?(");
+ this->ifpart->Write(to);
+ to->Write("):(");
+ this->elsepart->Write(to);
+ to->Write("))");
+}
+
+Cast::Cast(const Type* t, Expression* e) : type(t), expression(e) {}
+
+void Cast::Write(CodeWriter* to) const {
+ to->Write("((%s)", this->type->JavaType().c_str());
+ expression->Write(to);
+ to->Write(")");
+}
+
+VariableDeclaration::VariableDeclaration(Variable* l, Expression* r,
+ const Type* c)
+ : lvalue(l), cast(c), rvalue(r) {}
+
+VariableDeclaration::VariableDeclaration(Variable* l) : lvalue(l) {}
+
+void VariableDeclaration::Write(CodeWriter* to) const {
+ this->lvalue->WriteDeclaration(to);
+ if (this->rvalue != NULL) {
+ to->Write(" = ");
+ if (this->cast != NULL) {
+ to->Write("(%s)", this->cast->JavaType().c_str());
+ }
+ this->rvalue->Write(to);
+ }
+ to->Write(";\n");
+}
+
+void IfStatement::Write(CodeWriter* to) const {
+ if (this->expression != NULL) {
+ to->Write("if (");
+ this->expression->Write(to);
+ to->Write(") ");
+ }
+ this->statements->Write(to);
+ if (this->elseif != NULL) {
+ to->Write("else ");
+ this->elseif->Write(to);
+ }
+}
+
+ReturnStatement::ReturnStatement(Expression* e) : expression(e) {}
+
+void ReturnStatement::Write(CodeWriter* to) const {
+ to->Write("return ");
+ this->expression->Write(to);
+ to->Write(";\n");
+}
+
+void TryStatement::Write(CodeWriter* to) const {
+ to->Write("try ");
+ this->statements->Write(to);
+}
+
+CatchStatement::CatchStatement(Variable* e)
+ : statements(new StatementBlock), exception(e) {}
+
+void CatchStatement::Write(CodeWriter* to) const {
+ to->Write("catch ");
+ if (this->exception != NULL) {
+ to->Write("(");
+ this->exception->WriteDeclaration(to);
+ to->Write(") ");
+ }
+ this->statements->Write(to);
+}
+
+void FinallyStatement::Write(CodeWriter* to) const {
+ to->Write("finally ");
+ this->statements->Write(to);
+}
+
+Case::Case(const string& c) { cases.push_back(c); }
+
+void Case::Write(CodeWriter* to) const {
+ int N = this->cases.size();
+ if (N > 0) {
+ for (int i = 0; i < N; i++) {
+ string s = this->cases[i];
+ if (s.length() != 0) {
+ to->Write("case %s:\n", s.c_str());
+ } else {
+ to->Write("default:\n");
+ }
+ }
+ } else {
+ to->Write("default:\n");
+ }
+ statements->Write(to);
+}
+
+SwitchStatement::SwitchStatement(Expression* e) : expression(e) {}
+
+void SwitchStatement::Write(CodeWriter* to) const {
+ to->Write("switch (");
+ this->expression->Write(to);
+ to->Write(")\n{\n");
+ int N = this->cases.size();
+ for (int i = 0; i < N; i++) {
+ this->cases[i]->Write(to);
+ }
+ to->Write("}\n");
+}
+
+void Break::Write(CodeWriter* to) const { to->Write("break;\n"); }
+
+void Method::Write(CodeWriter* to) const {
+ size_t N, i;
+
+ if (this->comment.length() != 0) {
+ to->Write("%s\n", this->comment.c_str());
+ }
+
+ WriteModifiers(to, this->modifiers,
+ SCOPE_MASK | STATIC | ABSTRACT | FINAL | OVERRIDE);
+
+ if (this->returnType != NULL) {
+ string dim;
+ for (i = 0; i < this->returnTypeDimension; i++) {
+ dim += "[]";
+ }
+ to->Write("%s%s ", this->returnType->JavaType().c_str(), dim.c_str());
+ }
+
+ to->Write("%s(", this->name.c_str());
+
+ N = this->parameters.size();
+ for (i = 0; i < N; i++) {
+ this->parameters[i]->WriteDeclaration(to);
+ if (i != N - 1) {
+ to->Write(", ");
+ }
+ }
+
+ to->Write(")");
+
+ N = this->exceptions.size();
+ for (i = 0; i < N; i++) {
+ if (i == 0) {
+ to->Write(" throws ");
+ } else {
+ to->Write(", ");
+ }
+ to->Write("%s", this->exceptions[i]->JavaType().c_str());
+ }
+
+ if (this->statements == NULL) {
+ to->Write(";\n");
+ } else {
+ to->Write("\n");
+ this->statements->Write(to);
+ }
+}
+
+void IntConstant::Write(CodeWriter* to) const {
+ WriteModifiers(to, STATIC | FINAL | PUBLIC, ALL_MODIFIERS);
+ to->Write("int %s = %d;\n", name.c_str(), value);
+}
+
+void StringConstant::Write(CodeWriter* to) const {
+ WriteModifiers(to, STATIC | FINAL | PUBLIC, ALL_MODIFIERS);
+ to->Write("String %s = %s;\n", name.c_str(), value.c_str());
+}
+
+void Class::Write(CodeWriter* to) const {
+ size_t N, i;
+
+ if (this->comment.length() != 0) {
+ to->Write("%s\n", this->comment.c_str());
+ }
+
+ WriteModifiers(to, this->modifiers, ALL_MODIFIERS);
+
+ if (this->what == Class::CLASS) {
+ to->Write("class ");
+ } else {
+ to->Write("interface ");
+ }
+
+ string name = this->type->JavaType();
+ size_t pos = name.rfind('.');
+ if (pos != string::npos) {
+ name = name.c_str() + pos + 1;
+ }
+
+ to->Write("%s", name.c_str());
+
+ if (this->extends != NULL) {
+ to->Write(" extends %s", this->extends->JavaType().c_str());
+ }
+
+ N = this->interfaces.size();
+ if (N != 0) {
+ if (this->what == Class::CLASS) {
+ to->Write(" implements");
+ } else {
+ to->Write(" extends");
+ }
+ for (i = 0; i < N; i++) {
+ to->Write(" %s", this->interfaces[i]->JavaType().c_str());
+ }
+ }
+
+ to->Write("\n");
+ to->Write("{\n");
+
+ N = this->elements.size();
+ for (i = 0; i < N; i++) {
+ this->elements[i]->Write(to);
+ }
+
+ to->Write("}\n");
+}
+
+static string escape_backslashes(const string& str) {
+ string result;
+ const size_t I = str.length();
+ for (size_t i = 0; i < I; i++) {
+ char c = str[i];
+ if (c == '\\') {
+ result += "\\\\";
+ } else {
+ result += c;
+ }
+ }
+ return result;
+}
+
+Document::Document(const std::string& comment,
+ const std::string& package,
+ const std::string& original_src,
+ std::unique_ptr<Class> clazz)
+ : comment_(comment),
+ package_(package),
+ original_src_(original_src),
+ clazz_(std::move(clazz)) {
+}
+
+void Document::Write(CodeWriter* to) const {
+ if (!comment_.empty()) {
+ to->Write("%s\n", comment_.c_str());
+ }
+ to->Write(
+ "/*\n"
+ " * This file is auto-generated. DO NOT MODIFY.\n"
+ " * Original file: %s\n"
+ " */\n",
+ escape_backslashes(original_src_).c_str());
+ if (!package_.empty()) {
+ to->Write("package %s;\n", package_.c_str());
+ }
+
+ if (clazz_) {
+ clazz_->Write(to);
+ }
+}
+
+} // namespace java
+} // namespace aidl
+} // namespace android
diff --git a/aidl/ast_java.h b/aidl/ast_java.h
new file mode 100644
index 0000000..c5ded58
--- /dev/null
+++ b/aidl/ast_java.h
@@ -0,0 +1,388 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_AST_JAVA_H_
+#define AIDL_AST_JAVA_H_
+
+#include <memory>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+enum {
+ PACKAGE_PRIVATE = 0x00000000,
+ PUBLIC = 0x00000001,
+ PRIVATE = 0x00000002,
+ PROTECTED = 0x00000003,
+ SCOPE_MASK = 0x00000003,
+
+ STATIC = 0x00000010,
+ FINAL = 0x00000020,
+ ABSTRACT = 0x00000040,
+
+ OVERRIDE = 0x00000100,
+
+ ALL_MODIFIERS = 0xffffffff
+};
+
+namespace android {
+namespace aidl {
+class CodeWriter;
+} // namespace aidl
+} // namespace android
+
+namespace android {
+namespace aidl {
+namespace java {
+
+class Type;
+
+// Write the modifiers that are set in both mod and mask
+void WriteModifiers(CodeWriter* to, int mod, int mask);
+
+struct ClassElement {
+ ClassElement() = default;
+ virtual ~ClassElement() = default;
+
+ virtual void Write(CodeWriter* to) const = 0;
+};
+
+struct Expression {
+ virtual ~Expression() = default;
+ virtual void Write(CodeWriter* to) const = 0;
+};
+
+struct LiteralExpression : public Expression {
+ std::string value;
+
+ explicit LiteralExpression(const std::string& value);
+ virtual ~LiteralExpression() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+// TODO: also escape the contents. not needed for now
+struct StringLiteralExpression : public Expression {
+ std::string value;
+
+ explicit StringLiteralExpression(const std::string& value);
+ virtual ~StringLiteralExpression() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct Variable : public Expression {
+ const Type* type = nullptr;
+ std::string name;
+ int dimension = 0;
+
+ Variable() = default;
+ Variable(const Type* type, const std::string& name);
+ Variable(const Type* type, const std::string& name, int dimension);
+ virtual ~Variable() = default;
+
+ void WriteDeclaration(CodeWriter* to) const;
+ void Write(CodeWriter* to) const;
+};
+
+struct FieldVariable : public Expression {
+ Expression* object;
+ const Type* clazz;
+ std::string name;
+
+ FieldVariable(Expression* object, const std::string& name);
+ FieldVariable(const Type* clazz, const std::string& name);
+ virtual ~FieldVariable() = default;
+
+ void Write(CodeWriter* to) const;
+};
+
+struct Field : public ClassElement {
+ std::string comment;
+ int modifiers = 0;
+ Variable* variable = nullptr;
+ std::string value;
+
+ Field() = default;
+ Field(int modifiers, Variable* variable);
+ virtual ~Field() = default;
+
+ void Write(CodeWriter* to) const override;
+};
+
+struct Statement {
+ virtual ~Statement() = default;
+ virtual void Write(CodeWriter* to) const = 0;
+};
+
+struct StatementBlock : public Statement {
+ std::vector<Statement*> statements;
+
+ StatementBlock() = default;
+ virtual ~StatementBlock() = default;
+ void Write(CodeWriter* to) const override;
+
+ void Add(Statement* statement);
+ void Add(Expression* expression);
+};
+
+struct ExpressionStatement : public Statement {
+ Expression* expression;
+
+ explicit ExpressionStatement(Expression* expression);
+ virtual ~ExpressionStatement() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct Assignment : public Expression {
+ Variable* lvalue;
+ Expression* rvalue;
+ const Type* cast;
+
+ Assignment(Variable* lvalue, Expression* rvalue);
+ Assignment(Variable* lvalue, Expression* rvalue, const Type* cast);
+ virtual ~Assignment() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct MethodCall : public Expression {
+ Expression* obj = nullptr;
+ const Type* clazz = nullptr;
+ std::string name;
+ std::vector<Expression*> arguments;
+ std::vector<std::string> exceptions;
+
+ explicit MethodCall(const std::string& name);
+ MethodCall(const std::string& name, int argc, ...);
+ MethodCall(Expression* obj, const std::string& name);
+ MethodCall(const Type* clazz, const std::string& name);
+ MethodCall(Expression* obj, const std::string& name, int argc, ...);
+ MethodCall(const Type* clazz, const std::string& name, int argc, ...);
+ virtual ~MethodCall() = default;
+ void Write(CodeWriter* to) const override;
+
+ private:
+ void init(int n, va_list args);
+};
+
+struct Comparison : public Expression {
+ Expression* lvalue;
+ std::string op;
+ Expression* rvalue;
+
+ Comparison(Expression* lvalue, const std::string& op, Expression* rvalue);
+ virtual ~Comparison() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct NewExpression : public Expression {
+ const Type* type;
+ std::vector<Expression*> arguments;
+
+ explicit NewExpression(const Type* type);
+ NewExpression(const Type* type, int argc, ...);
+ virtual ~NewExpression() = default;
+ void Write(CodeWriter* to) const override;
+
+ private:
+ void init(int n, va_list args);
+};
+
+struct NewArrayExpression : public Expression {
+ const Type* type;
+ Expression* size;
+
+ NewArrayExpression(const Type* type, Expression* size);
+ virtual ~NewArrayExpression() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct Ternary : public Expression {
+ Expression* condition = nullptr;
+ Expression* ifpart = nullptr;
+ Expression* elsepart = nullptr;
+
+ Ternary() = default;
+ Ternary(Expression* condition, Expression* ifpart, Expression* elsepart);
+ virtual ~Ternary() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct Cast : public Expression {
+ const Type* type = nullptr;
+ Expression* expression = nullptr;
+
+ Cast() = default;
+ Cast(const Type* type, Expression* expression);
+ virtual ~Cast() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct VariableDeclaration : public Statement {
+ Variable* lvalue = nullptr;
+ const Type* cast = nullptr;
+ Expression* rvalue = nullptr;
+
+ explicit VariableDeclaration(Variable* lvalue);
+ VariableDeclaration(Variable* lvalue, Expression* rvalue,
+ const Type* cast = NULL);
+ virtual ~VariableDeclaration() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct IfStatement : public Statement {
+ Expression* expression = nullptr;
+ StatementBlock* statements = new StatementBlock;
+ IfStatement* elseif = nullptr;
+
+ IfStatement() = default;
+ virtual ~IfStatement() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct ReturnStatement : public Statement {
+ Expression* expression;
+
+ explicit ReturnStatement(Expression* expression);
+ virtual ~ReturnStatement() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct TryStatement : public Statement {
+ StatementBlock* statements = new StatementBlock;
+
+ TryStatement() = default;
+ virtual ~TryStatement() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct CatchStatement : public Statement {
+ StatementBlock* statements;
+ Variable* exception;
+
+ explicit CatchStatement(Variable* exception);
+ virtual ~CatchStatement() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct FinallyStatement : public Statement {
+ StatementBlock* statements = new StatementBlock;
+
+ FinallyStatement() = default;
+ virtual ~FinallyStatement() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct Case {
+ std::vector<std::string> cases;
+ StatementBlock* statements = new StatementBlock;
+
+ Case() = default;
+ explicit Case(const std::string& c);
+ virtual ~Case() = default;
+ virtual void Write(CodeWriter* to) const;
+};
+
+struct SwitchStatement : public Statement {
+ Expression* expression;
+ std::vector<Case*> cases;
+
+ explicit SwitchStatement(Expression* expression);
+ virtual ~SwitchStatement() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct Break : public Statement {
+ Break() = default;
+ virtual ~Break() = default;
+ void Write(CodeWriter* to) const override;
+};
+
+struct Method : public ClassElement {
+ std::string comment;
+ int modifiers = 0;
+ const Type* returnType = nullptr; // nullptr means constructor
+ size_t returnTypeDimension = 0;
+ std::string name;
+ std::vector<Variable*> parameters;
+ std::vector<const Type*> exceptions;
+ StatementBlock* statements = nullptr;
+
+ Method() = default;
+ virtual ~Method() = default;
+
+ void Write(CodeWriter* to) const override;
+};
+
+struct IntConstant : public ClassElement {
+ const std::string name;
+ const int value;
+
+ IntConstant(std::string name, int value)
+ : name(name), value(value) {}
+ virtual ~IntConstant() = default;
+
+ void Write(CodeWriter* to) const override;
+};
+
+struct StringConstant : public ClassElement {
+ const std::string name;
+ const std::string value;
+
+ StringConstant(std::string name, std::string value)
+ : name(name), value(value) {}
+ ~StringConstant() override = default;
+
+ void Write(CodeWriter* to) const override;
+};
+
+struct Class : public ClassElement {
+ enum { CLASS, INTERFACE };
+
+ std::string comment;
+ int modifiers = 0;
+ int what = CLASS; // CLASS or INTERFACE
+ const Type* type = nullptr;
+ const Type* extends = nullptr;
+ std::vector<const Type*> interfaces;
+ std::vector<ClassElement*> elements;
+
+ Class() = default;
+ virtual ~Class() = default;
+
+ void Write(CodeWriter* to) const override;
+};
+
+class Document {
+ public:
+ Document(const std::string& comment,
+ const std::string& package,
+ const std::string& original_src,
+ std::unique_ptr<Class> clazz);
+ virtual ~Document() = default;
+ virtual void Write(CodeWriter* to) const;
+
+ private:
+ std::string comment_;
+ std::string package_;
+ std::string original_src_;
+ std::unique_ptr<Class> clazz_;
+};
+
+} // namespace java
+} // namespace aidl
+} // namespace android
+
+#endif // AIDL_AST_JAVA_H_
diff --git a/aidl/ast_java_unittest.cpp b/aidl/ast_java_unittest.cpp
new file mode 100644
index 0000000..b2aad23
--- /dev/null
+++ b/aidl/ast_java_unittest.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 <string>
+
+#include <gtest/gtest.h>
+
+#include "ast_java.h"
+#include "code_writer.h"
+#include "type_java.h"
+
+using std::string;
+
+namespace android {
+namespace aidl {
+namespace java {
+namespace {
+
+const char kExpectedClassOutput[] =
+R"(// class comment
+final class TestClass extends SuperClass
+{
+}
+)";
+
+} // namespace
+
+TEST(AstJavaTests, GeneratesClass) {
+ JavaTypeNamespace types;
+ types.Init();
+ Type class_type(&types, "TestClass", ValidatableType::KIND_GENERATED,
+ false, false);
+ Type extend_type(&types, "SuperClass", ValidatableType::KIND_BUILT_IN,
+ false, false);
+ Class a_class;
+ a_class.comment = "// class comment";
+ a_class.modifiers = FINAL;
+ a_class.what = Class::CLASS;
+ a_class.type = &class_type;
+ a_class.extends = &extend_type;
+
+ string actual_output;
+ CodeWriterPtr writer = GetStringWriter(&actual_output);
+ a_class.Write(writer.get());
+ EXPECT_EQ(string(kExpectedClassOutput), actual_output);
+}
+
+} // namespace java
+} // namespace aidl
+} // namespace android
diff --git a/aidl/code_writer.cpp b/aidl/code_writer.cpp
new file mode 100644
index 0000000..58b1dc1
--- /dev/null
+++ b/aidl/code_writer.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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 "code_writer.h"
+
+#include <iostream>
+#include <stdarg.h>
+
+#include <android-base/stringprintf.h>
+
+using std::cerr;
+using std::endl;
+
+namespace android {
+namespace aidl {
+
+namespace {
+
+class StringCodeWriter : public CodeWriter {
+ public:
+ explicit StringCodeWriter(std::string* output_buffer) : output_(output_buffer) {}
+ virtual ~StringCodeWriter() = default;
+
+ bool Write(const char* format, ...) override {
+ va_list ap;
+ va_start(ap, format);
+ android::base::StringAppendV(output_, format, ap);
+ va_end(ap);
+ return true;
+ }
+
+ bool Close() override { return true; }
+
+ private:
+ std::string* output_;
+}; // class StringCodeWriter
+
+class FileCodeWriter : public CodeWriter {
+ public:
+ FileCodeWriter(FILE* output_file, bool close_on_destruction)
+ : output_(output_file),
+ close_on_destruction_(close_on_destruction) {}
+ virtual ~FileCodeWriter() {
+ if (close_on_destruction_ && output_ != nullptr) {
+ fclose(output_);
+ }
+ }
+
+ bool Write(const char* format, ...) override {
+ bool success;
+ va_list ap;
+ va_start(ap, format);
+ success = vfprintf(output_, format, ap) >= 0;
+ va_end(ap);
+ no_error_ = no_error_ && success;
+ return success;
+ }
+
+ bool Close() override {
+ if (output_ != nullptr) {
+ no_error_ = fclose(output_) == 0 && no_error_;
+ output_ = nullptr;
+ }
+ return no_error_;
+ }
+
+ private:
+ bool no_error_ = true;
+ FILE* output_;
+ bool close_on_destruction_;
+}; // class StringCodeWriter
+
+} // namespace
+
+CodeWriterPtr GetFileWriter(const std::string& output_file) {
+ CodeWriterPtr result;
+ FILE* to = nullptr;
+ bool close_on_destruction = true;
+ if (output_file == "-") {
+ to = stdout;
+ close_on_destruction = false;
+ } else {
+ // open file in binary mode to ensure that the tool produces the
+ // same output on all platforms !!
+ to = fopen(output_file.c_str(), "wb");
+ }
+
+ if (to != nullptr) {
+ result.reset(new FileCodeWriter(to, close_on_destruction));
+ } else {
+ cerr << "unable to open " << output_file << " for write" << endl;
+ }
+
+ return result;
+}
+
+CodeWriterPtr GetStringWriter(std::string* output_buffer) {
+ return CodeWriterPtr(new StringCodeWriter(output_buffer));
+}
+
+} // namespace aidl
+} // namespace android
diff --git a/aidl/code_writer.h b/aidl/code_writer.h
new file mode 100644
index 0000000..4b2fe16
--- /dev/null
+++ b/aidl/code_writer.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_CODE_WRITER_H_
+#define AIDL_CODE_WRITER_H_
+
+#include <memory>
+#include <string>
+
+#include <stdio.h>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace aidl {
+
+class CodeWriter {
+ public:
+ // Write a formatted string to this writer in the usual printf sense.
+ // Returns false on error.
+ virtual bool Write(const char* format, ...) = 0;
+ virtual bool Close() = 0;
+ virtual ~CodeWriter() = default;
+}; // class CodeWriter
+
+using CodeWriterPtr = std::unique_ptr<CodeWriter>;
+
+// Get a CodeWriter that writes to |output_file|.
+CodeWriterPtr GetFileWriter(const std::string& output_file);
+
+// Get a CodeWriter that writes to a string buffer.
+// Caller retains ownership of the buffer.
+// The buffer must outlive the CodeWriter.
+CodeWriterPtr GetStringWriter(std::string* output_buffer);
+
+} // namespace aidl
+} // namespace android
+
+#endif // AIDL_CODE_WRITER_H_
diff --git a/aidl/docs/aidl-cpp.md b/aidl/docs/aidl-cpp.md
new file mode 100644
index 0000000..65cc8e4
--- /dev/null
+++ b/aidl/docs/aidl-cpp.md
@@ -0,0 +1,276 @@
+# Generating C++ Binder Interfaces with `aidl-cpp`
+
+## Background
+
+“aidl” refers to several related but distinct concepts:
+
+ - the AIDL interface [definition language](http://developer.android.com/guide/components/aidl.html)
+ - .aidl files (which contain AIDL)
+ - the aidl generator which transforms AIDL into client/server IPC interfaces
+
+The _aidl generator_ is a command line tool that generates client and server
+stubs for Binder interfaces from a specification in a file with the .aidl
+extension. For Java interfaces, the executable is called `aidl` while for C++
+the binary is called `aidl-cpp`. In this document, we’ll use AIDL to describe
+the language of .aidl files and _aidl generator_ to refer to the code generation
+tool that takes an .aidl file, parses the AIDL, and outputs code.
+
+Previously, the _aidl generator_ only generated Java interface/stub/proxy
+objects. C++ Binder interfaces were handcrafted with various degrees of
+compatibility with the Java equivalents. The Brillo project added support for
+generating C++ with the _aidl generator_. This generated C++ is cross-language
+compatible (e.g. Java clients are tested to interoperate with native services).
+
+## Overview
+
+This document describes how C++ generation works with attention to:
+
+ - build interface
+ - cross-language type mapping
+ - implementing a generated interface
+ - C++ parcelables
+ - cross-language error reporting
+ - cross-language null reference handling
+
+## Detailed Design
+
+### Build Interface
+
+Write AIDL in .aidl files and add them to `LOCAL_SRC_FILES` in your Android.mk.
+If your build target is a binary (e.g. you include `$(BUILD_SHARED_LIBRARY)`),
+then the generated code will be C++, not Java.
+
+AIDL definitions should be hosted from the same repository as the
+implementation. Any system that needs the definition will also need the
+implementation (for both parcelables and interface). If there are multiple
+implementations (i.e. one in Java and one in C++), keep the definition with the
+native implementation. Android
+[now has systems](https://developers.google.com/brillo/?hl=en) that run the
+native components of the system without the Java.
+
+If you use an import statement in your AIDL, even from the same package, you
+need to add a path to `LOCAL_AIDL_INCLUDES`. This path should be relative to
+the root of the Android tree. For instance, a file IFoo.aidl defining
+com.example.IFoo might sit in a folder hierarchy
+something/something-else/com/example/IFoo.aidl. Then we would write:
+
+```
+LOCAL_AIDL_INCLUDES := something/something-else
+```
+
+Generated C++ ends up in nested namespaces corresponding to the interface’s
+package. The generated header also corresponds to the interface package. So
+com.example.IFoo becomes ::com::example::IFoo in header “com/example/IFoo.h”.
+
+Similar to how Java works, the suffix of the path to a .aidl file must match
+the package. So if IFoo.aidl declares itself to be in package com.example, the
+folder structure (as given to `LOCAL_SRC_FILES`) must look like:
+`some/prefix/com/example/IFoo.aidl`.
+
+To generate code from .aidl files from another build target (e.g. another
+binary or java), just add a relative path to the .aidl files to
+`LOCAL_SRC_FILES`. Remember that importing AIDL works the same, even for code
+in other directory hierarchies: add the include root path relative to the
+checkout root to `LOCAL_AIDL_INCLUDES`.
+
+### Type Mapping
+
+The following table summarizes the equivalent C++ types for common Java types
+and whether those types may be used as in/out/inout parameters in AIDL
+interfaces.
+
+| Java Type | C++ Type | inout | Notes |
+|-----------------------|---------------------|-------|-------------------------------------------------------|
+| boolean | bool | in | "These 8 types are all considered primitives. |
+| byte | int8\_t | in | |
+| char | char16\_t | in | |
+| int | int32\_t | in | |
+| long | int64\_t | in | |
+| float | float | in | |
+| double | double | in | |
+| String | String16 | in | Supports null references. |
+| @utf8InCpp String | std::string | in | @utf8InCpp causes UTF16 to UTF8 conversion in C++. |
+| android.os.Parcelable | android::Parcelable | inout | |
+| T extends IBinder | sp<T> | in | |
+| Arrays (T[]) | vector<T> | inout | May contain only primitives, Strings and parcelables. |
+| List<String> | vector<String16> | inout | |
+| PersistableBundle | PersistableBundle | inout | binder/PersistableBundle.h |
+| List<IBinder> | vector<sp<IBinder>> | inout | |
+| FileDescriptor | unique_fd | inout | android-base/unique_fd.h from libbase |
+
+Note that java.util.Map and java.utils.List are not good candidates for cross
+language communication because they may contain arbitrary types on the Java
+side. For instance, Map is cast to Map<String,Object> and then the object
+values dynamically inspected and serialized as type/value pairs. Support
+exists for sending arbitrary Java serializables, Android Bundles, etc.
+
+Note that annotations may be placed at the interface level, as well as on a
+type by type basis. Interface level annotations will be applied
+opportunistically and be overridden by per type annotations. For instance, an
+interface marked @nullable will still not allow null int parameters.
+
+### Implementing a generated interface
+
+Given an interface declaration like:
+
+```
+package foo;
+
+import bar.IAnotherInterface;
+
+interface IFoo {
+ IAnotherInterface DoSomething(int count, out List<String> output);
+}
+```
+
+`aidl-cpp` will generate a C++ interface:
+
+```
+namespace foo {
+
+// Some headers have been omitted for clarity.
+#include <android/String16.h>
+#include <cstdint>
+#include <vector>
+#include <bar/IAnotherInterface.h>
+
+// Some class members have been omitted for clarity.
+class IFoo : public android::IInterface {
+ public:
+ virtual android::binder::Status DoSomething(
+ int32_t count,
+ std::vector<android::String16>* output,
+ android::sp<bar::IAnotherInterface>* returned_value) = 0;
+};
+```
+
+Note that `aidl-cpp` will import headers for types used in the interface. For
+imported types (e.g. parcelables and interfaces), it will import a header
+corresponding to the package/class name of the import. For instance,
+`import bar.IAnotherInterface` causes aidl-cpp to generate
+`#include <bar/IAnotherInterface.h>`.
+
+When writing a service that implements this interface, write:
+
+```
+#include "foo/BnFoo.h"
+
+namespace unrelated_namespace {
+
+class MyFoo : public foo::BnFoo {
+ public:
+ android::binder::Status DoSomething(
+ int32_t count,
+ std::vector<android::String16>* output,
+ android::sp<bar::IAnotherInterface>* returned_value) override {
+ for (int32_t i = 0; i < count; ++i) {
+ output->push_back(String16("..."));
+ }
+ *returned_value = new InstanceOfAnotherInterface;
+ return Status::ok();
+ }
+}; // class MyFoo
+
+} // namespace unrelated_namespace
+```
+
+Note that the output values, `output` and `returned_value` are passed by
+pointer, and that this pointer is always valid.
+
+#### Dependencies
+
+The generated C++ code will use symbols from libbinder as well as libutils.
+AIDL files using the FileDescriptor type will also explicitly require
+libnativehelper, although this is likely a transitive dependency of the other
+two, and should be included automatically within the Android build tree
+regardless.
+
+### C++ Parcelables
+
+In Java, a parcelable should extend android.os.Parcelable and provide a static
+final CREATOR field that acts as a factory for new instances/arrays of
+instances of the parcelable. In addition, in order to be used as an out
+parameter, a parcelable class must define a readFromParcel method.
+
+In C++, parcelables must implement android::Parcelable from binder/Parcelable.h
+in libbinder. Parcelables must define a constructor that takes no arguments.
+In order to be used in arrays, a parcelable must implement a copy or move
+constructor (called implicitly in vector).
+
+The C++ generator needs to know what header defines the C++ parcelable. It
+learns this from the `cpp_header` directive shown below. The generator takes
+this string and uses it as the literal include statement in generated code.
+The idea here is that you generate your code once, link it into a library along
+with parcelable implementations, and export appropriate header paths. This
+header include must make sense in the context of the Android.mk that compiles
+this generated code.
+
+```
+// ExampleParcelable.aidl
+package com.example.android;
+
+// Native types must be aliased at their declaration in the appropriate .aidl
+// file. This allows multiple interfaces to use a parcelable and its C++
+// equivalent without duplicating the mapping between the C++ and Java types.
+// Generator will assume bar/foo.h declares class
+// com::example::android::ExampleParcelable
+parcelable ExampleParcelable cpp_header "bar/foo.h";
+```
+
+### Null Reference Handling
+
+The aidl generator for both C++ and Java languages has been expanded to
+understand nullable annotations.
+
+Given an interface definition like:
+
+```
+interface IExample {
+ void ReadStrings(String neverNull, in @nullable String maybeNull);
+};
+```
+
+the generated C++ header code looks like:
+
+```
+class IExample {
+ android::binder::Status ReadStrings(
+ const android::String16& in_neverNull,
+ const std::unique_ptr<android::String16>& in_maybeNull);
+};
+```
+
+Note that by default, the generated C++ passes a const reference to the value
+of a parameter and rejects null references with a NullPointerException sent
+back the caller. Parameters marked with @nullable are passed by pointer,
+allowing native services to explicitly control whether they allow method
+overloading via null parameters. Java stubs and proxies currently do nothing
+with the @nullable annotation.
+
+Consider an AIDL type `in @nullable List<String> bar`. This type
+indicates that the remote caller may pass in a list of strings, and that both
+the list and any string in the list may be null. This type will map to a C++
+type `unique_ptr<vector<unique_ptr<String16>>>* bar`. In this case:
+
+ - `bar` is never null
+ - `*bar` might be null
+ - `(*bar)->empty()` could be true
+ - `(**bar)[0]` could be null (and so on)
+
+### Exception Reporting
+
+C++ methods generated by the aidl generator return `android::binder::Status`
+objects, rather than `android::status_t`. This Status object allows generated
+C++ code to send and receive exceptions (an exception type and a String16 error
+message) since we do not use real exceptions in C++. More background on Status
+objects can be found here.
+
+For legacy support and migration ease, the Status object includes a mechanism
+to report a `android::status_t`. However, that return code is interpreted by a
+different code path and does not include a helpful String message.
+
+For situations where your native service needs to throw an error code specific
+to the service, use `Status::fromServiceSpecificError()`. This kind of
+exception comes with a helpful message and an integer error code. Make your
+error codes consistent across services by using interface constants (see
+below).
diff --git a/aidl/docs/constants.md b/aidl/docs/constants.md
new file mode 100644
index 0000000..55fbd97
--- /dev/null
+++ b/aidl/docs/constants.md
@@ -0,0 +1,35 @@
+# Defining constants as part of an interface
+
+AIDL has been enhanced to support defining integer and string constants
+as part of an interface.
+
+## Integer constants
+
+```
+interface IMyInterface {
+ const int CONST_A = 1;
+ const int CONST_B = 2;
+ const int CONST_C = 3;
+ ...
+}
+```
+
+These map to appropriate 32 bit integer class constants in Java and C++ (e.g.
+`IMyInterface.CONST_A` and `IMyInterface::CONST_A` respectively).
+
+## String constants
+
+```
+interface IMyInterface {
+ const String CONST_A = "foo";
+ const String CONST_B = "bar";
+ ...
+}
+```
+
+These map to class level String constants in Java, and static getter
+functions that return a const android::String16& in C++.
+
+The constants are limited to contain printable ASCII characters < 0x10
+and without backspaces (i.e. no '\' character).
+
diff --git a/aidl/docs/making-changes.md b/aidl/docs/making-changes.md
new file mode 100644
index 0000000..ac84b86
--- /dev/null
+++ b/aidl/docs/making-changes.md
@@ -0,0 +1,21 @@
+# Making changes
+
+## Coding style
+
+This project was originally written in C, in the Android platform style.
+It has been substantially re-written in C++, in the Google C++ style.
+
+This style
+[is summarized here](https://google.github.io/styleguide/cppguide.html).
+
+When in doubt, clang-format -style=google is a good reference.
+
+## Testing
+
+This codebase has both integration and unittests, all of which are expected to
+consistently pass against a device/emulator:
+
+```
+$ ./runtests.sh && echo "All tests pass"
+
+```
diff --git a/aidl/generate_cpp.cpp b/aidl/generate_cpp.cpp
new file mode 100644
index 0000000..41e9fb4
--- /dev/null
+++ b/aidl/generate_cpp.cpp
@@ -0,0 +1,858 @@
+/*
+ * 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 "generate_cpp.h"
+
+#include <cctype>
+#include <cstring>
+#include <memory>
+#include <random>
+#include <set>
+#include <string>
+
+#include <android-base/stringprintf.h>
+
+#include "aidl_language.h"
+#include "ast_cpp.h"
+#include "code_writer.h"
+#include "logging.h"
+#include "os.h"
+
+using android::base::StringPrintf;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+using std::set;
+
+namespace android {
+namespace aidl {
+namespace cpp {
+namespace internals {
+namespace {
+
+const char kAndroidStatusVarName[] = "_aidl_ret_status";
+const char kCodeVarName[] = "_aidl_code";
+const char kFlagsVarName[] = "_aidl_flags";
+const char kDataVarName[] = "_aidl_data";
+const char kErrorLabel[] = "_aidl_error";
+const char kImplVarName[] = "_aidl_impl";
+const char kReplyVarName[] = "_aidl_reply";
+const char kReturnVarName[] = "_aidl_return";
+const char kStatusVarName[] = "_aidl_status";
+const char kAndroidParcelLiteral[] = "::android::Parcel";
+const char kAndroidStatusLiteral[] = "::android::status_t";
+const char kAndroidStatusOk[] = "::android::OK";
+const char kBinderStatusLiteral[] = "::android::binder::Status";
+const char kIBinderHeader[] = "binder/IBinder.h";
+const char kIInterfaceHeader[] = "binder/IInterface.h";
+const char kParcelHeader[] = "binder/Parcel.h";
+const char kStatusHeader[] = "binder/Status.h";
+const char kString16Header[] = "utils/String16.h";
+const char kStrongPointerHeader[] = "utils/StrongPointer.h";
+
+unique_ptr<AstNode> BreakOnStatusNotOk() {
+ IfStatement* ret = new IfStatement(new Comparison(
+ new LiteralExpression(kAndroidStatusVarName), "!=",
+ new LiteralExpression(kAndroidStatusOk)));
+ ret->OnTrue()->AddLiteral("break");
+ return unique_ptr<AstNode>(ret);
+}
+
+unique_ptr<AstNode> GotoErrorOnBadStatus() {
+ IfStatement* ret = new IfStatement(new Comparison(
+ new LiteralExpression(kAndroidStatusVarName), "!=",
+ new LiteralExpression(kAndroidStatusOk)));
+ ret->OnTrue()->AddLiteral(StringPrintf("goto %s", kErrorLabel));
+ return unique_ptr<AstNode>(ret);
+}
+
+
+unique_ptr<AstNode> ReturnOnStatusNotOk() {
+ IfStatement* ret = new IfStatement(new Comparison(
+ new LiteralExpression(kAndroidStatusVarName), "!=",
+ new LiteralExpression(kAndroidStatusOk)));
+ ret->OnTrue()->AddLiteral(StringPrintf("return %s", kAndroidStatusVarName));
+ return unique_ptr<AstNode>(ret);
+}
+
+string UpperCase(const std::string& s) {
+ string result = s;
+ for (char& c : result)
+ c = toupper(c);
+ return result;
+}
+
+string BuildVarName(const AidlArgument& a) {
+ string prefix = "out_";
+ if (a.GetDirection() & AidlArgument::IN_DIR) {
+ prefix = "in_";
+ }
+ return prefix + a.GetName();
+}
+
+ArgList BuildArgList(const TypeNamespace& types,
+ const AidlMethod& method,
+ bool for_declaration) {
+ // Build up the argument list for the server method call.
+ vector<string> method_arguments;
+ for (const unique_ptr<AidlArgument>& a : method.GetArguments()) {
+ string literal;
+ if (for_declaration) {
+ // Method declarations need types, pointers to out params, and variable
+ // names that match the .aidl specification.
+ const Type* type = a->GetType().GetLanguageType<Type>();
+
+ literal = type->CppType();
+
+ if (a->IsOut()) {
+ literal = literal + "*";
+ } else {
+ // We pass in parameters that are not primitives by const reference.
+ // Arrays of primitives are not primitives.
+ if (!type->IsCppPrimitive() || a->GetType().IsArray()) {
+ literal = "const " + literal + "&";
+ }
+ }
+
+ literal += " " + a->GetName();
+ } else {
+ if (a->IsOut()) { literal = "&"; }
+ literal += BuildVarName(*a);
+ }
+ method_arguments.push_back(literal);
+ }
+
+ const Type* return_type = method.GetType().GetLanguageType<Type>();
+
+ if (return_type != types.VoidType()) {
+ string literal;
+ if (for_declaration) {
+ literal = StringPrintf(
+ "%s* %s", return_type->CppType().c_str(),
+ kReturnVarName);
+ } else {
+ literal = string{"&"} + kReturnVarName;
+ }
+ method_arguments.push_back(literal);
+ }
+
+ return ArgList(method_arguments);
+}
+
+unique_ptr<Declaration> BuildMethodDecl(const AidlMethod& method,
+ const TypeNamespace& types,
+ bool for_interface) {
+ uint32_t modifiers = 0;
+ if (for_interface) {
+ modifiers |= MethodDecl::IS_VIRTUAL;
+ modifiers |= MethodDecl::IS_PURE_VIRTUAL;
+ } else {
+ modifiers |= MethodDecl::IS_OVERRIDE;
+ }
+
+ return unique_ptr<Declaration>{
+ new MethodDecl{kBinderStatusLiteral,
+ method.GetName(),
+ BuildArgList(types, method, true /* for method decl */),
+ modifiers}};
+}
+
+unique_ptr<CppNamespace> NestInNamespaces(
+ vector<unique_ptr<Declaration>> decls,
+ const vector<string>& package) {
+ if (package.empty()) {
+ // We should also be checking this before we get this far, but do it again
+ // for the sake of unit tests and meaningful errors.
+ LOG(FATAL) << "C++ generation requires a package declaration "
+ "for namespacing";
+ }
+ auto it = package.crbegin(); // Iterate over the namespaces inner to outer
+ unique_ptr<CppNamespace> inner{new CppNamespace{*it, std::move(decls)}};
+ ++it;
+ for (; it != package.crend(); ++it) {
+ inner.reset(new CppNamespace{*it, std::move(inner)});
+ }
+ return inner;
+}
+
+unique_ptr<CppNamespace> NestInNamespaces(unique_ptr<Declaration> decl,
+ const vector<string>& package) {
+ vector<unique_ptr<Declaration>> decls;
+ decls.push_back(std::move(decl));
+ return NestInNamespaces(std::move(decls), package);
+}
+
+bool DeclareLocalVariable(const TypeNamespace& types, const AidlArgument& a,
+ StatementBlock* b) {
+ const Type* cpp_type = a.GetType().GetLanguageType<Type>();
+ if (!cpp_type) { return false; }
+
+ string type = cpp_type->CppType();
+
+ b->AddLiteral(type + " " + BuildVarName(a));
+ return true;
+}
+
+string ClassName(const AidlInterface& interface, ClassNames type) {
+ string c_name = interface.GetName();
+
+ if (c_name.length() >= 2 && c_name[0] == 'I' && isupper(c_name[1]))
+ c_name = c_name.substr(1);
+
+ switch (type) {
+ case ClassNames::CLIENT:
+ c_name = "Bp" + c_name;
+ break;
+ case ClassNames::SERVER:
+ c_name = "Bn" + c_name;
+ break;
+ case ClassNames::INTERFACE:
+ c_name = "I" + c_name;
+ break;
+ case ClassNames::BASE:
+ break;
+ }
+ return c_name;
+}
+
+string BuildHeaderGuard(const AidlInterface& interface,
+ ClassNames header_type) {
+ string class_name = ClassName(interface, header_type);
+ for (size_t i = 1; i < class_name.size(); ++i) {
+ if (isupper(class_name[i])) {
+ class_name.insert(i, "_");
+ ++i;
+ }
+ }
+ string ret = StringPrintf("AIDL_GENERATED_%s_%s_H_",
+ interface.GetPackage().c_str(),
+ class_name.c_str());
+ for (char& c : ret) {
+ if (c == '.') {
+ c = '_';
+ }
+ c = toupper(c);
+ }
+ return ret;
+}
+
+unique_ptr<Declaration> DefineClientTransaction(const TypeNamespace& types,
+ const AidlInterface& interface,
+ const AidlMethod& method) {
+ const string i_name = ClassName(interface, ClassNames::INTERFACE);
+ const string bp_name = ClassName(interface, ClassNames::CLIENT);
+ unique_ptr<MethodImpl> ret{new MethodImpl{
+ kBinderStatusLiteral, bp_name, method.GetName(),
+ ArgList{BuildArgList(types, method, true /* for method decl */)}}};
+ StatementBlock* b = ret->GetStatementBlock();
+
+ // Declare parcels to hold our query and the response.
+ b->AddLiteral(StringPrintf("%s %s", kAndroidParcelLiteral, kDataVarName));
+ // Even if we're oneway, the transact method still takes a parcel.
+ b->AddLiteral(StringPrintf("%s %s", kAndroidParcelLiteral, kReplyVarName));
+
+ // Declare the status_t variable we need for error handling.
+ b->AddLiteral(StringPrintf("%s %s = %s", kAndroidStatusLiteral,
+ kAndroidStatusVarName,
+ kAndroidStatusOk));
+ // We unconditionally return a Status object.
+ b->AddLiteral(StringPrintf("%s %s", kBinderStatusLiteral, kStatusVarName));
+
+ // Add the name of the interface we're hoping to call.
+ b->AddStatement(new Assignment(
+ kAndroidStatusVarName,
+ new MethodCall(StringPrintf("%s.writeInterfaceToken",
+ kDataVarName),
+ "getInterfaceDescriptor()")));
+ b->AddStatement(GotoErrorOnBadStatus());
+
+ for (const auto& a: method.GetArguments()) {
+ const Type* type = a->GetType().GetLanguageType<Type>();
+ string var_name = ((a->IsOut()) ? "*" : "") + a->GetName();
+ var_name = type->WriteCast(var_name);
+
+ if (a->IsIn()) {
+ // Serialization looks roughly like:
+ // _aidl_ret_status = _aidl_data.WriteInt32(in_param_name);
+ // if (_aidl_ret_status != ::android::OK) { goto error; }
+ const string& method = type->WriteToParcelMethod();
+ b->AddStatement(new Assignment(
+ kAndroidStatusVarName,
+ new MethodCall(StringPrintf("%s.%s", kDataVarName, method.c_str()),
+ ArgList(var_name))));
+ b->AddStatement(GotoErrorOnBadStatus());
+ } else if (a->IsOut() && a->GetType().IsArray()) {
+ // Special case, the length of the out array is written into the parcel.
+ // _aidl_ret_status = _aidl_data.writeVectorSize(&out_param_name);
+ // if (_aidl_ret_status != ::android::OK) { goto error; }
+ b->AddStatement(new Assignment(
+ kAndroidStatusVarName,
+ new MethodCall(StringPrintf("%s.writeVectorSize", kDataVarName),
+ ArgList(var_name))));
+ b->AddStatement(GotoErrorOnBadStatus());
+ }
+ }
+
+ // Invoke the transaction on the remote binder and confirm status.
+ string transaction_code = StringPrintf(
+ "%s::%s", i_name.c_str(), UpperCase(method.GetName()).c_str());
+
+ vector<string> args = {transaction_code, kDataVarName,
+ StringPrintf("&%s", kReplyVarName)};
+
+ if (interface.IsOneway() || method.IsOneway()) {
+ args.push_back("::android::IBinder::FLAG_ONEWAY");
+ }
+
+ b->AddStatement(new Assignment(
+ kAndroidStatusVarName,
+ new MethodCall("remote()->transact",
+ ArgList(args))));
+ b->AddStatement(GotoErrorOnBadStatus());
+
+ if (!interface.IsOneway() && !method.IsOneway()) {
+ // Strip off the exception header and fail if we see a remote exception.
+ // _aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+ // if (_aidl_ret_status != ::android::OK) { goto error; }
+ // if (!_aidl_status.isOk()) { return _aidl_ret_status; }
+ b->AddStatement(new Assignment(
+ kAndroidStatusVarName,
+ StringPrintf("%s.readFromParcel(%s)", kStatusVarName, kReplyVarName)));
+ b->AddStatement(GotoErrorOnBadStatus());
+ IfStatement* exception_check = new IfStatement(
+ new LiteralExpression(StringPrintf("!%s.isOk()", kStatusVarName)));
+ b->AddStatement(exception_check);
+ exception_check->OnTrue()->AddLiteral(
+ StringPrintf("return %s", kStatusVarName));
+ }
+
+ // Type checking should guarantee that nothing below emits code until "return
+ // status" if we are a oneway method, so no more fear of accessing reply.
+
+ // If the method is expected to return something, read it first by convention.
+ const Type* return_type = method.GetType().GetLanguageType<Type>();
+ if (return_type != types.VoidType()) {
+ const string& method_call = return_type->ReadFromParcelMethod();
+ b->AddStatement(new Assignment(
+ kAndroidStatusVarName,
+ new MethodCall(StringPrintf("%s.%s", kReplyVarName,
+ method_call.c_str()),
+ ArgList(kReturnVarName))));
+ b->AddStatement(GotoErrorOnBadStatus());
+ }
+
+ for (const AidlArgument* a : method.GetOutArguments()) {
+ // Deserialization looks roughly like:
+ // _aidl_ret_status = _aidl_reply.ReadInt32(out_param_name);
+ // if (_aidl_status != ::android::OK) { goto _aidl_error; }
+ string method =
+ a->GetType().GetLanguageType<Type>()->ReadFromParcelMethod();
+
+ b->AddStatement(new Assignment(
+ kAndroidStatusVarName,
+ new MethodCall(StringPrintf("%s.%s", kReplyVarName,
+ method.c_str()),
+ ArgList(a->GetName()))));
+ b->AddStatement(GotoErrorOnBadStatus());
+ }
+
+ // If we've gotten to here, one of two things is true:
+ // 1) We've read some bad status_t
+ // 2) We've only read status_t == OK and there was no exception in the
+ // response.
+ // In both cases, we're free to set Status from the status_t and return.
+ b->AddLiteral(StringPrintf("%s:\n", kErrorLabel), false /* no semicolon */);
+ b->AddLiteral(
+ StringPrintf("%s.setFromStatusT(%s)", kStatusVarName,
+ kAndroidStatusVarName));
+ b->AddLiteral(StringPrintf("return %s", kStatusVarName));
+
+ return unique_ptr<Declaration>(ret.release());
+}
+
+} // namespace
+
+unique_ptr<Document> BuildClientSource(const TypeNamespace& types,
+ const AidlInterface& interface) {
+ vector<string> include_list = {
+ HeaderFile(interface, ClassNames::CLIENT, false),
+ kParcelHeader
+ };
+ vector<unique_ptr<Declaration>> file_decls;
+
+ // The constructor just passes the IBinder instance up to the super
+ // class.
+ const string i_name = ClassName(interface, ClassNames::INTERFACE);
+ file_decls.push_back(unique_ptr<Declaration>{new ConstructorImpl{
+ ClassName(interface, ClassNames::CLIENT),
+ ArgList{StringPrintf("const ::android::sp<::android::IBinder>& %s",
+ kImplVarName)},
+ { "BpInterface<" + i_name + ">(" + kImplVarName + ")" }}});
+
+ // Clients define a method per transaction.
+ for (const auto& method : interface.GetMethods()) {
+ unique_ptr<Declaration> m = DefineClientTransaction(
+ types, interface, *method);
+ if (!m) { return nullptr; }
+ file_decls.push_back(std::move(m));
+ }
+ return unique_ptr<Document>{new CppSource{
+ include_list,
+ NestInNamespaces(std::move(file_decls), interface.GetSplitPackage())}};
+}
+
+namespace {
+
+bool HandleServerTransaction(const TypeNamespace& types,
+ const AidlMethod& method,
+ StatementBlock* b) {
+ // Declare all the parameters now. In the common case, we expect no errors
+ // in serialization.
+ for (const unique_ptr<AidlArgument>& a : method.GetArguments()) {
+ if (!DeclareLocalVariable(types, *a, b)) { return false; }
+ }
+
+ // Declare a variable to hold the return value.
+ const Type* return_type = method.GetType().GetLanguageType<Type>();
+ if (return_type != types.VoidType()) {
+ b->AddLiteral(StringPrintf(
+ "%s %s", return_type->CppType().c_str(),
+ kReturnVarName));
+ }
+
+ // Check that the client is calling the correct interface.
+ IfStatement* interface_check = new IfStatement(
+ new MethodCall(StringPrintf("%s.checkInterface",
+ kDataVarName), "this"),
+ true /* invert the check */);
+ b->AddStatement(interface_check);
+ interface_check->OnTrue()->AddStatement(
+ new Assignment(kAndroidStatusVarName, "::android::BAD_TYPE"));
+ interface_check->OnTrue()->AddLiteral("break");
+
+ // Deserialize each "in" parameter to the transaction.
+ for (const auto& a: method.GetArguments()) {
+ // Deserialization looks roughly like:
+ // _aidl_ret_status = _aidl_data.ReadInt32(&in_param_name);
+ // if (_aidl_ret_status != ::android::OK) { break; }
+ const Type* type = a->GetType().GetLanguageType<Type>();
+ const string& readMethod = type->ReadFromParcelMethod();
+
+ if (a->IsIn()) {
+ b->AddStatement(new Assignment{
+ kAndroidStatusVarName,
+ new MethodCall{string(kDataVarName) + "." + readMethod,
+ "&" + BuildVarName(*a)}});
+ b->AddStatement(BreakOnStatusNotOk());
+ } else if (a->IsOut() && a->GetType().IsArray()) {
+ // Special case, the length of the out array is written into the parcel.
+ // _aidl_ret_status = _aidl_data.resizeOutVector(&out_param_name);
+ // if (_aidl_ret_status != ::android::OK) { break; }
+ b->AddStatement(new Assignment{
+ kAndroidStatusVarName,
+ new MethodCall{string(kDataVarName) + ".resizeOutVector",
+ "&" + BuildVarName(*a)}});
+ b->AddStatement(BreakOnStatusNotOk());
+ }
+ }
+
+ // Call the actual method. This is implemented by the subclass.
+ vector<unique_ptr<AstNode>> status_args;
+ status_args.emplace_back(new MethodCall(
+ method.GetName(),
+ BuildArgList(types, method, false /* not for method decl */)));
+ b->AddStatement(new Statement(new MethodCall(
+ StringPrintf("%s %s", kBinderStatusLiteral, kStatusVarName),
+ ArgList(std::move(status_args)))));
+
+ // Write exceptions during transaction handling to parcel.
+ if (!method.IsOneway()) {
+ b->AddStatement(new Assignment(
+ kAndroidStatusVarName,
+ StringPrintf("%s.writeToParcel(%s)", kStatusVarName, kReplyVarName)));
+ b->AddStatement(BreakOnStatusNotOk());
+ IfStatement* exception_check = new IfStatement(
+ new LiteralExpression(StringPrintf("!%s.isOk()", kStatusVarName)));
+ b->AddStatement(exception_check);
+ exception_check->OnTrue()->AddLiteral("break");
+ }
+
+ // If we have a return value, write it first.
+ if (return_type != types.VoidType()) {
+ string writeMethod =
+ string(kReplyVarName) + "->" +
+ return_type->WriteToParcelMethod();
+ b->AddStatement(new Assignment{
+ kAndroidStatusVarName, new MethodCall{writeMethod,
+ ArgList{return_type->WriteCast(kReturnVarName)}}});
+ b->AddStatement(BreakOnStatusNotOk());
+ }
+
+ // Write each out parameter to the reply parcel.
+ for (const AidlArgument* a : method.GetOutArguments()) {
+ // Serialization looks roughly like:
+ // _aidl_ret_status = data.WriteInt32(out_param_name);
+ // if (_aidl_ret_status != ::android::OK) { break; }
+ const Type* type = a->GetType().GetLanguageType<Type>();
+ const string& writeMethod = type->WriteToParcelMethod();
+
+ b->AddStatement(new Assignment{
+ kAndroidStatusVarName,
+ new MethodCall{string(kReplyVarName) + "->" + writeMethod,
+ type->WriteCast(BuildVarName(*a))}});
+ b->AddStatement(BreakOnStatusNotOk());
+ }
+
+ return true;
+}
+
+} // namespace
+
+unique_ptr<Document> BuildServerSource(const TypeNamespace& types,
+ const AidlInterface& interface) {
+ const string bn_name = ClassName(interface, ClassNames::SERVER);
+ vector<string> include_list{
+ HeaderFile(interface, ClassNames::SERVER, false),
+ kParcelHeader
+ };
+ unique_ptr<MethodImpl> on_transact{new MethodImpl{
+ kAndroidStatusLiteral, bn_name, "onTransact",
+ ArgList{{StringPrintf("uint32_t %s", kCodeVarName),
+ StringPrintf("const %s& %s", kAndroidParcelLiteral,
+ kDataVarName),
+ StringPrintf("%s* %s", kAndroidParcelLiteral, kReplyVarName),
+ StringPrintf("uint32_t %s", kFlagsVarName)}}
+ }};
+
+ // Declare the status_t variable
+ on_transact->GetStatementBlock()->AddLiteral(
+ StringPrintf("%s %s = %s", kAndroidStatusLiteral, kAndroidStatusVarName,
+ kAndroidStatusOk));
+
+ // Add the all important switch statement, but retain a pointer to it.
+ SwitchStatement* s = new SwitchStatement{kCodeVarName};
+ on_transact->GetStatementBlock()->AddStatement(s);
+
+ // The switch statement has a case statement for each transaction code.
+ for (const auto& method : interface.GetMethods()) {
+ StatementBlock* b = s->AddCase("Call::" + UpperCase(method->GetName()));
+ if (!b) { return nullptr; }
+
+ if (!HandleServerTransaction(types, *method, b)) { return nullptr; }
+ }
+
+ // The switch statement has a default case which defers to the super class.
+ // The superclass handles a few pre-defined transactions.
+ StatementBlock* b = s->AddCase("");
+ b->AddLiteral(StringPrintf(
+ "%s = ::android::BBinder::onTransact(%s, %s, "
+ "%s, %s)", kAndroidStatusVarName, kCodeVarName,
+ kDataVarName, kReplyVarName, kFlagsVarName));
+
+ // If we saw a null reference, we can map that to an appropriate exception.
+ IfStatement* null_check = new IfStatement(
+ new LiteralExpression(string(kAndroidStatusVarName) +
+ " == ::android::UNEXPECTED_NULL"));
+ on_transact->GetStatementBlock()->AddStatement(null_check);
+ null_check->OnTrue()->AddStatement(new Assignment(
+ kAndroidStatusVarName,
+ StringPrintf("%s::fromExceptionCode(%s::EX_NULL_POINTER)"
+ ".writeToParcel(%s)",
+ kBinderStatusLiteral, kBinderStatusLiteral,
+ kReplyVarName)));
+
+ // Finally, the server's onTransact method just returns a status code.
+ on_transact->GetStatementBlock()->AddLiteral(
+ StringPrintf("return %s", kAndroidStatusVarName));
+
+ return unique_ptr<Document>{new CppSource{
+ include_list,
+ NestInNamespaces(std::move(on_transact), interface.GetSplitPackage())}};
+}
+
+unique_ptr<Document> BuildInterfaceSource(const TypeNamespace& /* types */,
+ const AidlInterface& interface) {
+ vector<string> include_list{
+ HeaderFile(interface, ClassNames::INTERFACE, false),
+ HeaderFile(interface, ClassNames::CLIENT, false),
+ };
+
+ string fq_name = ClassName(interface, ClassNames::INTERFACE);
+ if (!interface.GetPackage().empty()) {
+ fq_name = interface.GetPackage() + "." + fq_name;
+ }
+
+ vector<unique_ptr<Declaration>> decls;
+
+ unique_ptr<MacroDecl> meta_if{new MacroDecl{
+ "IMPLEMENT_META_INTERFACE",
+ ArgList{vector<string>{ClassName(interface, ClassNames::BASE),
+ '"' + fq_name + '"'}}}};
+ decls.push_back(std::move(meta_if));
+
+ for (const auto& constant: interface.GetStringConstants()) {
+ unique_ptr<MethodImpl> getter(new MethodImpl(
+ "const ::android::String16&",
+ ClassName(interface, ClassNames::INTERFACE),
+ constant->GetName(),
+ {}));
+ getter->GetStatementBlock()->AddLiteral(
+ StringPrintf("static const ::android::String16 value(%s)",
+ constant->GetValue().c_str()));
+ getter->GetStatementBlock()->AddLiteral("return value");
+ decls.push_back(std::move(getter));
+ }
+
+ return unique_ptr<Document>{new CppSource{
+ include_list,
+ NestInNamespaces(std::move(decls), interface.GetSplitPackage())}};
+}
+
+unique_ptr<Document> BuildClientHeader(const TypeNamespace& types,
+ const AidlInterface& interface) {
+ const string i_name = ClassName(interface, ClassNames::INTERFACE);
+ const string bp_name = ClassName(interface, ClassNames::CLIENT);
+
+ unique_ptr<ConstructorDecl> constructor{new ConstructorDecl{
+ bp_name,
+ ArgList{StringPrintf("const ::android::sp<::android::IBinder>& %s",
+ kImplVarName)},
+ ConstructorDecl::IS_EXPLICIT
+ }};
+ unique_ptr<ConstructorDecl> destructor{new ConstructorDecl{
+ "~" + bp_name,
+ ArgList{},
+ ConstructorDecl::IS_VIRTUAL | ConstructorDecl::IS_DEFAULT}};
+
+ vector<unique_ptr<Declaration>> publics;
+ publics.push_back(std::move(constructor));
+ publics.push_back(std::move(destructor));
+
+ for (const auto& method: interface.GetMethods()) {
+ publics.push_back(BuildMethodDecl(*method, types, false));
+ }
+
+ unique_ptr<ClassDecl> bp_class{
+ new ClassDecl{bp_name,
+ "::android::BpInterface<" + i_name + ">",
+ std::move(publics),
+ {}
+ }};
+
+ return unique_ptr<Document>{new CppHeader{
+ BuildHeaderGuard(interface, ClassNames::CLIENT),
+ {kIBinderHeader,
+ kIInterfaceHeader,
+ "utils/Errors.h",
+ HeaderFile(interface, ClassNames::INTERFACE, false)},
+ NestInNamespaces(std::move(bp_class), interface.GetSplitPackage())}};
+}
+
+unique_ptr<Document> BuildServerHeader(const TypeNamespace& /* types */,
+ const AidlInterface& interface) {
+ const string i_name = ClassName(interface, ClassNames::INTERFACE);
+ const string bn_name = ClassName(interface, ClassNames::SERVER);
+
+ unique_ptr<Declaration> on_transact{new MethodDecl{
+ kAndroidStatusLiteral, "onTransact",
+ ArgList{{StringPrintf("uint32_t %s", kCodeVarName),
+ StringPrintf("const %s& %s", kAndroidParcelLiteral,
+ kDataVarName),
+ StringPrintf("%s* %s", kAndroidParcelLiteral, kReplyVarName),
+ StringPrintf("uint32_t %s = 0", kFlagsVarName)}},
+ MethodDecl::IS_OVERRIDE
+ }};
+
+ std::vector<unique_ptr<Declaration>> publics;
+ publics.push_back(std::move(on_transact));
+
+ unique_ptr<ClassDecl> bn_class{
+ new ClassDecl{bn_name,
+ "::android::BnInterface<" + i_name + ">",
+ std::move(publics),
+ {}
+ }};
+
+ return unique_ptr<Document>{new CppHeader{
+ BuildHeaderGuard(interface, ClassNames::SERVER),
+ {"binder/IInterface.h",
+ HeaderFile(interface, ClassNames::INTERFACE, false)},
+ NestInNamespaces(std::move(bn_class), interface.GetSplitPackage())}};
+}
+
+unique_ptr<Document> BuildInterfaceHeader(const TypeNamespace& types,
+ const AidlInterface& interface) {
+ set<string> includes = { kIBinderHeader, kIInterfaceHeader,
+ kStatusHeader, kStrongPointerHeader };
+
+ for (const auto& method : interface.GetMethods()) {
+ for (const auto& argument : method->GetArguments()) {
+ const Type* type = argument->GetType().GetLanguageType<Type>();
+ type->GetHeaders(&includes);
+ }
+
+ const Type* return_type = method->GetType().GetLanguageType<Type>();
+ return_type->GetHeaders(&includes);
+ }
+
+ unique_ptr<ClassDecl> if_class{
+ new ClassDecl{ClassName(interface, ClassNames::INTERFACE),
+ "::android::IInterface"}};
+ if_class->AddPublic(unique_ptr<Declaration>{new MacroDecl{
+ "DECLARE_META_INTERFACE",
+ ArgList{vector<string>{ClassName(interface, ClassNames::BASE)}}}});
+
+ unique_ptr<Enum> constant_enum{new Enum{"", "int32_t"}};
+ for (const auto& constant : interface.GetIntConstants()) {
+ constant_enum->AddValue(
+ constant->GetName(), std::to_string(constant->GetValue()));
+ }
+ if (constant_enum->HasValues()) {
+ if_class->AddPublic(std::move(constant_enum));
+ }
+
+ if (!interface.GetStringConstants().empty()) {
+ includes.insert(kString16Header);
+ }
+ for (const auto& constant : interface.GetStringConstants()) {
+ unique_ptr<MethodDecl> getter(new MethodDecl(
+ "const ::android::String16&", constant->GetName(),
+ {}, MethodDecl::IS_STATIC));
+ if_class->AddPublic(std::move(getter));
+ }
+
+ if (!interface.GetMethods().empty()) {
+ unique_ptr<Enum> call_enum{new Enum{"Call"}};
+ for (const auto& method : interface.GetMethods()) {
+ // Each method gets an enum entry and pure virtual declaration.
+ if_class->AddPublic(BuildMethodDecl(*method, types, true));
+ call_enum->AddValue(
+ UpperCase(method->GetName()),
+ StringPrintf("::android::IBinder::FIRST_CALL_TRANSACTION + %d",
+ method->GetId()));
+ }
+ if_class->AddPublic(std::move(call_enum));
+ }
+
+ return unique_ptr<Document>{new CppHeader{
+ BuildHeaderGuard(interface, ClassNames::INTERFACE),
+ vector<string>(includes.begin(), includes.end()),
+ NestInNamespaces(std::move(if_class), interface.GetSplitPackage())}};
+}
+
+bool WriteHeader(const CppOptions& options,
+ const TypeNamespace& types,
+ const AidlInterface& interface,
+ const IoDelegate& io_delegate,
+ ClassNames header_type) {
+ unique_ptr<Document> header;
+ switch (header_type) {
+ case ClassNames::INTERFACE:
+ header = BuildInterfaceHeader(types, interface);
+ break;
+ case ClassNames::CLIENT:
+ header = BuildClientHeader(types, interface);
+ break;
+ case ClassNames::SERVER:
+ header = BuildServerHeader(types, interface);
+ break;
+ default:
+ LOG(FATAL) << "aidl internal error";
+ }
+ if (!header) {
+ LOG(ERROR) << "aidl internal error: Failed to generate header.";
+ return false;
+ }
+
+ const string header_path = options.OutputHeaderDir() + OS_PATH_SEPARATOR +
+ HeaderFile(interface, header_type);
+ unique_ptr<CodeWriter> code_writer(io_delegate.GetCodeWriter(header_path));
+ header->Write(code_writer.get());
+
+ const bool success = code_writer->Close();
+ if (!success) {
+ io_delegate.RemovePath(header_path);
+ }
+
+ return success;
+}
+
+} // namespace internals
+
+using namespace internals;
+
+string HeaderFile(const AidlInterface& interface,
+ ClassNames class_type,
+ bool use_os_sep) {
+ string file_path = interface.GetPackage();
+ for (char& c: file_path) {
+ if (c == '.') {
+ c = (use_os_sep) ? OS_PATH_SEPARATOR : '/';
+ }
+ }
+ if (!file_path.empty()) {
+ file_path += (use_os_sep) ? OS_PATH_SEPARATOR : '/';
+ }
+ file_path += ClassName(interface, class_type);
+ file_path += ".h";
+
+ return file_path;
+}
+
+bool GenerateCpp(const CppOptions& options,
+ const TypeNamespace& types,
+ const AidlInterface& interface,
+ const IoDelegate& io_delegate) {
+ auto interface_src = BuildInterfaceSource(types, interface);
+ auto client_src = BuildClientSource(types, interface);
+ auto server_src = BuildServerSource(types, interface);
+
+ if (!interface_src || !client_src || !server_src) {
+ return false;
+ }
+
+ if (!io_delegate.CreatedNestedDirs(options.OutputHeaderDir(),
+ interface.GetSplitPackage())) {
+ LOG(ERROR) << "Failed to create directory structure for headers.";
+ return false;
+ }
+
+ if (!WriteHeader(options, types, interface, io_delegate,
+ ClassNames::INTERFACE) ||
+ !WriteHeader(options, types, interface, io_delegate,
+ ClassNames::CLIENT) ||
+ !WriteHeader(options, types, interface, io_delegate,
+ ClassNames::SERVER)) {
+ return false;
+ }
+
+ unique_ptr<CodeWriter> writer = io_delegate.GetCodeWriter(
+ options.OutputCppFilePath());
+ interface_src->Write(writer.get());
+ client_src->Write(writer.get());
+ server_src->Write(writer.get());
+
+ const bool success = writer->Close();
+ if (!success) {
+ io_delegate.RemovePath(options.OutputCppFilePath());
+ }
+
+ return success;
+}
+
+} // namespace cpp
+} // namespace aidl
+} // namespace android
diff --git a/aidl/generate_cpp.h b/aidl/generate_cpp.h
new file mode 100644
index 0000000..1ffc681
--- /dev/null
+++ b/aidl/generate_cpp.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_GENERATE_CPP_H_
+#define AIDL_GENERATE_CPP_H_
+
+#include <memory>
+#include <string>
+
+#include "aidl_language.h"
+#include "ast_cpp.h"
+#include "options.h"
+#include "type_cpp.h"
+
+namespace android {
+namespace aidl {
+namespace cpp {
+
+bool GenerateCpp(const CppOptions& options,
+ const cpp::TypeNamespace& types,
+ const AidlInterface& parsed_doc,
+ const IoDelegate& io_delegate);
+
+// These roughly correspond to the various class names in the C++ hierarchy:
+enum class ClassNames {
+ BASE, // Foo (not a real class, but useful in some circumstances).
+ CLIENT, // BpFoo
+ SERVER, // BnFoo
+ INTERFACE, // IFoo
+};
+
+// Generate the relative path to a header file. If |use_os_sep| we'll use the
+// operating system specific path separator rather than C++'s expected '/' when
+// including headers.
+std::string HeaderFile(const AidlInterface& interface, ClassNames class_type,
+ bool use_os_sep = true);
+
+namespace internals {
+std::unique_ptr<Document> BuildClientSource(const TypeNamespace& types,
+ const AidlInterface& parsed_doc);
+std::unique_ptr<Document> BuildServerSource(const TypeNamespace& types,
+ const AidlInterface& parsed_doc);
+std::unique_ptr<Document> BuildInterfaceSource(const TypeNamespace& types,
+ const AidlInterface& parsed_doc);
+std::unique_ptr<Document> BuildClientHeader(const TypeNamespace& types,
+ const AidlInterface& parsed_doc);
+std::unique_ptr<Document> BuildServerHeader(const TypeNamespace& types,
+ const AidlInterface& parsed_doc);
+std::unique_ptr<Document> BuildInterfaceHeader(const TypeNamespace& types,
+ const AidlInterface& parsed_doc);
+}
+} // namespace cpp
+} // namespace aidl
+} // namespace android
+
+#endif // AIDL_GENERATE_CPP_H_
diff --git a/aidl/generate_cpp_unittest.cpp b/aidl/generate_cpp_unittest.cpp
new file mode 100644
index 0000000..50bfd9c
--- /dev/null
+++ b/aidl/generate_cpp_unittest.cpp
@@ -0,0 +1,888 @@
+/*
+ * 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 <string>
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "aidl.h"
+#include "aidl_language.h"
+#include "ast_cpp.h"
+#include "code_writer.h"
+#include "generate_cpp.h"
+#include "os.h"
+#include "tests/fake_io_delegate.h"
+#include "tests/test_util.h"
+#include "type_cpp.h"
+
+using ::android::aidl::test::FakeIoDelegate;
+using ::android::base::StringPrintf;
+using std::string;
+using std::unique_ptr;
+
+namespace android {
+namespace aidl {
+namespace cpp {
+namespace {
+
+const string kComplexTypeInterfaceAIDL =
+R"(package android.os;
+import foo.IFooType;
+interface IComplexTypeInterface {
+ const int MY_CONSTANT = 3;
+ int[] Send(in @nullable int[] goes_in, inout double[] goes_in_and_out, out boolean[] goes_out);
+ oneway void Piff(int times);
+ IFooType TakesABinder(IFooType f);
+ @nullable IFooType NullableBinder();
+ List<String> StringListMethod(in java.util.List<String> input, out List<String> output);
+ List<IBinder> BinderListMethod(in java.util.List<IBinder> input, out List<IBinder> output);
+ FileDescriptor TakesAFileDescriptor(in FileDescriptor f);
+ FileDescriptor[] TakesAFileDescriptorArray(in FileDescriptor[] f);
+})";
+
+const char kExpectedComplexTypeClientHeaderOutput[] =
+R"(#ifndef AIDL_GENERATED_ANDROID_OS_BP_COMPLEX_TYPE_INTERFACE_H_
+#define AIDL_GENERATED_ANDROID_OS_BP_COMPLEX_TYPE_INTERFACE_H_
+
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+#include <utils/Errors.h>
+#include <android/os/IComplexTypeInterface.h>
+
+namespace android {
+
+namespace os {
+
+class BpComplexTypeInterface : public ::android::BpInterface<IComplexTypeInterface> {
+public:
+explicit BpComplexTypeInterface(const ::android::sp<::android::IBinder>& _aidl_impl);
+virtual ~BpComplexTypeInterface() = default;
+::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;
+}; // class BpComplexTypeInterface
+
+} // namespace os
+
+} // namespace android
+
+#endif // AIDL_GENERATED_ANDROID_OS_BP_COMPLEX_TYPE_INTERFACE_H_
+)";
+
+const char kExpectedComplexTypeClientSourceOutput[] =
+R"(#include <android/os/BpComplexTypeInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+namespace os {
+
+BpComplexTypeInterface::BpComplexTypeInterface(const ::android::sp<::android::IBinder>& _aidl_impl)
+ : BpInterface<IComplexTypeInterface>(_aidl_impl){
+}
+
+::android::binder::Status BpComplexTypeInterface::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) {
+::android::Parcel _aidl_data;
+::android::Parcel _aidl_reply;
+::android::status_t _aidl_ret_status = ::android::OK;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeInt32Vector(goes_in);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeDoubleVector(*goes_in_and_out);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeVectorSize(*goes_out);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IComplexTypeInterface::SEND, _aidl_data, &_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+if (!_aidl_status.isOk()) {
+return _aidl_status;
+}
+_aidl_ret_status = _aidl_reply.readInt32Vector(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_reply.readDoubleVector(goes_in_and_out);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_reply.readBoolVector(goes_out);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+::android::binder::Status BpComplexTypeInterface::Piff(int32_t times) {
+::android::Parcel _aidl_data;
+::android::Parcel _aidl_reply;
+::android::status_t _aidl_ret_status = ::android::OK;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeInt32(times);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IComplexTypeInterface::PIFF, _aidl_data, &_aidl_reply, ::android::IBinder::FLAG_ONEWAY);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+::android::binder::Status BpComplexTypeInterface::TakesABinder(const ::android::sp<::foo::IFooType>& f, ::android::sp<::foo::IFooType>* _aidl_return) {
+::android::Parcel _aidl_data;
+::android::Parcel _aidl_reply;
+::android::status_t _aidl_ret_status = ::android::OK;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeStrongBinder(::foo::IFooType::asBinder(f));
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IComplexTypeInterface::TAKESABINDER, _aidl_data, &_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+if (!_aidl_status.isOk()) {
+return _aidl_status;
+}
+_aidl_ret_status = _aidl_reply.readStrongBinder(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+::android::binder::Status BpComplexTypeInterface::NullableBinder(::android::sp<::foo::IFooType>* _aidl_return) {
+::android::Parcel _aidl_data;
+::android::Parcel _aidl_reply;
+::android::status_t _aidl_ret_status = ::android::OK;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IComplexTypeInterface::NULLABLEBINDER, _aidl_data, &_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+if (!_aidl_status.isOk()) {
+return _aidl_status;
+}
+_aidl_ret_status = _aidl_reply.readNullableStrongBinder(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+::android::binder::Status BpComplexTypeInterface::StringListMethod(const ::std::vector<::android::String16>& input, ::std::vector<::android::String16>* output, ::std::vector<::android::String16>* _aidl_return) {
+::android::Parcel _aidl_data;
+::android::Parcel _aidl_reply;
+::android::status_t _aidl_ret_status = ::android::OK;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeString16Vector(input);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IComplexTypeInterface::STRINGLISTMETHOD, _aidl_data, &_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+if (!_aidl_status.isOk()) {
+return _aidl_status;
+}
+_aidl_ret_status = _aidl_reply.readString16Vector(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_reply.readString16Vector(output);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+::android::binder::Status BpComplexTypeInterface::BinderListMethod(const ::std::vector<::android::sp<::android::IBinder>>& input, ::std::vector<::android::sp<::android::IBinder>>* output, ::std::vector<::android::sp<::android::IBinder>>* _aidl_return) {
+::android::Parcel _aidl_data;
+::android::Parcel _aidl_reply;
+::android::status_t _aidl_ret_status = ::android::OK;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeStrongBinderVector(input);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IComplexTypeInterface::BINDERLISTMETHOD, _aidl_data, &_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+if (!_aidl_status.isOk()) {
+return _aidl_status;
+}
+_aidl_ret_status = _aidl_reply.readStrongBinderVector(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_reply.readStrongBinderVector(output);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+::android::binder::Status BpComplexTypeInterface::TakesAFileDescriptor(const ::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;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeUniqueFileDescriptor(f);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IComplexTypeInterface::TAKESAFILEDESCRIPTOR, _aidl_data, &_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+if (!_aidl_status.isOk()) {
+return _aidl_status;
+}
+_aidl_ret_status = _aidl_reply.readUniqueFileDescriptor(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+::android::binder::Status BpComplexTypeInterface::TakesAFileDescriptorArray(const ::std::vector<::android::base::unique_fd>& f, ::std::vector<::android::base::unique_fd>* _aidl_return) {
+::android::Parcel _aidl_data;
+::android::Parcel _aidl_reply;
+::android::status_t _aidl_ret_status = ::android::OK;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeUniqueFileDescriptorVector(f);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IComplexTypeInterface::TAKESAFILEDESCRIPTORARRAY, _aidl_data, &_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+if (!_aidl_status.isOk()) {
+return _aidl_status;
+}
+_aidl_ret_status = _aidl_reply.readUniqueFileDescriptorVector(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+} // namespace os
+
+} // namespace android
+)";
+
+const char kExpectedComplexTypeServerHeaderOutput[] =
+R"(#ifndef AIDL_GENERATED_ANDROID_OS_BN_COMPLEX_TYPE_INTERFACE_H_
+#define AIDL_GENERATED_ANDROID_OS_BN_COMPLEX_TYPE_INTERFACE_H_
+
+#include <binder/IInterface.h>
+#include <android/os/IComplexTypeInterface.h>
+
+namespace android {
+
+namespace os {
+
+class BnComplexTypeInterface : public ::android::BnInterface<IComplexTypeInterface> {
+public:
+::android::status_t onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags = 0) override;
+}; // class BnComplexTypeInterface
+
+} // namespace os
+
+} // namespace android
+
+#endif // AIDL_GENERATED_ANDROID_OS_BN_COMPLEX_TYPE_INTERFACE_H_
+)";
+
+const char kExpectedComplexTypeServerSourceOutput[] =
+R"(#include <android/os/BnComplexTypeInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+namespace os {
+
+::android::status_t BnComplexTypeInterface::onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) {
+::android::status_t _aidl_ret_status = ::android::OK;
+switch (_aidl_code) {
+case Call::SEND:
+{
+::std::unique_ptr<::std::vector<int32_t>> in_goes_in;
+::std::vector<double> in_goes_in_and_out;
+::std::vector<bool> out_goes_out;
+::std::vector<int32_t> _aidl_return;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+_aidl_ret_status = _aidl_data.readInt32Vector(&in_goes_in);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+_aidl_ret_status = _aidl_data.readDoubleVector(&in_goes_in_and_out);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+_aidl_ret_status = _aidl_data.resizeOutVector(&out_goes_out);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+::android::binder::Status _aidl_status(Send(in_goes_in, &in_goes_in_and_out, &out_goes_out, &_aidl_return));
+_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+if (!_aidl_status.isOk()) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeInt32Vector(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeDoubleVector(in_goes_in_and_out);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeBoolVector(out_goes_out);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+}
+break;
+case Call::PIFF:
+{
+int32_t in_times;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+_aidl_ret_status = _aidl_data.readInt32(&in_times);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+::android::binder::Status _aidl_status(Piff(in_times));
+}
+break;
+case Call::TAKESABINDER:
+{
+::android::sp<::foo::IFooType> in_f;
+::android::sp<::foo::IFooType> _aidl_return;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+_aidl_ret_status = _aidl_data.readStrongBinder(&in_f);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+::android::binder::Status _aidl_status(TakesABinder(in_f, &_aidl_return));
+_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+if (!_aidl_status.isOk()) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeStrongBinder(::foo::IFooType::asBinder(_aidl_return));
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+}
+break;
+case Call::NULLABLEBINDER:
+{
+::android::sp<::foo::IFooType> _aidl_return;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+::android::binder::Status _aidl_status(NullableBinder(&_aidl_return));
+_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+if (!_aidl_status.isOk()) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeStrongBinder(::foo::IFooType::asBinder(_aidl_return));
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+}
+break;
+case Call::STRINGLISTMETHOD:
+{
+::std::vector<::android::String16> in_input;
+::std::vector<::android::String16> out_output;
+::std::vector<::android::String16> _aidl_return;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+_aidl_ret_status = _aidl_data.readString16Vector(&in_input);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+::android::binder::Status _aidl_status(StringListMethod(in_input, &out_output, &_aidl_return));
+_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+if (!_aidl_status.isOk()) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeString16Vector(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeString16Vector(out_output);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+}
+break;
+case Call::BINDERLISTMETHOD:
+{
+::std::vector<::android::sp<::android::IBinder>> in_input;
+::std::vector<::android::sp<::android::IBinder>> out_output;
+::std::vector<::android::sp<::android::IBinder>> _aidl_return;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+_aidl_ret_status = _aidl_data.readStrongBinderVector(&in_input);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+::android::binder::Status _aidl_status(BinderListMethod(in_input, &out_output, &_aidl_return));
+_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+if (!_aidl_status.isOk()) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeStrongBinderVector(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeStrongBinderVector(out_output);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+}
+break;
+case Call::TAKESAFILEDESCRIPTOR:
+{
+::android::base::unique_fd in_f;
+::android::base::unique_fd _aidl_return;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+_aidl_ret_status = _aidl_data.readUniqueFileDescriptor(&in_f);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+::android::binder::Status _aidl_status(TakesAFileDescriptor(in_f, &_aidl_return));
+_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+if (!_aidl_status.isOk()) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeUniqueFileDescriptor(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+}
+break;
+case Call::TAKESAFILEDESCRIPTORARRAY:
+{
+::std::vector<::android::base::unique_fd> in_f;
+::std::vector<::android::base::unique_fd> _aidl_return;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+_aidl_ret_status = _aidl_data.readUniqueFileDescriptorVector(&in_f);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+::android::binder::Status _aidl_status(TakesAFileDescriptorArray(in_f, &_aidl_return));
+_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+if (!_aidl_status.isOk()) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeUniqueFileDescriptorVector(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+}
+break;
+default:
+{
+_aidl_ret_status = ::android::BBinder::onTransact(_aidl_code, _aidl_data, _aidl_reply, _aidl_flags);
+}
+break;
+}
+if (_aidl_ret_status == ::android::UNEXPECTED_NULL) {
+_aidl_ret_status = ::android::binder::Status::fromExceptionCode(::android::binder::Status::EX_NULL_POINTER).writeToParcel(_aidl_reply);
+}
+return _aidl_ret_status;
+}
+
+} // namespace os
+
+} // namespace android
+)";
+
+const char kExpectedComplexTypeInterfaceHeaderOutput[] =
+R"(#ifndef AIDL_GENERATED_ANDROID_OS_I_COMPLEX_TYPE_INTERFACE_H_
+#define AIDL_GENERATED_ANDROID_OS_I_COMPLEX_TYPE_INTERFACE_H_
+
+#include <android-base/unique_fd.h>
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+#include <binder/Status.h>
+#include <cstdint>
+#include <foo/IFooType.h>
+#include <memory>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+#include <vector>
+
+namespace android {
+
+namespace os {
+
+class IComplexTypeInterface : public ::android::IInterface {
+public:
+DECLARE_META_INTERFACE(ComplexTypeInterface)
+enum : int32_t {
+ MY_CONSTANT = 3,
+};
+virtual ::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) = 0;
+virtual ::android::binder::Status Piff(int32_t times) = 0;
+virtual ::android::binder::Status TakesABinder(const ::android::sp<::foo::IFooType>& f, ::android::sp<::foo::IFooType>* _aidl_return) = 0;
+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 TakesAFileDescriptorArray(const ::std::vector<::android::base::unique_fd>& f, ::std::vector<::android::base::unique_fd>* _aidl_return) = 0;
+enum Call {
+ SEND = ::android::IBinder::FIRST_CALL_TRANSACTION + 0,
+ PIFF = ::android::IBinder::FIRST_CALL_TRANSACTION + 1,
+ TAKESABINDER = ::android::IBinder::FIRST_CALL_TRANSACTION + 2,
+ NULLABLEBINDER = ::android::IBinder::FIRST_CALL_TRANSACTION + 3,
+ STRINGLISTMETHOD = ::android::IBinder::FIRST_CALL_TRANSACTION + 4,
+ BINDERLISTMETHOD = ::android::IBinder::FIRST_CALL_TRANSACTION + 5,
+ TAKESAFILEDESCRIPTOR = ::android::IBinder::FIRST_CALL_TRANSACTION + 6,
+ TAKESAFILEDESCRIPTORARRAY = ::android::IBinder::FIRST_CALL_TRANSACTION + 7,
+};
+}; // class IComplexTypeInterface
+
+} // namespace os
+
+} // namespace android
+
+#endif // AIDL_GENERATED_ANDROID_OS_I_COMPLEX_TYPE_INTERFACE_H_
+)";
+
+const char kExpectedComplexTypeInterfaceSourceOutput[] =
+R"(#include <android/os/IComplexTypeInterface.h>
+#include <android/os/BpComplexTypeInterface.h>
+
+namespace android {
+
+namespace os {
+
+IMPLEMENT_META_INTERFACE(ComplexTypeInterface, "android.os.IComplexTypeInterface")
+
+} // namespace os
+
+} // namespace android
+)";
+
+} // namespace
+
+class ASTTest : public ::testing::Test {
+ protected:
+ ASTTest(string file_path, string file_contents)
+ : file_path_(file_path),
+ file_contents_(file_contents) {
+ types_.Init();
+ }
+
+ unique_ptr<AidlInterface> Parse() {
+ io_delegate_.SetFileContents(file_path_, file_contents_);
+
+ unique_ptr<AidlInterface> ret;
+ std::vector<std::unique_ptr<AidlImport>> imports;
+ AidlError err = ::android::aidl::internals::load_and_validate_aidl(
+ {}, // no preprocessed files
+ {"."},
+ file_path_,
+ io_delegate_,
+ &types_,
+ &ret,
+ &imports);
+
+ if (err != AidlError::OK)
+ return nullptr;
+
+ return ret;
+ }
+
+ void Compare(Document* doc, const char* expected) {
+ string output;
+ unique_ptr<CodeWriter> cw = GetStringWriter(&output);
+
+ doc->Write(cw.get());
+
+ if (expected == output) {
+ return; // Success
+ }
+
+ test::PrintDiff(expected, output);
+ FAIL() << "Document contents did not match expected contents";
+ }
+
+ const string file_path_;
+ const string file_contents_;
+ FakeIoDelegate io_delegate_;
+ TypeNamespace types_;
+};
+
+class ComplexTypeInterfaceASTTest : public ASTTest {
+ public:
+ ComplexTypeInterfaceASTTest()
+ : ASTTest("android/os/IComplexTypeInterface.aidl",
+ kComplexTypeInterfaceAIDL) {
+ io_delegate_.SetFileContents("foo/IFooType.aidl",
+ "package foo; interface IFooType {}");
+ }
+};
+
+TEST_F(ComplexTypeInterfaceASTTest, GeneratesClientHeader) {
+ unique_ptr<AidlInterface> interface = Parse();
+ ASSERT_NE(interface, nullptr);
+ unique_ptr<Document> doc = internals::BuildClientHeader(types_, *interface);
+ Compare(doc.get(), kExpectedComplexTypeClientHeaderOutput);
+}
+
+TEST_F(ComplexTypeInterfaceASTTest, GeneratesClientSource) {
+ unique_ptr<AidlInterface> interface = Parse();
+ ASSERT_NE(interface, nullptr);
+ unique_ptr<Document> doc = internals::BuildClientSource(types_, *interface);
+ Compare(doc.get(), kExpectedComplexTypeClientSourceOutput);
+}
+
+TEST_F(ComplexTypeInterfaceASTTest, GeneratesServerHeader) {
+ unique_ptr<AidlInterface> interface = Parse();
+ ASSERT_NE(interface, nullptr);
+ unique_ptr<Document> doc = internals::BuildServerHeader(types_, *interface);
+ Compare(doc.get(), kExpectedComplexTypeServerHeaderOutput);
+}
+
+TEST_F(ComplexTypeInterfaceASTTest, GeneratesServerSource) {
+ unique_ptr<AidlInterface> interface = Parse();
+ ASSERT_NE(interface, nullptr);
+ unique_ptr<Document> doc = internals::BuildServerSource(types_, *interface);
+ Compare(doc.get(), kExpectedComplexTypeServerSourceOutput);
+}
+
+TEST_F(ComplexTypeInterfaceASTTest, GeneratesInterfaceHeader) {
+ unique_ptr<AidlInterface> interface = Parse();
+ ASSERT_NE(interface, nullptr);
+ unique_ptr<Document> doc = internals::BuildInterfaceHeader(types_, *interface);
+ Compare(doc.get(), kExpectedComplexTypeInterfaceHeaderOutput);
+}
+
+TEST_F(ComplexTypeInterfaceASTTest, GeneratesInterfaceSource) {
+ unique_ptr<AidlInterface> interface = Parse();
+ ASSERT_NE(interface, nullptr);
+ unique_ptr<Document> doc = internals::BuildInterfaceSource(types_, *interface);
+ Compare(doc.get(), kExpectedComplexTypeInterfaceSourceOutput);
+}
+
+namespace test_io_handling {
+
+const char kInputPath[] = "a/IFoo.aidl";
+const char kOutputPath[] = "output.cpp";
+const char kHeaderDir[] = "headers";
+const char kInterfaceHeaderRelPath[] = "a/IFoo.h";
+
+} // namespace test_io_handling
+
+class IoErrorHandlingTest : public ASTTest {
+ public:
+ IoErrorHandlingTest ()
+ : ASTTest(test_io_handling::kInputPath,
+ "package a; interface IFoo {}"),
+ options_(GetOptions()) {}
+
+ const unique_ptr<CppOptions> options_;
+
+ private:
+ static unique_ptr<CppOptions> GetOptions() {
+ using namespace test_io_handling;
+
+ const int argc = 4;
+ const char* cmdline[argc] = {
+ "aidl-cpp", kInputPath, kHeaderDir, kOutputPath
+ };
+ return CppOptions::Parse(argc, cmdline);
+ }
+};
+
+TEST_F(IoErrorHandlingTest, GenerateCorrectlyAbsentErrors) {
+ // Confirm that this is working correctly without I/O problems.
+ const unique_ptr<AidlInterface> interface = Parse();
+ ASSERT_NE(interface, nullptr);
+ ASSERT_TRUE(GenerateCpp(*options_, types_, *interface, io_delegate_));
+}
+
+TEST_F(IoErrorHandlingTest, HandlesBadHeaderWrite) {
+ using namespace test_io_handling;
+ const unique_ptr<AidlInterface> interface = Parse();
+ ASSERT_NE(interface, nullptr);
+
+ // Simulate issues closing the interface header.
+ const string header_path =
+ StringPrintf("%s%c%s", kHeaderDir, OS_PATH_SEPARATOR,
+ kInterfaceHeaderRelPath);
+ io_delegate_.AddBrokenFilePath(header_path);
+ ASSERT_FALSE(GenerateCpp(*options_, types_, *interface, io_delegate_));
+ // We should never attempt to write the C++ file if we fail writing headers.
+ ASSERT_FALSE(io_delegate_.GetWrittenContents(kOutputPath, nullptr));
+ // We should remove partial results.
+ ASSERT_TRUE(io_delegate_.PathWasRemoved(header_path));
+}
+
+TEST_F(IoErrorHandlingTest, HandlesBadCppWrite) {
+ using test_io_handling::kOutputPath;
+ const unique_ptr<AidlInterface> interface = Parse();
+ ASSERT_NE(interface, nullptr);
+
+ // Simulate issues closing the cpp file.
+ io_delegate_.AddBrokenFilePath(kOutputPath);
+ ASSERT_FALSE(GenerateCpp(*options_, types_, *interface, io_delegate_));
+ // We should remove partial results.
+ ASSERT_TRUE(io_delegate_.PathWasRemoved(kOutputPath));
+}
+
+} // namespace cpp
+} // namespace aidl
+} // namespace android
diff --git a/aidl/generate_java.cpp b/aidl/generate_java.cpp
new file mode 100644
index 0000000..14df353
--- /dev/null
+++ b/aidl/generate_java.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016, 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 "generate_java.h"
+
+#include <memory>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <android-base/stringprintf.h>
+
+#include "code_writer.h"
+#include "type_java.h"
+
+using std::unique_ptr;
+using ::android::aidl::java::Variable;
+using std::string;
+using android::base::StringPrintf;
+
+namespace android {
+namespace aidl {
+
+// =================================================
+VariableFactory::VariableFactory(const string& base)
+ : base_(base),
+ index_(0) {
+}
+
+Variable* VariableFactory::Get(const Type* type) {
+ Variable* v = new Variable(
+ type, StringPrintf("%s%d", base_.c_str(), index_));
+ vars_.push_back(v);
+ index_++;
+ return v;
+}
+
+Variable* VariableFactory::Get(int index) {
+ return vars_[index];
+}
+
+namespace java {
+
+int generate_java(const string& filename, const string& originalSrc,
+ AidlInterface* iface, JavaTypeNamespace* types,
+ const IoDelegate& io_delegate) {
+ Class* cl = generate_binder_interface_class(iface, types);
+
+ Document* document = new Document(
+ "" /* no comment */,
+ (!iface->GetPackage().empty()) ? iface->GetPackage() : "",
+ originalSrc,
+ unique_ptr<Class>(cl));
+
+ CodeWriterPtr code_writer = io_delegate.GetCodeWriter(filename);
+ document->Write(code_writer.get());
+
+ return 0;
+}
+
+} // namespace java
+} // namespace android
+} // namespace aidl
diff --git a/aidl/generate_java.h b/aidl/generate_java.h
new file mode 100644
index 0000000..ed04ba1
--- /dev/null
+++ b/aidl/generate_java.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016, 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.
+ */
+
+#ifndef AIDL_GENERATE_JAVA_H_
+#define AIDL_GENERATE_JAVA_H_
+
+#include <string>
+
+#include "aidl_language.h"
+#include "ast_java.h"
+#include "io_delegate.h"
+
+namespace android {
+namespace aidl {
+
+namespace java {
+
+class JavaTypeNamespace;
+
+int generate_java(const std::string& filename, const std::string& originalSrc,
+ AidlInterface* iface, java::JavaTypeNamespace* types,
+ const IoDelegate& io_delegate);
+
+android::aidl::java::Class* generate_binder_interface_class(
+ const AidlInterface* iface, java::JavaTypeNamespace* types);
+
+} // namespace java
+
+class VariableFactory {
+ public:
+ using Variable = ::android::aidl::java::Variable;
+ using Type = ::android::aidl::java::Type;
+
+ explicit VariableFactory(const std::string& base); // base must be short
+ Variable* Get(const Type* type);
+ Variable* Get(int index);
+
+ private:
+ std::vector<Variable*> vars_;
+ std::string base_;
+ int index_;
+
+ DISALLOW_COPY_AND_ASSIGN(VariableFactory);
+};
+
+} // namespace android
+} // namespace aidl
+
+#endif // AIDL_GENERATE_JAVA_H_
diff --git a/aidl/generate_java_binder.cpp b/aidl/generate_java_binder.cpp
new file mode 100644
index 0000000..02a6922
--- /dev/null
+++ b/aidl/generate_java_binder.cpp
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C) 2016, 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 "generate_java.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string.h>
+
+#include <android-base/macros.h>
+
+#include "type_java.h"
+
+using std::string;
+
+namespace android {
+namespace aidl {
+namespace java {
+
+// =================================================
+class StubClass : public Class {
+ public:
+ StubClass(const Type* type, const InterfaceType* interfaceType,
+ JavaTypeNamespace* types);
+ virtual ~StubClass() = default;
+
+ Variable* transact_code;
+ Variable* transact_data;
+ Variable* transact_reply;
+ Variable* transact_flags;
+ SwitchStatement* transact_switch;
+
+ private:
+ void make_as_interface(const InterfaceType* interfaceType,
+ JavaTypeNamespace* types);
+
+ DISALLOW_COPY_AND_ASSIGN(StubClass);
+};
+
+StubClass::StubClass(const Type* type, const InterfaceType* interfaceType,
+ JavaTypeNamespace* types)
+ : Class() {
+ this->comment = "/** Local-side IPC implementation stub class. */";
+ this->modifiers = PUBLIC | ABSTRACT | STATIC;
+ this->what = Class::CLASS;
+ this->type = type;
+ this->extends = types->BinderNativeType();
+ this->interfaces.push_back(interfaceType);
+
+ // descriptor
+ Field* descriptor =
+ new Field(STATIC | FINAL | PRIVATE,
+ new Variable(types->StringType(), "DESCRIPTOR"));
+ descriptor->value = "\"" + interfaceType->JavaType() + "\"";
+ this->elements.push_back(descriptor);
+
+ // ctor
+ Method* ctor = new Method;
+ ctor->modifiers = PUBLIC;
+ ctor->comment =
+ "/** Construct the stub at attach it to the "
+ "interface. */";
+ ctor->name = "Stub";
+ ctor->statements = new StatementBlock;
+ MethodCall* attach =
+ new MethodCall(THIS_VALUE, "attachInterface", 2, THIS_VALUE,
+ new LiteralExpression("DESCRIPTOR"));
+ ctor->statements->Add(attach);
+ this->elements.push_back(ctor);
+
+ // asInterface
+ make_as_interface(interfaceType, types);
+
+ // asBinder
+ Method* asBinder = new Method;
+ asBinder->modifiers = PUBLIC | OVERRIDE;
+ asBinder->returnType = types->IBinderType();
+ asBinder->name = "asBinder";
+ asBinder->statements = new StatementBlock;
+ asBinder->statements->Add(new ReturnStatement(THIS_VALUE));
+ this->elements.push_back(asBinder);
+
+ // onTransact
+ this->transact_code = new Variable(types->IntType(), "code");
+ this->transact_data = new Variable(types->ParcelType(), "data");
+ this->transact_reply = new Variable(types->ParcelType(), "reply");
+ this->transact_flags = new Variable(types->IntType(), "flags");
+ Method* onTransact = new Method;
+ onTransact->modifiers = PUBLIC | OVERRIDE;
+ onTransact->returnType = types->BoolType();
+ onTransact->name = "onTransact";
+ onTransact->parameters.push_back(this->transact_code);
+ onTransact->parameters.push_back(this->transact_data);
+ onTransact->parameters.push_back(this->transact_reply);
+ onTransact->parameters.push_back(this->transact_flags);
+ onTransact->statements = new StatementBlock;
+ onTransact->exceptions.push_back(types->RemoteExceptionType());
+ this->elements.push_back(onTransact);
+ this->transact_switch = new SwitchStatement(this->transact_code);
+
+ onTransact->statements->Add(this->transact_switch);
+ MethodCall* superCall = new MethodCall(
+ SUPER_VALUE, "onTransact", 4, this->transact_code, this->transact_data,
+ this->transact_reply, this->transact_flags);
+ onTransact->statements->Add(new ReturnStatement(superCall));
+}
+
+void StubClass::make_as_interface(const InterfaceType* interfaceType,
+ JavaTypeNamespace* types) {
+ Variable* obj = new Variable(types->IBinderType(), "obj");
+
+ Method* m = new Method;
+ m->comment = "/**\n * Cast an IBinder object into an ";
+ m->comment += interfaceType->JavaType();
+ m->comment += " interface,\n";
+ m->comment += " * generating a proxy if needed.\n */";
+ m->modifiers = PUBLIC | STATIC;
+ m->returnType = interfaceType;
+ m->name = "asInterface";
+ m->parameters.push_back(obj);
+ m->statements = new StatementBlock;
+
+ IfStatement* ifstatement = new IfStatement();
+ ifstatement->expression = new Comparison(obj, "==", NULL_VALUE);
+ ifstatement->statements = new StatementBlock;
+ ifstatement->statements->Add(new ReturnStatement(NULL_VALUE));
+ m->statements->Add(ifstatement);
+
+ // IInterface iin = obj.queryLocalInterface(DESCRIPTOR)
+ MethodCall* queryLocalInterface = new MethodCall(obj, "queryLocalInterface");
+ queryLocalInterface->arguments.push_back(new LiteralExpression("DESCRIPTOR"));
+ IInterfaceType* iinType = new IInterfaceType(types);
+ Variable* iin = new Variable(iinType, "iin");
+ VariableDeclaration* iinVd =
+ new VariableDeclaration(iin, queryLocalInterface, NULL);
+ m->statements->Add(iinVd);
+
+ // Ensure the instance type of the local object is as expected.
+ // One scenario where this is needed is if another package (with a
+ // different class loader) runs in the same process as the service.
+
+ // if (iin != null && iin instanceof <interfaceType>) return (<interfaceType>)
+ // iin;
+ Comparison* iinNotNull = new Comparison(iin, "!=", NULL_VALUE);
+ Comparison* instOfCheck =
+ new Comparison(iin, " instanceof ",
+ new LiteralExpression(interfaceType->JavaType()));
+ IfStatement* instOfStatement = new IfStatement();
+ instOfStatement->expression = new Comparison(iinNotNull, "&&", instOfCheck);
+ instOfStatement->statements = new StatementBlock;
+ instOfStatement->statements->Add(
+ new ReturnStatement(new Cast(interfaceType, iin)));
+ m->statements->Add(instOfStatement);
+
+ NewExpression* ne = new NewExpression(interfaceType->GetProxy());
+ ne->arguments.push_back(obj);
+ m->statements->Add(new ReturnStatement(ne));
+
+ this->elements.push_back(m);
+}
+
+// =================================================
+class ProxyClass : public Class {
+ public:
+ ProxyClass(const JavaTypeNamespace* types, const Type* type,
+ const InterfaceType* interfaceType);
+ virtual ~ProxyClass();
+
+ Variable* mRemote;
+ bool mOneWay;
+};
+
+ProxyClass::ProxyClass(const JavaTypeNamespace* types, const Type* type,
+ const InterfaceType* interfaceType)
+ : Class() {
+ this->modifiers = PRIVATE | STATIC;
+ this->what = Class::CLASS;
+ this->type = type;
+ this->interfaces.push_back(interfaceType);
+
+ mOneWay = interfaceType->OneWay();
+
+ // IBinder mRemote
+ mRemote = new Variable(types->IBinderType(), "mRemote");
+ this->elements.push_back(new Field(PRIVATE, mRemote));
+
+ // Proxy()
+ Variable* remote = new Variable(types->IBinderType(), "remote");
+ Method* ctor = new Method;
+ ctor->name = "Proxy";
+ ctor->statements = new StatementBlock;
+ ctor->parameters.push_back(remote);
+ ctor->statements->Add(new Assignment(mRemote, remote));
+ this->elements.push_back(ctor);
+
+ // IBinder asBinder()
+ Method* asBinder = new Method;
+ asBinder->modifiers = PUBLIC | OVERRIDE;
+ asBinder->returnType = types->IBinderType();
+ asBinder->name = "asBinder";
+ asBinder->statements = new StatementBlock;
+ asBinder->statements->Add(new ReturnStatement(mRemote));
+ this->elements.push_back(asBinder);
+}
+
+ProxyClass::~ProxyClass() {}
+
+// =================================================
+static void generate_new_array(const Type* t, StatementBlock* addTo,
+ Variable* v, Variable* parcel,
+ JavaTypeNamespace* types) {
+ Variable* len = new Variable(types->IntType(), v->name + "_length");
+ addTo->Add(new VariableDeclaration(len, new MethodCall(parcel, "readInt")));
+ IfStatement* lencheck = new IfStatement();
+ lencheck->expression = new Comparison(len, "<", new LiteralExpression("0"));
+ lencheck->statements->Add(new Assignment(v, NULL_VALUE));
+ lencheck->elseif = new IfStatement();
+ lencheck->elseif->statements->Add(
+ new Assignment(v, new NewArrayExpression(t, len)));
+ addTo->Add(lencheck);
+}
+
+static void generate_write_to_parcel(const Type* t, StatementBlock* addTo,
+ Variable* v, Variable* parcel, int flags) {
+ t->WriteToParcel(addTo, v, parcel, flags);
+}
+
+static void generate_create_from_parcel(const Type* t, StatementBlock* addTo,
+ Variable* v, Variable* parcel,
+ Variable** cl) {
+ t->CreateFromParcel(addTo, v, parcel, cl);
+}
+
+static void generate_int_constant(const AidlIntConstant& constant,
+ Class* interface) {
+ IntConstant* decl = new IntConstant(constant.GetName(), constant.GetValue());
+ interface->elements.push_back(decl);
+}
+
+static void generate_string_constant(const AidlStringConstant& constant,
+ Class* interface) {
+ StringConstant* decl = new StringConstant(constant.GetName(),
+ constant.GetValue());
+ interface->elements.push_back(decl);
+}
+
+static void generate_method(const AidlMethod& method, Class* interface,
+ StubClass* stubClass, ProxyClass* proxyClass,
+ int index, JavaTypeNamespace* types) {
+ int i;
+
+ const bool oneway = proxyClass->mOneWay || method.IsOneway();
+
+ // == the TRANSACT_ constant =============================================
+ string transactCodeName = "TRANSACTION_";
+ transactCodeName += method.GetName();
+
+ char transactCodeValue[60];
+ sprintf(transactCodeValue, "(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)",
+ index);
+
+ Field* transactCode = new Field(
+ STATIC | FINAL, new Variable(types->IntType(), transactCodeName));
+ transactCode->value = transactCodeValue;
+ stubClass->elements.push_back(transactCode);
+
+ // == the declaration in the interface ===================================
+ Method* decl = new Method;
+ decl->comment = method.GetComments();
+ decl->modifiers = PUBLIC;
+ decl->returnType = method.GetType().GetLanguageType<Type>();
+ decl->returnTypeDimension = method.GetType().IsArray() ? 1 : 0;
+ decl->name = method.GetName();
+
+ for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
+ decl->parameters.push_back(
+ new Variable(arg->GetType().GetLanguageType<Type>(), arg->GetName(),
+ arg->GetType().IsArray() ? 1 : 0));
+ }
+
+ decl->exceptions.push_back(types->RemoteExceptionType());
+
+ interface->elements.push_back(decl);
+
+ // == the stub method ====================================================
+
+ Case* c = new Case(transactCodeName);
+
+ MethodCall* realCall = new MethodCall(THIS_VALUE, method.GetName());
+
+ // interface token validation is the very first thing we do
+ c->statements->Add(new MethodCall(stubClass->transact_data,
+ "enforceInterface", 1,
+ new LiteralExpression("DESCRIPTOR")));
+
+ // args
+ Variable* cl = NULL;
+ VariableFactory stubArgs("_arg");
+ for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
+ const Type* t = arg->GetType().GetLanguageType<Type>();
+ Variable* v = stubArgs.Get(t);
+ v->dimension = arg->GetType().IsArray() ? 1 : 0;
+
+ c->statements->Add(new VariableDeclaration(v));
+
+ if (arg->GetDirection() & AidlArgument::IN_DIR) {
+ generate_create_from_parcel(t, c->statements, v, stubClass->transact_data,
+ &cl);
+ } else {
+ if (!arg->GetType().IsArray()) {
+ c->statements->Add(new Assignment(v, new NewExpression(v->type)));
+ } else {
+ generate_new_array(v->type, c->statements, v, stubClass->transact_data,
+ types);
+ }
+ }
+
+ realCall->arguments.push_back(v);
+ }
+
+ // the real call
+ Variable* _result = NULL;
+ if (method.GetType().GetName() == "void") {
+ c->statements->Add(realCall);
+
+ if (!oneway) {
+ // report that there were no exceptions
+ MethodCall* ex =
+ new MethodCall(stubClass->transact_reply, "writeNoException", 0);
+ c->statements->Add(ex);
+ }
+ } else {
+ _result =
+ new Variable(decl->returnType, "_result", decl->returnTypeDimension);
+ c->statements->Add(new VariableDeclaration(_result, realCall));
+
+ if (!oneway) {
+ // report that there were no exceptions
+ MethodCall* ex =
+ new MethodCall(stubClass->transact_reply, "writeNoException", 0);
+ c->statements->Add(ex);
+ }
+
+ // marshall the return value
+ generate_write_to_parcel(decl->returnType, c->statements, _result,
+ stubClass->transact_reply,
+ Type::PARCELABLE_WRITE_RETURN_VALUE);
+ }
+
+ // out parameters
+ i = 0;
+ for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
+ const Type* t = arg->GetType().GetLanguageType<Type>();
+ Variable* v = stubArgs.Get(i++);
+
+ if (arg->GetDirection() & AidlArgument::OUT_DIR) {
+ generate_write_to_parcel(t, c->statements, v, stubClass->transact_reply,
+ Type::PARCELABLE_WRITE_RETURN_VALUE);
+ }
+ }
+
+ // return true
+ c->statements->Add(new ReturnStatement(TRUE_VALUE));
+ stubClass->transact_switch->cases.push_back(c);
+
+ // == the proxy method ===================================================
+ Method* proxy = new Method;
+ proxy->comment = method.GetComments();
+ proxy->modifiers = PUBLIC | OVERRIDE;
+ proxy->returnType = method.GetType().GetLanguageType<Type>();
+ proxy->returnTypeDimension = method.GetType().IsArray() ? 1 : 0;
+ proxy->name = method.GetName();
+ proxy->statements = new StatementBlock;
+ for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
+ proxy->parameters.push_back(
+ new Variable(arg->GetType().GetLanguageType<Type>(), arg->GetName(),
+ arg->GetType().IsArray() ? 1 : 0));
+ }
+ proxy->exceptions.push_back(types->RemoteExceptionType());
+ proxyClass->elements.push_back(proxy);
+
+ // the parcels
+ Variable* _data = new Variable(types->ParcelType(), "_data");
+ proxy->statements->Add(new VariableDeclaration(
+ _data, new MethodCall(types->ParcelType(), "obtain")));
+ Variable* _reply = NULL;
+ if (!oneway) {
+ _reply = new Variable(types->ParcelType(), "_reply");
+ proxy->statements->Add(new VariableDeclaration(
+ _reply, new MethodCall(types->ParcelType(), "obtain")));
+ }
+
+ // the return value
+ _result = NULL;
+ if (method.GetType().GetName() != "void") {
+ _result = new Variable(proxy->returnType, "_result",
+ method.GetType().IsArray() ? 1 : 0);
+ proxy->statements->Add(new VariableDeclaration(_result));
+ }
+
+ // try and finally
+ TryStatement* tryStatement = new TryStatement();
+ proxy->statements->Add(tryStatement);
+ FinallyStatement* finallyStatement = new FinallyStatement();
+ proxy->statements->Add(finallyStatement);
+
+ // the interface identifier token: the DESCRIPTOR constant, marshalled as a
+ // string
+ tryStatement->statements->Add(new MethodCall(
+ _data, "writeInterfaceToken", 1, new LiteralExpression("DESCRIPTOR")));
+
+ // the parameters
+ for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
+ const Type* t = arg->GetType().GetLanguageType<Type>();
+ Variable* v =
+ new Variable(t, arg->GetName(), arg->GetType().IsArray() ? 1 : 0);
+ AidlArgument::Direction dir = arg->GetDirection();
+ if (dir == AidlArgument::OUT_DIR && arg->GetType().IsArray()) {
+ IfStatement* checklen = new IfStatement();
+ checklen->expression = new Comparison(v, "==", NULL_VALUE);
+ checklen->statements->Add(
+ new MethodCall(_data, "writeInt", 1, new LiteralExpression("-1")));
+ checklen->elseif = new IfStatement();
+ checklen->elseif->statements->Add(
+ new MethodCall(_data, "writeInt", 1, new FieldVariable(v, "length")));
+ tryStatement->statements->Add(checklen);
+ } else if (dir & AidlArgument::IN_DIR) {
+ generate_write_to_parcel(t, tryStatement->statements, v, _data, 0);
+ } else {
+ delete v;
+ }
+ }
+
+ // the transact call
+ MethodCall* call = new MethodCall(
+ proxyClass->mRemote, "transact", 4,
+ new LiteralExpression("Stub." + transactCodeName), _data,
+ _reply ? _reply : NULL_VALUE,
+ new LiteralExpression(oneway ? "android.os.IBinder.FLAG_ONEWAY" : "0"));
+ tryStatement->statements->Add(call);
+
+ // throw back exceptions.
+ if (_reply) {
+ MethodCall* ex = new MethodCall(_reply, "readException", 0);
+ tryStatement->statements->Add(ex);
+ }
+
+ // returning and cleanup
+ if (_reply != NULL) {
+ if (_result != NULL) {
+ generate_create_from_parcel(proxy->returnType, tryStatement->statements,
+ _result, _reply, &cl);
+ }
+
+ // the out/inout parameters
+ for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
+ const Type* t = arg->GetType().GetLanguageType<Type>();
+ if (arg->GetDirection() & AidlArgument::OUT_DIR) {
+ Variable* v =
+ new Variable(t, arg->GetName(), arg->GetType().IsArray() ? 1 : 0);
+ t->ReadFromParcel(tryStatement->statements, v, _reply, &cl);
+ }
+ }
+
+ finallyStatement->statements->Add(new MethodCall(_reply, "recycle"));
+ }
+ finallyStatement->statements->Add(new MethodCall(_data, "recycle"));
+
+ if (_result != NULL) {
+ proxy->statements->Add(new ReturnStatement(_result));
+ }
+}
+
+static void generate_interface_descriptors(StubClass* stub, ProxyClass* proxy,
+ const JavaTypeNamespace* types) {
+ // the interface descriptor transaction handler
+ Case* c = new Case("INTERFACE_TRANSACTION");
+ c->statements->Add(new MethodCall(stub->transact_reply, "writeString", 1,
+ new LiteralExpression("DESCRIPTOR")));
+ c->statements->Add(new ReturnStatement(TRUE_VALUE));
+ stub->transact_switch->cases.push_back(c);
+
+ // and the proxy-side method returning the descriptor directly
+ Method* getDesc = new Method;
+ getDesc->modifiers = PUBLIC;
+ getDesc->returnType = types->StringType();
+ getDesc->returnTypeDimension = 0;
+ getDesc->name = "getInterfaceDescriptor";
+ getDesc->statements = new StatementBlock;
+ getDesc->statements->Add(
+ new ReturnStatement(new LiteralExpression("DESCRIPTOR")));
+ proxy->elements.push_back(getDesc);
+}
+
+Class* generate_binder_interface_class(const AidlInterface* iface,
+ JavaTypeNamespace* types) {
+ const InterfaceType* interfaceType = iface->GetLanguageType<InterfaceType>();
+
+ // the interface class
+ Class* interface = new Class;
+ interface->comment = iface->GetComments();
+ interface->modifiers = PUBLIC;
+ interface->what = Class::INTERFACE;
+ interface->type = interfaceType;
+ interface->interfaces.push_back(types->IInterfaceType());
+
+ // the stub inner class
+ StubClass* stub =
+ new StubClass(interfaceType->GetStub(), interfaceType, types);
+ interface->elements.push_back(stub);
+
+ // the proxy inner class
+ ProxyClass* proxy =
+ new ProxyClass(types, interfaceType->GetProxy(), interfaceType);
+ stub->elements.push_back(proxy);
+
+ // stub and proxy support for getInterfaceDescriptor()
+ generate_interface_descriptors(stub, proxy, types);
+
+ // all the declared constants of the interface
+ for (const auto& item : iface->GetIntConstants()) {
+ generate_int_constant(*item, interface);
+ }
+ for (const auto& item : iface->GetStringConstants()) {
+ generate_string_constant(*item, interface);
+ }
+
+ // all the declared methods of the interface
+ for (const auto& item : iface->GetMethods()) {
+ generate_method(*item, interface, stub, proxy, item->GetId(), types);
+ }
+
+ return interface;
+}
+
+} // namespace java
+} // namespace android
+} // namespace aidl
diff --git a/aidl/import_resolver.cpp b/aidl/import_resolver.cpp
new file mode 100644
index 0000000..4957c76
--- /dev/null
+++ b/aidl/import_resolver.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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 "import_resolver.h"
+
+#include <unistd.h>
+
+#ifdef _WIN32
+#include <io.h>
+#endif
+
+#include "os.h"
+
+using std::string;
+using std::vector;
+
+namespace android {
+namespace aidl {
+
+ImportResolver::ImportResolver(const IoDelegate& io_delegate,
+ const vector<string>& import_paths)
+ : io_delegate_(io_delegate) {
+ for (string path : import_paths) {
+ if (path.empty()) {
+ path = ".";
+ }
+ if (path[path.size() - 1] != OS_PATH_SEPARATOR) {
+ path += OS_PATH_SEPARATOR;
+ }
+ import_paths_.push_back(std::move(path));
+ }
+}
+
+
+string ImportResolver::FindImportFile(const string& canonical_name) const {
+ // Convert the canonical name to a relative file path.
+ string relative_path = canonical_name;
+ for (char& c : relative_path) {
+ if (c == '.') {
+ c = OS_PATH_SEPARATOR;
+ }
+ }
+ relative_path += ".aidl";
+
+ // Look for that relative path at each of our import roots.
+ for (string path : import_paths_) {
+ path = path + relative_path;
+ if (io_delegate_.FileIsReadable(path)) {
+ return path;
+ }
+ }
+
+ return "";
+}
+
+} // namespace android
+} // namespace aidl
diff --git a/aidl/import_resolver.h b/aidl/import_resolver.h
new file mode 100644
index 0000000..e7562fb
--- /dev/null
+++ b/aidl/import_resolver.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_IMPORT_RESOLVER_H_
+#define AIDL_IMPORT_RESOLVER_H_
+
+#include <string>
+#include <vector>
+
+#include <android-base/macros.h>
+
+#include "io_delegate.h"
+
+namespace android {
+namespace aidl {
+
+class ImportResolver {
+ public:
+ ImportResolver(const IoDelegate& io_delegate,
+ const std::vector<std::string>& import_paths);
+ virtual ~ImportResolver() = default;
+
+ // Resolve the canonical name for a class to a file that exists
+ // in one of the import paths given to the ImportResolver.
+ std::string FindImportFile(const std::string& canonical_name) const;
+
+ private:
+ const IoDelegate& io_delegate_;
+ std::vector<std::string> import_paths_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImportResolver);
+};
+
+} // namespace android
+} // namespace aidl
+
+#endif // AIDL_IMPORT_RESOLVER_H_
diff --git a/aidl/io_delegate.cpp b/aidl/io_delegate.cpp
new file mode 100644
index 0000000..62be3f5
--- /dev/null
+++ b/aidl/io_delegate.cpp
@@ -0,0 +1,190 @@
+/*
+ * 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 "io_delegate.h"
+
+#include <cstring>
+#include <fstream>
+#include <vector>
+
+#ifdef _WIN32
+#include <direct.h>
+#else
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
+#include <android-base/strings.h>
+
+#include "logging.h"
+#include "os.h"
+
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+using android::base::Split;
+
+namespace android {
+namespace aidl {
+
+bool IoDelegate::GetAbsolutePath(const string& path, string* absolute_path) {
+#ifdef _WIN32
+
+ char buf[4096];
+ DWORD path_len = GetFullPathName(path.c_str(), sizeof(buf), buf, nullptr);
+ if (path_len <= 0 || path_len >= sizeof(buf)) {
+ LOG(ERROR) << "Failed to GetFullPathName(" << path << ")";
+ return false;
+ }
+ *absolute_path = buf;
+
+ return true;
+
+#else
+
+ if (path.empty()) {
+ LOG(ERROR) << "Giving up on finding an absolute path to represent the "
+ "empty string.";
+ return false;
+ }
+ if (path[0] == OS_PATH_SEPARATOR) {
+ *absolute_path = path;
+ return true;
+ }
+
+ char buf[4096];
+ if (getcwd(buf, sizeof(buf)) == nullptr) {
+ LOG(ERROR) << "Path of current working directory does not fit in "
+ << sizeof(buf) << " bytes";
+ return false;
+ }
+
+ *absolute_path = buf;
+ *absolute_path += OS_PATH_SEPARATOR;
+ *absolute_path += path;
+ return true;
+#endif
+}
+
+unique_ptr<string> IoDelegate::GetFileContents(
+ const string& filename,
+ const string& content_suffix) const {
+ unique_ptr<string> contents;
+ std::ifstream in(filename, std::ios::in | std::ios::binary);
+ if (!in) {
+ return contents;
+ }
+ contents.reset(new string);
+ in.seekg(0, std::ios::end);
+ ssize_t file_size = in.tellg();
+ contents->resize(file_size + content_suffix.length());
+ in.seekg(0, std::ios::beg);
+ // Read the file contents into the beginning of the string
+ in.read(&(*contents)[0], file_size);
+ // Drop the suffix in at the end.
+ contents->replace(file_size, content_suffix.length(), content_suffix);
+ in.close();
+
+ return contents;
+}
+
+unique_ptr<LineReader> IoDelegate::GetLineReader(
+ const string& file_path) const {
+ return LineReader::ReadFromFile(file_path);
+}
+
+bool IoDelegate::FileIsReadable(const string& path) const {
+#ifdef _WIN32
+ // check that the file exists and is not write-only
+ return (0 == _access(path.c_str(), 0)) && // mode 0=exist
+ (0 == _access(path.c_str(), 4)); // mode 4=readable
+#else
+ return (0 == access(path.c_str(), R_OK));
+#endif
+}
+
+bool IoDelegate::CreatedNestedDirs(
+ const string& caller_base_dir,
+ const vector<string>& nested_subdirs) const {
+ string base_dir = caller_base_dir;
+ if (base_dir.empty()) {
+ base_dir = ".";
+ }
+ for (const string& subdir : nested_subdirs) {
+ if (base_dir[base_dir.size() - 1] != OS_PATH_SEPARATOR) {
+ base_dir += OS_PATH_SEPARATOR;
+ }
+ base_dir += subdir;
+ bool success;
+#ifdef _WIN32
+ success = _mkdir(base_dir.c_str()) == 0;
+#else
+ success = mkdir(base_dir.c_str(),
+ S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0;
+#endif
+ // On darwin when you try to mkdir("/", ...) we get EISDIR.
+ if (!success && (errno != EEXIST && errno != EISDIR)) {
+ LOG(ERROR) << "Error while creating " << base_dir << ": "
+ << strerror(errno);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool IoDelegate::CreatePathForFile(const string& path) const {
+ if (path.empty()) {
+ return true;
+ }
+
+ string absolute_path;
+ if (!GetAbsolutePath(path, &absolute_path)) {
+ return false;
+ }
+
+ auto directories = Split(absolute_path, string{1u, OS_PATH_SEPARATOR});
+
+ // The "base" directory is just the root of the file system. On Windows,
+ // this will look like "C:\" but on Unix style file systems we get an empty
+ // string after splitting "/foo" with "/"
+ string base = directories[0];
+ if (base.empty()) {
+ base = "/";
+ }
+ directories.erase(directories.begin());
+
+ // Remove the actual file in question, we're just creating the directory path.
+ directories.pop_back();
+
+ return CreatedNestedDirs(base, directories);
+}
+
+unique_ptr<CodeWriter> IoDelegate::GetCodeWriter(
+ const string& file_path) const {
+ return GetFileWriter(file_path);
+}
+
+void IoDelegate::RemovePath(const std::string& file_path) const {
+#ifdef _WIN32
+ _unlink(file_path.c_str());
+#else
+ unlink(file_path.c_str());
+#endif
+}
+
+} // namespace android
+} // namespace aidl
diff --git a/aidl/io_delegate.h b/aidl/io_delegate.h
new file mode 100644
index 0000000..dc9a3e3
--- /dev/null
+++ b/aidl/io_delegate.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_IO_DELEGATE_H_
+#define AIDL_IO_DELEGATE_H_
+
+#include <android-base/macros.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "code_writer.h"
+#include "line_reader.h"
+
+namespace android {
+namespace aidl {
+
+class IoDelegate {
+ public:
+ IoDelegate() = default;
+ virtual ~IoDelegate() = default;
+
+ // Stores an absolute version of |path| to |*absolute_path|,
+ // possibly prefixing it with the current working directory.
+ // Returns false and does not set |*absolute_path| on error.
+ static bool GetAbsolutePath(const std::string& path,
+ std::string* absolute_path);
+
+ // Returns a unique_ptr to the contents of |filename|.
+ // Will append the optional |content_suffix| to the returned contents.
+ virtual std::unique_ptr<std::string> GetFileContents(
+ const std::string& filename,
+ const std::string& content_suffix = "") const;
+
+ virtual std::unique_ptr<LineReader> GetLineReader(
+ const std::string& file_path) const;
+
+ virtual bool FileIsReadable(const std::string& path) const;
+
+ virtual bool CreatedNestedDirs(
+ const std::string& base_dir,
+ const std::vector<std::string>& nested_subdirs) const;
+
+ bool CreatePathForFile(const std::string& path) const;
+
+ virtual std::unique_ptr<CodeWriter> GetCodeWriter(
+ const std::string& file_path) const;
+
+ virtual void RemovePath(const std::string& file_path) const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(IoDelegate);
+}; // class IoDelegate
+
+} // namespace android
+} // namespace aidl
+
+#endif // AIDL_IO_DELEGATE_H_
diff --git a/aidl/io_delegate_unittest.cpp b/aidl/io_delegate_unittest.cpp
new file mode 100644
index 0000000..5227d0b
--- /dev/null
+++ b/aidl/io_delegate_unittest.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016, 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 <string>
+
+#include <gtest/gtest.h>
+
+#include "io_delegate.h"
+
+using std::string;
+
+namespace android {
+namespace aidl {
+
+TEST(IoDelegateTest, CannotGetAbsolutePathFromEmptyString) {
+ string absolute_path;
+ EXPECT_FALSE(IoDelegate::GetAbsolutePath("", &absolute_path));
+ EXPECT_TRUE(absolute_path.empty());
+}
+
+TEST(IoDelegateTest, CurrentlyInfersLinuxAbsolutePath) {
+ string absolute_path;
+ EXPECT_TRUE(IoDelegate::GetAbsolutePath("foo", &absolute_path));
+ ASSERT_FALSE(absolute_path.empty());
+ // Should find our desired file at the end of |absolute_path|
+ // But we don't know the prefix, since it's the current working directory
+ EXPECT_TRUE(absolute_path.rfind("/foo") == absolute_path.length() - 4);
+ // Whatever our current working directory, the path is absolute.
+ EXPECT_EQ(absolute_path[0], '/');
+}
+
+} // namespace android
+} // namespace aidl
diff --git a/aidl/line_reader.cpp b/aidl/line_reader.cpp
new file mode 100644
index 0000000..0bcaa1d
--- /dev/null
+++ b/aidl/line_reader.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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 "line_reader.h"
+
+#include <fstream>
+#include <sstream>
+
+using std::istringstream;
+using std::ifstream;
+using std::string;
+using std::unique_ptr;
+
+namespace android {
+namespace aidl {
+
+class FileLineReader : public LineReader {
+ public:
+ FileLineReader() = default;
+ virtual ~FileLineReader() {
+ input_stream_.close();
+ }
+
+ bool Init(const std::string& file_path) {
+ input_stream_.open(file_path, ifstream::in | ifstream::binary);
+ return input_stream_.is_open() && input_stream_.good();
+ }
+
+ bool ReadLine(string* line) override {
+ if (!input_stream_.good()) {
+ return false;
+ }
+ line->clear();
+ std::getline(input_stream_, *line);
+ return true;
+ }
+
+ private:
+ ifstream input_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileLineReader);
+}; // class FileLineReader
+
+class MemoryLineReader : public LineReader {
+ public:
+ explicit MemoryLineReader(const string& contents) : input_stream_(contents) {}
+ virtual ~MemoryLineReader() = default;
+
+ bool ReadLine(string* line) override {
+ if (!input_stream_.good()) {
+ return false;
+ }
+ line->clear();
+ std::getline(input_stream_, *line);
+ return true;
+ }
+
+ private:
+ istringstream input_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryLineReader);
+}; // class MemoryLineReader
+
+unique_ptr<LineReader> LineReader::ReadFromFile(const string& file_path) {
+ unique_ptr<FileLineReader> file_reader(new FileLineReader());
+ unique_ptr<LineReader> ret;
+ if (file_reader->Init(file_path)) {
+ ret.reset(file_reader.release());
+ }
+ return ret;
+}
+
+unique_ptr<LineReader> LineReader::ReadFromMemory(const string& contents) {
+ return unique_ptr<LineReader>(new MemoryLineReader(contents));
+}
+
+} // namespace android
+} // namespace aidl
diff --git a/aidl/line_reader.h b/aidl/line_reader.h
new file mode 100644
index 0000000..885327e
--- /dev/null
+++ b/aidl/line_reader.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_LINE_READER_H_
+#define AIDL_LINE_READER_H_
+
+#include <memory>
+#include <string>
+
+#include <android-base/macros.h>
+
+namespace android {
+namespace aidl {
+
+class LineReader {
+ public:
+ LineReader() = default;
+ virtual ~LineReader() = default;
+ virtual bool ReadLine(std::string* line) = 0;
+
+ static std::unique_ptr<LineReader> ReadFromFile(
+ const std::string& file_path);
+ static std::unique_ptr<LineReader> ReadFromMemory(
+ const std::string& contents);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LineReader);
+}; // class LineReader
+
+} // namespace android
+} // namespace aidl
+
+#endif // AIDL_LINE_READER_H_
diff --git a/aidl/logging.h b/aidl/logging.h
new file mode 100644
index 0000000..f76c4e0
--- /dev/null
+++ b/aidl/logging.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_LOGGING_H_
+#define AIDL_LOGGING_H_
+
+// We must include windows.h before android-base/logging.h on Windows.
+#ifdef _WIN32
+#include <windows.h>
+#endif
+
+#include <android-base/logging.h>
+
+#endif // AIDL_LOGGING_H_
diff --git a/aidl/main_cpp.cpp b/aidl/main_cpp.cpp
new file mode 100644
index 0000000..790efdc
--- /dev/null
+++ b/aidl/main_cpp.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 <memory>
+
+#include "aidl.h"
+#include "io_delegate.h"
+#include "logging.h"
+#include "options.h"
+
+using android::aidl::CppOptions;
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv);
+ LOG(DEBUG) << "aidl starting";
+
+ std::unique_ptr<CppOptions> options = CppOptions::Parse(argc, argv);
+ if (!options) {
+ return 1;
+ }
+
+ android::aidl::IoDelegate io_delegate;
+ return android::aidl::compile_aidl_to_cpp(*options, io_delegate);
+}
diff --git a/aidl/main_java.cpp b/aidl/main_java.cpp
new file mode 100644
index 0000000..7d32b24
--- /dev/null
+++ b/aidl/main_java.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 <iostream>
+#include <memory>
+
+#include "aidl.h"
+#include "io_delegate.h"
+#include "logging.h"
+#include "options.h"
+
+using android::aidl::JavaOptions;
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv);
+ LOG(DEBUG) << "aidl starting";
+ std::unique_ptr<JavaOptions> options = JavaOptions::Parse(argc, argv);
+ if (!options) {
+ return 1;
+ }
+
+ android::aidl::IoDelegate io_delegate;
+ switch (options->task) {
+ case JavaOptions::COMPILE_AIDL_TO_JAVA:
+ return android::aidl::compile_aidl_to_java(*options, io_delegate);
+ case JavaOptions::PREPROCESS_AIDL:
+ if (android::aidl::preprocess_aidl(*options, io_delegate))
+ return 0;
+ return 1;
+ }
+ std::cerr << "aidl: internal error" << std::endl;
+ return 1;
+}
diff --git a/aidl/options.cpp b/aidl/options.cpp
new file mode 100644
index 0000000..c7cef5c
--- /dev/null
+++ b/aidl/options.cpp
@@ -0,0 +1,267 @@
+/*
+ * 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 "options.h"
+
+#include <cstring>
+#include <iostream>
+#include <stdio.h>
+
+#include "logging.h"
+#include "os.h"
+
+using std::cerr;
+using std::endl;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace android {
+namespace aidl {
+namespace {
+
+unique_ptr<JavaOptions> java_usage() {
+ fprintf(stderr,
+ "usage: aidl OPTIONS INPUT [OUTPUT]\n"
+ " aidl --preprocess OUTPUT INPUT...\n"
+ "\n"
+ "OPTIONS:\n"
+ " -I<DIR> search path for import statements.\n"
+ " -d<FILE> generate dependency file.\n"
+ " -a generate dependency file next to the output file with "
+ "the name based on the input file.\n"
+ " -p<FILE> file created by --preprocess to import.\n"
+ " -o<FOLDER> base output folder for generated files.\n"
+ " -b fail when trying to compile a parcelable.\n"
+ "\n"
+ "INPUT:\n"
+ " An aidl interface file.\n"
+ "\n"
+ "OUTPUT:\n"
+ " The generated interface files.\n"
+ " If omitted and the -o option is not used, the input filename is "
+ "used, with the .aidl extension changed to a .java extension.\n"
+ " If the -o option is used, the generated files will be placed in "
+ "the base output folder, under their package folder\n");
+ return unique_ptr<JavaOptions>(nullptr);
+}
+
+} // namespace
+
+unique_ptr<JavaOptions> JavaOptions::Parse(int argc, const char* const* argv) {
+ unique_ptr<JavaOptions> options(new JavaOptions());
+ int i = 1;
+
+ if (argc >= 2 && 0 == strcmp(argv[1], "--preprocess")) {
+ if (argc < 4) {
+ return java_usage();
+ }
+ options->output_file_name_ = argv[2];
+ for (int i = 3; i < argc; i++) {
+ options->files_to_preprocess_.push_back(argv[i]);
+ }
+ options->task = PREPROCESS_AIDL;
+ return options;
+ }
+
+ options->task = COMPILE_AIDL_TO_JAVA;
+ // OPTIONS
+ while (i < argc) {
+ const char* s = argv[i];
+ const size_t len = strlen(s);
+ if (s[0] != '-') {
+ break;
+ }
+ if (len <= 1) {
+ fprintf(stderr, "unknown option (%d): %s\n", i, s);
+ return java_usage();
+ }
+ // -I<system-import-path>
+ if (s[1] == 'I') {
+ if (len > 2) {
+ options->import_paths_.push_back(s + 2);
+ } else {
+ fprintf(stderr, "-I option (%d) requires a path.\n", i);
+ return java_usage();
+ }
+ } else if (s[1] == 'd') {
+ if (len > 2) {
+ options->dep_file_name_ = s + 2;
+ } else {
+ fprintf(stderr, "-d option (%d) requires a file.\n", i);
+ return java_usage();
+ }
+ } else if (strcmp(s, "-a") == 0) {
+ options->auto_dep_file_ = true;
+ } else if (s[1] == 'p') {
+ if (len > 2) {
+ options->preprocessed_files_.push_back(s + 2);
+ } else {
+ fprintf(stderr, "-p option (%d) requires a file.\n", i);
+ return java_usage();
+ }
+ } else if (s[1] == 'o') {
+ if (len > 2) {
+ options->output_base_folder_= s + 2;
+ } else {
+ fprintf(stderr, "-o option (%d) requires a path.\n", i);
+ return java_usage();
+ }
+ } else if (strcmp(s, "-b") == 0) {
+ options->fail_on_parcelable_ = true;
+ } else {
+ // s[1] is not known
+ fprintf(stderr, "unknown option (%d): %s\n", i, s);
+ return java_usage();
+ }
+ i++;
+ }
+ // INPUT
+ if (i < argc) {
+ options->input_file_name_ = argv[i];
+ i++;
+ } else {
+ fprintf(stderr, "INPUT required\n");
+ return java_usage();
+ }
+ if (!EndsWith(options->input_file_name_, ".aidl")) {
+ cerr << "Expected .aidl file for input but got "
+ << options->input_file_name_ << endl;
+ return java_usage();
+ }
+
+ // OUTPUT
+ if (i < argc) {
+ options->output_file_name_ = argv[i];
+ i++;
+ } else if (options->output_base_folder_.empty()) {
+ // copy input into output and change the extension from .aidl to .java
+ options->output_file_name_= options->input_file_name_;
+ if (!ReplaceSuffix(".aidl", ".java", &options->output_file_name_)) {
+ // we should never get here since we validated the suffix.
+ LOG(FATAL) << "Internal aidl error.";
+ return java_usage();
+ }
+ }
+
+ // anything remaining?
+ if (i != argc) {
+ fprintf(stderr, "unknown option%s:",
+ (i == argc - 1 ? (const char*)"" : (const char*)"s"));
+ for (; i < argc - 1; i++) {
+ fprintf(stderr, " %s", argv[i]);
+ }
+ fprintf(stderr, "\n");
+ return java_usage();
+ }
+
+ return options;
+}
+
+string JavaOptions::DependencyFilePath() const {
+ if (auto_dep_file_) {
+ return output_file_name_ + ".d";
+ }
+ return dep_file_name_;
+}
+
+namespace {
+
+unique_ptr<CppOptions> cpp_usage() {
+ cerr << "usage: aidl-cpp INPUT_FILE HEADER_DIR OUTPUT_FILE" << endl
+ << endl
+ << "OPTIONS:" << endl
+ << " -I<DIR> search path for import statements" << endl
+ << " -d<FILE> generate dependency file" << endl
+ << endl
+ << "INPUT_FILE:" << endl
+ << " an aidl interface file" << endl
+ << "HEADER_DIR:" << endl
+ << " empty directory to put generated headers" << endl
+ << "OUTPUT_FILE:" << endl
+ << " path to write generated .cpp code" << endl;
+ return unique_ptr<CppOptions>(nullptr);
+}
+
+} // namespace
+
+unique_ptr<CppOptions> CppOptions::Parse(int argc, const char* const* argv) {
+ unique_ptr<CppOptions> options(new CppOptions());
+ int i = 1;
+
+ // Parse flags, all of which start with '-'
+ for ( ; i < argc; ++i) {
+ const size_t len = strlen(argv[i]);
+ const char *s = argv[i];
+ if (s[0] != '-') {
+ break; // On to the positional arguments.
+ }
+ if (len < 2) {
+ cerr << "Invalid argument '" << s << "'." << endl;
+ return cpp_usage();
+ }
+ const string the_rest = s + 2;
+ if (s[1] == 'I') {
+ options->import_paths_.push_back(the_rest);
+ } else if (s[1] == 'd') {
+ options->dep_file_name_ = the_rest;
+ } else {
+ cerr << "Invalid argument '" << s << "'." << endl;
+ return cpp_usage();
+ }
+ }
+
+ // There are exactly three positional arguments.
+ const int remaining_args = argc - i;
+ if (remaining_args != 3) {
+ cerr << "Expected 3 positional arguments but got " << remaining_args << "." << endl;
+ return cpp_usage();
+ }
+
+ options->input_file_name_ = argv[i];
+ options->output_header_dir_ = argv[i + 1];
+ options->output_file_name_ = argv[i + 2];
+
+ if (!EndsWith(options->input_file_name_, ".aidl")) {
+ cerr << "Expected .aidl file for input but got " << options->input_file_name_ << endl;
+ return cpp_usage();
+ }
+
+ return options;
+}
+
+bool EndsWith(const string& str, const string& suffix) {
+ if (str.length() < suffix.length()) {
+ return false;
+ }
+ return std::equal(str.crbegin(), str.crbegin() + suffix.length(),
+ suffix.crbegin());
+}
+
+bool ReplaceSuffix(const string& old_suffix,
+ const string& new_suffix,
+ string* str) {
+ if (!EndsWith(*str, old_suffix)) return false;
+ str->replace(str->length() - old_suffix.length(),
+ old_suffix.length(),
+ new_suffix);
+ return true;
+}
+
+
+
+} // namespace android
+} // namespace aidl
diff --git a/aidl/options.h b/aidl/options.h
new file mode 100644
index 0000000..6f32c62
--- /dev/null
+++ b/aidl/options.h
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_OPTIONS_H_
+#define AIDL_OPTIONS_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <gtest/gtest_prod.h>
+
+namespace android {
+namespace aidl {
+
+// This object represents the parsed options to the Java generating aidl.
+class JavaOptions final {
+ public:
+ enum {
+ COMPILE_AIDL_TO_JAVA,
+ PREPROCESS_AIDL,
+ };
+
+ ~JavaOptions() = default;
+
+ // Parses the command line and returns a non-null pointer to an JavaOptions
+ // object on success.
+ // Prints the usage statement on failure.
+ static std::unique_ptr<JavaOptions> Parse(int argc, const char* const* argv);
+
+ std::string DependencyFilePath() const;
+
+ int task{COMPILE_AIDL_TO_JAVA};
+ bool fail_on_parcelable_{false};
+ std::vector<std::string> import_paths_;
+ std::vector<std::string> preprocessed_files_;
+ std::string input_file_name_;
+ std::string output_file_name_;
+ std::string output_base_folder_;
+ std::string dep_file_name_;
+ bool auto_dep_file_{false};
+ std::vector<std::string> files_to_preprocess_;
+
+ private:
+ JavaOptions() = default;
+
+ FRIEND_TEST(EndToEndTest, IExampleInterface);
+ FRIEND_TEST(AidlTest, FailOnParcelable);
+ FRIEND_TEST(AidlTest, WritePreprocessedFile);
+ FRIEND_TEST(AidlTest, WritesCorrectDependencyFile);
+ FRIEND_TEST(AidlTest, WritesTrivialDependencyFileForParcelable);
+
+ DISALLOW_COPY_AND_ASSIGN(JavaOptions);
+};
+
+class CppOptions final {
+ public:
+
+ ~CppOptions() = default;
+
+ // Parses the command line and returns a non-null pointer to an CppOptions
+ // object on success.
+ // Prints the usage statement on failure.
+ static std::unique_ptr<CppOptions> Parse(int argc, const char* const* argv);
+
+ std::string InputFileName() const { return input_file_name_; }
+ std::string OutputHeaderDir() const { return output_header_dir_; }
+ std::string OutputCppFilePath() const { return output_file_name_; }
+
+ std::vector<std::string> ImportPaths() const { return import_paths_; }
+ std::string DependencyFilePath() const { return dep_file_name_; }
+
+ private:
+ CppOptions() = default;
+
+ std::string input_file_name_;
+ std::vector<std::string> import_paths_;
+ std::string output_header_dir_;
+ std::string output_file_name_;
+ std::string dep_file_name_;
+
+ FRIEND_TEST(CppOptionsTests, ParsesCompileCpp);
+ DISALLOW_COPY_AND_ASSIGN(CppOptions);
+};
+
+bool EndsWith(const std::string& str, const std::string& suffix);
+bool ReplaceSuffix(const std::string& old_suffix,
+ const std::string& new_suffix,
+ std::string* str);
+
+} // namespace android
+} // namespace aidl
+
+#endif // AIDL_OPTIONS_H_
diff --git a/aidl/options_unittest.cpp b/aidl/options_unittest.cpp
new file mode 100644
index 0000000..568765b
--- /dev/null
+++ b/aidl/options_unittest.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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 <iostream>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "options.h"
+
+using std::cerr;
+using std::endl;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace android {
+namespace aidl {
+namespace {
+
+const char kPreprocessCommandOutputFile[] = "output_file_name";
+const char kPreprocessCommandInput1[] = "input1";
+const char kPreprocessCommandInput2[] = "input2";
+const char kPreprocessCommandInput3[] = "input3";
+const char* kPreprocessCommand[] = {
+ "aidl", "--preprocess",
+ kPreprocessCommandOutputFile,
+ kPreprocessCommandInput1,
+ kPreprocessCommandInput2,
+ kPreprocessCommandInput3,
+ nullptr,
+};
+
+const char kCompileCommandInput[] = "directory/ITool.aidl";
+const char kCompileCommandIncludePath[] = "-Iinclude_path";
+const char* kCompileJavaCommand[] = {
+ "aidl",
+ "-b",
+ kCompileCommandIncludePath,
+ kCompileCommandInput,
+ nullptr,
+};
+const char kCompileCommandJavaOutput[] = "directory/ITool.java";
+
+const char kCompileDepFile[] = "-doutput.deps";
+const char kCompileCommandHeaderDir[] = "output/dir";
+const char kCompileCommandCppOutput[] = "some/file.cpp";
+const char* kCompileCppCommand[] = {
+ "aidl-cpp",
+ kCompileCommandIncludePath,
+ kCompileDepFile,
+ kCompileCommandInput,
+ kCompileCommandHeaderDir,
+ kCompileCommandCppOutput,
+ nullptr,
+};
+
+template <typename T>
+unique_ptr<T> GetOptions(const char* command[]) {
+ int argc = 0;
+ const char** command_part = command;
+ for (; *command_part; ++argc, ++command_part) {}
+ unique_ptr<T> options(T::Parse(argc, command));
+ if (!options) {
+ cerr << "Failed to parse command line:";
+ for (int i = 0; i < argc; ++i) {
+ cerr << " " << command[i];
+ cerr << endl;
+ }
+ }
+ EXPECT_NE(options, nullptr) << "Failed to parse options!";
+ return options;
+}
+
+} // namespace
+
+TEST(JavaOptionsTests, ParsesPreprocess) {
+ unique_ptr<JavaOptions> options = GetOptions<JavaOptions>(kPreprocessCommand);
+ EXPECT_EQ(JavaOptions::PREPROCESS_AIDL, options->task);
+ EXPECT_EQ(false, options->fail_on_parcelable_);
+ EXPECT_EQ(0u, options->import_paths_.size());
+ EXPECT_EQ(0u, options->preprocessed_files_.size());
+ EXPECT_EQ(string{}, options->input_file_name_);
+ EXPECT_EQ(string{kPreprocessCommandOutputFile}, options->output_file_name_);
+ EXPECT_EQ(false, options->auto_dep_file_);
+ const vector<string> expected_input{kPreprocessCommandInput1,
+ kPreprocessCommandInput2,
+ kPreprocessCommandInput3};
+ EXPECT_EQ(expected_input, options->files_to_preprocess_);
+}
+
+TEST(JavaOptionsTests, ParsesCompileJava) {
+ unique_ptr<JavaOptions> options =
+ GetOptions<JavaOptions>(kCompileJavaCommand);
+ EXPECT_EQ(JavaOptions::COMPILE_AIDL_TO_JAVA, options->task);
+ EXPECT_EQ(true, options->fail_on_parcelable_);
+ EXPECT_EQ(1u, options->import_paths_.size());
+ EXPECT_EQ(0u, options->preprocessed_files_.size());
+ EXPECT_EQ(string{kCompileCommandInput}, options->input_file_name_);
+ EXPECT_EQ(string{kCompileCommandJavaOutput}, options->output_file_name_);
+ EXPECT_EQ(false, options->auto_dep_file_);
+}
+
+TEST(CppOptionsTests, ParsesCompileCpp) {
+ unique_ptr<CppOptions> options = GetOptions<CppOptions>(kCompileCppCommand);
+ ASSERT_EQ(1u, options->import_paths_.size());
+ EXPECT_EQ(string{kCompileCommandIncludePath}.substr(2),
+ options->import_paths_[0]);
+ EXPECT_EQ(string{kCompileDepFile}.substr(2), options->dep_file_name_);
+ EXPECT_EQ(kCompileCommandInput, options->InputFileName());
+ EXPECT_EQ(kCompileCommandHeaderDir, options->OutputHeaderDir());
+ EXPECT_EQ(kCompileCommandCppOutput, options->OutputCppFilePath());
+}
+
+TEST(OptionsTests, EndsWith) {
+ EXPECT_TRUE(EndsWith("foo", ""));
+ EXPECT_TRUE(EndsWith("foo", "o"));
+ EXPECT_TRUE(EndsWith("foo", "foo"));
+ EXPECT_FALSE(EndsWith("foo", "fooo"));
+ EXPECT_FALSE(EndsWith("", "o"));
+ EXPECT_TRUE(EndsWith("", ""));
+}
+
+TEST(OptionsTests, ReplaceSuffix) {
+ struct test_case_t {
+ const char* input;
+ const char* old_suffix;
+ const char* new_suffix;
+ const char* result;
+ };
+ const size_t kNumCases = 3;
+ test_case_t kTestInput[kNumCases] = {
+ {"foo.bar", "bar", "foo", "foo.foo"},
+ {"whole", "whole", "new", "new"},
+ {"", "", "", ""},
+ };
+ for (const auto& test_case : kTestInput) {
+ string mutated = test_case.input;
+ EXPECT_TRUE(ReplaceSuffix(test_case.old_suffix,
+ test_case.new_suffix,
+ &mutated));
+ EXPECT_EQ(mutated, test_case.result);
+ }
+}
+
+} // namespace android
+} // namespace aidl
diff --git a/aidl/os.h b/aidl/os.h
new file mode 100644
index 0000000..752ed47
--- /dev/null
+++ b/aidl/os.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef AIDL_OS_H_
+#define AIDL_OS_H_
+
+#if defined(_WIN32)
+#define OS_PATH_SEPARATOR '\\'
+#else
+#define OS_PATH_SEPARATOR '/'
+#endif
+
+#endif // AIDL_OS_H_
diff --git a/aidl/runtests.sh b/aidl/runtests.sh
new file mode 100755
index 0000000..05809f9
--- /dev/null
+++ b/aidl/runtests.sh
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2016 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.
+
+if [ -z $ANDROID_BUILD_TOP ]; then
+ echo "You need to source and lunch before you can use this script"
+ exit 1
+fi
+
+echo "Running tests"
+set -e # fail early
+
+# NOTE We can't actually run these commands, since they rely on functions added
+# by build/envsetup.sh to the bash shell environment.
+echo "+ mmma -j32 $ANDROID_BUILD_TOP/system/tools/aidl"
+make -j32 -C $ANDROID_BUILD_TOP -f build/core/main.mk \
+ MODULES-IN-system-tools-aidl
+
+set -x # print commands
+
+${ANDROID_HOST_OUT}/nativetest64/aidl_unittests/aidl_unittests
+
+adb root
+adb wait-for-device
+adb remount
+adb sync
+adb install -r \
+ ${ANDROID_PRODUCT_OUT}/system/app/aidl_test_services/aidl_test_services.apk
+${ANDROID_BUILD_TOP}/system/tools/aidl/tests/integration-test.py
diff --git a/aidl/tests/aidl_test_client.cpp b/aidl/tests/aidl_test_client.cpp
new file mode 100644
index 0000000..84c8183
--- /dev/null
+++ b/aidl/tests/aidl_test_client.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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 <iostream>
+
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+#include "android/aidl/tests/ITestService.h"
+
+#include "aidl_test_client_file_descriptors.h"
+#include "aidl_test_client_nullables.h"
+#include "aidl_test_client_parcelables.h"
+#include "aidl_test_client_primitives.h"
+#include "aidl_test_client_service_exceptions.h"
+#include "aidl_test_client_utf8_strings.h"
+
+// libutils:
+using android::OK;
+using android::sp;
+using android::status_t;
+using android::String16;
+
+// libbinder:
+using android::getService;
+
+// generated
+using android::aidl::tests::ITestService;
+
+using std::cerr;
+using std::cout;
+using std::endl;
+
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+const char kServiceName[] = "android.aidl.tests.ITestService";
+
+bool GetService(sp<ITestService>* service) {
+ cout << "Retrieving test service binder" << endl;
+ status_t status = getService(String16(kServiceName), service);
+ if (status != OK) {
+ cerr << "Failed to get service binder: '" << kServiceName
+ << "' status=" << status << endl;
+ return false;
+ }
+ return true;
+}
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
+
+/* Runs all the test cases in aidl_test_client_*.cpp files. */
+int main(int /* argc */, char * argv []) {
+ android::base::InitLogging(argv, android::base::StderrLogger);
+ sp<ITestService> service;
+ namespace client_tests = android::aidl::tests::client;
+
+
+ if (!client_tests::GetService(&service)) return 1;
+
+ if (!client_tests::ConfirmPrimitiveRepeat(service)) return 1;
+
+ if (!client_tests::ConfirmReverseArrays(service)) return 1;
+
+ if (!client_tests::ConfirmReverseLists(service)) return 1;
+
+ if (!client_tests::ConfirmReverseBinderLists(service)) return 1;
+
+ if (!client_tests::ConfirmSimpleParcelables(service)) return 1;
+
+ if (!client_tests::ConfirmPersistableBundles(service)) return 1;
+
+ if (!client_tests::ConfirmFileDescriptors(service)) return 1;
+
+ if (!client_tests::ConfirmFileDescriptorArrays(service)) return 1;
+
+ if (!client_tests::ConfirmServiceSpecificExceptions(service)) return 1;
+
+ if (!client_tests::ConfirmNullables(service)) return 1;
+
+ if (!client_tests::ConfirmUtf8InCppStringRepeat(service)) return 1;
+
+ if (!client_tests::ConfirmUtf8InCppStringArrayReverse(service)) return 1;
+
+ if (!client_tests::ConfirmUtf8InCppStringListReverse(service)) return 1;
+
+ return 0;
+}
diff --git a/aidl/tests/aidl_test_client_file_descriptors.cpp b/aidl/tests/aidl_test_client_file_descriptors.cpp
new file mode 100644
index 0000000..b5913a3
--- /dev/null
+++ b/aidl/tests/aidl_test_client_file_descriptors.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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_test_client_file_descriptors.h"
+
+#include <iostream>
+#include <vector>
+
+ #include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+
+// libbase
+using android::base::unique_fd;
+
+// libutils:
+using android::sp;
+
+// libbinder:
+using android::binder::Status;
+
+// generated
+using android::aidl::tests::ITestService;
+
+using std::cerr;
+using std::cout;
+using std::endl;
+using std::string;
+using std::vector;
+
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+#define FdByName(_fd) #_fd, _fd
+
+bool DoWrite(const string& name, const unique_fd& fd, const string& buf) {
+ int wrote;
+
+ while ((wrote = write(fd.get(), buf.data(), buf.size())) < 0 && errno == EINTR);
+
+ if (wrote == (signed)buf.size()) {
+ return true;
+ }
+
+ if (wrote < 0) {
+ cerr << "Error writing to file descriptor '" << name << "': "
+ << strerror(errno) << endl;
+ } else {
+ cerr << "File descriptor '" << name << "'accepted short data." << endl;
+ }
+
+ return false;
+}
+
+bool DoRead(const string& name, const unique_fd& fd, const string& expected) {
+ size_t length = expected.size();
+ int got;
+ string buf;
+ buf.resize(length);
+
+ while ((got = read(fd.get(), &buf[0], length)) < 0 && errno == EINTR);
+
+ if (got < 0) {
+ cerr << "Error reading from '" << name << "': " << strerror(errno) << endl;
+ return false;
+ }
+
+ if (buf != expected) {
+ cerr << "Expected '" << expected << "' got '" << buf << "'" << endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool DoPipe(unique_fd* read_side, unique_fd* write_side) {
+ int fds[2];
+ unique_fd return_fd;
+
+ if (pipe(fds)) {
+ cout << "Error creating pipes: " << strerror(errno) << endl;
+ return false;
+ }
+
+ read_side->reset(fds[0]);
+ write_side->reset(fds[1]);
+ return true;
+}
+
+bool ConfirmFileDescriptors(const sp<ITestService>& s) {
+ Status status;
+ cout << "Confirming passing and returning file descriptors works." << endl;
+
+ unique_fd return_fd;
+ unique_fd read_fd;
+ unique_fd write_fd;
+
+ if (!DoPipe(&read_fd, &write_fd)) {
+ return false;
+ }
+
+ status = s->RepeatFileDescriptor(write_fd, &return_fd);
+
+ if (!status.isOk()) {
+ cerr << "Could not repeat file descriptors." << endl;
+ return false;
+ }
+
+ /* A note on some of the spookier stuff going on here: IIUC writes to pipes
+ * should be atomic and non-blocking so long as the total size doesn't exceed
+ * PIPE_BUF. We thus play a bit fast and loose with failure modes here.
+ */
+
+ bool ret =
+ DoWrite(FdByName(return_fd), "ReturnString") &&
+ DoRead(FdByName(read_fd), "ReturnString");
+
+ return ret;
+}
+
+bool ConfirmFileDescriptorArrays(const sp<ITestService>& s) {
+ Status status;
+ cout << "Confirming passing and returning file descriptor arrays works." << endl;
+
+ vector<unique_fd> array;
+ array.resize(2);
+
+ if (!DoPipe(&array[0], &array[1])) {
+ return false;
+ }
+
+ vector<unique_fd> repeated;
+ vector<unique_fd> reversed;
+
+ status = s->ReverseFileDescriptorArray(array, &repeated, &reversed);
+
+ if (!status.isOk()) {
+ cerr << "Could not reverse file descriptor array." << endl;
+ return false;
+ }
+
+ bool ret =
+ DoWrite(FdByName(array[1]), "First") &&
+ DoWrite(FdByName(repeated[1]), "Second") &&
+ DoWrite(FdByName(reversed[0]), "Third") &&
+ DoRead(FdByName(reversed[1]), "FirstSecondThird");
+
+ return ret;
+}
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
diff --git a/aidl/tests/aidl_test_client_file_descriptors.h b/aidl/tests/aidl_test_client_file_descriptors.h
new file mode 100644
index 0000000..306e551
--- /dev/null
+++ b/aidl/tests/aidl_test_client_file_descriptors.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_AIDL_TESTS_CLIENT_FILE_DESCRIPTORS_H
+#define ANDROID_AIDL_TESTS_CLIENT_FILE_DESCRIPTORS_H
+
+#include <utils/StrongPointer.h>
+
+#include "android/aidl/tests/ITestService.h"
+
+// Tests for passing and returning file descriptors.
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+bool ConfirmFileDescriptors(const sp<ITestService>& s);
+bool ConfirmFileDescriptorArrays(const sp<ITestService>& s);
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
+
+#endif // ANDROID_AIDL_TESTS_CLIENT_FILE_DESCRIPTORS_H
diff --git a/aidl/tests/aidl_test_client_nullables.cpp b/aidl/tests/aidl_test_client_nullables.cpp
new file mode 100644
index 0000000..805827c
--- /dev/null
+++ b/aidl/tests/aidl_test_client_nullables.cpp
@@ -0,0 +1,280 @@
+/*
+ * 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_test_client_nullables.h"
+
+#include <utils/String16.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+// libutils:
+using android::sp;
+using android::String16;
+
+// libbinder:
+using android::binder::Status;
+
+// generated
+using android::aidl::tests::ITestService;
+using android::aidl::tests::SimpleParcelable;
+
+using std::string;
+using std::unique_ptr;
+using std::vector;
+using std::cout;
+using std::cerr;
+using std::endl;
+
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+namespace {
+template<typename T>
+bool ValuesEqual(const unique_ptr<T>& in, const unique_ptr<T>& out) {
+ return *in == *out;
+}
+
+template<>
+bool ValuesEqual<vector<unique_ptr<String16>>>(
+ const unique_ptr<vector<unique_ptr<String16>>>& in,
+ const unique_ptr<vector<unique_ptr<String16>>>& out) {
+ if (!in) {
+ return !out;
+ }
+
+ if (!out) {
+ return false;
+ }
+
+ if (in->size() != out->size()) {
+ return false;
+ }
+
+ for (size_t i = 0; i < in->size(); i++) {
+ const unique_ptr<String16>& a = (*in)[i];
+ const unique_ptr<String16>& b = (*out)[i];
+
+ if (!(a || b)) {
+ continue;
+ }
+
+ if (!(a && b)) {
+ return false;
+ }
+
+ if (*a != *b) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+template<typename T>
+bool ConfirmNullableType(const sp<ITestService>& s, const string& type_name,
+ unique_ptr<T> in,
+ Status(ITestService::*func)(const unique_ptr<T>&,
+ unique_ptr<T>*)) {
+ cout << "... Confirming nullables for " << type_name << " ..." << endl;
+ Status status;
+ unique_ptr<T> out;
+
+ status = (*s.*func)(in, &out);
+
+ if (!status.isOk()) {
+ cerr << "Could not repeat nullable " << type_name << "." << endl;
+ return false;
+ }
+
+ if (!out) {
+ cerr << "Got back null when repeating " << type_name << "." << endl;
+ return false;
+ }
+
+ if (!ValuesEqual(in, out)) {
+ cerr << "Got back a non-matching value when repeating " << type_name
+ << "." << endl;
+ return false;
+ }
+
+ in.reset();
+ status = (*s.*func)(in, &out);
+
+ if (!status.isOk()) {
+ cerr << "Could not repeat null as " << type_name << "." << endl;
+ return false;
+ }
+
+ if (out) {
+ cerr << "Got back a value when sent null for " << type_name << "."
+ << endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool CheckAppropriateIBinderHandling(const sp<ITestService>& s) {
+
+ Status status;
+ sp<IBinder> binder = new BBinder();
+ sp<IBinder> null_binder = nullptr;
+ unique_ptr<vector<sp<IBinder>>> list_with_nulls(
+ new vector<sp<IBinder>>{binder, null_binder});
+ unique_ptr<vector<sp<IBinder>>> list_without_nulls(
+ new vector<sp<IBinder>>{binder, binder});
+
+ // Methods without @nullable throw up when given null binders
+ if (s->TakesAnIBinder(null_binder).exceptionCode() !=
+ binder::Status::EX_NULL_POINTER) {
+ cerr << "Did not receive expected null exception on line: "
+ << __LINE__ << endl;
+ return false;
+ }
+ if (s->TakesAnIBinderList(*list_with_nulls).exceptionCode() !=
+ binder::Status::EX_NULL_POINTER) {
+ cerr << "Did not receive expected null exception on line: "
+ << __LINE__ << endl;
+ return false;
+ }
+
+ // But those same methods are fine with valid binders
+ if (!s->TakesAnIBinder(binder).isOk()) {
+ cerr << "Received unexpected exception on line "
+ << __LINE__ << endl;
+ return false;
+ }
+ if (!s->TakesAnIBinderList(*list_without_nulls).isOk()) {
+ cerr << "Received unexpected exception on line "
+ << __LINE__ << endl;
+ return false;
+ }
+
+ // And methods with @nullable don't care.
+ if (!s->TakesANullableIBinder(null_binder).isOk()) {
+ cerr << "Received unexpected exception on line "
+ << __LINE__ << endl;
+ return false;
+ }
+ if (!s->TakesANullableIBinder(binder).isOk()) {
+ cerr << "Received unexpected exception on line "
+ << __LINE__ << endl;
+ return false;
+ }
+ if (!s->TakesANullableIBinderList(list_with_nulls).isOk()) {
+ cerr << "Received unexpected exception on line "
+ << __LINE__ << endl;
+ return false;
+ }
+ if (!s->TakesANullableIBinderList(list_without_nulls).isOk()) {
+ cerr << "Received unexpected exception on line "
+ << __LINE__ << endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool CheckAppropriateIInterfaceHandling(const sp<ITestService>& s) {
+
+ sp<INamedCallback> callback;
+ if (!s->GetCallback(false, &callback).isOk()) {
+ cerr << "Received unexpected exception on line "
+ << __LINE__ << endl;
+ return false;
+ }
+ if (callback.get() == nullptr) {
+ cerr << "Expected to receive a non-null binder on line: "
+ << __LINE__ << endl;
+ return false;
+ }
+ if (!s->GetCallback(true, &callback).isOk()) {
+ cerr << "Received unexpected exception on line "
+ << __LINE__ << endl;
+ return false;
+ }
+ if (callback.get() != nullptr) {
+ cerr << "Expected to receive a null binder on line: "
+ << __LINE__ << endl;
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+bool ConfirmNullables(const sp<ITestService>& s) {
+ Status status;
+ cout << "Confirming passing and returning nullable values works." << endl;
+
+ if (!ConfirmNullableType(s, "integer array",
+ unique_ptr<vector<int32_t>>(
+ new vector<int32_t>({1,2,3})),
+ &ITestService::RepeatNullableIntArray)) {
+ return false;
+ }
+
+ if (!ConfirmNullableType(s, "string",
+ unique_ptr<String16>(new String16("Blooob")),
+ &ITestService::RepeatNullableString)) {
+ return false;
+ }
+
+ unique_ptr<vector<unique_ptr<String16>>> test_string_array(
+ new vector<unique_ptr<String16>>());
+ test_string_array->push_back(unique_ptr<String16>(new String16("Wat")));
+ test_string_array->push_back(unique_ptr<String16>(
+ new String16("Blooob")));
+ test_string_array->push_back(unique_ptr<String16>(new String16("Wat")));
+ test_string_array->push_back(unique_ptr<String16>(nullptr));
+ test_string_array->push_back(unique_ptr<String16>(new String16("YEAH")));
+ test_string_array->push_back(unique_ptr<String16>(
+ new String16("OKAAAAY")));
+
+ if (!ConfirmNullableType(s, "string array", std::move(test_string_array),
+ &ITestService::RepeatNullableStringList)) {
+ return false;
+ }
+
+ if (!ConfirmNullableType(s, "parcelable",
+ unique_ptr<SimpleParcelable>(
+ new SimpleParcelable("Booya", 42)),
+ &ITestService::RepeatNullableParcelable)) {
+ return false;
+ }
+
+ if (!CheckAppropriateIBinderHandling(s)) {
+ cerr << "Handled null IBinders poorly." << endl;
+ return false;
+ }
+
+ if (!CheckAppropriateIInterfaceHandling(s)) {
+ cerr << "Handled nullable IInterface instances poorly." << endl;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
diff --git a/aidl/tests/aidl_test_client_nullables.h b/aidl/tests/aidl_test_client_nullables.h
new file mode 100644
index 0000000..807aa4e
--- /dev/null
+++ b/aidl/tests/aidl_test_client_nullables.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_AIDL_TESTS_CLIENT_NULLABLES_H
+#define ANDROID_AIDL_TESTS_CLIENT_NULLABLES_H
+
+#include <utils/StrongPointer.h>
+
+#include "android/aidl/tests/ITestService.h"
+
+// Tests for passing and returning file descriptors.
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+bool ConfirmNullables(const sp<ITestService>& s);
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
+
+#endif // ANDROID_AIDL_TESTS_CLIENT_NULLABLES_H
diff --git a/aidl/tests/aidl_test_client_parcelables.cpp b/aidl/tests/aidl_test_client_parcelables.cpp
new file mode 100644
index 0000000..09e7f0a
--- /dev/null
+++ b/aidl/tests/aidl_test_client_parcelables.cpp
@@ -0,0 +1,151 @@
+/*
+ * 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_test_client_parcelables.h"
+
+#include <iostream>
+#include <vector>
+
+// libutils:
+using android::sp;
+
+// libbinder:
+using android::binder::Status;
+
+// generated
+using android::aidl::tests::ITestService;
+using android::aidl::tests::SimpleParcelable;
+using android::os::PersistableBundle;
+
+using std::cout;
+using std::endl;
+using std::vector;
+
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+bool ConfirmSimpleParcelables(const sp<ITestService>& s) {
+ cout << "Confirming passing and returning SimpleParcelable objects works."
+ << endl;
+
+ SimpleParcelable input("Booya", 42);
+ SimpleParcelable out_param, returned;
+ Status status = s->RepeatSimpleParcelable(input, &out_param, &returned);
+ if (!status.isOk()) {
+ cout << "Binder call failed." << endl;
+ return false;
+ }
+ if (input != out_param || input != returned) {
+ cout << "Failed to repeat SimpleParcelable objects." << endl;
+ return false;
+ }
+
+ cout << "Attempting to reverse an array of SimpleParcelable objects." << endl;
+ const vector<SimpleParcelable> original{SimpleParcelable("first", 0),
+ SimpleParcelable("second", 1),
+ SimpleParcelable("third", 2)};
+ vector<SimpleParcelable> repeated;
+ vector<SimpleParcelable> reversed;
+ status = s->ReverseSimpleParcelables(original, &repeated, &reversed);
+ if (!status.isOk()) {
+ cout << "Binder call failed." << endl;
+ return false;
+ }
+ std::reverse(reversed.begin(), reversed.end());
+ if (repeated != original || reversed != original) {
+ cout << "Failed to reverse an array of SimpleParcelable objects." << endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool ConfirmPersistableBundles(const sp<ITestService>& s) {
+ cout << "Confirming passing and returning PersistableBundle objects works."
+ << endl;
+
+ PersistableBundle empty_bundle, returned;
+ Status status = s->RepeatPersistableBundle(empty_bundle, &returned);
+ if (!status.isOk()) {
+ cout << "Binder call failed for empty PersistableBundle." << endl;
+ return false;
+ }
+ if (empty_bundle != returned) {
+ cout << "Failed to repeat empty PersistableBundle." << endl;
+ return false;
+ }
+
+ PersistableBundle non_empty_bundle;
+ non_empty_bundle.putBoolean(String16("test_bool"), false);
+ non_empty_bundle.putInt(String16("test_int"), 33);
+ non_empty_bundle.putLong(String16("test_long"), 34359738368l);
+ non_empty_bundle.putDouble(String16("test_double"), 1.1);
+ non_empty_bundle.putString(String16("test_string"), String16("Woot!"));
+ non_empty_bundle.putBooleanVector(String16("test_bool_vector"),
+ {true, false, true});
+ non_empty_bundle.putIntVector(String16("test_int_vector"), {33, 44, 55, 142});
+ non_empty_bundle.putLongVector(String16("test_long_vector"),
+ {34l, 8371l, 34359738375l});
+ non_empty_bundle.putDoubleVector(String16("test_double_vector"), {2.2, 5.4});
+ non_empty_bundle.putStringVector(String16("test_string_vector"),
+ {String16("hello"), String16("world!")});
+ PersistableBundle nested_bundle;
+ nested_bundle.putInt(String16("test_nested_int"), 345);
+ non_empty_bundle.putPersistableBundle(String16("test_persistable_bundle"),
+ nested_bundle);
+
+ status = s->RepeatPersistableBundle(non_empty_bundle, &returned);
+ if (!status.isOk()) {
+ cout << "Binder call failed. " << endl;
+ return false;
+ }
+ if (non_empty_bundle != returned) {
+ cout << "Failed to repeat PersistableBundle object." << endl;
+ return false;
+ }
+
+ cout << "Attempting to reverse an array of PersistableBundle objects."
+ << endl;
+ PersistableBundle first;
+ PersistableBundle second;
+ PersistableBundle third;
+ first.putInt(String16("test_int"), 1231);
+ second.putLong(String16("test_long"), 222222l);
+ third.putDouble(String16("test_double"), 10.8);
+ const vector<PersistableBundle> original{first, second, third};
+
+ vector<PersistableBundle> repeated;
+ vector<PersistableBundle> reversed;
+ status = s->ReversePersistableBundles(original, &repeated, &reversed);
+ if (!status.isOk()) {
+ cout << "Binder call failed." << endl;
+ return false;
+ }
+ std::reverse(reversed.begin(), reversed.end());
+ if (repeated != original || reversed != original) {
+ cout << "Failed to reverse an array of PersistableBundle objects." << endl;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
diff --git a/aidl/tests/aidl_test_client_parcelables.h b/aidl/tests/aidl_test_client_parcelables.h
new file mode 100644
index 0000000..9616e02
--- /dev/null
+++ b/aidl/tests/aidl_test_client_parcelables.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_AIDL_TESTS_CLIENT_PARCELABLES_H
+#define ANDROID_AIDL_TESTS_CLIENT_PARCELABLES_H
+
+#include <utils/StrongPointer.h>
+
+#include "android/aidl/tests/ITestService.h"
+
+// Tests for passing and returning parcelable types.
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+bool ConfirmSimpleParcelables(const sp<ITestService>& s);
+bool ConfirmPersistableBundles(const sp<ITestService>& s);
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
+
+#endif // ANDROID_AIDL_TESTS_CLIENT_PARCELABLES_H
diff --git a/aidl/tests/aidl_test_client_primitives.cpp b/aidl/tests/aidl_test_client_primitives.cpp
new file mode 100644
index 0000000..6f70f43
--- /dev/null
+++ b/aidl/tests/aidl_test_client_primitives.cpp
@@ -0,0 +1,231 @@
+/*
+ * 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_test_client_primitives.h"
+
+#include <iostream>
+#include <vector>
+
+#include <utils/String16.h>
+#include <utils/String8.h>
+
+#include "android/aidl/tests/INamedCallback.h"
+
+#include "test_helpers.h"
+
+// libutils:
+using android::sp;
+using android::String16;
+using android::String8;
+
+// libbinder:
+using android::binder::Status;
+
+// generated
+using android::aidl::tests::ITestService;
+using android::aidl::tests::INamedCallback;
+
+using std::cerr;
+using std::cout;
+using std::endl;
+using std::vector;
+
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+bool ConfirmPrimitiveRepeat(const sp<ITestService>& s) {
+ cout << "Confirming passing and returning primitives works." << endl;
+
+ if (!RepeatPrimitive(s, &ITestService::RepeatBoolean, true) ||
+ !RepeatPrimitive(s, &ITestService::RepeatByte, int8_t{-128}) ||
+ !RepeatPrimitive(s, &ITestService::RepeatChar, char16_t{'A'}) ||
+ !RepeatPrimitive(s, &ITestService::RepeatInt, int32_t{1 << 30}) ||
+ !RepeatPrimitive(s, &ITestService::RepeatLong, int64_t{1ll << 60}) ||
+ !RepeatPrimitive(s, &ITestService::RepeatFloat, float{1.0f/3.0f}) ||
+ !RepeatPrimitive(s, &ITestService::RepeatDouble, double{1.0/3.0}) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT2) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT3) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT4) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT5) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT6) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT7) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT8) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT9) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT10) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT11) ||
+ !RepeatPrimitive(
+ s, &ITestService::RepeatInt, ITestService::TEST_CONSTANT12)
+ ) {
+ return false;
+ }
+
+ vector<String16> inputs = {
+ String16("Deliver us from evil."),
+ String16(),
+ String16("\0\0", 2),
+ // This is actually two unicode code points:
+ // U+10437: The 'small letter yee' character in the deseret alphabet
+ // U+20AC: A euro sign
+ String16("\xD8\x01\xDC\x37\x20\xAC"),
+ ITestService::STRING_TEST_CONSTANT(),
+ ITestService::STRING_TEST_CONSTANT2(),
+ };
+ for (const auto& input : inputs) {
+ String16 reply;
+ Status status = s->RepeatString(input, &reply);
+ if (!status.isOk() || input != reply) {
+ cerr << "Failed while requesting service to repeat String16=\""
+ << String8(input).string()
+ << "\". Got status=" << status.toString8() << endl;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ConfirmReverseArrays(const sp<ITestService>& s) {
+ cout << "Confirming passing and returning arrays works." << endl;
+
+ if (!ReverseArray(s, &ITestService::ReverseBoolean,
+ {true, false, false}) ||
+ !ReverseArray(s, &ITestService::ReverseByte,
+ {uint8_t{255}, uint8_t{0}, uint8_t{127}}) ||
+ !ReverseArray(s, &ITestService::ReverseChar,
+ {char16_t{'A'}, char16_t{'B'}, char16_t{'C'}}) ||
+ !ReverseArray(s, &ITestService::ReverseInt,
+ {1, 2, 3}) ||
+ !ReverseArray(s, &ITestService::ReverseLong,
+ {-1ll, 0ll, int64_t{1ll << 60}}) ||
+ !ReverseArray(s, &ITestService::ReverseFloat,
+ {-0.3f, -0.7f, 8.0f}) ||
+ !ReverseArray(s, &ITestService::ReverseDouble,
+ {1.0/3.0, 1.0/7.0, 42.0}) ||
+ !ReverseArray(s, &ITestService::ReverseString,
+ {String16{"f"}, String16{"a"}, String16{"b"}})) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ConfirmReverseLists(const sp<ITestService>& s) {
+ cout << "Confirming passing and returning List<T> works." << endl;
+
+ if (!ReverseArray(s, &ITestService::ReverseStringList,
+ {String16{"f"}, String16{"a"}, String16{"b"}})) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ConfirmReverseBinderLists(const sp<ITestService>& s) {
+ Status status;
+ cout << "Confirming passing and returning List<T> works with binders." << endl;
+
+ vector<String16> names = {
+ String16{"Larry"},
+ String16{"Curly"},
+ String16{"Moe"}
+ };
+
+ vector<sp<IBinder>> input;
+
+ for (int i = 0; i < 3; i++) {
+ sp<INamedCallback> got;
+
+ status = s->GetOtherTestService(names[i], &got);
+ if (!status.isOk()) {
+ cerr << "Could not retrieve service for test." << endl;
+ return false;
+ }
+
+ input.push_back(INamedCallback::asBinder(got));
+ }
+
+ vector<sp<IBinder>> output;
+ vector<sp<IBinder>> reversed;
+
+ status = s->ReverseNamedCallbackList(input, &output, &reversed);
+ if (!status.isOk()) {
+ cerr << "Failed to reverse named callback list." << endl;
+ }
+
+ if (output.size() != 3) {
+ cerr << "ReverseNamedCallbackList gave repetition with wrong length." << endl;
+ return false;
+ }
+
+ if (reversed.size() != 3) {
+ cerr << "ReverseNamedCallbackList gave reversal with wrong length." << endl;
+ return false;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ String16 ret;
+ sp<INamedCallback> named_callback =
+ android::interface_cast<INamedCallback>(output[i]);
+ status = named_callback->GetName(&ret);
+
+ if (!status.isOk()) {
+ cerr << "Could not query INamedCallback from output" << endl;
+ return false;
+ }
+
+ if (ret != names[i]) {
+ cerr << "Output had wrong INamedCallback" << endl;
+ return false;
+ }
+ }
+
+ for (int i = 0; i < 3; i++) {
+ String16 ret;
+ sp<INamedCallback> named_callback =
+ android::interface_cast<INamedCallback>(reversed[i]);
+ status = named_callback->GetName(&ret);
+
+ if (!status.isOk()) {
+ cerr << "Could not query INamedCallback from reversed output" << endl;
+ return false;
+ }
+
+ if (ret != names[2 - i]) {
+ cerr << "Reversed output had wrong INamedCallback" << endl;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
diff --git a/aidl/tests/aidl_test_client_primitives.h b/aidl/tests/aidl_test_client_primitives.h
new file mode 100644
index 0000000..f9b39da
--- /dev/null
+++ b/aidl/tests/aidl_test_client_primitives.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_AIDL_TESTS_CLIENT_PRIMITIVES_H
+#define ANDROID_AIDL_TESTS_CLIENT_PRIMITIVES_H
+
+#include <utils/StrongPointer.h>
+
+#include "android/aidl/tests/ITestService.h"
+
+// Tests for passing and returning primitive types defined in the AIDL docs.
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+bool ConfirmPrimitiveRepeat(const sp<ITestService>& s);
+bool ConfirmReverseArrays(const android::sp<ITestService>& s);
+bool ConfirmReverseLists(const android::sp<ITestService>& s);
+bool ConfirmReverseBinderLists(const android::sp<ITestService>& s);
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
+
+#endif // ANDROID_AIDL_TESTS_CLIENT_PRIMITIVES_H
diff --git a/aidl/tests/aidl_test_client_service_exceptions.cpp b/aidl/tests/aidl_test_client_service_exceptions.cpp
new file mode 100644
index 0000000..3f1f374
--- /dev/null
+++ b/aidl/tests/aidl_test_client_service_exceptions.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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_test_client_service_exceptions.h"
+
+#include <iostream>
+
+#include "binder/Status.h"
+
+using android::binder::Status;
+using std::cout;
+using std::endl;
+
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+bool ConfirmServiceSpecificExceptions(const sp<ITestService>& s) {
+ cout << "Confirming application exceptions work" << endl;
+
+ for (int32_t i = -1; i < 2; ++i) {
+ Status status = s->ThrowServiceException(i);
+ if (status.exceptionCode() != Status::EX_SERVICE_SPECIFIC ||
+ status.serviceSpecificErrorCode() != i) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
diff --git a/aidl/tests/aidl_test_client_service_exceptions.h b/aidl/tests/aidl_test_client_service_exceptions.h
new file mode 100644
index 0000000..e4a1dd3
--- /dev/null
+++ b/aidl/tests/aidl_test_client_service_exceptions.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_AIDL_TESTS_CLIENT_SERVICE_EXCEPTIONS_H
+#define ANDROID_AIDL_TESTS_CLIENT_SERVICE_EXCEPTIONS_H
+
+#include <utils/StrongPointer.h>
+
+#include "android/aidl/tests/ITestService.h"
+
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+// Tests for service specific exception support.
+bool ConfirmServiceSpecificExceptions(const sp<ITestService>& s);
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
+
+#endif // ANDROID_AIDL_TESTS_CLIENT_SERVICE_EXCEPTIONS_H
diff --git a/aidl/tests/aidl_test_client_utf8_strings.cpp b/aidl/tests/aidl_test_client_utf8_strings.cpp
new file mode 100644
index 0000000..216aef7
--- /dev/null
+++ b/aidl/tests/aidl_test_client_utf8_strings.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2016 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_test_client_utf8_strings.h"
+
+#include <android-base/logging.h>
+#include <binder/Status.h>
+#include <utils/StrongPointer.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "android/aidl/tests/ITestService.h"
+#include "test_helpers.h"
+
+// libutils:
+using android::sp;
+
+// libbinder:
+using android::binder::Status;
+
+// generated
+using android::aidl::tests::ITestService;
+
+using std::unique_ptr;
+using std::string;
+using std::vector;
+
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+bool ConfirmUtf8InCppStringRepeat(const sp<ITestService>& s) {
+ const vector<string> utf8_inputs = {
+ string("Deliver us from evil."),
+ string(),
+ string("\0\0", 2),
+ // Similarly, the utf8 encodings of the small letter yee and euro sign.
+ string("\xF0\x90\x90\xB7\xE2\x82\xAC"),
+ };
+ LOG(INFO) << "Confirming repeating utf8 strings works.";
+
+ for (const auto& input : utf8_inputs) {
+ string reply;
+ Status status = s->RepeatUtf8CppString(input, &reply);
+ if (!status.isOk() || input != reply) {
+ LOG(ERROR) << "Failed while requesting service to repeat utf8 string=\""
+ << input
+ << "\". Got status=" << status.toString8()
+ << " and output=" << reply;
+ return false;
+ }
+ }
+
+ unique_ptr<string> ret;
+ Status repeat_null_status = s->RepeatNullableUtf8CppString(nullptr, &ret);
+ if (!repeat_null_status.isOk() || ret) {
+ LOG(ERROR) << "RepeatNullableUtf8CppString(null) did not return null";
+ return false;
+ }
+
+ for (const auto& input : utf8_inputs) {
+ unique_ptr<string> reply;
+ Status status = s->RepeatNullableUtf8CppString(
+ unique_ptr<string>(new string(input)), &reply);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Got status=" << status.toString8() << " while repeating "
+ "nullable utf8 string " << input;
+ return false;
+ }
+ if (!reply) {
+ LOG(ERROR) << "Got null reply while repeating nullable utf8 string "
+ << input;
+ return false;
+ }
+ if (input != *reply) {
+ LOG(ERROR) << "Failed while requesting service to repeat utf8 string=\""
+ << input
+ << "\". Got status=" << status.toString8()
+ << " and output=" << *reply;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ConfirmUtf8InCppStringArrayReverse(const sp<ITestService>& s) {
+ LOG(INFO) << "Confirming passing and returning utf8 string arrays works.";
+ if (!ReverseArray(s, &ITestService::ReverseUtf8CppString,
+ {string{"a"}, string{}, string{"\xc3\xb8"}})) {
+ return false;
+ }
+
+ return true;
+}
+
+bool ConfirmUtf8InCppStringListReverse(const sp<ITestService>& s) {
+ LOG(INFO) << "Confirming reversing a list of utf8 strings works";
+ unique_ptr<vector<unique_ptr<string>>> input, reversed, repeated;
+ Status status = s->ReverseUtf8CppStringList(input, &reversed, &repeated);
+ if (!status.isOk() || reversed || repeated) {
+ LOG(ERROR) << "Reversing null list of utf8 strings failed.";
+ return false;
+ }
+
+ input.reset(new vector<unique_ptr<string>>);
+ input->emplace_back(new string("Deliver us from evil."));
+ input->emplace_back(nullptr);
+ input->emplace_back(new string("\xF0\x90\x90\xB7\xE2\x82\xAC"));
+
+ status = s->ReverseUtf8CppStringList(input, &repeated, &reversed);
+ if (!status.isOk() || !reversed || !repeated) {
+ LOG(ERROR) << "Reversing list of utf8 strings failed.";
+ return false;
+ }
+ if (reversed->size() != input->size() || repeated->size() != input->size()) {
+ LOG(ERROR) << "Bad output sizes.";
+ return false;
+ }
+
+ for (size_t i = 0; i < input->size(); ++i) {
+ const string* input_str = (*input)[i].get();
+ const string* repeated_str = (*repeated)[i].get();
+ const string* reversed_str = (*reversed)[(reversed->size() - 1) - i].get();
+ if (!input_str) {
+ if(repeated_str || reversed_str) {
+ LOG(ERROR) << "Expected null values, but got non-null.";
+ return false;
+ }
+ // 3 nullptrs to strings. No need to compare values.
+ continue;
+ }
+ if (!repeated_str || !reversed_str) {
+ LOG(ERROR) << "Expected non-null values, but got null.";
+ return false;
+ }
+ if (*input_str != *repeated_str || *input_str != *reversed_str) {
+ LOG(ERROR) << "Expected '" << *input_str << "' but got "
+ << "repeated='" << *repeated_str << "' and "
+ << "reversed='" << *reversed_str;
+ return false;
+ }
+ }
+ return true;
+}
+
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
diff --git a/aidl/tests/aidl_test_client_utf8_strings.h b/aidl/tests/aidl_test_client_utf8_strings.h
new file mode 100644
index 0000000..a362fd5
--- /dev/null
+++ b/aidl/tests/aidl_test_client_utf8_strings.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ANDROID_AIDL_TESTS_CLIENT_UTF8_STRINGS_H
+#define ANDROID_AIDL_TESTS_CLIENT_UTF8_STRINGS_H
+
+#include <utils/StrongPointer.h>
+
+#include "android/aidl/tests/ITestService.h"
+
+// Tests for passing and returning utf8 strings.
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+bool ConfirmUtf8InCppStringRepeat(
+ const android::sp<android::aidl::tests::ITestService>& s);
+bool ConfirmUtf8InCppStringArrayReverse(
+ const android::sp<android::aidl::tests::ITestService>& s);
+bool ConfirmUtf8InCppStringListReverse(
+ const android::sp<android::aidl::tests::ITestService>& s);
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
+
+#endif // ANDROID_AIDL_TESTS_CLIENT_UTF8_STRINGS_H
+
diff --git a/aidl/tests/aidl_test_sentinel_searcher.cpp b/aidl/tests/aidl_test_sentinel_searcher.cpp
new file mode 100644
index 0000000..e260366
--- /dev/null
+++ b/aidl/tests/aidl_test_sentinel_searcher.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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 <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+
+using std::cerr;
+using std::cout;
+using std::endl;
+using std::getline;
+using std::ifstream;
+using std::string;
+using std::vector;
+
+namespace {
+
+bool ReadLines(const string& input_file_path, vector<string>* lines) {
+ ifstream watched_file(input_file_path);
+ if (!watched_file.is_open()) {
+ cerr << "Unable to open input file: " << input_file_path << endl;
+ return false;
+ }
+
+ string line;
+ while (getline(watched_file, line)) {
+ lines->push_back(line);
+ }
+ watched_file.close();
+ return true;
+}
+
+bool HasSentinel(const vector<string>& lines, const string& sentinel) {
+ for (const auto& line : lines) {
+ if (line.find(sentinel) != string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+int main(int argc, const char* argv[]) {
+ if (argc != 5) {
+ cerr << "Invalid usage." << endl;
+ cerr << argv[0]
+ << " <timeout in seconds>"
+ << " <input file path>"
+ << " <success sentinel>"
+ << " <failure sentinel>" << endl;
+ return -EINVAL;
+ }
+ const string timeout_as_str = argv[1];
+ const string input_file_path = argv[2];
+ const string success_sentinel = argv[3];
+ const string failure_sentinel = argv[4];
+
+ const int timeout_seconds = atoi(timeout_as_str.c_str());
+ if (timeout_seconds <= 0) {
+ cerr << "Invalid timeout value (in seconds): " << timeout_as_str << endl;
+ return -EINVAL;
+ }
+
+ int exit_code = 1;
+ const time_t start_time = time(nullptr);
+ vector<string> lines;
+ while (true) {
+ sleep(1);
+ if (time(nullptr) - start_time > timeout_seconds) {
+ cerr << "Timed out waiting for success/failure sentinel." << endl;
+ break;
+ }
+ // Ignore errors when reading lines. The file may not immediately exist
+ // because it takes the Java process some time to create it.
+ lines.clear();
+ ReadLines(input_file_path, &lines);
+
+ if (HasSentinel(lines, success_sentinel)) {
+ exit_code = 0;
+ break;
+ }
+ if (HasSentinel(lines, failure_sentinel)) {
+ break;
+ }
+ }
+
+ cout << "Found output:" << endl;
+ for (const auto& line : lines) {
+ cout << " " << line << endl;
+ }
+ return exit_code;
+}
diff --git a/aidl/tests/aidl_test_service.cpp b/aidl/tests/aidl_test_service.cpp
new file mode 100644
index 0000000..9e2304e
--- /dev/null
+++ b/aidl/tests/aidl_test_service.cpp
@@ -0,0 +1,459 @@
+/*
+ * 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 <map>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+#include <binder/IInterface.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <binder/Status.h>
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+#include <utils/StrongPointer.h>
+
+#include "android/aidl/tests/BnTestService.h"
+#include "android/aidl/tests/ITestService.h"
+
+#include "android/aidl/tests/BnNamedCallback.h"
+#include "android/aidl/tests/INamedCallback.h"
+
+// Used implicitly.
+#undef LOG_TAG
+#define LOG_TAG "aidl_native_service"
+
+// libbase
+using android::base::unique_fd;
+
+// libutils:
+using android::Looper;
+using android::LooperCallback;
+using android::OK;
+using android::sp;
+using android::String16;
+
+// libbinder:
+using android::BnInterface;
+using android::defaultServiceManager;
+using android::IInterface;
+using android::IPCThreadState;
+using android::Parcel;
+using android::ProcessState;
+using android::binder::Status;
+
+// Generated code:
+using android::aidl::tests::BnNamedCallback;
+using android::aidl::tests::BnTestService;
+using android::aidl::tests::INamedCallback;
+using android::aidl::tests::SimpleParcelable;
+using android::os::PersistableBundle;
+
+// Standard library
+using std::map;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace {
+
+class BinderCallback : public LooperCallback {
+ public:
+ BinderCallback() {}
+ ~BinderCallback() override {}
+
+ int handleEvent(int /* fd */, int /* events */, void* /* data */) override {
+ IPCThreadState::self()->handlePolledCommands();
+ return 1; // Continue receiving callbacks.
+ }
+};
+
+class NamedCallback : public BnNamedCallback {
+ public:
+ explicit NamedCallback(String16 name) : name_(name) {}
+
+ Status GetName(String16* ret) {
+ *ret = name_;
+ return Status::ok();
+ }
+
+ private:
+ String16 name_;
+};
+
+class NativeService : public BnTestService {
+ public:
+ NativeService() {}
+ virtual ~NativeService() = default;
+
+ void LogRepeatedStringToken(const String16& token) {
+ ALOGI("Repeating '%s' of length=%zu", android::String8(token).string(),
+ token.size());
+ }
+
+ template <typename T>
+ void LogRepeatedToken(const T& token) {
+ std::ostringstream token_str;
+ token_str << token;
+ ALOGI("Repeating token %s", token_str.str().c_str());
+ }
+
+ Status RepeatBoolean(bool token, bool* _aidl_return) override {
+ LogRepeatedToken(token ? 1 : 0);
+ *_aidl_return = token;
+ return Status::ok();
+ }
+ Status RepeatByte(int8_t token, int8_t* _aidl_return) override {
+ LogRepeatedToken(token);
+ *_aidl_return = token;
+ return Status::ok();
+ }
+ Status RepeatChar(char16_t token, char16_t* _aidl_return) override {
+ LogRepeatedStringToken(String16(&token, 1));
+ *_aidl_return = token;
+ return Status::ok();
+ }
+ Status RepeatInt(int32_t token, int32_t* _aidl_return) override {
+ LogRepeatedToken(token);
+ *_aidl_return = token;
+ return Status::ok();
+ }
+ Status RepeatLong(int64_t token, int64_t* _aidl_return) override {
+ LogRepeatedToken(token);
+ *_aidl_return = token;
+ return Status::ok();
+ }
+ Status RepeatFloat(float token, float* _aidl_return) override {
+ LogRepeatedToken(token);
+ *_aidl_return = token;
+ return Status::ok();
+ }
+ Status RepeatDouble(double token, double* _aidl_return) override {
+ LogRepeatedToken(token);
+ *_aidl_return = token;
+ return Status::ok();
+ }
+ Status RepeatString(const String16& token, String16* _aidl_return) override {
+ LogRepeatedStringToken(token);
+ *_aidl_return = token;
+ return Status::ok();
+ }
+
+ Status RepeatSimpleParcelable(const SimpleParcelable& input,
+ SimpleParcelable* repeat,
+ SimpleParcelable* _aidl_return) override {
+ ALOGI("Repeated a SimpleParcelable %s", input.toString().c_str());
+ *repeat = input;
+ *_aidl_return = input;
+ return Status::ok();
+ }
+
+ Status RepeatPersistableBundle(const PersistableBundle& input,
+ PersistableBundle* _aidl_return) override {
+ ALOGI("Repeated a PersistableBundle");
+ *_aidl_return = input;
+ return Status::ok();
+ }
+
+ template <typename T>
+ Status ReverseArray(const vector<T>& input, vector<T>* repeated,
+ vector<T>* _aidl_return) {
+ ALOGI("Reversing array of length %zu", input.size());
+ *repeated = input;
+ *_aidl_return = input;
+ std::reverse(_aidl_return->begin(), _aidl_return->end());
+ return Status::ok();
+ }
+
+ template<typename T>
+ Status RepeatNullable(const unique_ptr<T>& input,
+ unique_ptr<T>* _aidl_return) {
+ ALOGI("Repeating nullable value");
+
+ _aidl_return->reset();
+ if (input) {
+ _aidl_return->reset(new T(*input));
+ }
+
+ return Status::ok();
+ }
+
+ Status ReverseBoolean(const vector<bool>& input,
+ vector<bool>* repeated,
+ vector<bool>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+ Status ReverseByte(const vector<uint8_t>& input,
+ vector<uint8_t>* repeated,
+ vector<uint8_t>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+ Status ReverseChar(const vector<char16_t>& input,
+ vector<char16_t>* repeated,
+ vector<char16_t>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+ Status ReverseInt(const vector<int32_t>& input,
+ vector<int32_t>* repeated,
+ vector<int32_t>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+ Status ReverseLong(const vector<int64_t>& input,
+ vector<int64_t>* repeated,
+ vector<int64_t>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+ Status ReverseFloat(const vector<float>& input,
+ vector<float>* repeated,
+ vector<float>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+ Status ReverseDouble(const vector<double>& input,
+ vector<double>* repeated,
+ vector<double>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+ Status ReverseString(const vector<String16>& input,
+ vector<String16>* repeated,
+ vector<String16>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+ Status ReverseSimpleParcelables(
+ const vector<SimpleParcelable>& input,
+ vector<SimpleParcelable>* repeated,
+ vector<SimpleParcelable>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+ Status ReversePersistableBundles(
+ const vector<PersistableBundle>& input,
+ vector<PersistableBundle>* repeated,
+ vector<PersistableBundle>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+
+ Status GetOtherTestService(const String16& name,
+ sp<INamedCallback>* returned_service) override {
+ if (service_map_.find(name) == service_map_.end()) {
+ sp<INamedCallback> new_item(new NamedCallback(name));
+ service_map_[name] = new_item;
+ }
+
+ *returned_service = service_map_[name];
+ return Status::ok();
+ }
+
+ Status VerifyName(const sp<INamedCallback>& service, const String16& name,
+ bool* returned_value) override {
+ String16 foundName;
+ Status status = service->GetName(&foundName);
+
+ if (status.isOk()) {
+ *returned_value = foundName == name;
+ }
+
+ return status;
+ }
+
+ Status ReverseStringList(const vector<String16>& input,
+ vector<String16>* repeated,
+ vector<String16>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+
+ Status ReverseNamedCallbackList(const vector<sp<IBinder>>& input,
+ vector<sp<IBinder>>* repeated,
+ vector<sp<IBinder>>* _aidl_return) override {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+
+ Status RepeatFileDescriptor(const unique_fd& read,
+ unique_fd* _aidl_return) override {
+ ALOGE("Repeating file descriptor");
+ *_aidl_return = unique_fd(dup(read.get()));
+ return Status::ok();
+ }
+
+ Status ReverseFileDescriptorArray(const vector<unique_fd>& input,
+ vector<unique_fd>* repeated,
+ vector<unique_fd>* _aidl_return) override {
+ ALOGI("Reversing descriptor array of length %zu", input.size());
+ for (const auto& item : input) {
+ repeated->push_back(unique_fd(dup(item.get())));
+ _aidl_return->push_back(unique_fd(dup(item.get())));
+ }
+ std::reverse(_aidl_return->begin(), _aidl_return->end());
+ return Status::ok();
+ }
+
+ Status ThrowServiceException(int code) override {
+ return Status::fromServiceSpecificError(code);
+ }
+
+ Status RepeatNullableIntArray(const unique_ptr<vector<int32_t>>& input,
+ unique_ptr<vector<int32_t>>* _aidl_return) {
+ return RepeatNullable(input, _aidl_return);
+ }
+
+ Status RepeatNullableStringList(
+ const unique_ptr<vector<unique_ptr<String16>>>& input,
+ unique_ptr<vector<unique_ptr<String16>>>* _aidl_return) {
+ ALOGI("Repeating nullable string list");
+ if (!input) {
+ _aidl_return->reset();
+ return Status::ok();
+ }
+
+ _aidl_return->reset(new vector<unique_ptr<String16>>);
+
+ for (const auto& item : *input) {
+ if (!item) {
+ (*_aidl_return)->emplace_back(nullptr);
+ } else {
+ (*_aidl_return)->emplace_back(new String16(*item));
+ }
+ }
+
+ return Status::ok();
+ }
+
+ Status RepeatNullableString(const unique_ptr<String16>& input,
+ unique_ptr<String16>* _aidl_return) {
+ return RepeatNullable(input, _aidl_return);
+ }
+
+ Status RepeatNullableParcelable(const unique_ptr<SimpleParcelable>& input,
+ unique_ptr<SimpleParcelable>* _aidl_return) {
+ return RepeatNullable(input, _aidl_return);
+ }
+
+ Status TakesAnIBinder(const sp<IBinder>& input) override {
+ (void)input;
+ return Status::ok();
+ }
+ Status TakesAnIBinderList(const vector<sp<IBinder>>& input) override {
+ (void)input;
+ return Status::ok();
+ }
+ Status TakesANullableIBinder(const sp<IBinder>& input) {
+ (void)input;
+ return Status::ok();
+ }
+ Status TakesANullableIBinderList(const unique_ptr<vector<sp<IBinder>>>& input) {
+ (void)input;
+ return Status::ok();
+ }
+
+ Status RepeatUtf8CppString(const string& token,
+ string* _aidl_return) override {
+ ALOGI("Repeating utf8 string '%s' of length=%zu", token.c_str(), token.size());
+ *_aidl_return = token;
+ return Status::ok();
+ }
+
+ Status RepeatNullableUtf8CppString(
+ const unique_ptr<string>& token,
+ unique_ptr<string>* _aidl_return) override {
+ if (!token) {
+ ALOGI("Received null @utf8InCpp string");
+ return Status::ok();
+ }
+ ALOGI("Repeating utf8 string '%s' of length=%zu",
+ token->c_str(), token->size());
+ _aidl_return->reset(new string(*token));
+ return Status::ok();
+ }
+
+ Status ReverseUtf8CppString(const vector<string>& input,
+ vector<string>* repeated,
+ vector<string>* _aidl_return) {
+ return ReverseArray(input, repeated, _aidl_return);
+ }
+
+ Status ReverseUtf8CppStringList(
+ const unique_ptr<vector<unique_ptr<::string>>>& input,
+ unique_ptr<vector<unique_ptr<string>>>* repeated,
+ unique_ptr<vector<unique_ptr<string>>>* _aidl_return) {
+ if (!input) {
+ ALOGI("Received null list of utf8 strings");
+ return Status::ok();
+ }
+ _aidl_return->reset(new vector<unique_ptr<string>>);
+ repeated->reset(new vector<unique_ptr<string>>);
+
+ for (const auto& item : *input) {
+ (*repeated)->emplace_back(nullptr);
+ (*_aidl_return)->emplace_back(nullptr);
+ if (item) {
+ (*repeated)->back().reset(new string(*item));
+ (*_aidl_return)->back().reset(new string(*item));
+ }
+ }
+ std::reverse((*_aidl_return)->begin(), (*_aidl_return)->end());
+
+ return Status::ok();
+ }
+
+ Status GetCallback(bool return_null, sp<INamedCallback>* ret) {
+ if (!return_null) {
+ return GetOtherTestService(String16("ABT: always be testing"), ret);
+ }
+ return Status::ok();
+ }
+
+ private:
+ map<String16, sp<INamedCallback>> service_map_;
+};
+
+int Run() {
+ android::sp<NativeService> service = new NativeService;
+ sp<Looper> looper(Looper::prepare(0 /* opts */));
+
+ int binder_fd = -1;
+ ProcessState::self()->setThreadPoolMaxThreadCount(0);
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+ IPCThreadState::self()->setupPolling(&binder_fd);
+ ALOGI("Got binder FD %d", binder_fd);
+ if (binder_fd < 0) return -1;
+
+ sp<BinderCallback> cb(new BinderCallback);
+ if (looper->addFd(binder_fd, Looper::POLL_CALLBACK, Looper::EVENT_INPUT, cb,
+ nullptr) != 1) {
+ ALOGE("Failed to add binder FD to Looper");
+ return -1;
+ }
+
+ defaultServiceManager()->addService(service->getInterfaceDescriptor(),
+ service);
+
+ ALOGI("Entering loop");
+ while (true) {
+ const int result = looper->pollAll(-1 /* timeoutMillis */);
+ ALOGI("Looper returned %d", result);
+ }
+ return 0;
+}
+
+} // namespace
+
+int main(int /* argc */, char* /* argv */ []) {
+ return Run();
+}
diff --git a/aidl/tests/android/aidl/tests/INamedCallback.aidl b/aidl/tests/android/aidl/tests/INamedCallback.aidl
new file mode 100644
index 0000000..c295fef
--- /dev/null
+++ b/aidl/tests/android/aidl/tests/INamedCallback.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+package android.aidl.tests;
+
+interface INamedCallback {
+ String GetName();
+}
diff --git a/aidl/tests/android/aidl/tests/ITestService.aidl b/aidl/tests/android/aidl/tests/ITestService.aidl
new file mode 100644
index 0000000..4de6925
--- /dev/null
+++ b/aidl/tests/android/aidl/tests/ITestService.aidl
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+package android.aidl.tests;
+
+import android.aidl.tests.INamedCallback;
+import android.aidl.tests.SimpleParcelable;
+import android.os.PersistableBundle;
+
+interface ITestService {
+ // Test that constants are accessible
+ const int TEST_CONSTANT = 42;
+ const int TEST_CONSTANT2 = -42;
+ const int TEST_CONSTANT3 = +42;
+ const int TEST_CONSTANT4 = +4;
+ const int TEST_CONSTANT5 = -4;
+ const int TEST_CONSTANT6 = -0;
+ const int TEST_CONSTANT7 = +0;
+ const int TEST_CONSTANT8 = 0;
+ const int TEST_CONSTANT9 = 0x56;
+ const int TEST_CONSTANT10 = 0xa5;
+ const int TEST_CONSTANT11 = 0xFA;
+ const int TEST_CONSTANT12 = 0xffffffff;
+
+ const String STRING_TEST_CONSTANT = "foo";
+ const String STRING_TEST_CONSTANT2 = "bar";
+
+ // Test that primitives work as parameters and return types.
+ boolean RepeatBoolean(boolean token);
+ byte RepeatByte(byte token);
+ char RepeatChar(char token);
+ int RepeatInt(int token);
+ long RepeatLong(long token);
+ float RepeatFloat(float token);
+ double RepeatDouble(double token);
+ String RepeatString(String token);
+
+ SimpleParcelable RepeatSimpleParcelable(in SimpleParcelable input,
+ out SimpleParcelable repeat);
+ PersistableBundle RepeatPersistableBundle(in PersistableBundle input);
+
+ // Test that arrays work as parameters and return types.
+ boolean[] ReverseBoolean(in boolean[] input, out boolean[] repeated);
+ byte[] ReverseByte (in byte[] input, out byte[] repeated);
+ char[] ReverseChar (in char[] input, out char[] repeated);
+ int[] ReverseInt (in int[] input, out int[] repeated);
+ long[] ReverseLong (in long[] input, out long[] repeated);
+ float[] ReverseFloat (in float[] input, out float[] repeated);
+ double[] ReverseDouble (in double[] input, out double[] repeated);
+ String[] ReverseString (in String[] input, out String[] repeated);
+
+ SimpleParcelable[] ReverseSimpleParcelables(in SimpleParcelable[] input,
+ out SimpleParcelable[] repeated);
+ PersistableBundle[] ReversePersistableBundles(
+ in PersistableBundle[] input, out PersistableBundle[] repeated);
+
+ // Test that clients can send and receive Binders.
+ INamedCallback GetOtherTestService(String name);
+ boolean VerifyName(INamedCallback service, String name);
+
+ // Test that List<T> types work correctly.
+ List<String> ReverseStringList(in List<String> input,
+ out List<String> repeated);
+ List<IBinder> ReverseNamedCallbackList(in List<IBinder> input,
+ out List<IBinder> repeated);
+
+ FileDescriptor RepeatFileDescriptor(in FileDescriptor read);
+ FileDescriptor[] ReverseFileDescriptorArray(in FileDescriptor[] input,
+ out FileDescriptor[] repeated);
+
+ // Test that service specific exceptions work correctly.
+ void ThrowServiceException(int code);
+
+ // Test nullability
+ @nullable int[] RepeatNullableIntArray(in @nullable int[] input);
+ @nullable String RepeatNullableString(in @nullable String input);
+ @nullable List<String> RepeatNullableStringList(in @nullable List<String> input);
+ @nullable SimpleParcelable RepeatNullableParcelable(in @nullable SimpleParcelable input);
+
+ void TakesAnIBinder(in IBinder input);
+ void TakesAnIBinderList(in List<IBinder> input);
+ void TakesANullableIBinder(in @nullable IBinder input);
+ void TakesANullableIBinderList(in @nullable List<IBinder> input);
+
+ // Test utf8 decoding from utf16 wire format
+ @utf8InCpp String RepeatUtf8CppString(@utf8InCpp String token);
+ @nullable @utf8InCpp String RepeatNullableUtf8CppString(
+ @nullable @utf8InCpp String token);
+
+ @utf8InCpp String[] ReverseUtf8CppString (in @utf8InCpp String[] input,
+ out @utf8InCpp String[] repeated);
+
+ @nullable @utf8InCpp List<String> ReverseUtf8CppStringList(
+ in @nullable @utf8InCpp List<String> input,
+ out @nullable @utf8InCpp List<String> repeated);
+
+ @nullable INamedCallback GetCallback(boolean return_null);
+}
diff --git a/aidl/tests/android/aidl/tests/SimpleParcelable.aidl b/aidl/tests/android/aidl/tests/SimpleParcelable.aidl
new file mode 100644
index 0000000..8573999
--- /dev/null
+++ b/aidl/tests/android/aidl/tests/SimpleParcelable.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+package android.aidl.tests;
+
+parcelable SimpleParcelable cpp_header "tests/simple_parcelable.h";
diff --git a/aidl/tests/end_to_end_tests.cpp b/aidl/tests/end_to_end_tests.cpp
new file mode 100644
index 0000000..480bfd9
--- /dev/null
+++ b/aidl/tests/end_to_end_tests.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+#include "aidl.h"
+#include "options.h"
+#include "tests/fake_io_delegate.h"
+#include "tests/test_data.h"
+#include "tests/test_util.h"
+
+using android::aidl::test::CanonicalNameToPath;
+using android::aidl::test::FakeIoDelegate;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace android {
+namespace aidl {
+
+class EndToEndTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ }
+
+ void AddStubAidls(const char** parcelables, const char** interfaces,
+ const char* cpp_header=nullptr) {
+ for ( ; *parcelables; ++parcelables) {
+ io_delegate_.AddStubParcelable(
+ *parcelables, (cpp_header) ? cpp_header : "");
+ }
+ for ( ; *interfaces; ++interfaces) {
+ io_delegate_.AddStubInterface(*interfaces);
+ }
+ }
+
+ void CheckFileContents(const string& rel_path,
+ const string& expected_content) {
+ string actual_content;
+ ASSERT_TRUE(io_delegate_.GetWrittenContents(rel_path, &actual_content))
+ << "Expected aidl to write to " << rel_path << " but it did not.";
+
+ if (actual_content == expected_content) {
+ return; // success!
+ }
+
+ test::PrintDiff(expected_content, actual_content);
+ FAIL() << "Actual contents of " << rel_path
+ << " did not match expected content";
+ }
+
+ FakeIoDelegate io_delegate_;
+};
+
+TEST_F(EndToEndTest, IExampleInterface) {
+ using namespace ::android::aidl::test_data::example_interface;
+
+ JavaOptions options;
+ options.fail_on_parcelable_ = true;
+ options.import_paths_.push_back("");
+ options.input_file_name_ = CanonicalNameToPath(kCanonicalName, ".aidl");
+ options.output_file_name_ = kJavaOutputPath;
+ options.dep_file_name_ = "an/arbitrary/path/to/deps.P";
+
+ // Load up our fake file system with data.
+ io_delegate_.SetFileContents(options.input_file_name_, kInterfaceDefinition);
+ io_delegate_.AddCompoundParcelable("android.test.CompoundParcelable",
+ {"Subclass1", "Subclass2"});
+ AddStubAidls(kImportedParcelables, kImportedInterfaces);
+
+ // Check that we parse correctly.
+ EXPECT_EQ(android::aidl::compile_aidl_to_java(options, io_delegate_), 0);
+ CheckFileContents(kJavaOutputPath, kExpectedJavaOutput);
+ CheckFileContents(options.DependencyFilePath(), kExpectedJavaDepsOutput);
+}
+
+TEST_F(EndToEndTest, IPingResponderCpp) {
+ using namespace ::android::aidl::test_data::ping_responder;
+
+ const string input_path = CanonicalNameToPath(kCanonicalName, ".aidl");
+ const string output_file = kCppOutputPath;
+ const size_t argc = 6;
+ const char* cmdline[argc + 1] = {
+ "aidl-cpp", "-ddeps.P", "-I.", input_path.c_str(), kGenHeaderDir,
+ output_file.c_str(), nullptr
+ };
+ auto options = CppOptions::Parse(argc, cmdline);
+
+ // Set up input paths.
+ io_delegate_.SetFileContents(input_path, kInterfaceDefinition);
+ AddStubAidls(kImportedParcelables, kImportedInterfaces, kCppParcelableHeader);
+
+ // Check that we parse and generate code correctly.
+ EXPECT_EQ(android::aidl::compile_aidl_to_cpp(*options, io_delegate_), 0);
+ CheckFileContents(output_file, kExpectedCppOutput);
+ CheckFileContents(kGenInterfaceHeaderPath, kExpectedIHeaderOutput);
+ CheckFileContents(kGenClientHeaderPath, kExpectedBpHeaderOutput);
+ CheckFileContents(kGenServerHeaderPath, kExpectedBnHeaderOutput);
+ CheckFileContents(options->DependencyFilePath(), kExpectedCppDepsOutput);
+}
+
+TEST_F(EndToEndTest, StringConstantsInCpp) {
+ using namespace ::android::aidl::test_data::string_constants;
+
+ const string input_path = CanonicalNameToPath(kCanonicalName, ".aidl");
+ const string output_file = kCppOutputPath;
+ const size_t argc = 4;
+ const char* cmdline[argc + 1] = {
+ "aidl-cpp", input_path.c_str(), kGenHeaderDir,
+ output_file.c_str(), nullptr
+ };
+ auto options = CppOptions::Parse(argc, cmdline);
+
+ // Set up input paths.
+ io_delegate_.SetFileContents(input_path, kInterfaceDefinition);
+
+ // Check that we parse and generate code correctly.
+ EXPECT_EQ(android::aidl::compile_aidl_to_cpp(*options, io_delegate_), 0);
+ CheckFileContents(output_file, kExpectedCppOutput);
+ CheckFileContents(kGenInterfaceHeaderPath, kExpectedIHeaderOutput);
+}
+
+TEST_F(EndToEndTest, StringConstantsInJava) {
+ using namespace ::android::aidl::test_data::string_constants;
+
+ const string input_path = CanonicalNameToPath(kCanonicalName, ".aidl");
+ const string output_file = kJavaOutputPath;
+ const size_t argc = 4;
+ const char* cmdline[argc + 1] = {
+ "aidl",
+ "-b",
+ input_path.c_str(),
+ output_file.c_str(),
+ nullptr,
+};
+ auto options = JavaOptions::Parse(argc, cmdline);
+
+ // Load up our fake file system with data.
+ io_delegate_.SetFileContents(input_path, kInterfaceDefinition);
+
+ // Check that we parse correctly.
+ EXPECT_EQ(android::aidl::compile_aidl_to_java(*options, io_delegate_), 0);
+ CheckFileContents(kJavaOutputPath, kExpectedJavaOutput);
+}
+
+} // namespace android
+} // namespace aidl
diff --git a/aidl/tests/fake_io_delegate.cpp b/aidl/tests/fake_io_delegate.cpp
new file mode 100644
index 0000000..c5575f6
--- /dev/null
+++ b/aidl/tests/fake_io_delegate.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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 "fake_io_delegate.h"
+
+#include <android-base/stringprintf.h>
+
+#include "logging.h"
+#include "os.h"
+#include "tests/test_util.h"
+
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+namespace android {
+namespace aidl {
+namespace test {
+
+// Claims to always write successfully, but can't close the file.
+class BrokenCodeWriter : public CodeWriter {
+ bool Write(const char* /* format */, ...) override { return true; }
+ bool Close() override { return false; }
+ virtual ~BrokenCodeWriter() = default;
+}; // class BrokenCodeWriter
+
+unique_ptr<string> FakeIoDelegate::GetFileContents(
+ const string& relative_filename,
+ const string& content_suffix) const {
+ string filename = CleanPath(relative_filename);
+ unique_ptr<string> contents;
+ auto it = file_contents_.find(filename);
+ if (it == file_contents_.end()) {
+ return contents;
+ }
+ contents.reset(new string);
+ *contents = it->second;
+ contents->append(content_suffix);
+
+ return contents;
+}
+
+unique_ptr<LineReader> FakeIoDelegate::GetLineReader(
+ const string& file_path) const {
+ unique_ptr<LineReader> ret;
+ const auto& it = file_contents_.find(CleanPath(file_path));
+ if (it != file_contents_.cend()) {
+ ret = LineReader::ReadFromMemory(it->second);
+ }
+ return ret;
+}
+
+bool FakeIoDelegate::FileIsReadable(const string& path) const {
+ return file_contents_.find(CleanPath(path)) != file_contents_.end();
+}
+
+bool FakeIoDelegate::CreatedNestedDirs(
+ const std::string& /* base_dir */,
+ const std::vector<std::string>& /* nested_subdirs */) const {
+ // We don't test directory creation explicitly.
+ return true;
+}
+
+std::unique_ptr<CodeWriter> FakeIoDelegate::GetCodeWriter(
+ const std::string& file_path) const {
+ if (broken_files_.count(file_path) > 0) {
+ return unique_ptr<CodeWriter>(new BrokenCodeWriter);
+ }
+ removed_files_.erase(file_path);
+ written_file_contents_[file_path] = "";
+ return GetStringWriter(&written_file_contents_[file_path]);
+}
+
+void FakeIoDelegate::RemovePath(const std::string& file_path) const {
+ removed_files_.insert(file_path);
+}
+
+void FakeIoDelegate::SetFileContents(const string& filename,
+ const string& contents) {
+ file_contents_[filename] = contents;
+}
+
+void FakeIoDelegate::AddStubParcelable(const string& canonical_name,
+ const string& cpp_header) {
+ string package, class_name, rel_path;
+ SplitPackageClass(canonical_name, &rel_path, &package, &class_name);
+ string contents;
+ if (cpp_header.empty()) {
+ contents = StringPrintf("package %s;\nparcelable %s;",
+ package.c_str(), class_name.c_str());
+ } else {
+ contents = StringPrintf("package %s;\nparcelable %s cpp_header \"%s\";",
+ package.c_str(), class_name.c_str(),
+ cpp_header.c_str());
+ }
+ SetFileContents(rel_path, contents);
+}
+
+void FakeIoDelegate::AddStubInterface(const string& canonical_name) {
+ string package, class_name, rel_path;
+ SplitPackageClass(canonical_name, &rel_path, &package, &class_name);
+ string contents = StringPrintf("package %s;\ninterface %s { }",
+ package.c_str(), class_name.c_str());
+ SetFileContents(rel_path, contents);
+}
+
+void FakeIoDelegate::AddCompoundParcelable(const string& canonical_name,
+ const vector<string>& subclasses) {
+ string package, class_name, rel_path;
+ SplitPackageClass(canonical_name, &rel_path, &package, &class_name);
+ string contents = StringPrintf("package %s;\n", package.c_str());
+ for (const string& subclass : subclasses) {
+ StringAppendF(&contents, "parcelable %s.%s;\n",
+ class_name.c_str(), subclass.c_str());
+ }
+ SetFileContents(rel_path, contents);
+}
+
+void FakeIoDelegate::AddBrokenFilePath(const std::string& path) {
+ broken_files_.insert(path);
+}
+
+bool FakeIoDelegate::GetWrittenContents(const string& path, string* content) {
+ const auto it = written_file_contents_.find(path);
+ if (it == written_file_contents_.end()) {
+ return false;
+ }
+ if (content) {
+ *content = it->second;
+ }
+ return true;
+}
+
+bool FakeIoDelegate::PathWasRemoved(const std::string& path) {
+ if (removed_files_.count(path) > 0) {
+ return true;
+ }
+ return false;
+}
+
+string FakeIoDelegate::CleanPath(const string& path) const {
+ string clean_path = path;
+ while (clean_path.length() >= 2 &&
+ clean_path[0] == '.' &&
+ clean_path[1] == OS_PATH_SEPARATOR) {
+ clean_path = clean_path.substr(2);
+ }
+ return clean_path;
+}
+
+} // namespace test
+} // namespace android
+} // namespace aidl
diff --git a/aidl/tests/fake_io_delegate.h b/aidl/tests/fake_io_delegate.h
new file mode 100644
index 0000000..aae2091
--- /dev/null
+++ b/aidl/tests/fake_io_delegate.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_TESTS_FAKE_IO_DELEGATE_H_
+#define AIDL_TESTS_FAKE_IO_DELEGATE_H_
+
+#include <android-base/macros.h>
+
+#include <map>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "io_delegate.h"
+
+namespace android {
+namespace aidl {
+namespace test {
+
+class FakeIoDelegate : public IoDelegate {
+ public:
+ FakeIoDelegate() = default;
+ virtual ~FakeIoDelegate() = default;
+
+ // Overrides from the real IoDelegate
+ std::unique_ptr<std::string> GetFileContents(
+ const std::string& filename,
+ const std::string& append_content_suffix = "") const override;
+ std::unique_ptr<LineReader> GetLineReader(
+ const std::string& file_path) const override;
+ bool FileIsReadable(const std::string& path) const override;
+ bool CreatedNestedDirs(
+ const std::string& base_dir,
+ const std::vector<std::string>& nested_subdirs) const override;
+ std::unique_ptr<CodeWriter> GetCodeWriter(
+ const std::string& file_path) const override;
+ void RemovePath(const std::string& file_path) const override;
+
+ // Methods added to facilitate testing.
+ void SetFileContents(const std::string& filename,
+ const std::string& contents);
+ void AddStubParcelable(const std::string& canonical_name,
+ const std::string& cpp_header);
+ void AddStubInterface(const std::string& canonical_name);
+ void AddCompoundParcelable(const std::string& canonical_name,
+ const std::vector<std::string>& subclasses);
+ void AddBrokenFilePath(const std::string& path);
+ // 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);
+
+ bool PathWasRemoved(const std::string& path);
+
+ private:
+ // Remove leading "./" from |path|.
+ std::string CleanPath(const std::string& path) const;
+
+ std::map<std::string, std::string> file_contents_;
+ // Normally, writing to files leaves the IoDelegate unchanged, so
+ // GetCodeWriter is a const method. However, for tests, we break this
+ // intentionally by storing the written strings.
+ mutable std::map<std::string, std::string> written_file_contents_;
+
+ // We normally just write to strings in |written_file_contents_| but for
+ // files in this list, we simulate I/O errors.
+ std::set<std::string> broken_files_;
+ mutable std::set<std::string> removed_files_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeIoDelegate);
+}; // class FakeIoDelegate
+
+} // namespace test
+} // namespace android
+} // namespace aidl
+
+#endif // AIDL_TESTS_FAKE_IO_DELEGATE_H_
diff --git a/aidl/tests/integration-test.py b/aidl/tests/integration-test.py
new file mode 100755
index 0000000..0079ba9
--- /dev/null
+++ b/aidl/tests/integration-test.py
@@ -0,0 +1,192 @@
+#!/usr/bin/env python
+
+"""
+Test that aidl generates functional code by running it on an Android device.
+"""
+
+import argparse
+import pipes
+import subprocess
+import shlex
+
+JAVA_OUTPUT_READER = 'aidl_test_sentinel_searcher'
+NATIVE_TEST_CLIENT = 'aidl_test_client'
+NATIVE_TEST_SERVICE = 'aidl_test_service'
+
+TEST_FILTER_ALL = 'all'
+TEST_FILTER_JAVA = 'java'
+TEST_FILTER_NATIVE = 'native'
+
+JAVA_CLIENT_TIMEOUT_SECONDS = 30
+JAVA_LOG_FILE = '/data/data/android.aidl.tests/files/test-client.log'
+JAVA_SUCCESS_SENTINEL = '>>> Java Client Success <<<'
+JAVA_FAILURE_SENTINEL = '>>> Java Client Failure <<<'
+
+class TestFail(Exception):
+ """Raised on test failures."""
+ pass
+
+
+class ShellResult(object):
+ """Represents the result of running a shell command."""
+
+ def __init__(self, exit_status, stdout, stderr):
+ """Construct an instance.
+
+ Args:
+ exit_status: integer exit code of shell command
+ stdout: string stdout of shell command
+ stderr: string stderr of shell command
+ """
+ self.stdout = stdout
+ self.stderr = stderr
+ self.exit_status = exit_status
+
+ def printable_string(self):
+ """Get a string we could print to the logs and understand."""
+ output = []
+ output.append('stdout:')
+ for line in self.stdout.splitlines():
+ output.append(' > %s' % line)
+ output.append('stderr:')
+ for line in self.stderr.splitlines():
+ output.append(' > %s' % line)
+ return '\n'.join(output)
+
+
+class AdbHost(object):
+ """Represents a device connected via ADB."""
+
+ def __init__(self, device_serial=None, verbose=None):
+ """Construct an instance.
+
+ Args:
+ device_serial: options string serial number of attached device.
+ verbose: True iff we should print out ADB commands we run.
+ """
+ self._device_serial = device_serial
+ self._verbose = verbose
+
+ def run(self, command, background=False, ignore_status=False):
+ """Run a command on the device via adb shell.
+
+ Args:
+ command: string containing a shell command to run.
+ background: True iff we should run this command in the background.
+ ignore_status: True iff we should ignore the command's exit code.
+
+ Returns:
+ instance of ShellResult.
+
+ Raises:
+ subprocess.CalledProcessError on command exit != 0.
+ """
+ if background:
+ command = '( %s ) </dev/null >/dev/null 2>&1 &' % command
+ return self.adb('shell %s' % pipes.quote(command),
+ ignore_status=ignore_status)
+
+ def mktemp(self):
+ """Make a temp file on the device.
+
+ Returns:
+ path to created file as a string
+
+ Raises:
+ subprocess.CalledProcessError on failure.
+ """
+ # Work around b/19635681
+ result = self.run('source /system/etc/mkshrc && mktemp')
+ return result.stdout.strip()
+
+ def adb(self, command, ignore_status=False):
+ """Run an ADB command (e.g. `adb sync`).
+
+ Args:
+ command: string containing command to run
+ ignore_status: True iff we should ignore the command's exit code.
+
+ Returns:
+ instance of ShellResult.
+
+ Raises:
+ subprocess.CalledProcessError on command exit != 0.
+ """
+ command = 'adb %s' % command
+ if self._verbose:
+ print(command)
+ p = subprocess.Popen(command, shell=True, close_fds=True,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+ universal_newlines=True)
+ stdout, stderr = p.communicate()
+ if not ignore_status and p.returncode:
+ raise subprocess.CalledProcessError(p.returncode, command)
+ return ShellResult(p.returncode, stdout, stderr)
+
+
+def run_test(host, test_native, test_java):
+ """Body of the test.
+
+ Args:
+ host: AdbHost object to run tests on
+ test_native: True iff we should test native Binder clients.
+ test_java: True iff we shoudl test Java Binder clients.
+ """
+
+ print('Starting aidl integration testing...')
+
+ # Kill any previous test context
+ host.run('rm -f %s' % JAVA_LOG_FILE, ignore_status=True)
+ host.run('pkill %s' % NATIVE_TEST_SERVICE, ignore_status=True)
+
+ # Start up a native server
+ host.run(NATIVE_TEST_SERVICE, background=True)
+
+ # Start up clients
+ if test_native:
+ host.run('pkill %s' % NATIVE_TEST_CLIENT, ignore_status=True)
+ result = host.run(NATIVE_TEST_CLIENT, ignore_status=True)
+ if result.exit_status:
+ print(result.printable_string())
+ raise TestFail('%s returned status code %d' %
+ (NATIVE_TEST_CLIENT, result.exit_status))
+
+ if test_java:
+ host.run('am start -S -a android.intent.action.MAIN '
+ '-n android.aidl.tests/.TestServiceClient '
+ '--es sentinel.success "%s" '
+ '--es sentinel.failure "%s"' %
+ (JAVA_SUCCESS_SENTINEL, JAVA_FAILURE_SENTINEL))
+ result = host.run('%s %d %s "%s" "%s"' %
+ (JAVA_OUTPUT_READER, JAVA_CLIENT_TIMEOUT_SECONDS,
+ JAVA_LOG_FILE, JAVA_SUCCESS_SENTINEL,
+ JAVA_FAILURE_SENTINEL),
+ ignore_status=True)
+ if result.exit_status:
+ print(result.printable_string())
+ raise TestFail('Java client did not complete successfully.')
+
+ print('Success!')
+
+
+def main():
+ """Main entry point."""
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument(
+ '--test-filter', default=TEST_FILTER_ALL,
+ choices=[TEST_FILTER_ALL, TEST_FILTER_JAVA, TEST_FILTER_NATIVE])
+ parser.add_argument('--verbose', '-v', action='store_true', default=False)
+ args = parser.parse_args()
+ host = AdbHost(verbose=args.verbose)
+ try:
+ # Tragically, SELinux interferes with our testing
+ host.run('setenforce 0')
+ run_test(host,
+ args.test_filter in (TEST_FILTER_ALL, TEST_FILTER_NATIVE),
+ args.test_filter in (TEST_FILTER_ALL, TEST_FILTER_JAVA))
+ finally:
+ host.run('setenforce 1')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/aidl/tests/java_app/AndroidManifest.xml b/aidl/tests/java_app/AndroidManifest.xml
new file mode 100644
index 0000000..9093892
--- /dev/null
+++ b/aidl/tests/java_app/AndroidManifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.aidl.tests">
+
+ <application android:label="@string/application_title">
+
+ <activity android:name="android.aidl.tests.TestServiceClient">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/aidl/tests/java_app/README b/aidl/tests/java_app/README
new file mode 100644
index 0000000..a3dfceb
--- /dev/null
+++ b/aidl/tests/java_app/README
@@ -0,0 +1,17 @@
+To use this APK do something like:
+
+1) Build your favorite AOSP flavor.
+2) Start an emulator:
+ $ emulator
+3) Push over hellod binary:
+ $ adb remount && adb sync
+4) Install the Java client:
+ $ adb install -r <path-to-APK-in-out-directory>
+5) Put selinux in permissive mode.
+6) Start hellod:
+ $ adb shell "(hellod ) </dev/null >/dev/null 2>&1 &"
+7) Start Java client:
+ $ adb shell am start -S -a android.intent.action.MAIN \
+ -n "android.aidl.tests/.TestServiceClient"
+8) Watch output on logcat:
+ $ adb logcat -s TestServiceClient hellod
diff --git a/aidl/tests/java_app/resources/values/strings.xml b/aidl/tests/java_app/resources/values/strings.xml
new file mode 100644
index 0000000..d5ee63e
--- /dev/null
+++ b/aidl/tests/java_app/resources/values/strings.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Strictly speaking, we don't need this. However, without something in
+ resources/ the build rebuilds the APK despite having no changes to the
+ source code. This seems to be because it fails to generate some
+ resources metafile (R.stamp?) -->
+<resources>
+ <string
+ name="application_title"
+ >AIDL Test Services</string>
+</resources>
diff --git a/aidl/tests/java_app/src/android/aidl/tests/NullableTests.java b/aidl/tests/java_app/src/android/aidl/tests/NullableTests.java
new file mode 100644
index 0000000..83a9877
--- /dev/null
+++ b/aidl/tests/java_app/src/android/aidl/tests/NullableTests.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016, 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.TestFailException;
+import android.os.IBinder;
+import java.util.ArrayList;
+import java.util.List;
+
+class NullableTests {
+ private static final String TAG = "TestServiceClient";
+ private ITestService mService;
+ private TestLogger mLog;
+
+ public NullableTests(ITestService service, TestLogger logger) {
+ mService = service;
+ mLog = logger;
+ }
+
+ public void checkNullHandling() throws TestFailException {
+ mLog.log("Checking that sending null strings reports an error...");
+ try {
+ String response = mService.RepeatString(null);
+ mLog.logAndThrow("Expected to fail on null string input!");
+ } catch (NullPointerException ex) {
+ mLog.log("Caught an exception on null string parameter (expected)");
+ mLog.log("null strings behave as expected");
+ return;
+ } catch (Exception ex) {
+ mLog.logAndThrow("Expected to receive NullPointerException on " +
+ "null parameter, but got " + ex.toString());
+ }
+ mLog.logAndThrow("Expected to receive NullPointerException on " +
+ "null parameter, but nothing was thrown??");
+ }
+
+ public void checkNullBinderDetection() throws TestFailException {
+ mLog.log("Checking that service handles @nullable IBinder...");
+ try {
+ mService.TakesAnIBinder(null);
+ mLog.logAndThrow("Expected to fail on null Binder!");
+ } catch (NullPointerException ex) {
+ mLog.log("Caught an exception on null Binder parameter (expected)");
+ return;
+ } catch (Exception ex) {
+ mLog.logAndThrow("Expected to receive NullPointerException," +
+ "but got " + ex.toString());
+ }
+ mLog.logAndThrow("Expected to receive NullPointerException on " +
+ "null parameter, but nothing was thrown??");
+ }
+
+ public void checkNullBinderInListDetection() throws TestFailException {
+ List<IBinder> listWithNulls = new ArrayList<IBinder>();
+ listWithNulls.add(null);
+ try {
+ mService.TakesAnIBinderList(listWithNulls);
+ mLog.logAndThrow("Expected to fail on list with null Binder!");
+ } catch (NullPointerException ex) {
+ mLog.log("Caught an exception on list with null Binder (expected)");
+ return;
+ } catch (Exception ex) {
+ mLog.logAndThrow("Expected to receive NullPointerException," +
+ "but got " + ex.toString());
+ }
+ mLog.logAndThrow("Expected to receive NullPointerException on " +
+ "null parameter, but nothing was thrown??");
+ }
+
+ public void checkNullInterfaceHandling() throws TestFailException {
+ mLog.log("Checking @nullable IInterface handling...");
+ try {
+ INamedCallback callback = mService.GetCallback(false);
+ if (callback == null) {
+ mLog.logAndThrow("Expected to get non-null INamedCallback.");
+ }
+ callback = mService.GetCallback(true);
+ if (callback != null) {
+ mLog.logAndThrow("Expected to get null INamedCallback.");
+ }
+ } catch (Exception ex) {
+ mLog.logAndThrow("Unexpected exception during @nullable IInterface test: " +
+ ex.toString());
+ }
+ mLog.log("@nullable IInterface handling works as expected.");
+ }
+
+ public void runTests() throws TestFailException {
+ checkNullHandling();
+ checkNullBinderDetection();
+ checkNullBinderInListDetection();
+ checkNullInterfaceHandling();
+ }
+}
diff --git a/aidl/tests/java_app/src/android/aidl/tests/SimpleParcelable.java b/aidl/tests/java_app/src/android/aidl/tests/SimpleParcelable.java
new file mode 100644
index 0000000..1def98c
--- /dev/null
+++ b/aidl/tests/java_app/src/android/aidl/tests/SimpleParcelable.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+package android.aidl.tests;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class SimpleParcelable implements Parcelable {
+ private String mName;
+ private int mNumber;
+
+ SimpleParcelable() {}
+ SimpleParcelable(String name, int number) {
+ mName = name;
+ mNumber = number;
+ }
+
+ public int describeContents() { return 0; }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mName);
+ dest.writeInt(mNumber);
+ }
+
+ public void readFromParcel(Parcel source) {
+ mName = source.readString();
+ mNumber = source.readInt();
+ }
+
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ if (!(o instanceof SimpleParcelable)) {
+ return false;
+ }
+ SimpleParcelable p = (SimpleParcelable)o;
+ if ((mName == null && p.mName != null) ||
+ (mName != null && !mName.equals(p.mName))) {
+ return false;
+ }
+ return mNumber == p.mNumber;
+ }
+
+ public String toString() {
+ return "SimpleParcelable(" + mName + ", " + mNumber + ")";
+ }
+
+ public static final Parcelable.Creator<SimpleParcelable> CREATOR =
+ new Parcelable.Creator<SimpleParcelable>() {
+ public SimpleParcelable createFromParcel(Parcel source) {
+ String name = source.readString();
+ int number = source.readInt();
+ return new SimpleParcelable(name, number);
+ }
+
+ public SimpleParcelable[] newArray(int size) {
+ return new SimpleParcelable[size];
+ }
+ };
+}
diff --git a/aidl/tests/java_app/src/android/aidl/tests/TestFailException.java b/aidl/tests/java_app/src/android/aidl/tests/TestFailException.java
new file mode 100644
index 0000000..de000fa
--- /dev/null
+++ b/aidl/tests/java_app/src/android/aidl/tests/TestFailException.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016, 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;
+
+public class TestFailException extends Exception {
+ public TestFailException(String message) {
+ super(message);
+ }
+}
+
diff --git a/aidl/tests/java_app/src/android/aidl/tests/TestLogger.java b/aidl/tests/java_app/src/android/aidl/tests/TestLogger.java
new file mode 100644
index 0000000..b2e400b
--- /dev/null
+++ b/aidl/tests/java_app/src/android/aidl/tests/TestLogger.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016, 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.util.Log;
+import java.io.PrintWriter;
+import java.io.IOException;
+import android.content.Context;
+
+public class TestLogger {
+ private static final String TAG = "TestServiceClient";
+ private PrintWriter mLogFile;
+
+ public TestLogger(Context context) {
+ try {
+ mLogFile = new PrintWriter(context.openFileOutput(
+ "test-client.log", Context.MODE_PRIVATE));
+ } catch (IOException ex) {
+ throw new RuntimeException("Failed to open log file for writing.");
+ }
+ }
+
+ public void log(String line) {
+ Log.i(TAG, line);
+ mLogFile.println(line);
+ }
+
+ public void logAndThrow(String line) throws TestFailException {
+ Log.e(TAG, line);
+ mLogFile.println(line);
+ throw new TestFailException(line);
+ }
+
+ public void close() {
+ if (mLogFile != null) {
+ mLogFile.close();
+ }
+ }
+}
diff --git a/aidl/tests/java_app/src/android/aidl/tests/TestServiceClient.java b/aidl/tests/java_app/src/android/aidl/tests/TestServiceClient.java
new file mode 100644
index 0000000..4af4aa0
--- /dev/null
+++ b/aidl/tests/java_app/src/android/aidl/tests/TestServiceClient.java
@@ -0,0 +1,685 @@
+/*
+ * 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.
+ */
+
+package android.aidl.tests;
+
+import android.aidl.tests.SimpleParcelable;
+import android.aidl.tests.TestFailException;
+import android.aidl.tests.TestLogger;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.ServiceSpecificException;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+// Generated
+import android.aidl.tests.INamedCallback;
+import android.aidl.tests.ITestService;
+
+public class TestServiceClient extends Activity {
+ private static final String TAG = "TestServiceClient";
+
+ private TestLogger mLog;
+ private String mSuccessSentinel;
+ private String mFailureSentinel;
+
+ private void init() {
+ Intent intent = getIntent();
+ mLog = new TestLogger(this);
+ mLog.log("Reading sentinels from intent...");
+ mSuccessSentinel = intent.getStringExtra("sentinel.success");
+ mFailureSentinel = intent.getStringExtra("sentinel.failure");
+ if (mSuccessSentinel == null || mFailureSentinel == null) {
+ String message = "Failed to read intent extra input.";
+ Log.e(TAG, message);
+ mLog.close();
+ throw new RuntimeException(message);
+ }
+ }
+
+ private ITestService getService() throws TestFailException {
+ IBinder service = new ServiceManager().getService(
+ ITestService.class.getName());
+ if (service == null) {
+ mLog.logAndThrow("Failed to obtain binder...");
+ }
+ ITestService ret = ITestService.Stub.asInterface(service);
+ if (ret == null) {
+ mLog.logAndThrow("Failed to cast IBinder instance.");
+ }
+ return ret;
+ }
+
+ private void checkPrimitiveRepeat(ITestService service)
+ throws TestFailException {
+ mLog.log("Checking that service can repeat primitives back...");
+ try {
+ {
+ boolean query = true;
+ boolean response = service.RepeatBoolean(query);
+ if (query != response) {
+ mLog.logAndThrow("Repeat with " + query +
+ " responded " + response);
+ }
+ }
+ {
+ char query = 'A';
+ char response = service.RepeatChar(query);
+ if (query != response) {
+ mLog.logAndThrow("Repeat with " + query +
+ " responded " + response);
+ }
+ }
+ {
+ byte query = -128;
+ byte response = service.RepeatByte(query);
+ if (query != response) {
+ mLog.logAndThrow("Repeat with " + query +
+ " responded " + response);
+ }
+ }
+ {
+ int query = 1 << 30;
+ int response = service.RepeatInt(query);
+ if (query != response) {
+ mLog.logAndThrow("Repeat with " + query +
+ " responded " + response);
+ }
+ }
+ {
+ int query[] = {ITestService.TEST_CONSTANT,
+ ITestService.TEST_CONSTANT2,
+ ITestService.TEST_CONSTANT3,
+ ITestService.TEST_CONSTANT4,
+ ITestService.TEST_CONSTANT5,
+ ITestService.TEST_CONSTANT6,
+ ITestService.TEST_CONSTANT7,
+ ITestService.TEST_CONSTANT8};
+ for (int i = 0; i < query.length; i++) {
+ int response = service.RepeatInt(query[i]);
+ if (query[i] != response) {
+ mLog.logAndThrow("Repeat with " + query[i] +
+ " responded " + response);
+ }
+ }
+ }
+ {
+ long query = 1L << 60;
+ long response = service.RepeatLong(query);
+ if (query != response) {
+ mLog.logAndThrow("Repeat with " + query +
+ " responded " + response);
+ }
+ }
+ {
+ float query = 1.0f/3.0f;
+ float response = service.RepeatFloat(query);
+ if (query != response) {
+ mLog.logAndThrow("Repeat with " + query +
+ " responded " + response);
+ }
+ }
+ {
+ double query = 1.0/3.0;
+ double response = service.RepeatDouble(query);
+ if (query != response) {
+ mLog.logAndThrow("Repeat with " + query +
+ " responded " + response);
+ }
+ }
+
+ List<String> queries = Arrays.asList(
+ "not empty", "", "\0",
+ ITestService.STRING_TEST_CONSTANT,
+ ITestService.STRING_TEST_CONSTANT2);
+ for (String query : queries) {
+ String response = service.RepeatString(query);
+ if (!query.equals(response)) {
+ mLog.logAndThrow("Repeat request with '" + query + "'" +
+ " of length " + query.length() +
+ " responded with '" + response + "'" +
+ " of length " + response.length());
+ }
+ }
+ } catch (RemoteException ex) {
+ mLog.log(ex.toString());
+ mLog.logAndThrow("Service failed to repeat a primitive back.");
+ }
+ mLog.log("...Basic primitive repeating works.");
+ }
+
+ private void checkArrayReversal(ITestService service)
+ throws TestFailException {
+ mLog.log("Checking that service can reverse and return arrays...");
+ try {
+ {
+ boolean[] input = {true, false, false, false};
+ boolean echoed[] = new boolean[input.length];
+ boolean[] reversed = service.ReverseBoolean(input, echoed);
+ if (!Arrays.equals(input, echoed)) {
+ mLog.logAndThrow("Failed to echo input array back.");
+ }
+ if (input.length != reversed.length) {
+ mLog.logAndThrow("Reversed array is the wrong size.");
+ }
+ for (int i = 0; i < input.length; ++i) {
+ int j = reversed.length - (1 + i);
+ if (input[i] != reversed[j]) {
+ mLog.logAndThrow(
+ "input[" + i + "] = " + input[i] +
+ " but reversed value = " + reversed[j]);
+ }
+ }
+ }
+ {
+ byte[] input = {0, 1, 2};
+ byte echoed[] = new byte[input.length];
+ byte[] reversed = service.ReverseByte(input, echoed);
+ if (!Arrays.equals(input, echoed)) {
+ mLog.logAndThrow("Failed to echo input array back.");
+ }
+ if (input.length != reversed.length) {
+ mLog.logAndThrow("Reversed array is the wrong size.");
+ }
+ for (int i = 0; i < input.length; ++i) {
+ int j = reversed.length - (1 + i);
+ if (input[i] != reversed[j]) {
+ mLog.logAndThrow(
+ "input[" + i + "] = " + input[i] +
+ " but reversed value = " + reversed[j]);
+ }
+ }
+ }
+ {
+ char[] input = {'A', 'B', 'C', 'D', 'E'};
+ char echoed[] = new char[input.length];
+ char[] reversed = service.ReverseChar(input, echoed);
+ if (!Arrays.equals(input, echoed)) {
+ mLog.logAndThrow("Failed to echo input array back.");
+ }
+ if (input.length != reversed.length) {
+ mLog.logAndThrow("Reversed array is the wrong size.");
+ }
+ for (int i = 0; i < input.length; ++i) {
+ int j = reversed.length - (1 + i);
+ if (input[i] != reversed[j]) {
+ mLog.logAndThrow(
+ "input[" + i + "] = " + input[i] +
+ " but reversed value = " + reversed[j]);
+ }
+ }
+ }
+ {
+ int[] input = {-1, 0, 1, 2, 3, 4, 5, 6};
+ int echoed[] = new int[input.length];
+ int[] reversed = service.ReverseInt(input, echoed);
+ if (!Arrays.equals(input, echoed)) {
+ mLog.logAndThrow("Failed to echo input array back.");
+ }
+ if (input.length != reversed.length) {
+ mLog.logAndThrow("Reversed array is the wrong size.");
+ }
+ for (int i = 0; i < input.length; ++i) {
+ int j = reversed.length - (1 + i);
+ if (input[i] != reversed[j]) {
+ mLog.logAndThrow(
+ "input[" + i + "] = " + input[i] +
+ " but reversed value = " + reversed[j]);
+ }
+ }
+ }
+ {
+ long[] input = {-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8};
+ long echoed[] = new long[input.length];
+ long[] reversed = service.ReverseLong(input, echoed);
+ if (!Arrays.equals(input, echoed)) {
+ mLog.logAndThrow("Failed to echo input array back.");
+ }
+ if (input.length != reversed.length) {
+ mLog.logAndThrow("Reversed array is the wrong size.");
+ }
+ for (int i = 0; i < input.length; ++i) {
+ int j = reversed.length - (1 + i);
+ if (input[i] != reversed[j]) {
+ mLog.logAndThrow(
+ "input[" + i + "] = " + input[i] +
+ " but reversed value = " + reversed[j]);
+ }
+ }
+ }
+ {
+ float[] input = {0.0f, 1.0f, -0.3f};
+ float echoed[] = new float[input.length];
+ float[] reversed = service.ReverseFloat(input, echoed);
+ if (!Arrays.equals(input, echoed)) {
+ mLog.logAndThrow("Failed to echo input array back.");
+ }
+ if (input.length != reversed.length) {
+ mLog.logAndThrow("Reversed array is the wrong size.");
+ }
+ for (int i = 0; i < input.length; ++i) {
+ int j = reversed.length - (1 + i);
+ if (input[i] != reversed[j]) {
+ mLog.logAndThrow(
+ "input[" + i + "] = " + input[i] +
+ " but reversed value = " + reversed[j]);
+ }
+ }
+ }
+ {
+ double[] input = {-1.0, -4.0, -2.0};
+ double echoed[] = new double[input.length];
+ double[] reversed = service.ReverseDouble(input, echoed);
+ if (!Arrays.equals(input, echoed)) {
+ mLog.logAndThrow("Failed to echo input array back.");
+ }
+ if (input.length != reversed.length) {
+ mLog.logAndThrow("Reversed array is the wrong size.");
+ }
+ for (int i = 0; i < input.length; ++i) {
+ int j = reversed.length - (1 + i);
+ if (input[i] != reversed[j]) {
+ mLog.logAndThrow(
+ "input[" + i + "] = " + input[i] +
+ " but reversed value = " + reversed[j]);
+ }
+ }
+ }
+ {
+ String[] input = {"For", "relaxing", "times"};
+ String echoed[] = new String[input.length];
+ String[] reversed = service.ReverseString(input, echoed);
+ if (!Arrays.equals(input, echoed)) {
+ mLog.logAndThrow("Failed to echo input array back.");
+ }
+ if (input.length != reversed.length) {
+ mLog.logAndThrow("Reversed array is the wrong size.");
+ }
+ for (int i = 0; i < input.length; ++i) {
+ int j = reversed.length - (1 + i);
+ if (!input[i].equals(reversed[j])) {
+ mLog.logAndThrow(
+ "input[" + i + "] = " + input[i] +
+ " but reversed value = " + reversed[j]);
+ }
+ }
+ }
+ } catch (RemoteException ex) {
+ mLog.log(ex.toString());
+ mLog.logAndThrow("Service failed to reverse an array.");
+ }
+ mLog.log("...service can reverse and return arrays.");
+ }
+
+ private void checkBinderExchange(
+ ITestService service) throws TestFailException {
+ mLog.log("Checking exchange of binders...");
+ try {
+ INamedCallback got = service.GetOtherTestService("Smythe");
+ mLog.log("Received test service");
+ String name = got.GetName();
+
+ if (!name.equals("Smythe")) {
+ mLog.logAndThrow("Tried to get service with name 'Smythe'" +
+ " and found service with name '" + name + "'");
+ }
+
+ if (!service.VerifyName(got, "Smythe")) {
+ mLog.logAndThrow("Test service could not verify name of 'Smythe'");
+ }
+ } catch (RemoteException ex) {
+ mLog.log(ex.toString());
+ mLog.logAndThrow("Service failed to exchange binders.");
+ }
+ mLog.log("...Exchange of binders works");
+ }
+
+ private void checkListReversal(ITestService service)
+ throws TestFailException {
+ mLog.log("Checking that service can reverse and return lists...");
+ try {
+ {
+ List<String> input = Arrays.asList("Walk", "into", "Córdoba");
+ List<String> echoed = new ArrayList<String>();
+ List<String> reversed = service.ReverseStringList(input, echoed);
+ if (!input.equals(echoed)) {
+ mLog.logAndThrow("Failed to echo input List<String> back.");
+ }
+ Collections.reverse(input);
+ if (!input.equals(reversed)) {
+ mLog.logAndThrow("Reversed list is not correct.");
+ }
+ }
+ } catch (RemoteException ex) {
+ mLog.log(ex.toString());
+ mLog.logAndThrow("Service failed to reverse an List<String>.");
+ }
+ mLog.log("...service can reverse and return lists.");
+ }
+
+ private void checkSimpleParcelables(ITestService service)
+ throws TestFailException {
+ mLog.log("Checking that service can repeat and reverse SimpleParcelable objects...");
+ try {
+ {
+ SimpleParcelable input = new SimpleParcelable("foo", 42);
+ SimpleParcelable out_param = new SimpleParcelable();
+ SimpleParcelable returned =
+ service.RepeatSimpleParcelable(input, out_param);
+ if (!input.equals(out_param)) {
+ mLog.log(input.toString() + " != " + out_param.toString());
+ mLog.logAndThrow("out param SimpleParcelable was not equivalent");
+ }
+ if (!input.equals(returned)) {
+ mLog.log(input.toString() + " != " + returned.toString());
+ mLog.logAndThrow("returned SimpleParcelable was not equivalent");
+ }
+ }
+ {
+ SimpleParcelable[] input = new SimpleParcelable[3];
+ input[0] = new SimpleParcelable("a", 1);
+ input[1] = new SimpleParcelable("b", 2);
+ input[2] = new SimpleParcelable("c", 3);
+ SimpleParcelable[] repeated = new SimpleParcelable[3];
+ SimpleParcelable[] reversed = service.ReverseSimpleParcelables(
+ input, repeated);
+ if (!Arrays.equals(input, repeated)) {
+ mLog.logAndThrow(
+ "Repeated list of SimpleParcelable objects did not match.");
+ }
+ if (input.length != reversed.length) {
+ mLog.logAndThrow(
+ "Reversed list of SimpleParcelable objects had wrong length.");
+ }
+ for (int i = 0, k = input.length - 1;
+ i < input.length;
+ ++i, --k) {
+ if (!input[i].equals(reversed[k])) {
+ mLog.log(input[i].toString() + " != " +
+ reversed[k].toString());
+ mLog.logAndThrow("reversed SimpleParcelable was not equivalent");
+ }
+ }
+ }
+ } catch (Exception ex) {
+ mLog.log(ex.toString());
+ mLog.logAndThrow("Service failed to handle SimpleParcelable objects.");
+ }
+ mLog.log("...service can manipulate SimpleParcelable objects.");
+ }
+
+ private void checkPersistableBundles(ITestService service)
+ throws TestFailException {
+ mLog.log("Checking that service can repeat and reverse PersistableBundle objects...");
+ try {
+ {
+ PersistableBundle emptyBundle = new PersistableBundle();
+ PersistableBundle returned = service.RepeatPersistableBundle(emptyBundle);
+ if (emptyBundle.size() != 0 || returned.size() != 0) {
+ mLog.log(emptyBundle.toString() + " != " + returned.toString());
+ mLog.logAndThrow("returned empty PersistableBundle object was not equivalent");
+ }
+ mLog.log("...service can repeat and reverse empty PersistableBundle objects...");
+ }
+ {
+ final String testBoolKey = new String("testBool");
+ final String testIntKey = new String("testInt");
+ final String testNestedIntKey = new String("testNestedInt");
+ final String testLongKey = new String("testLong");
+ final String testDoubleKey = new String("testDouble");
+ final String testStringKey = new String("testString");
+ final String testBoolArrayKey = new String("testBoolArray");
+ final String testIntArrayKey = new String("testIntArray");
+ final String testLongArrayKey = new String("testLongArray");
+ final String testDoubleArrayKey = new String("testDoubleArray");
+ final String testStringArrayKey = new String("testStringArray");
+ final String testPersistableBundleKey = new String("testPersistableBundle");
+ PersistableBundle nonEmptyBundle = new PersistableBundle();
+ nonEmptyBundle.putBoolean(testBoolKey, false);
+ nonEmptyBundle.putInt(testIntKey, 33);
+ nonEmptyBundle.putLong(testLongKey, 34359738368L);
+ nonEmptyBundle.putDouble(testDoubleKey, 1.1);
+ nonEmptyBundle.putString(testStringKey, new String("Woot!"));
+ nonEmptyBundle.putBooleanArray(testBoolArrayKey, new boolean[] {true, false, true});
+ nonEmptyBundle.putIntArray(testIntArrayKey, new int[] {33, 44, 55, 142});
+ nonEmptyBundle.putLongArray(
+ testLongArrayKey, new long[] {34L, 8371L, 34359738375L});
+ nonEmptyBundle.putDoubleArray(testDoubleArrayKey, new double[] {2.2, 5.4});
+ nonEmptyBundle.putStringArray(testStringArrayKey, new String[] {"hello", "world!"});
+ PersistableBundle testNestedPersistableBundle = new PersistableBundle();
+ testNestedPersistableBundle.putInt(testNestedIntKey, 345);
+ nonEmptyBundle.putPersistableBundle(
+ testPersistableBundleKey, testNestedPersistableBundle);
+ PersistableBundle returned = service.RepeatPersistableBundle(nonEmptyBundle);
+ if (returned.size() != nonEmptyBundle.size()
+ || returned.getBoolean(testBoolKey) != nonEmptyBundle.getBoolean(testBoolKey)
+ || returned.getInt(testIntKey) != nonEmptyBundle.getInt(testIntKey)
+ || returned.getLong(testLongKey) != nonEmptyBundle.getLong(testLongKey)
+ || returned.getDouble(testDoubleKey) != nonEmptyBundle.getDouble(testDoubleKey)
+ || !returned.getString(testStringKey)
+ .equals(nonEmptyBundle.getString(testStringKey))
+ || !Arrays.equals(nonEmptyBundle.getBooleanArray(testBoolArrayKey),
+ returned.getBooleanArray(testBoolArrayKey))
+ || !Arrays.equals(nonEmptyBundle.getIntArray(testIntArrayKey),
+ returned.getIntArray(testIntArrayKey))
+ || !Arrays.equals(nonEmptyBundle.getLongArray(testLongArrayKey),
+ returned.getLongArray(testLongArrayKey))
+ || !Arrays.equals(nonEmptyBundle.getDoubleArray(testDoubleArrayKey),
+ returned.getDoubleArray(testDoubleArrayKey))
+ || !Arrays.equals(nonEmptyBundle.getStringArray(testStringArrayKey),
+ returned.getStringArray(testStringArrayKey))) {
+ PersistableBundle temp =
+ returned.getPersistableBundle(testPersistableBundleKey);
+ if (temp == null
+ || temp.getInt(testNestedIntKey)
+ != testNestedPersistableBundle.getInt(testNestedIntKey)) {
+ mLog.log(nonEmptyBundle.toString() + " != " + returned.toString());
+ mLog.logAndThrow("returned non-empty PersistableBundle " +
+ "object was not equivalent");
+ }
+ }
+ mLog.log("...service can repeat and reverse non-empty " +
+ "PersistableBundle objects...");
+ }
+ {
+ PersistableBundle[] input = new PersistableBundle[3];
+ PersistableBundle first = new PersistableBundle();
+ PersistableBundle second = new PersistableBundle();
+ PersistableBundle third = new PersistableBundle();
+ final String testIntKey = new String("testInt");
+ final String testLongKey = new String("testLong");
+ final String testDoubleKey = new String("testDouble");
+ first.putInt(testIntKey, 1231);
+ second.putLong(testLongKey, 222222L);
+ third.putDouble(testDoubleKey, 10.8);
+ input[0] = first;
+ input[1] = second;
+ input[2] = third;
+ final int original_input_size = input.length;
+ PersistableBundle[] repeated = new PersistableBundle[input.length];
+ PersistableBundle[] reversed = service.ReversePersistableBundles(input, repeated);
+ if (input.length != repeated.length || input.length != original_input_size) {
+ mLog.logAndThrow("Repeated list of PersistableBundle objects had " +
+ "wrong length.");
+ }
+ if (input[0].getInt(testIntKey) != repeated[0].getInt(testIntKey)
+ || input[1].getLong(testLongKey) != repeated[1].getLong(testLongKey)
+ || input[2].getDouble(testDoubleKey) != repeated[2].getDouble(testDoubleKey)) {
+ mLog.logAndThrow("Repeated list of PersistableBundle objects did not match.");
+ }
+ if (input.length != reversed.length || input.length != original_input_size) {
+ mLog.logAndThrow("Reversed list of PersistableBundle objects had " +
+ "wrong length.");
+ }
+ if (input[0].getInt(testIntKey) != reversed[2].getInt(testIntKey)
+ || input[1].getLong(testLongKey) != reversed[1].getLong(testLongKey)
+ || input[2].getDouble(testDoubleKey) != reversed[0].getDouble(testDoubleKey)) {
+ mLog.logAndThrow("reversed PersistableBundle objects were not equivalent");
+ }
+ mLog.log("...service can repeat and reverse arrays of " +
+ "non-empty PersistableBundle objects...");
+ }
+ } catch (Exception ex) {
+ mLog.log(ex.toString());
+ mLog.logAndThrow("Service failed to handle PersistableBundle objects.");
+ }
+ mLog.log("...service can manipulate PersistableBundle objects.");
+ }
+
+ private void checkFileDescriptorPassing(ITestService service)
+ throws TestFailException {
+ mLog.log("Checking that service can receive and return file descriptors...");
+ try {
+ FileOutputStream fileOutputStream =
+ openFileOutput("test-dummy", Context.MODE_PRIVATE);
+
+ FileDescriptor descriptor = fileOutputStream.getFD();
+ FileDescriptor journeyed = service.RepeatFileDescriptor(descriptor);
+ fileOutputStream.close();
+
+ FileOutputStream journeyedStream = new FileOutputStream(journeyed);
+
+ String testData = "FrazzleSnazzleFlimFlamFlibbityGumboChops";
+ byte[] output = testData.getBytes();
+ journeyedStream.write(output);
+ journeyedStream.close();
+
+ FileInputStream fileInputStream = openFileInput("test-dummy");
+ byte[] input = new byte[output.length];
+ if (fileInputStream.read(input) != input.length) {
+ mLog.logAndThrow("Read short count from file");
+ }
+
+ if (!Arrays.equals(input, output)) {
+ mLog.logAndThrow("Read incorrect data");
+ }
+ } catch (RemoteException ex) {
+ mLog.log(ex.toString());
+ mLog.logAndThrow("Service failed to repeat a file descriptor.");
+ } catch (IOException ex) {
+ mLog.log(ex.toString());
+ mLog.logAndThrow("Exception while operating on temporary file");
+ }
+ mLog.log("...service can receive and return file descriptors.");
+ }
+
+ private void checkServiceSpecificExceptions(
+ ITestService service) throws TestFailException {
+ mLog.log("Checking application exceptions...");
+ for (int i = -1; i < 2; ++i) {
+ try {
+ service.ThrowServiceException(i);
+ } catch (RemoteException ex) {
+ mLog.logAndThrow("Service threw RemoteException: " +
+ ex.toString());
+ } catch (ServiceSpecificException ex) {
+ if (ex.errorCode != i) {
+ mLog.logAndThrow("Service threw wrong error code: " + i);
+ }
+ }
+ }
+ mLog.log("...application exceptions work");
+ }
+
+ private void checkUtf8Strings(ITestService service)
+ throws TestFailException {
+ mLog.log("Checking that service can work with UTF8 strings...");
+ // Note that Java's underlying encoding is UTF16.
+ final List<String> utf8_queries = Arrays.asList(
+ "typical string",
+ "",
+ "\0\0\0",
+ // Java doesn't handle unicode code points above U+FFFF well.
+ new String(Character.toChars(0x1F701)) + "\u03A9");
+ try {
+ for (String query : utf8_queries) {
+ String response = service.RepeatUtf8CppString(query);
+ if (!query.equals(response)) {
+ mLog.logAndThrow("Repeat request with '" + query + "'" +
+ " of length " + query.length() +
+ " responded with '" + response + "'" +
+ " of length " + response.length());
+ }
+ }
+ {
+ String[] input = (String[])utf8_queries.toArray();
+ String echoed[] = new String[input.length];
+ String[] reversed = service.ReverseUtf8CppString(input, echoed);
+ if (!Arrays.equals(input, echoed)) {
+ mLog.logAndThrow("Failed to echo utf8 input array back.");
+ }
+ if (input.length != reversed.length) {
+ mLog.logAndThrow("Reversed utf8 array is the wrong size.");
+ }
+ for (int i = 0; i < input.length; ++i) {
+ int j = reversed.length - (1 + i);
+ if (!input[i].equals(reversed[j])) {
+ mLog.logAndThrow(
+ "input[" + i + "] = " + input[i] +
+ " but reversed value = " + reversed[j]);
+ }
+ }
+ }
+ } catch (RemoteException ex) {
+ mLog.log(ex.toString());
+ mLog.logAndThrow("Service failed to handle utf8 strings.");
+ }
+ mLog.log("...UTF8 annotations work.");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "Starting!");
+ try {
+ init();
+ ITestService service = getService();
+ checkPrimitiveRepeat(service);
+ checkArrayReversal(service);
+ checkBinderExchange(service);
+ checkListReversal(service);
+ checkSimpleParcelables(service);
+ checkPersistableBundles(service);
+ checkFileDescriptorPassing(service);
+ checkServiceSpecificExceptions(service);
+ checkUtf8Strings(service);
+ new NullableTests(service, mLog).runTests();
+
+ mLog.log(mSuccessSentinel);
+ } catch (TestFailException e) {
+ mLog.log(mFailureSentinel);
+ throw new RuntimeException(e);
+ } finally {
+ if (mLog != null) {
+ mLog.close();
+ }
+ }
+ }
+}
diff --git a/aidl/tests/main.cpp b/aidl/tests/main.cpp
new file mode 100644
index 0000000..4d820af
--- /dev/null
+++ b/aidl/tests/main.cpp
@@ -0,0 +1,6 @@
+#include <gtest/gtest.h>
+
+int main(int argc, char **argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/aidl/tests/simple_parcelable.cpp b/aidl/tests/simple_parcelable.cpp
new file mode 100644
index 0000000..e092d25
--- /dev/null
+++ b/aidl/tests/simple_parcelable.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "tests/simple_parcelable.h"
+
+#include <android-base/stringprintf.h>
+#include <binder/Parcel.h>
+#include <utils/String8.h>
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace aidl {
+namespace tests {
+
+SimpleParcelable::SimpleParcelable(const std::string& name, int32_t number)
+ : name_(name.c_str(), name.length()),
+ number_(number) {}
+
+status_t SimpleParcelable::writeToParcel(Parcel* parcel) const {
+ status_t status = parcel->writeString16(name_);
+ if (status != OK) {
+ return status;
+ }
+ status = parcel->writeInt32(number_);
+ return status;
+}
+
+status_t SimpleParcelable::readFromParcel(const Parcel* parcel) {
+ status_t status = parcel->readString16(&name_);
+ if (status != OK) {
+ return status;
+ }
+ return parcel->readInt32(&number_);
+}
+
+std::string SimpleParcelable::toString() const {
+ return StringPrintf("%s(%d)", String8(name_).string(), number_);
+}
+
+} // namespace tests
+} // namespace aidl
+} // namespace android
diff --git a/aidl/tests/simple_parcelable.h b/aidl/tests/simple_parcelable.h
new file mode 100644
index 0000000..52a20de
--- /dev/null
+++ b/aidl/tests/simple_parcelable.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_TESTS_SIMPLE_PARCELABLE_H_
+#define AIDL_TESTS_SIMPLE_PARCELABLE_H_
+
+#include <cstdint>
+#include <string>
+
+#include <binder/Parcelable.h>
+#include <utils/Errors.h>
+#include <utils/String16.h>
+
+namespace android {
+namespace aidl {
+namespace tests {
+
+class SimpleParcelable : public Parcelable {
+ public:
+ SimpleParcelable() = default;
+ SimpleParcelable(const std::string& name, int32_t number);
+ virtual ~SimpleParcelable() = default;
+
+ // Write |this| parcelable to the given |parcel|. Keep in mind that
+ // implementations of writeToParcel must be manually kept in sync
+ // with readFromParcel and the Java equivalent versions of these methods.
+ //
+ // Returns android::OK on success and an appropriate error otherwise.
+ status_t writeToParcel(Parcel* parcel) const override;
+
+ // Read data from the given |parcel| into |this|. After readFromParcel
+ // completes, |this| should have equivalent state to the object that
+ // wrote itself to the parcel.
+ //
+ // Returns android::OK on success and an appropriate error otherwise.
+ status_t readFromParcel(const Parcel* parcel) override;
+
+ std::string toString() const;
+
+ friend bool operator==(const SimpleParcelable& lhs,
+ const SimpleParcelable& rhs) {
+ return (lhs.name_ == rhs.name_) && (lhs.number_ == rhs.number_);
+ }
+ friend bool operator!=(const SimpleParcelable& lhs,
+ const SimpleParcelable& rhs) {
+ return !(lhs == rhs);
+ }
+
+ private:
+ String16 name_;
+ int32_t number_ = 0;
+}; // class SimpleParcelable
+
+} // namespace tests
+} // namespace aidl
+} // namespace android
+
+#endif // AIDL_TESTS_SIMPLE_PARCELABLE_H_
diff --git a/aidl/tests/test_data.h b/aidl/tests/test_data.h
new file mode 100644
index 0000000..d3a5f6c
--- /dev/null
+++ b/aidl/tests/test_data.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_TESTS_TEST_DATA_H_
+#define AIDL_TESTS_TEST_DATA_H_
+
+namespace android {
+namespace aidl {
+namespace test_data {
+
+namespace example_interface {
+
+extern const char kCanonicalName[];
+extern const char kJavaOutputPath[];
+extern const char kInterfaceDefinition[];
+extern const char* kImportedParcelables[];
+extern const char* kImportedInterfaces[];
+
+extern const char kExpectedJavaDepsOutput[];
+extern const char kExpectedJavaOutput[];
+
+} // namespace example_interface
+
+namespace ping_responder {
+
+extern const char kCanonicalName[];
+extern const char kInterfaceDefinition[];
+
+extern const char kCppOutputPath[];
+extern const char kCppParcelableHeader[];
+extern const char* kImportedParcelables[];
+extern const char* kImportedInterfaces[];
+
+extern const char kGenHeaderDir[];
+extern const char kGenInterfaceHeaderPath[];
+extern const char kGenClientHeaderPath[];
+extern const char kGenServerHeaderPath[];
+
+extern const char kExpectedCppDepsOutput[];
+
+extern const char kExpectedCppOutput[];
+extern const char kExpectedIHeaderOutput[];
+extern const char kExpectedBpHeaderOutput[];
+extern const char kExpectedBnHeaderOutput[];
+
+} // namespace ping_responder
+
+namespace string_constants {
+
+extern const char kCanonicalName[];
+extern const char kInterfaceDefinition[];
+
+extern const char kJavaOutputPath[];
+extern const char kExpectedJavaOutput[];
+
+extern const char kCppOutputPath[];
+extern const char kGenHeaderDir[];
+extern const char kGenInterfaceHeaderPath[];
+extern const char kExpectedIHeaderOutput[];
+extern const char kExpectedCppOutput[];
+
+} // namespace string_constants
+
+} // namespace test_data
+} // namespace aidl
+} // namespace android
+
+#endif // AIDL_TESTS_TEST_DATA_H_
diff --git a/aidl/tests/test_data_example_interface.cpp b/aidl/tests/test_data_example_interface.cpp
new file mode 100644
index 0000000..81b919f
--- /dev/null
+++ b/aidl/tests/test_data_example_interface.cpp
@@ -0,0 +1,456 @@
+/*
+ * 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 "tests/test_data.h"
+
+namespace android {
+namespace aidl {
+namespace test_data {
+namespace example_interface {
+
+const char kCanonicalName[] = "android.test.IExampleInterface";
+
+const char kJavaOutputPath[] = "some/path/to/output.java";
+
+const char* kImportedParcelables[] = {
+ "android.foo.ExampleParcelable",
+ nullptr,
+};
+
+const char* kImportedInterfaces[] = {
+ "android.bar.IAuxInterface",
+ "android.test.IAuxInterface2",
+ nullptr,
+};
+
+const char kInterfaceDefinition[] = R"(
+package android.test;
+
+import android.foo.ExampleParcelable;
+import android.test.CompoundParcelable;
+import android.bar.IAuxInterface;
+import android.test.IAuxInterface2;
+
+interface IExampleInterface {
+ const int EXAMPLE_CONSTANT = 3;
+ boolean isEnabled();
+ int getState();
+ String getAddress();
+
+ /* Test long comment */
+ ExampleParcelable[] getParcelables();
+
+ // Test short comment
+ boolean setScanMode(int mode, int duration);
+
+ /* Test long comment */
+ // And short comment
+ void registerBinder(IAuxInterface foo);
+ IExampleInterface getRecursiveBinder();
+
+ int takesAnInterface(in IAuxInterface2 arg);
+ int takesAParcelable(in CompoundParcelable.Subclass1 arg,
+ inout CompoundParcelable.Subclass2 arg2);
+}
+)";
+
+const char kExpectedJavaDepsOutput[] =
+R"(some/path/to/output.java : \
+ android/test/IExampleInterface.aidl \
+ ./android/foo/ExampleParcelable.aidl \
+ ./android/test/CompoundParcelable.aidl \
+ ./android/bar/IAuxInterface.aidl \
+ ./android/test/IAuxInterface2.aidl
+
+android/test/IExampleInterface.aidl :
+./android/foo/ExampleParcelable.aidl :
+./android/test/CompoundParcelable.aidl :
+./android/bar/IAuxInterface.aidl :
+./android/test/IAuxInterface2.aidl :
+)";
+
+const char kExpectedJavaOutput[] =
+R"(/*
+ * This file is auto-generated. DO NOT MODIFY.
+ * Original file: android/test/IExampleInterface.aidl
+ */
+package android.test;
+public interface IExampleInterface extends android.os.IInterface
+{
+/** Local-side IPC implementation stub class. */
+public static abstract class Stub extends android.os.Binder implements android.test.IExampleInterface
+{
+private static final java.lang.String DESCRIPTOR = "android.test.IExampleInterface";
+/** Construct the stub at attach it to the interface. */
+public Stub()
+{
+this.attachInterface(this, DESCRIPTOR);
+}
+/**
+ * Cast an IBinder object into an android.test.IExampleInterface interface,
+ * generating a proxy if needed.
+ */
+public static android.test.IExampleInterface asInterface(android.os.IBinder obj)
+{
+if ((obj==null)) {
+return null;
+}
+android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+if (((iin!=null)&&(iin instanceof android.test.IExampleInterface))) {
+return ((android.test.IExampleInterface)iin);
+}
+return new android.test.IExampleInterface.Stub.Proxy(obj);
+}
+@Override public android.os.IBinder asBinder()
+{
+return this;
+}
+@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
+{
+switch (code)
+{
+case INTERFACE_TRANSACTION:
+{
+reply.writeString(DESCRIPTOR);
+return true;
+}
+case TRANSACTION_isEnabled:
+{
+data.enforceInterface(DESCRIPTOR);
+boolean _result = this.isEnabled();
+reply.writeNoException();
+reply.writeInt(((_result)?(1):(0)));
+return true;
+}
+case TRANSACTION_getState:
+{
+data.enforceInterface(DESCRIPTOR);
+int _result = this.getState();
+reply.writeNoException();
+reply.writeInt(_result);
+return true;
+}
+case TRANSACTION_getAddress:
+{
+data.enforceInterface(DESCRIPTOR);
+java.lang.String _result = this.getAddress();
+reply.writeNoException();
+reply.writeString(_result);
+return true;
+}
+case TRANSACTION_getParcelables:
+{
+data.enforceInterface(DESCRIPTOR);
+android.foo.ExampleParcelable[] _result = this.getParcelables();
+reply.writeNoException();
+reply.writeTypedArray(_result, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+return true;
+}
+case TRANSACTION_setScanMode:
+{
+data.enforceInterface(DESCRIPTOR);
+int _arg0;
+_arg0 = data.readInt();
+int _arg1;
+_arg1 = data.readInt();
+boolean _result = this.setScanMode(_arg0, _arg1);
+reply.writeNoException();
+reply.writeInt(((_result)?(1):(0)));
+return true;
+}
+case TRANSACTION_registerBinder:
+{
+data.enforceInterface(DESCRIPTOR);
+android.bar.IAuxInterface _arg0;
+_arg0 = android.bar.IAuxInterface.Stub.asInterface(data.readStrongBinder());
+this.registerBinder(_arg0);
+reply.writeNoException();
+return true;
+}
+case TRANSACTION_getRecursiveBinder:
+{
+data.enforceInterface(DESCRIPTOR);
+android.test.IExampleInterface _result = this.getRecursiveBinder();
+reply.writeNoException();
+reply.writeStrongBinder((((_result!=null))?(_result.asBinder()):(null)));
+return true;
+}
+case TRANSACTION_takesAnInterface:
+{
+data.enforceInterface(DESCRIPTOR);
+android.test.IAuxInterface2 _arg0;
+_arg0 = android.test.IAuxInterface2.Stub.asInterface(data.readStrongBinder());
+int _result = this.takesAnInterface(_arg0);
+reply.writeNoException();
+reply.writeInt(_result);
+return true;
+}
+case TRANSACTION_takesAParcelable:
+{
+data.enforceInterface(DESCRIPTOR);
+android.test.CompoundParcelable.Subclass1 _arg0;
+if ((0!=data.readInt())) {
+_arg0 = android.test.CompoundParcelable.Subclass1.CREATOR.createFromParcel(data);
+}
+else {
+_arg0 = null;
+}
+android.test.CompoundParcelable.Subclass2 _arg1;
+if ((0!=data.readInt())) {
+_arg1 = android.test.CompoundParcelable.Subclass2.CREATOR.createFromParcel(data);
+}
+else {
+_arg1 = null;
+}
+int _result = this.takesAParcelable(_arg0, _arg1);
+reply.writeNoException();
+reply.writeInt(_result);
+if ((_arg1!=null)) {
+reply.writeInt(1);
+_arg1.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+}
+else {
+reply.writeInt(0);
+}
+return true;
+}
+}
+return super.onTransact(code, data, reply, flags);
+}
+private static class Proxy implements android.test.IExampleInterface
+{
+private android.os.IBinder mRemote;
+Proxy(android.os.IBinder remote)
+{
+mRemote = remote;
+}
+@Override public android.os.IBinder asBinder()
+{
+return mRemote;
+}
+public java.lang.String getInterfaceDescriptor()
+{
+return DESCRIPTOR;
+}
+@Override public boolean isEnabled() throws android.os.RemoteException
+{
+android.os.Parcel _data = android.os.Parcel.obtain();
+android.os.Parcel _reply = android.os.Parcel.obtain();
+boolean _result;
+try {
+_data.writeInterfaceToken(DESCRIPTOR);
+mRemote.transact(Stub.TRANSACTION_isEnabled, _data, _reply, 0);
+_reply.readException();
+_result = (0!=_reply.readInt());
+}
+finally {
+_reply.recycle();
+_data.recycle();
+}
+return _result;
+}
+@Override public int getState() throws android.os.RemoteException
+{
+android.os.Parcel _data = android.os.Parcel.obtain();
+android.os.Parcel _reply = android.os.Parcel.obtain();
+int _result;
+try {
+_data.writeInterfaceToken(DESCRIPTOR);
+mRemote.transact(Stub.TRANSACTION_getState, _data, _reply, 0);
+_reply.readException();
+_result = _reply.readInt();
+}
+finally {
+_reply.recycle();
+_data.recycle();
+}
+return _result;
+}
+@Override public java.lang.String getAddress() throws android.os.RemoteException
+{
+android.os.Parcel _data = android.os.Parcel.obtain();
+android.os.Parcel _reply = android.os.Parcel.obtain();
+java.lang.String _result;
+try {
+_data.writeInterfaceToken(DESCRIPTOR);
+mRemote.transact(Stub.TRANSACTION_getAddress, _data, _reply, 0);
+_reply.readException();
+_result = _reply.readString();
+}
+finally {
+_reply.recycle();
+_data.recycle();
+}
+return _result;
+}
+/* Test long comment */
+@Override public android.foo.ExampleParcelable[] getParcelables() throws android.os.RemoteException
+{
+android.os.Parcel _data = android.os.Parcel.obtain();
+android.os.Parcel _reply = android.os.Parcel.obtain();
+android.foo.ExampleParcelable[] _result;
+try {
+_data.writeInterfaceToken(DESCRIPTOR);
+mRemote.transact(Stub.TRANSACTION_getParcelables, _data, _reply, 0);
+_reply.readException();
+_result = _reply.createTypedArray(android.foo.ExampleParcelable.CREATOR);
+}
+finally {
+_reply.recycle();
+_data.recycle();
+}
+return _result;
+}
+// Test short comment
+
+@Override public boolean setScanMode(int mode, int duration) throws android.os.RemoteException
+{
+android.os.Parcel _data = android.os.Parcel.obtain();
+android.os.Parcel _reply = android.os.Parcel.obtain();
+boolean _result;
+try {
+_data.writeInterfaceToken(DESCRIPTOR);
+_data.writeInt(mode);
+_data.writeInt(duration);
+mRemote.transact(Stub.TRANSACTION_setScanMode, _data, _reply, 0);
+_reply.readException();
+_result = (0!=_reply.readInt());
+}
+finally {
+_reply.recycle();
+_data.recycle();
+}
+return _result;
+}
+/* Test long comment */// And short comment
+
+@Override public void registerBinder(android.bar.IAuxInterface foo) throws android.os.RemoteException
+{
+android.os.Parcel _data = android.os.Parcel.obtain();
+android.os.Parcel _reply = android.os.Parcel.obtain();
+try {
+_data.writeInterfaceToken(DESCRIPTOR);
+_data.writeStrongBinder((((foo!=null))?(foo.asBinder()):(null)));
+mRemote.transact(Stub.TRANSACTION_registerBinder, _data, _reply, 0);
+_reply.readException();
+}
+finally {
+_reply.recycle();
+_data.recycle();
+}
+}
+@Override public android.test.IExampleInterface getRecursiveBinder() throws android.os.RemoteException
+{
+android.os.Parcel _data = android.os.Parcel.obtain();
+android.os.Parcel _reply = android.os.Parcel.obtain();
+android.test.IExampleInterface _result;
+try {
+_data.writeInterfaceToken(DESCRIPTOR);
+mRemote.transact(Stub.TRANSACTION_getRecursiveBinder, _data, _reply, 0);
+_reply.readException();
+_result = android.test.IExampleInterface.Stub.asInterface(_reply.readStrongBinder());
+}
+finally {
+_reply.recycle();
+_data.recycle();
+}
+return _result;
+}
+@Override public int takesAnInterface(android.test.IAuxInterface2 arg) throws android.os.RemoteException
+{
+android.os.Parcel _data = android.os.Parcel.obtain();
+android.os.Parcel _reply = android.os.Parcel.obtain();
+int _result;
+try {
+_data.writeInterfaceToken(DESCRIPTOR);
+_data.writeStrongBinder((((arg!=null))?(arg.asBinder()):(null)));
+mRemote.transact(Stub.TRANSACTION_takesAnInterface, _data, _reply, 0);
+_reply.readException();
+_result = _reply.readInt();
+}
+finally {
+_reply.recycle();
+_data.recycle();
+}
+return _result;
+}
+@Override public int takesAParcelable(android.test.CompoundParcelable.Subclass1 arg, android.test.CompoundParcelable.Subclass2 arg2) throws android.os.RemoteException
+{
+android.os.Parcel _data = android.os.Parcel.obtain();
+android.os.Parcel _reply = android.os.Parcel.obtain();
+int _result;
+try {
+_data.writeInterfaceToken(DESCRIPTOR);
+if ((arg!=null)) {
+_data.writeInt(1);
+arg.writeToParcel(_data, 0);
+}
+else {
+_data.writeInt(0);
+}
+if ((arg2!=null)) {
+_data.writeInt(1);
+arg2.writeToParcel(_data, 0);
+}
+else {
+_data.writeInt(0);
+}
+mRemote.transact(Stub.TRANSACTION_takesAParcelable, _data, _reply, 0);
+_reply.readException();
+_result = _reply.readInt();
+if ((0!=_reply.readInt())) {
+arg2.readFromParcel(_reply);
+}
+}
+finally {
+_reply.recycle();
+_data.recycle();
+}
+return _result;
+}
+}
+static final int TRANSACTION_isEnabled = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
+static final int TRANSACTION_getState = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
+static final int TRANSACTION_getAddress = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
+static final int TRANSACTION_getParcelables = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
+static final int TRANSACTION_setScanMode = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
+static final int TRANSACTION_registerBinder = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
+static final int TRANSACTION_getRecursiveBinder = (android.os.IBinder.FIRST_CALL_TRANSACTION + 6);
+static final int TRANSACTION_takesAnInterface = (android.os.IBinder.FIRST_CALL_TRANSACTION + 7);
+static final int TRANSACTION_takesAParcelable = (android.os.IBinder.FIRST_CALL_TRANSACTION + 8);
+}
+public static final int EXAMPLE_CONSTANT = 3;
+public boolean isEnabled() throws android.os.RemoteException;
+public int getState() throws android.os.RemoteException;
+public java.lang.String getAddress() throws android.os.RemoteException;
+/* Test long comment */
+public android.foo.ExampleParcelable[] getParcelables() throws android.os.RemoteException;
+// Test short comment
+
+public boolean setScanMode(int mode, int duration) throws android.os.RemoteException;
+/* Test long comment */// And short comment
+
+public void registerBinder(android.bar.IAuxInterface foo) throws android.os.RemoteException;
+public android.test.IExampleInterface getRecursiveBinder() throws android.os.RemoteException;
+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;
+}
+)";
+
+} // namespace example_interface
+} // namespace test_data
+} // namespace aidl
+} // namespace android
diff --git a/aidl/tests/test_data_ping_responder.cpp b/aidl/tests/test_data_ping_responder.cpp
new file mode 100644
index 0000000..40cb602
--- /dev/null
+++ b/aidl/tests/test_data_ping_responder.cpp
@@ -0,0 +1,461 @@
+/*
+ * 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 "tests/test_data.h"
+
+namespace android {
+namespace aidl {
+namespace test_data {
+namespace ping_responder {
+
+const char kCanonicalName[] = "android.os.IPingResponder";
+const char kInterfaceDefinition[] = R"(
+package android.os;
+
+import bar.Unused;
+
+interface IPingResponder {
+ String Ping(String input);
+
+ @nullable
+ String NullablePing(@nullable String input);
+
+ @utf8InCpp
+ String Utf8Ping(@utf8InCpp String input);
+
+ @utf8InCpp @nullable
+ String NullableUtf8Ping(@utf8InCpp @nullable String input);
+}
+)";
+
+const char kCppOutputPath[] = "some/path/to/output.cpp";
+
+const char kCppParcelableHeader[] = "cpp-header-str";
+
+const char* kImportedParcelables[] = {
+ "bar.Unused",
+ nullptr,
+};
+
+const char* kImportedInterfaces[] = {
+ nullptr,
+};
+
+const char kGenHeaderDir[] = "some/path";
+const char kGenInterfaceHeaderPath[] = "some/path/android/os/IPingResponder.h";
+const char kGenClientHeaderPath[] = "some/path/android/os/BpPingResponder.h";
+const char kGenServerHeaderPath[] = "some/path/android/os/BnPingResponder.h";
+
+const char kExpectedCppDepsOutput[] =
+R"(some/path/to/output.cpp : \
+ android/os/IPingResponder.aidl \
+ ./bar/Unused.aidl
+
+android/os/IPingResponder.aidl :
+./bar/Unused.aidl :
+
+some/path/android/os/BpPingResponder.h \
+ some/path/android/os/BnPingResponder.h \
+ some/path/android/os/IPingResponder.h : \
+ android/os/IPingResponder.aidl \
+ ./bar/Unused.aidl
+)";
+
+const char kExpectedCppOutput[] =
+R"(#include <android/os/IPingResponder.h>
+#include <android/os/BpPingResponder.h>
+
+namespace android {
+
+namespace os {
+
+IMPLEMENT_META_INTERFACE(PingResponder, "android.os.IPingResponder")
+
+} // namespace os
+
+} // namespace android
+#include <android/os/BpPingResponder.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+namespace os {
+
+BpPingResponder::BpPingResponder(const ::android::sp<::android::IBinder>& _aidl_impl)
+ : BpInterface<IPingResponder>(_aidl_impl){
+}
+
+::android::binder::Status BpPingResponder::Ping(const ::android::String16& input, ::android::String16* _aidl_return) {
+::android::Parcel _aidl_data;
+::android::Parcel _aidl_reply;
+::android::status_t _aidl_ret_status = ::android::OK;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeString16(input);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IPingResponder::PING, _aidl_data, &_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+if (!_aidl_status.isOk()) {
+return _aidl_status;
+}
+_aidl_ret_status = _aidl_reply.readString16(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+::android::binder::Status BpPingResponder::NullablePing(const ::std::unique_ptr<::android::String16>& input, ::std::unique_ptr<::android::String16>* _aidl_return) {
+::android::Parcel _aidl_data;
+::android::Parcel _aidl_reply;
+::android::status_t _aidl_ret_status = ::android::OK;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeString16(input);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IPingResponder::NULLABLEPING, _aidl_data, &_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+if (!_aidl_status.isOk()) {
+return _aidl_status;
+}
+_aidl_ret_status = _aidl_reply.readString16(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+::android::binder::Status BpPingResponder::Utf8Ping(const ::std::string& input, ::std::string* _aidl_return) {
+::android::Parcel _aidl_data;
+::android::Parcel _aidl_reply;
+::android::status_t _aidl_ret_status = ::android::OK;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeUtf8AsUtf16(input);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IPingResponder::UTF8PING, _aidl_data, &_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+if (!_aidl_status.isOk()) {
+return _aidl_status;
+}
+_aidl_ret_status = _aidl_reply.readUtf8FromUtf16(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+::android::binder::Status BpPingResponder::NullableUtf8Ping(const ::std::unique_ptr<::std::string>& input, ::std::unique_ptr<::std::string>* _aidl_return) {
+::android::Parcel _aidl_data;
+::android::Parcel _aidl_reply;
+::android::status_t _aidl_ret_status = ::android::OK;
+::android::binder::Status _aidl_status;
+_aidl_ret_status = _aidl_data.writeInterfaceToken(getInterfaceDescriptor());
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_data.writeUtf8AsUtf16(input);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = remote()->transact(IPingResponder::NULLABLEUTF8PING, _aidl_data, &_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_ret_status = _aidl_status.readFromParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+if (!_aidl_status.isOk()) {
+return _aidl_status;
+}
+_aidl_ret_status = _aidl_reply.readUtf8FromUtf16(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+goto _aidl_error;
+}
+_aidl_error:
+_aidl_status.setFromStatusT(_aidl_ret_status);
+return _aidl_status;
+}
+
+} // namespace os
+
+} // namespace android
+#include <android/os/BnPingResponder.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+namespace os {
+
+::android::status_t BnPingResponder::onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) {
+::android::status_t _aidl_ret_status = ::android::OK;
+switch (_aidl_code) {
+case Call::PING:
+{
+::android::String16 in_input;
+::android::String16 _aidl_return;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+_aidl_ret_status = _aidl_data.readString16(&in_input);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+::android::binder::Status _aidl_status(Ping(in_input, &_aidl_return));
+_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+if (!_aidl_status.isOk()) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeString16(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+}
+break;
+case Call::NULLABLEPING:
+{
+::std::unique_ptr<::android::String16> in_input;
+::std::unique_ptr<::android::String16> _aidl_return;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+_aidl_ret_status = _aidl_data.readString16(&in_input);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+::android::binder::Status _aidl_status(NullablePing(in_input, &_aidl_return));
+_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+if (!_aidl_status.isOk()) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeString16(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+}
+break;
+case Call::UTF8PING:
+{
+::std::string in_input;
+::std::string _aidl_return;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+_aidl_ret_status = _aidl_data.readUtf8FromUtf16(&in_input);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+::android::binder::Status _aidl_status(Utf8Ping(in_input, &_aidl_return));
+_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+if (!_aidl_status.isOk()) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeUtf8AsUtf16(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+}
+break;
+case Call::NULLABLEUTF8PING:
+{
+::std::unique_ptr<::std::string> in_input;
+::std::unique_ptr<::std::string> _aidl_return;
+if (!(_aidl_data.checkInterface(this))) {
+_aidl_ret_status = ::android::BAD_TYPE;
+break;
+}
+_aidl_ret_status = _aidl_data.readUtf8FromUtf16(&in_input);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+::android::binder::Status _aidl_status(NullableUtf8Ping(in_input, &_aidl_return));
+_aidl_ret_status = _aidl_status.writeToParcel(_aidl_reply);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+if (!_aidl_status.isOk()) {
+break;
+}
+_aidl_ret_status = _aidl_reply->writeUtf8AsUtf16(_aidl_return);
+if (((_aidl_ret_status) != (::android::OK))) {
+break;
+}
+}
+break;
+default:
+{
+_aidl_ret_status = ::android::BBinder::onTransact(_aidl_code, _aidl_data, _aidl_reply, _aidl_flags);
+}
+break;
+}
+if (_aidl_ret_status == ::android::UNEXPECTED_NULL) {
+_aidl_ret_status = ::android::binder::Status::fromExceptionCode(::android::binder::Status::EX_NULL_POINTER).writeToParcel(_aidl_reply);
+}
+return _aidl_ret_status;
+}
+
+} // namespace os
+
+} // namespace android
+)";
+
+const char kExpectedIHeaderOutput[] =
+R"(#ifndef AIDL_GENERATED_ANDROID_OS_I_PING_RESPONDER_H_
+#define AIDL_GENERATED_ANDROID_OS_I_PING_RESPONDER_H_
+
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+#include <binder/Status.h>
+#include <memory>
+#include <string>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace os {
+
+class IPingResponder : public ::android::IInterface {
+public:
+DECLARE_META_INTERFACE(PingResponder)
+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;
+enum Call {
+ PING = ::android::IBinder::FIRST_CALL_TRANSACTION + 0,
+ NULLABLEPING = ::android::IBinder::FIRST_CALL_TRANSACTION + 1,
+ UTF8PING = ::android::IBinder::FIRST_CALL_TRANSACTION + 2,
+ NULLABLEUTF8PING = ::android::IBinder::FIRST_CALL_TRANSACTION + 3,
+};
+}; // class IPingResponder
+
+} // namespace os
+
+} // namespace android
+
+#endif // AIDL_GENERATED_ANDROID_OS_I_PING_RESPONDER_H_
+)";
+
+const char kExpectedBpHeaderOutput[] =
+R"(#ifndef AIDL_GENERATED_ANDROID_OS_BP_PING_RESPONDER_H_
+#define AIDL_GENERATED_ANDROID_OS_BP_PING_RESPONDER_H_
+
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+#include <utils/Errors.h>
+#include <android/os/IPingResponder.h>
+
+namespace android {
+
+namespace os {
+
+class BpPingResponder : public ::android::BpInterface<IPingResponder> {
+public:
+explicit BpPingResponder(const ::android::sp<::android::IBinder>& _aidl_impl);
+virtual ~BpPingResponder() = default;
+::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;
+}; // class BpPingResponder
+
+} // namespace os
+
+} // namespace android
+
+#endif // AIDL_GENERATED_ANDROID_OS_BP_PING_RESPONDER_H_
+)";
+
+const char kExpectedBnHeaderOutput[] =
+R"(#ifndef AIDL_GENERATED_ANDROID_OS_BN_PING_RESPONDER_H_
+#define AIDL_GENERATED_ANDROID_OS_BN_PING_RESPONDER_H_
+
+#include <binder/IInterface.h>
+#include <android/os/IPingResponder.h>
+
+namespace android {
+
+namespace os {
+
+class BnPingResponder : public ::android::BnInterface<IPingResponder> {
+public:
+::android::status_t onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags = 0) override;
+}; // class BnPingResponder
+
+} // namespace os
+
+} // namespace android
+
+#endif // AIDL_GENERATED_ANDROID_OS_BN_PING_RESPONDER_H_
+)";
+
+} // namespace ping_responder
+} // namespace test_data
+} // namespace aidl
+} // namespace android
diff --git a/aidl/tests/test_data_string_constants.cpp b/aidl/tests/test_data_string_constants.cpp
new file mode 100644
index 0000000..4674843
--- /dev/null
+++ b/aidl/tests/test_data_string_constants.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2016, 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 "tests/test_data.h"
+
+namespace android {
+namespace aidl {
+namespace test_data {
+namespace string_constants {
+
+const char kCanonicalName[] = "android.os.IStringConstants";
+const char kInterfaceDefinition[] = R"(
+package android.os;
+
+interface IStringConstants {
+ const String EXAMPLE_CONSTANT = "foo";
+}
+)";
+
+const char kJavaOutputPath[] = "some/path/to/output.java";
+const char kExpectedJavaOutput[] =
+R"(/*
+ * This file is auto-generated. DO NOT MODIFY.
+ * Original file: android/os/IStringConstants.aidl
+ */
+package android.os;
+public interface IStringConstants extends android.os.IInterface
+{
+/** Local-side IPC implementation stub class. */
+public static abstract class Stub extends android.os.Binder implements android.os.IStringConstants
+{
+private static final java.lang.String DESCRIPTOR = "android.os.IStringConstants";
+/** Construct the stub at attach it to the interface. */
+public Stub()
+{
+this.attachInterface(this, DESCRIPTOR);
+}
+/**
+ * Cast an IBinder object into an android.os.IStringConstants interface,
+ * generating a proxy if needed.
+ */
+public static android.os.IStringConstants asInterface(android.os.IBinder obj)
+{
+if ((obj==null)) {
+return null;
+}
+android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
+if (((iin!=null)&&(iin instanceof android.os.IStringConstants))) {
+return ((android.os.IStringConstants)iin);
+}
+return new android.os.IStringConstants.Stub.Proxy(obj);
+}
+@Override public android.os.IBinder asBinder()
+{
+return this;
+}
+@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
+{
+switch (code)
+{
+case INTERFACE_TRANSACTION:
+{
+reply.writeString(DESCRIPTOR);
+return true;
+}
+}
+return super.onTransact(code, data, reply, flags);
+}
+private static class Proxy implements android.os.IStringConstants
+{
+private android.os.IBinder mRemote;
+Proxy(android.os.IBinder remote)
+{
+mRemote = remote;
+}
+@Override public android.os.IBinder asBinder()
+{
+return mRemote;
+}
+public java.lang.String getInterfaceDescriptor()
+{
+return DESCRIPTOR;
+}
+}
+}
+public static final String EXAMPLE_CONSTANT = "foo";
+}
+)";
+
+const char kCppOutputPath[] = "some/path/to/output.cpp";
+const char kGenHeaderDir[] = "output";
+const char kGenInterfaceHeaderPath[] = "output/android/os/IStringConstants.h";
+const char kExpectedIHeaderOutput[] =
+R"(#ifndef AIDL_GENERATED_ANDROID_OS_I_STRING_CONSTANTS_H_
+#define AIDL_GENERATED_ANDROID_OS_I_STRING_CONSTANTS_H_
+
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+#include <binder/Status.h>
+#include <utils/String16.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+namespace os {
+
+class IStringConstants : public ::android::IInterface {
+public:
+DECLARE_META_INTERFACE(StringConstants)
+static const ::android::String16& EXAMPLE_CONSTANT();
+}; // class IStringConstants
+
+} // namespace os
+
+} // namespace android
+
+#endif // AIDL_GENERATED_ANDROID_OS_I_STRING_CONSTANTS_H_
+)";
+
+const char kExpectedCppOutput[] =
+R"(#include <android/os/IStringConstants.h>
+#include <android/os/BpStringConstants.h>
+
+namespace android {
+
+namespace os {
+
+IMPLEMENT_META_INTERFACE(StringConstants, "android.os.IStringConstants")
+
+const ::android::String16& IStringConstants::EXAMPLE_CONSTANT() {
+static const ::android::String16 value("foo");
+return value;
+}
+
+} // namespace os
+
+} // namespace android
+#include <android/os/BpStringConstants.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+namespace os {
+
+BpStringConstants::BpStringConstants(const ::android::sp<::android::IBinder>& _aidl_impl)
+ : BpInterface<IStringConstants>(_aidl_impl){
+}
+
+} // namespace os
+
+} // namespace android
+#include <android/os/BnStringConstants.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+namespace os {
+
+::android::status_t BnStringConstants::onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) {
+::android::status_t _aidl_ret_status = ::android::OK;
+switch (_aidl_code) {
+default:
+{
+_aidl_ret_status = ::android::BBinder::onTransact(_aidl_code, _aidl_data, _aidl_reply, _aidl_flags);
+}
+break;
+}
+if (_aidl_ret_status == ::android::UNEXPECTED_NULL) {
+_aidl_ret_status = ::android::binder::Status::fromExceptionCode(::android::binder::Status::EX_NULL_POINTER).writeToParcel(_aidl_reply);
+}
+return _aidl_ret_status;
+}
+
+} // namespace os
+
+} // namespace android
+)";
+
+} // namespace string_constants
+} // namespace test_data
+} // namespace aidl
+} // namespace android
diff --git a/aidl/tests/test_helpers.h b/aidl/tests/test_helpers.h
new file mode 100644
index 0000000..7d5145b
--- /dev/null
+++ b/aidl/tests/test_helpers.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 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 <algorithm>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <binder/Status.h>
+#include <utils/StrongPointer.h>
+
+#include "android/aidl/tests/ITestService.h"
+
+namespace android {
+namespace aidl {
+namespace tests {
+namespace client {
+
+template <typename T, typename U>
+bool RepeatPrimitive(
+ const android::sp<android::aidl::tests::ITestService>& service,
+ android::binder::Status(android::aidl::tests::ITestService::*func)(T, T*),
+ U input) {
+ T reply;
+ android::binder::Status status = (*service.*func)(input, &reply);
+ if (!status.isOk() || input != reply) {
+ LOG(ERROR) << "Failed to repeat primitive. status=" << status.toString8()
+ << ".";
+ return false;
+ }
+ return true;
+}
+
+template <typename T>
+bool ReverseArray(
+ const android::sp<android::aidl::tests::ITestService>& service,
+ android::binder::Status(android::aidl::tests::ITestService::*func)(
+ const std::vector<T>&, std::vector<T>*, std::vector<T>*),
+ std::vector<T> input) {
+ std::vector<T> actual_reversed;
+ std::vector<T> actual_repeated;
+ android::binder::Status status = (*service.*func)(
+ input, &actual_repeated, &actual_reversed);
+ if (!status.isOk()) {
+ LOG(ERROR) << "Failed to repeat array. status=" << status.toString8()
+ << ".";
+ return false;
+ }
+ if (input != actual_repeated) {
+ LOG(ERROR) << "Repeated version of array did not match";
+ LOG(ERROR) << "input.size()=" << input.size()
+ << " repeated.size()=" << actual_repeated.size();
+ return false;
+ }
+ std::reverse(input.begin(), input.end());
+ if (input != actual_reversed) {
+ LOG(ERROR) << "Reversed version of array did not match";
+ return false;
+ }
+ return true;
+}
+
+} // namespace client
+} // namespace tests
+} // namespace aidl
+} // namespace android
diff --git a/aidl/tests/test_util.cpp b/aidl/tests/test_util.cpp
new file mode 100644
index 0000000..9cb52f3
--- /dev/null
+++ b/aidl/tests/test_util.cpp
@@ -0,0 +1,168 @@
+/*
+ * 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 <algorithm>
+#include <iostream>
+#include <iterator>
+
+#include <android-base/strings.h>
+
+#include "os.h"
+#include "tests/test_util.h"
+
+using android::base::Split;
+using android::base::Join;
+using std::string;
+using std::vector;
+using std::cout;
+using std::endl;
+using std::distance;
+
+namespace android {
+namespace aidl {
+namespace test {
+
+string CanonicalNameToPath(const char* package_class, const char* extension) {
+ string rel_path{package_class};
+ for (char& c : rel_path) {
+ if (c == '.') {
+ c = OS_PATH_SEPARATOR;
+ }
+ }
+ rel_path += extension;
+ return rel_path;
+}
+
+void SplitPackageClass(const string& package_class,
+ string* rel_path,
+ string* package,
+ string* class_name) {
+ *package = string{package_class, 0, package_class.rfind('.')};
+ *class_name = string{package_class, package_class.rfind('.') + 1};
+ *rel_path = CanonicalNameToPath(package_class.c_str(), ".aidl");
+}
+
+void PrintDiff(const string& a, const string& b) {
+ const int LEFT = 1;
+ const int UP = 2;
+ const int UP_LEFT = 4;
+
+ auto a_lines = Split(a, "\n");
+ auto b_lines = Split(b, "\n");
+
+ struct diff_table_entry {
+ size_t longest_common_subsequence_length;
+ int propagation_directions;
+ };
+
+ diff_table_entry table[a_lines.size() + 1][b_lines.size() + 1];
+
+ for (size_t i = 0; i < a_lines.size() + 1; ++i) {
+ for (size_t j = 0; j < b_lines.size() + 1; ++j) {
+ if (i == 0 || j == 0) {
+ int directions = 0;
+
+ if (i) {
+ directions |= UP;
+ }
+
+ if (j) {
+ directions |= LEFT;
+ }
+
+ table[i][j].longest_common_subsequence_length = 0;
+ table[i][j].propagation_directions = directions;
+ } else if (a_lines[i-1] == b_lines[j-1]) {
+ table[i][j].longest_common_subsequence_length =
+ table[i-1][j-1].longest_common_subsequence_length + 1;
+ table[i][j].propagation_directions = UP_LEFT;
+ } else {
+ size_t length_up = table[i-1][j].longest_common_subsequence_length;
+ size_t length_left = table[i][j-1].longest_common_subsequence_length;
+ int directions = 0;
+ size_t length;
+
+ if (length_up >= length_left) {
+ directions |= UP;
+ length = length_up;
+ }
+
+ if (length_left >= length_up) {
+ directions |= LEFT;
+ length = length_left;
+ }
+
+ table[i][j].longest_common_subsequence_length = length;
+ table[i][j].propagation_directions = directions;
+ }
+ }
+ }
+
+ size_t i = a_lines.size();
+ size_t j = b_lines.size();
+ vector<string> output;
+
+ while (table[i][j].propagation_directions) {
+ if (table[i][j].propagation_directions & UP_LEFT) {
+ output.push_back(" " + a_lines[i-1]);
+ i--;
+ j--;
+ } else if (table[i][j].propagation_directions & UP) {
+ output.push_back("-" + a_lines[i-1]);
+ i--;
+ } else {
+ output.push_back("+" + b_lines[j-1]);
+ j--;
+ }
+ }
+
+ int print_mask = 0;
+ bool printed_last = false;
+ size_t line_number = 0;
+
+ for (auto it = output.crbegin(), frontier = output.crbegin();
+ it != output.crend(); ++it) {
+ while (frontier != output.crend() && distance(it, frontier) <= 3) {
+ print_mask <<= 1;
+ print_mask &= 0x7f;
+
+ if ((*frontier)[0] != ' ') {
+ print_mask |= 1;
+ }
+
+ frontier++;
+ }
+
+ if ((*it)[0] != '-') {
+ line_number++;
+ }
+
+ if (print_mask) {
+ if (!printed_last) {
+ cout << "Line: " << line_number << endl;
+ }
+
+ cout << *it << endl;
+ printed_last = true;
+ } else {
+ printed_last = false;
+ }
+ }
+}
+
+} // namespace test
+} // namespace android
+} // namespace aidl
diff --git a/aidl/tests/test_util.h b/aidl/tests/test_util.h
new file mode 100644
index 0000000..34b9e5d
--- /dev/null
+++ b/aidl/tests/test_util.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_TESTS_TEST_UTIL_H_
+#define AIDL_TESTS_TEST_UTIL_H_
+
+#include <string>
+
+namespace android {
+namespace aidl {
+namespace test {
+
+std::string CanonicalNameToPath(const char* package_class,
+ const char* extension);
+
+void SplitPackageClass(const std::string& package_class,
+ std::string* rel_path,
+ std::string* package,
+ std::string* class_name);
+
+void PrintDiff(const std::string& a, const std::string& b);
+
+} // namespace test
+} // namespace android
+} // namespace aidl
+
+#endif // AIDL_TESTS_TEST_UTIL_H_
diff --git a/aidl/type_cpp.cpp b/aidl/type_cpp.cpp
new file mode 100644
index 0000000..2c0552d
--- /dev/null
+++ b/aidl/type_cpp.cpp
@@ -0,0 +1,567 @@
+/*
+ * 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 "type_cpp.h"
+
+#include <algorithm>
+#include <iostream>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "logging.h"
+
+using std::cerr;
+using std::endl;
+using std::set;
+using std::string;
+using std::unique_ptr;
+using std::vector;
+
+using android::base::Split;
+using android::base::Join;
+using android::base::StringPrintf;
+
+namespace android {
+namespace aidl {
+namespace cpp {
+namespace {
+
+const char kNoPackage[] = "";
+const char kNoHeader[] = "";
+const char kNoValidMethod[] = "";
+Type* const kNoArrayType = nullptr;
+Type* const kNoNullableType = nullptr;
+
+bool is_cpp_keyword(const std::string& str) {
+ static const std::vector<std::string> kCppKeywords{
+ "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor",
+ "bool", "break", "case", "catch", "char", "char16_t", "char32_t", "class",
+ "compl", "concept", "const", "constexpr", "const_cast", "continue",
+ "decltype", "default", "delete", "do", "double", "dynamic_cast", "else",
+ "enum", "explicit", "export", "extern", "false", "float", "for", "friend",
+ "goto", "if", "inline", "int", "long", "mutable", "namespace", "new",
+ "noexcept", "not", "not_eq", "nullptr", "operator", "or", "or_eq",
+ "private", "protected", "public", "register", "reinterpret_cast",
+ "requires", "return", "short", "signed", "sizeof", "static",
+ "static_assert", "static_cast", "struct", "switch", "template", "this",
+ "thread_local", "throw", "true", "try", "typedef", "typeid", "typename",
+ "union", "unsigned", "using", "virtual", "void", "volatile", "wchar_t",
+ "while", "xor", "xor_eq",
+ };
+ return std::find(kCppKeywords.begin(), kCppKeywords.end(), str) !=
+ kCppKeywords.end();
+}
+
+class VoidType : public Type {
+ public:
+ VoidType() : Type(ValidatableType::KIND_BUILT_IN, kNoPackage, "void",
+ {}, "void", kNoValidMethod, kNoValidMethod) {}
+ virtual ~VoidType() = default;
+ bool CanBeOutParameter() const override { return false; }
+ bool CanWriteToParcel() const override { return false; }
+}; // class VoidType
+
+class CppArrayType : public Type {
+ public:
+ CppArrayType(int kind, // from ValidatableType
+ const std::string& package,
+ const string& underlying_aidl_type,
+ const string& cpp_header,
+ const string& underlying_cpp_type,
+ const string& read_method,
+ const string& write_method,
+ bool is_nullable,
+ const string& src_file_name = "")
+ : Type(kind, package,
+ underlying_aidl_type + "[]",
+ GetHeaders(is_nullable, cpp_header),
+ GetCppType(is_nullable, underlying_cpp_type),
+ read_method, write_method, kNoArrayType,
+ (is_nullable)
+ ? kNoNullableType
+ // All arrays are nullable.
+ : new CppArrayType(kind, package, underlying_aidl_type,
+ cpp_header, underlying_cpp_type,
+ read_method, write_method, true),
+ src_file_name) {}
+
+ bool CanBeOutParameter() const override { return true; }
+
+ private:
+ static vector<string> GetHeaders(bool is_nullable, const string& cpp_header) {
+ vector<string> result = {"vector"};
+ if (is_nullable) {
+ result.push_back("memory");
+ }
+ if (!cpp_header.empty()) {
+ result.push_back(cpp_header);
+ }
+ return result;
+ }
+
+ static string GetCppType(bool is_nullable,
+ const string& underlying_cpp_type) {
+ if (is_nullable)
+ return StringPrintf("::std::unique_ptr<::std::vector<%s>>",
+ underlying_cpp_type.c_str());
+ return StringPrintf("::std::vector<%s>",
+ underlying_cpp_type.c_str());
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(CppArrayType);
+}; // class CppArrayType
+
+class PrimitiveType : public Type {
+ public:
+ PrimitiveType(const std::string& aidl_type,
+ const std::string& header,
+ const std::string& cpp_type,
+ const std::string& read_method,
+ const std::string& write_method,
+ const std::string& read_array_method,
+ const std::string& write_array_method)
+ : Type(ValidatableType::KIND_BUILT_IN, kNoPackage, aidl_type, {header},
+ cpp_type, read_method, write_method,
+ new CppArrayType(ValidatableType::KIND_BUILT_IN, kNoPackage,
+ aidl_type, header, cpp_type,
+ read_array_method, write_array_method,
+ false)) {}
+
+ virtual ~PrimitiveType() = default;
+ bool IsCppPrimitive() const override { return true; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrimitiveType);
+}; // class PrimitiveType
+
+// Unfortunately, bytes in Java are signed. However, most C authors would
+// say that a byte is not in fact signed. Compromise: customize this otherwise
+// normal primitive to use signed single bytes, but unsigned byte arrays.
+class ByteType : public Type {
+ public:
+ ByteType()
+ : Type(ValidatableType::KIND_BUILT_IN, kNoPackage, "byte",
+ {"cstdint"}, "int8_t", "readByte", "writeByte",
+ new CppArrayType(ValidatableType::KIND_BUILT_IN, kNoPackage,
+ "byte", "cstdint", "uint8_t",
+ "readByteVector", "writeByteVector",
+ false)) {}
+
+ virtual ~ByteType() = default;
+ bool IsCppPrimitive() const override { return true; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ByteType);
+}; // class PrimitiveType
+
+class BinderType : public Type {
+ public:
+ BinderType(const AidlInterface& interface, const std::string& src_file_name)
+ : BinderType(interface, src_file_name,
+ new BinderType(interface, src_file_name, kNoNullableType,
+ "readNullableStrongBinder"),
+ "readStrongBinder") {}
+ virtual ~BinderType() = default;
+
+ string WriteCast(const string& val) const override {
+ return write_cast_ + "(" + val + ")";
+ }
+
+ private:
+ BinderType(const AidlInterface& interface,
+ const std::string& src_file_name,
+ Type* nullable_type, const std::string& read)
+ : Type(ValidatableType::KIND_GENERATED,
+ interface.GetPackage(), interface.GetName(),
+ {GetCppHeader(interface)}, GetCppName(interface),
+ read, "writeStrongBinder", kNoArrayType, nullable_type,
+ src_file_name, interface.GetLine()),
+ write_cast_(GetRawCppName(interface) + "::asBinder") {}
+
+ static string GetCppName(const AidlInterface& interface) {
+ return "::android::sp<" + GetRawCppName(interface) + ">";
+ }
+
+ static string GetRawCppName(const AidlInterface& interface) {
+ vector<string> name = interface.GetSplitPackage();
+ string ret;
+
+ name.push_back(interface.GetName());
+
+ for (const auto& term : name) {
+ ret += "::" + term;
+ }
+
+ return ret;
+ }
+
+ static string GetCppHeader(const AidlInterface& interface) {
+ vector<string> name = interface.GetSplitPackage();
+ name.push_back(interface.GetName());
+ return Join(name, '/') + ".h";
+ }
+
+ std::string write_cast_;
+};
+
+class NullableParcelableType : public Type {
+ public:
+ NullableParcelableType(const AidlParcelable& parcelable,
+ const std::string& src_file_name)
+ : Type(ValidatableType::KIND_PARCELABLE,
+ parcelable.GetPackage(), parcelable.GetName(),
+ {parcelable.GetCppHeader()}, GetCppName(parcelable),
+ "readParcelable", "writeNullableParcelable",
+ kNoArrayType, kNoNullableType,
+ src_file_name, parcelable.GetLine()) {}
+ virtual ~NullableParcelableType() = default;
+ bool CanBeOutParameter() const override { return true; }
+
+ private:
+ static string GetCppName(const AidlParcelable& parcelable) {
+ return "::std::unique_ptr<::" + Join(parcelable.GetSplitPackage(), "::") +
+ "::" + parcelable.GetName() + ">";
+ }
+};
+
+class ParcelableType : public Type {
+ public:
+ ParcelableType(const AidlParcelable& parcelable,
+ const std::string& src_file_name)
+ : Type(ValidatableType::KIND_PARCELABLE,
+ parcelable.GetPackage(), parcelable.GetName(),
+ {parcelable.GetCppHeader()}, GetCppName(parcelable),
+ "readParcelable", "writeParcelable",
+ new CppArrayType(
+ ValidatableType::KIND_PARCELABLE, parcelable.GetPackage(),
+ parcelable.GetName(), parcelable.GetCppHeader(),
+ GetCppName(parcelable),
+ "readParcelableVector", "writeParcelableVector", false,
+ src_file_name),
+ new NullableParcelableType(parcelable, src_file_name),
+ src_file_name, parcelable.GetLine()) {}
+ virtual ~ParcelableType() = default;
+ bool CanBeOutParameter() const override { return true; }
+
+ private:
+ static string GetCppName(const AidlParcelable& parcelable) {
+ return "::" + Join(parcelable.GetSplitPackage(), "::") +
+ "::" + parcelable.GetName();
+ }
+};
+
+class NullableStringListType : public Type {
+ public:
+ NullableStringListType()
+ : Type(ValidatableType::KIND_BUILT_IN,
+ "java.util", "List<" + string(kStringCanonicalName) + ">",
+ {"utils/String16.h", "memory", "vector"},
+ "::std::unique_ptr<::std::vector<std::unique_ptr<::android::String16>>>",
+ "readString16Vector", "writeString16Vector") {}
+ virtual ~NullableStringListType() = default;
+ bool CanBeOutParameter() const override { return true; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NullableStringListType);
+}; // class NullableStringListType
+
+class StringListType : public Type {
+ public:
+ StringListType()
+ : Type(ValidatableType::KIND_BUILT_IN,
+ "java.util", "List<" + string(kStringCanonicalName) + ">",
+ {"utils/String16.h", "vector"},
+ "::std::vector<::android::String16>",
+ "readString16Vector", "writeString16Vector",
+ kNoArrayType, new NullableStringListType()) {}
+ virtual ~StringListType() = default;
+ bool CanBeOutParameter() const override { return true; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(StringListType);
+}; // class StringListType
+
+class NullableUtf8InCppStringListType : public Type {
+ public:
+ NullableUtf8InCppStringListType()
+ : Type(ValidatableType::KIND_BUILT_IN,
+ "java.util", "List<" + string(kUtf8InCppStringCanonicalName) + ">",
+ {"memory", "string", "vector"},
+ "::std::unique_ptr<::std::vector<std::unique_ptr<::std::string>>>",
+ "readUtf8VectorFromUtf16Vector", "writeUtf8VectorAsUtf16Vector") {}
+ virtual ~NullableUtf8InCppStringListType() = default;
+ bool CanBeOutParameter() const override { return true; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NullableUtf8InCppStringListType);
+}; // class NullableUtf8InCppStringListType
+
+class Utf8InCppStringListType : public Type {
+ public:
+ Utf8InCppStringListType()
+ : Type(ValidatableType::KIND_BUILT_IN,
+ "java.util", "List<" + string(kUtf8InCppStringCanonicalName) + ">",
+ {"string", "vector"},
+ "::std::vector<::std::string>",
+ "readUtf8VectorFromUtf16Vector", "writeUtf8VectorAsUtf16Vector",
+ kNoArrayType, new NullableUtf8InCppStringListType()) {}
+ virtual ~Utf8InCppStringListType() = default;
+ bool CanBeOutParameter() const override { return true; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Utf8InCppStringListType);
+}; // class Utf8InCppStringListType
+
+class NullableBinderListType : public Type {
+ public:
+ NullableBinderListType()
+ : Type(ValidatableType::KIND_BUILT_IN, "java.util",
+ "List<android.os.IBinder>", {"binder/IBinder.h", "vector"},
+ "::std::unique_ptr<::std::vector<::android::sp<::android::IBinder>>>",
+ "readStrongBinderVector", "writeStrongBinderVector") {}
+ virtual ~NullableBinderListType() = default;
+ bool CanBeOutParameter() const override { return true; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NullableBinderListType);
+}; // class NullableBinderListType
+
+class BinderListType : public Type {
+ public:
+ BinderListType()
+ : Type(ValidatableType::KIND_BUILT_IN, "java.util",
+ "List<android.os.IBinder>", {"binder/IBinder.h", "vector"},
+ "::std::vector<::android::sp<::android::IBinder>>",
+ "readStrongBinderVector", "writeStrongBinderVector",
+ kNoArrayType, new NullableBinderListType()) {}
+ virtual ~BinderListType() = default;
+ bool CanBeOutParameter() const override { return true; }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BinderListType);
+}; // class BinderListType
+
+} // namespace
+
+Type::Type(int kind,
+ const std::string& package,
+ const std::string& aidl_type,
+ const vector<string>& headers,
+ const string& cpp_type,
+ const string& read_method,
+ const string& write_method,
+ Type* array_type,
+ Type* nullable_type,
+ const string& src_file_name,
+ int line)
+ : ValidatableType(kind, package, aidl_type, src_file_name, line),
+ headers_(headers),
+ aidl_type_(aidl_type),
+ cpp_type_(cpp_type),
+ parcel_read_method_(read_method),
+ parcel_write_method_(write_method),
+ array_type_(array_type),
+ nullable_type_(nullable_type) {}
+
+bool Type::CanWriteToParcel() const { return true; }
+
+void TypeNamespace::Init() {
+ Add(new ByteType());
+ Add(new PrimitiveType(
+ "int",
+ "cstdint", "int32_t", "readInt32", "writeInt32",
+ "readInt32Vector", "writeInt32Vector"));
+ Add(new PrimitiveType(
+ "long",
+ "cstdint", "int64_t", "readInt64", "writeInt64",
+ "readInt64Vector", "writeInt64Vector"));
+ Add(new PrimitiveType(
+ "float",
+ kNoHeader, "float", "readFloat", "writeFloat",
+ "readFloatVector", "writeFloatVector"));
+ Add(new PrimitiveType(
+ "double",
+ kNoHeader, "double", "readDouble", "writeDouble",
+ "readDoubleVector", "writeDoubleVector"));
+ Add(new PrimitiveType(
+ "boolean",
+ kNoHeader, "bool", "readBool", "writeBool",
+ "readBoolVector", "writeBoolVector"));
+ // C++11 defines the char16_t type as a built in for Unicode characters.
+ Add(new PrimitiveType(
+ "char",
+ kNoHeader, "char16_t", "readChar", "writeChar",
+ "readCharVector", "writeCharVector"));
+
+ Type* string_array_type = new CppArrayType(
+ ValidatableType::KIND_BUILT_IN, "java.lang", "String",
+ "utils/String16.h", "::android::String16",
+ "readString16Vector", "writeString16Vector", false);
+
+ Type* nullable_string_type =
+ new Type(ValidatableType::KIND_BUILT_IN, "java.lang", "String",
+ {"memory", "utils/String16.h"}, "::std::unique_ptr<::android::String16>",
+ "readString16", "writeString16");
+
+ string_type_ = new Type(ValidatableType::KIND_BUILT_IN, "java.lang", "String",
+ {"utils/String16.h"}, "::android::String16",
+ "readString16", "writeString16",
+ string_array_type, nullable_string_type);
+ Add(string_type_);
+
+ using ::android::aidl::kAidlReservedTypePackage;
+ using ::android::aidl::kUtf8InCppStringClass;
+
+ // This type is a Utf16 string in the parcel, but deserializes to
+ // a std::string in Utf8 format when we use it in C++.
+ Type* cpp_utf8_string_array = new CppArrayType(
+ ValidatableType::KIND_BUILT_IN,
+ kAidlReservedTypePackage, kUtf8InCppStringClass,
+ "string", "::std::string",
+ "readUtf8VectorFromUtf16Vector", "writeUtf8VectorAsUtf16Vector",
+ false);
+ Type* nullable_cpp_utf8_string_type = new Type(
+ ValidatableType::KIND_BUILT_IN,
+ kAidlReservedTypePackage, kUtf8InCppStringClass,
+ {"string", "memory"}, "::std::unique_ptr<::std::string>",
+ "readUtf8FromUtf16", "writeUtf8AsUtf16");
+ Add(new Type(
+ ValidatableType::KIND_BUILT_IN,
+ kAidlReservedTypePackage, kUtf8InCppStringClass,
+ {"string"}, "::std::string", "readUtf8FromUtf16", "writeUtf8AsUtf16",
+ cpp_utf8_string_array, nullable_cpp_utf8_string_type));
+
+ Type* nullable_ibinder = new Type(
+ ValidatableType::KIND_BUILT_IN, "android.os", "IBinder",
+ {"binder/IBinder.h"}, "::android::sp<::android::IBinder>",
+ "readNullableStrongBinder", "writeStrongBinder");
+ ibinder_type_ = new Type(
+ ValidatableType::KIND_BUILT_IN, "android.os", "IBinder",
+ {"binder/IBinder.h"}, "::android::sp<::android::IBinder>",
+ "readStrongBinder", "writeStrongBinder",
+ kNoArrayType, nullable_ibinder);
+ Add(ibinder_type_);
+
+ Add(new BinderListType());
+ Add(new StringListType());
+ Add(new Utf8InCppStringListType());
+
+ Type* fd_vector_type = new CppArrayType(
+ ValidatableType::KIND_BUILT_IN, kNoPackage, "FileDescriptor",
+ "android-base/unique_fd.h",
+ "::android::base::unique_fd",
+ "readUniqueFileDescriptorVector", "writeUniqueFileDescriptorVector",
+ false);
+
+ Add(new Type(
+ ValidatableType::KIND_BUILT_IN, kNoPackage, "FileDescriptor",
+ {"android-base/unique_fd.h"}, "::android::base::unique_fd",
+ "readUniqueFileDescriptor", "writeUniqueFileDescriptor",
+ fd_vector_type));
+
+ void_type_ = new class VoidType();
+ Add(void_type_);
+}
+
+bool TypeNamespace::AddParcelableType(const AidlParcelable& p,
+ const string& filename) {
+ if (p.GetCppHeader().empty()) {
+ LOG(ERROR) << "Parcelable " << p.GetCanonicalName()
+ << " has no C++ header defined.";
+ return false;
+ }
+ Add(new ParcelableType(p, filename));
+ return true;
+}
+
+bool TypeNamespace::AddBinderType(const AidlInterface& b,
+ const string& file_name) {
+ Add(new BinderType(b, file_name));
+ return true;
+}
+
+bool TypeNamespace::AddListType(const std::string& type_name) {
+ const Type* contained_type = FindTypeByCanonicalName(type_name);
+ if (!contained_type) {
+ LOG(ERROR) << "Cannot create List<" << type_name << "> because contained "
+ "type cannot be found or is invalid.";
+ return false;
+ }
+ if (contained_type->IsCppPrimitive()) {
+ LOG(ERROR) << "Cannot create List<" << type_name << "> because contained "
+ "type is a primitive in Java and Java List cannot hold "
+ "primitives.";
+ return false;
+ }
+
+ if (contained_type->CanonicalName() == kStringCanonicalName ||
+ contained_type->CanonicalName() == kUtf8InCppStringCanonicalName ||
+ contained_type == IBinderType()) {
+ return true;
+ }
+
+ // TODO Support lists of parcelables b/23600712
+
+ LOG(ERROR) << "aidl-cpp does not yet support List<" << type_name << ">";
+ return false;
+}
+
+bool TypeNamespace::AddMapType(const std::string& /* key_type_name */,
+ const std::string& /* value_type_name */) {
+ // TODO Support list types b/25242025
+ LOG(ERROR) << "aidl does not implement support for typed maps!";
+ return false;
+}
+
+bool TypeNamespace::IsValidPackage(const string& package) const {
+ if (package.empty()) {
+ return false;
+ }
+
+ auto pieces = Split(package, ".");
+ for (const string& piece : pieces) {
+ if (is_cpp_keyword(piece)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+const ValidatableType* TypeNamespace::GetArgType(const AidlArgument& a,
+ int arg_index,
+ const std::string& filename,
+ const AidlInterface& interface) const {
+ const string error_prefix = StringPrintf(
+ "In file %s line %d parameter %s (%d):\n ",
+ filename.c_str(), a.GetLine(), a.GetName().c_str(), arg_index);
+
+ // check that the name doesn't match a keyword
+ if (is_cpp_keyword(a.GetName().c_str())) {
+ cerr << error_prefix << "Argument name is a C++ keyword"
+ << endl;
+ return nullptr;
+ }
+
+ return ::android::aidl::TypeNamespace::GetArgType(a, arg_index, filename,
+ interface);
+}
+
+} // namespace cpp
+} // namespace aidl
+} // namespace android
diff --git a/aidl/type_cpp.h b/aidl/type_cpp.h
new file mode 100644
index 0000000..0a6a4a9
--- /dev/null
+++ b/aidl/type_cpp.h
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_TYPE_CPP_H_
+#define AIDL_TYPE_CPP_H_
+
+#include <memory>
+#include <string>
+#include <set>
+#include <vector>
+
+#include <android-base/macros.h>
+
+#include "type_namespace.h"
+
+namespace android {
+namespace aidl {
+namespace cpp {
+
+class Type : public ValidatableType {
+ public:
+ Type(int kind, // from ValidatableType
+ const std::string& package,
+ const std::string& aidl_type,
+ const std::vector<std::string>& header,
+ const std::string& cpp_type,
+ const std::string& read_method,
+ const std::string& write_method,
+ Type* array_type = nullptr,
+ Type* nullable_type = nullptr,
+ const std::string& src_file_name = "",
+ int line = -1);
+ virtual ~Type() = default;
+
+ // overrides of ValidatableType
+ bool CanBeOutParameter() const override { return false; }
+ bool CanWriteToParcel() const override;
+
+ const Type* ArrayType() const override { return array_type_.get(); }
+ const Type* NullableType() const override { return nullable_type_.get(); }
+ std::string CppType() const { return cpp_type_; }
+ const std::string& ReadFromParcelMethod() const {
+ return parcel_read_method_;
+ }
+ const std::string& WriteToParcelMethod() const {
+ return parcel_write_method_;
+ }
+
+ void GetHeaders(std::set<std::string>* headers) const {
+ for (const std::string& header : headers_) {
+ if (!header.empty()) {
+ headers->insert(header);
+ }
+ }
+ }
+ virtual bool IsCppPrimitive() const { return false; }
+ virtual std::string WriteCast(const std::string& value) const {
+ return value;
+ }
+
+ private:
+ // |headers| are the headers we must include to use this type
+ const std::vector<std::string> headers_;
+ // |aidl_type| is what we find in the yacc generated AST (e.g. "int").
+ const std::string aidl_type_;
+ // |cpp_type| is what we use in the generated C++ code (e.g. "int32_t").
+ const std::string cpp_type_;
+ const std::string parcel_read_method_;
+ const std::string parcel_write_method_;
+
+ const std::unique_ptr<Type> array_type_;
+ const std::unique_ptr<Type> nullable_type_;
+
+ DISALLOW_COPY_AND_ASSIGN(Type);
+}; // class Type
+
+class TypeNamespace : public ::android::aidl::LanguageTypeNamespace<Type> {
+ public:
+ TypeNamespace() = default;
+ virtual ~TypeNamespace() = default;
+
+ void Init() override;
+ bool AddParcelableType(const AidlParcelable& p,
+ const std::string& filename) override;
+ bool AddBinderType(const AidlInterface& b,
+ const std::string& filename) override;
+ bool AddListType(const std::string& type_name) override;
+ bool AddMapType(const std::string& key_type_name,
+ const std::string& value_type_name) override;
+
+ bool IsValidPackage(const std::string& package) const override;
+ const ValidatableType* GetArgType(const AidlArgument& a,
+ int arg_index,
+ const std::string& filename,
+ const AidlInterface& interface) const override;
+
+ const Type* VoidType() const { return void_type_; }
+ const Type* IBinderType() const { return ibinder_type_; }
+
+ private:
+ Type* void_type_ = nullptr;
+ Type* string_type_ = nullptr;
+ Type* ibinder_type_ = nullptr;
+
+ DISALLOW_COPY_AND_ASSIGN(TypeNamespace);
+}; // class TypeNamespace
+
+} // namespace cpp
+} // namespace aidl
+} // namespace android
+
+#endif // AIDL_TYPE_NAMESPACE_CPP_H_
diff --git a/aidl/type_cpp_unittest.cpp b/aidl/type_cpp_unittest.cpp
new file mode 100644
index 0000000..35fc3d5
--- /dev/null
+++ b/aidl/type_cpp_unittest.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 <memory>
+
+#include <gtest/gtest.h>
+
+#include "type_cpp.h"
+
+namespace android {
+namespace aidl {
+namespace cpp {
+
+class CppTypeNamespaceTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ types_.Init();
+ }
+ TypeNamespace types_;
+};
+
+TEST_F(CppTypeNamespaceTest, HasSomeBasicTypes) {
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("byte"));
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("int"));
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("long"));
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("float"));
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("double"));
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("boolean"));
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("char"));
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("String"));
+}
+
+TEST_F(CppTypeNamespaceTest, SupportsListString) {
+ EXPECT_TRUE(
+ types_.HasTypeByCanonicalName("java.util.List<java.lang.String>"));
+}
+
+} // namespace cpp
+} // namespace android
+} // namespace aidl
diff --git a/aidl/type_java.cpp b/aidl/type_java.cpp
new file mode 100644
index 0000000..18d27a2
--- /dev/null
+++ b/aidl/type_java.cpp
@@ -0,0 +1,908 @@
+/*
+ * 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 "type_java.h"
+
+#include <sys/types.h>
+
+#include <android-base/strings.h>
+
+#include "aidl_language.h"
+#include "logging.h"
+
+using std::string;
+using android::base::Split;
+using android::base::Join;
+using android::base::Trim;
+
+namespace android {
+namespace aidl {
+namespace java {
+
+Expression* NULL_VALUE;
+Expression* THIS_VALUE;
+Expression* SUPER_VALUE;
+Expression* TRUE_VALUE;
+Expression* FALSE_VALUE;
+
+// ================================================================
+
+Type::Type(const JavaTypeNamespace* types, const string& name, int kind,
+ bool canWriteToParcel, bool canBeOut)
+ : Type(types, "", name, kind, canWriteToParcel, canBeOut, "", -1) {}
+
+Type::Type(const JavaTypeNamespace* types, const string& package,
+ const string& name, int kind, bool canWriteToParcel,
+ bool canBeOut, const string& declFile, int declLine)
+ : ValidatableType(kind, package, name, declFile, declLine),
+ m_types(types),
+ m_javaType((package.empty()) ? name : package + "." + name),
+ m_canWriteToParcel(canWriteToParcel),
+ m_canBeOut(canBeOut) {
+}
+
+string Type::CreatorName() const { return ""; }
+
+string Type::InstantiableName() const { return JavaType(); }
+
+void Type::WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const {
+ fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%sn", __FILE__,
+ __LINE__, m_javaType.c_str());
+ addTo->Add(new LiteralExpression("/* WriteToParcel error " + m_javaType +
+ " */"));
+}
+
+void Type::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", __FILE__,
+ __LINE__, m_javaType.c_str());
+ addTo->Add(new LiteralExpression("/* CreateFromParcel error " +
+ m_javaType + " */"));
+}
+
+void Type::ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable**) const {
+ fprintf(stderr, "aidl:internal error %s:%d qualifiedName=%s\n", __FILE__,
+ __LINE__, m_javaType.c_str());
+ addTo->Add(new LiteralExpression("/* ReadFromParcel error " +
+ m_javaType + " */"));
+}
+
+Expression* Type::BuildWriteToParcelFlags(int flags) const {
+ if (flags == 0) {
+ return new LiteralExpression("0");
+ }
+ if ((flags & PARCELABLE_WRITE_RETURN_VALUE) != 0) {
+ return new FieldVariable(m_types->ParcelableInterfaceType(),
+ "PARCELABLE_WRITE_RETURN_VALUE");
+ }
+ return new LiteralExpression("0");
+}
+
+// ================================================================
+
+BasicType::BasicType(const JavaTypeNamespace* types, const string& name,
+ const string& marshallParcel,
+ const string& unmarshallParcel,
+ const string& writeArrayParcel,
+ const string& createArrayParcel,
+ const string& readArrayParcel)
+ : Type(types, name, ValidatableType::KIND_BUILT_IN, true, false),
+ m_marshallParcel(marshallParcel),
+ m_unmarshallParcel(unmarshallParcel) {
+ m_array_type.reset(new BasicArrayType(types, name, writeArrayParcel,
+ createArrayParcel, readArrayParcel));
+}
+
+void BasicType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, m_marshallParcel, 1, v));
+}
+
+void BasicType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new Assignment(v, new MethodCall(parcel, m_unmarshallParcel)));
+}
+
+BasicArrayType::BasicArrayType(const JavaTypeNamespace* types,
+ const string& name,
+ const string& writeArrayParcel,
+ const string& createArrayParcel,
+ const string& readArrayParcel)
+ : Type(types, name, ValidatableType::KIND_BUILT_IN, true, true),
+ m_writeArrayParcel(writeArrayParcel),
+ m_createArrayParcel(createArrayParcel),
+ m_readArrayParcel(readArrayParcel) {}
+
+
+void BasicArrayType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, m_writeArrayParcel, 1, v));
+}
+
+void BasicArrayType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new Assignment(v, new MethodCall(parcel, m_createArrayParcel)));
+}
+
+void BasicArrayType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new MethodCall(parcel, m_readArrayParcel, 1, v));
+}
+
+// ================================================================
+
+FileDescriptorType::FileDescriptorType(const JavaTypeNamespace* types)
+ : Type(types, "java.io", "FileDescriptor", ValidatableType::KIND_BUILT_IN,
+ true, false) {
+ m_array_type.reset(new FileDescriptorArrayType(types));
+}
+
+void FileDescriptorType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, "writeRawFileDescriptor", 1, v));
+}
+
+void FileDescriptorType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new Assignment(v, new MethodCall(parcel, "readRawFileDescriptor")));
+}
+
+FileDescriptorArrayType::FileDescriptorArrayType(const JavaTypeNamespace* types)
+ : Type(types, "java.io", "FileDescriptor", ValidatableType::KIND_BUILT_IN,
+ true, true) {}
+
+void FileDescriptorArrayType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, "writeRawFileDescriptorArray", 1, v));
+}
+
+void FileDescriptorArrayType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new Assignment(v, new MethodCall(parcel, "createRawFileDescriptorArray")));
+}
+
+void FileDescriptorArrayType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new MethodCall(parcel, "readRawFileDescriptorArray", 1, v));
+}
+
+// ================================================================
+
+BooleanType::BooleanType(const JavaTypeNamespace* types)
+ : Type(types, "boolean", ValidatableType::KIND_BUILT_IN, true, false) {
+ m_array_type.reset(new BooleanArrayType(types));
+}
+
+void BooleanType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(
+ parcel, "writeInt", 1,
+ new Ternary(v, new LiteralExpression("1"), new LiteralExpression("0"))));
+}
+
+void BooleanType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(
+ new Assignment(v, new Comparison(new LiteralExpression("0"), "!=",
+ new MethodCall(parcel, "readInt"))));
+}
+
+BooleanArrayType::BooleanArrayType(const JavaTypeNamespace* types)
+ : Type(types, "boolean", ValidatableType::KIND_BUILT_IN, true, true) {}
+
+void BooleanArrayType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, "writeBooleanArray", 1, v));
+}
+
+void BooleanArrayType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new Assignment(v, new MethodCall(parcel, "createBooleanArray")));
+}
+
+void BooleanArrayType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new MethodCall(parcel, "readBooleanArray", 1, v));
+}
+
+// ================================================================
+
+CharType::CharType(const JavaTypeNamespace* types)
+ : Type(types, "char", ValidatableType::KIND_BUILT_IN, true, false) {
+ m_array_type.reset(new CharArrayType(types));
+}
+
+void CharType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(
+ new MethodCall(parcel, "writeInt", 1, new Cast(m_types->IntType(), v)));
+}
+
+void CharType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new Assignment(v, new MethodCall(parcel, "readInt"), this));
+}
+
+CharArrayType::CharArrayType(const JavaTypeNamespace* types)
+ : Type(types, "char", ValidatableType::KIND_BUILT_IN, true, true) {}
+
+void CharArrayType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, "writeCharArray", 1, v));
+}
+
+void CharArrayType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new Assignment(v, new MethodCall(parcel, "createCharArray")));
+}
+
+void CharArrayType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new MethodCall(parcel, "readCharArray", 1, v));
+}
+
+// ================================================================
+
+StringType::StringType(const JavaTypeNamespace* types,
+ const std::string& package,
+ const std::string& class_name)
+ : Type(types, package, class_name,
+ ValidatableType::KIND_BUILT_IN, true, false) {
+ m_array_type.reset(new StringArrayType(types));
+}
+
+string StringType::CreatorName() const {
+ return "android.os.Parcel.STRING_CREATOR";
+}
+
+void StringType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, "writeString", 1, v));
+}
+
+void StringType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new Assignment(v, new MethodCall(parcel, "readString")));
+}
+
+StringArrayType::StringArrayType(const JavaTypeNamespace* types)
+ : Type(types, "java.lang", "String", ValidatableType::KIND_BUILT_IN,
+ true, true) {}
+
+string StringArrayType::CreatorName() const {
+ return "android.os.Parcel.STRING_CREATOR";
+}
+
+void StringArrayType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, "writeStringArray", 1, v));
+}
+
+void StringArrayType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new Assignment(v, new MethodCall(parcel, "createStringArray")));
+}
+
+void StringArrayType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new MethodCall(parcel, "readStringArray", 1, v));
+}
+
+// ================================================================
+
+CharSequenceType::CharSequenceType(const JavaTypeNamespace* types)
+ : Type(types, "java.lang", "CharSequence", ValidatableType::KIND_BUILT_IN,
+ true, false) {}
+
+string CharSequenceType::CreatorName() const {
+ return "android.os.Parcel.STRING_CREATOR";
+}
+
+void CharSequenceType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ // if (v != null) {
+ // parcel.writeInt(1);
+ // v.writeToParcel(parcel);
+ // } else {
+ // parcel.writeInt(0);
+ // }
+ IfStatement* elsepart = new IfStatement();
+ elsepart->statements->Add(
+ new MethodCall(parcel, "writeInt", 1, new LiteralExpression("0")));
+ IfStatement* ifpart = new IfStatement;
+ ifpart->expression = new Comparison(v, "!=", NULL_VALUE);
+ ifpart->elseif = elsepart;
+ ifpart->statements->Add(
+ new MethodCall(parcel, "writeInt", 1, new LiteralExpression("1")));
+ ifpart->statements->Add(new MethodCall(m_types->TextUtilsType(),
+ "writeToParcel", 3, v, parcel,
+ BuildWriteToParcelFlags(flags)));
+
+ addTo->Add(ifpart);
+}
+
+void CharSequenceType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ // if (0 != parcel.readInt()) {
+ // v = TextUtils.createFromParcel(parcel)
+ // } else {
+ // v = null;
+ // }
+ IfStatement* elsepart = new IfStatement();
+ elsepart->statements->Add(new Assignment(v, NULL_VALUE));
+
+ IfStatement* ifpart = new IfStatement();
+ ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
+ new MethodCall(parcel, "readInt"));
+ ifpart->elseif = elsepart;
+ ifpart->statements->Add(new Assignment(
+ v, new MethodCall(m_types->TextUtilsType(),
+ "CHAR_SEQUENCE_CREATOR.createFromParcel", 1, parcel)));
+
+ addTo->Add(ifpart);
+}
+
+// ================================================================
+
+RemoteExceptionType::RemoteExceptionType(const JavaTypeNamespace* types)
+ : Type(types, "android.os", "RemoteException",
+ ValidatableType::KIND_BUILT_IN, false, false) {}
+
+void RemoteExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void RemoteExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+// ================================================================
+
+RuntimeExceptionType::RuntimeExceptionType(const JavaTypeNamespace* types)
+ : Type(types, "java.lang", "RuntimeException",
+ ValidatableType::KIND_BUILT_IN, false, false) {}
+
+void RuntimeExceptionType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void RuntimeExceptionType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel,
+ Variable**) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+// ================================================================
+
+IBinderType::IBinderType(const JavaTypeNamespace* types)
+ : Type(types, "android.os", "IBinder", ValidatableType::KIND_BUILT_IN,
+ true, false) {
+ m_array_type.reset(new IBinderArrayType(types));
+}
+
+void IBinderType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, "writeStrongBinder", 1, v));
+}
+
+void IBinderType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new Assignment(v, new MethodCall(parcel, "readStrongBinder")));
+}
+
+IBinderArrayType::IBinderArrayType(const JavaTypeNamespace* types)
+ : Type(types, "android.os", "IBinder", ValidatableType::KIND_BUILT_IN,
+ true, true) {}
+
+void IBinderArrayType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, "writeBinderArray", 1, v));
+}
+
+void IBinderArrayType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new Assignment(v, new MethodCall(parcel, "createBinderArray")));
+}
+
+void IBinderArrayType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ addTo->Add(new MethodCall(parcel, "readBinderArray", 1, v));
+}
+
+// ================================================================
+
+IInterfaceType::IInterfaceType(const JavaTypeNamespace* types)
+ : Type(types, "android.os", "IInterface", ValidatableType::KIND_BUILT_IN,
+ false, false) {}
+
+void IInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void IInterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+// ================================================================
+
+BinderType::BinderType(const JavaTypeNamespace* types)
+ : Type(types, "android.os", "Binder", ValidatableType::KIND_BUILT_IN,
+ false, false) {}
+
+void BinderType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void BinderType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+// ================================================================
+
+BinderProxyType::BinderProxyType(const JavaTypeNamespace* types)
+ : Type(types, "android.os", "BinderProxy", ValidatableType::KIND_BUILT_IN,
+ false, false) {}
+
+void BinderProxyType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void BinderProxyType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+// ================================================================
+
+ParcelType::ParcelType(const JavaTypeNamespace* types)
+ : Type(types, "android.os", "Parcel", ValidatableType::KIND_BUILT_IN,
+ false, false) {}
+
+void ParcelType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void ParcelType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+// ================================================================
+
+ParcelableInterfaceType::ParcelableInterfaceType(const JavaTypeNamespace* types)
+ : Type(types, "android.os", "Parcelable", ValidatableType::KIND_BUILT_IN,
+ false, false) {}
+
+void ParcelableInterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+void ParcelableInterfaceType::CreateFromParcel(StatementBlock* addTo,
+ Variable* v, Variable* parcel,
+ Variable**) const {
+ fprintf(stderr, "aidl:internal error %s:%d\n", __FILE__, __LINE__);
+}
+
+// ================================================================
+
+MapType::MapType(const JavaTypeNamespace* types)
+ : Type(types, "java.util", "Map", ValidatableType::KIND_BUILT_IN,
+ true, true) {}
+
+void MapType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, "writeMap", 1, v));
+}
+
+static void EnsureClassLoader(StatementBlock* addTo, Variable** cl,
+ const JavaTypeNamespace* types) {
+ // We don't want to look up the class loader once for every
+ // collection argument, so ensure we do it at most once per method.
+ if (*cl == NULL) {
+ *cl = new Variable(types->ClassLoaderType(), "cl");
+ addTo->Add(new VariableDeclaration(
+ *cl, new LiteralExpression("this.getClass().getClassLoader()"),
+ types->ClassLoaderType()));
+ }
+}
+
+void MapType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const {
+ EnsureClassLoader(addTo, cl, m_types);
+ addTo->Add(new Assignment(v, new MethodCall(parcel, "readHashMap", 1, *cl)));
+}
+
+void MapType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const {
+ EnsureClassLoader(addTo, cl, m_types);
+ addTo->Add(new MethodCall(parcel, "readMap", 2, v, *cl));
+}
+
+// ================================================================
+
+ListType::ListType(const JavaTypeNamespace* types)
+ : Type(types, "java.util", "List", ValidatableType::KIND_BUILT_IN,
+ true, true) {}
+
+string ListType::InstantiableName() const { return "java.util.ArrayList"; }
+
+void ListType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, "writeList", 1, v));
+}
+
+void ListType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const {
+ EnsureClassLoader(addTo, cl, m_types);
+ addTo->Add(
+ new Assignment(v, new MethodCall(parcel, "readArrayList", 1, *cl)));
+}
+
+void ListType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const {
+ EnsureClassLoader(addTo, cl, m_types);
+ addTo->Add(new MethodCall(parcel, "readList", 2, v, *cl));
+}
+
+// ================================================================
+
+UserDataType::UserDataType(const JavaTypeNamespace* types,
+ const string& package, const string& name,
+ bool builtIn, bool canWriteToParcel,
+ const string& declFile, int declLine)
+ : Type(types, package, name,
+ builtIn ? ValidatableType::KIND_BUILT_IN
+ : ValidatableType::KIND_PARCELABLE,
+ canWriteToParcel, true, declFile, declLine) {
+ m_array_type.reset(new UserDataArrayType(types, package, name, builtIn,
+ canWriteToParcel, declFile,
+ declLine));
+}
+
+string UserDataType::CreatorName() const {
+ return JavaType() + ".CREATOR";
+}
+
+void UserDataType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ // if (v != null) {
+ // parcel.writeInt(1);
+ // v.writeToParcel(parcel);
+ // } else {
+ // parcel.writeInt(0);
+ // }
+ IfStatement* elsepart = new IfStatement();
+ elsepart->statements->Add(
+ new MethodCall(parcel, "writeInt", 1, new LiteralExpression("0")));
+ IfStatement* ifpart = new IfStatement;
+ ifpart->expression = new Comparison(v, "!=", NULL_VALUE);
+ ifpart->elseif = elsepart;
+ ifpart->statements->Add(
+ new MethodCall(parcel, "writeInt", 1, new LiteralExpression("1")));
+ ifpart->statements->Add(new MethodCall(v, "writeToParcel", 2, parcel,
+ BuildWriteToParcelFlags(flags)));
+
+ addTo->Add(ifpart);
+}
+
+void UserDataType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ // if (0 != parcel.readInt()) {
+ // v = CLASS.CREATOR.createFromParcel(parcel)
+ // } else {
+ // v = null;
+ // }
+ IfStatement* elsepart = new IfStatement();
+ elsepart->statements->Add(new Assignment(v, NULL_VALUE));
+
+ IfStatement* ifpart = new IfStatement();
+ ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
+ new MethodCall(parcel, "readInt"));
+ ifpart->elseif = elsepart;
+ ifpart->statements->Add(new Assignment(
+ v, new MethodCall(v->type, "CREATOR.createFromParcel", 1, parcel)));
+
+ addTo->Add(ifpart);
+}
+
+void UserDataType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ // TODO: really, we don't need to have this extra check, but we
+ // don't have two separate marshalling code paths
+ // if (0 != parcel.readInt()) {
+ // v.readFromParcel(parcel)
+ // }
+ IfStatement* ifpart = new IfStatement();
+ ifpart->expression = new Comparison(new LiteralExpression("0"), "!=",
+ new MethodCall(parcel, "readInt"));
+ ifpart->statements->Add(new MethodCall(v, "readFromParcel", 1, parcel));
+ addTo->Add(ifpart);
+}
+
+UserDataArrayType::UserDataArrayType(const JavaTypeNamespace* types,
+ const string& package, const string& name,
+ bool builtIn, bool canWriteToParcel,
+ const string& declFile, int declLine)
+ : Type(types, package, name,
+ builtIn ? ValidatableType::KIND_BUILT_IN
+ : ValidatableType::KIND_PARCELABLE,
+ canWriteToParcel, true, declFile, declLine) {}
+
+string UserDataArrayType::CreatorName() const {
+ return JavaType() + ".CREATOR";
+}
+
+void UserDataArrayType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ addTo->Add(new MethodCall(parcel, "writeTypedArray", 2, v,
+ BuildWriteToParcelFlags(flags)));
+}
+
+void UserDataArrayType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ string creator = v->type->JavaType() + ".CREATOR";
+ addTo->Add(new Assignment(v, new MethodCall(parcel, "createTypedArray", 1,
+ new LiteralExpression(creator))));
+}
+
+void UserDataArrayType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ string creator = v->type->JavaType() + ".CREATOR";
+ addTo->Add(new MethodCall(parcel, "readTypedArray", 2, v,
+ new LiteralExpression(creator)));
+}
+
+// ================================================================
+
+InterfaceType::InterfaceType(const JavaTypeNamespace* types,
+ const string& package, const string& name,
+ bool builtIn, bool oneway, const string& declFile,
+ int declLine, const Type* stub, const Type* proxy)
+ : Type(types, package, name, builtIn ? ValidatableType::KIND_BUILT_IN
+ : ValidatableType::KIND_INTERFACE,
+ true, false, declFile, declLine),
+ m_oneway(oneway),
+ stub_(stub),
+ proxy_(proxy) {}
+
+bool InterfaceType::OneWay() const { return m_oneway; }
+
+void InterfaceType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ // parcel.writeStrongBinder(v != null ? v.asBinder() : null);
+ addTo->Add(
+ new MethodCall(parcel, "writeStrongBinder", 1,
+ new Ternary(new Comparison(v, "!=", NULL_VALUE),
+ new MethodCall(v, "asBinder"), NULL_VALUE)));
+}
+
+void InterfaceType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ // v = Interface.asInterface(parcel.readStrongBinder());
+ addTo->Add(new Assignment(
+ v, new MethodCall(stub_, "asInterface", 1,
+ new MethodCall(parcel, "readStrongBinder"))));
+}
+
+// ================================================================
+
+GenericListType::GenericListType(const JavaTypeNamespace* types,
+ const Type* contained_type)
+ : Type(types, "java.util", "List<" + contained_type->CanonicalName() + ">",
+ ValidatableType::KIND_BUILT_IN, true, true),
+ m_contained_type(contained_type),
+ m_creator(contained_type->CreatorName()) {}
+
+string GenericListType::CreatorName() const {
+ return "android.os.Parcel.arrayListCreator";
+}
+
+string GenericListType::InstantiableName() const {
+ return "java.util.ArrayList<" + m_contained_type->JavaType() + ">";
+}
+
+void GenericListType::WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const {
+ if (m_creator == m_types->StringType()->CreatorName()) {
+ addTo->Add(new MethodCall(parcel, "writeStringList", 1, v));
+ } else if (m_creator == m_types->IBinderType()->CreatorName()) {
+ addTo->Add(new MethodCall(parcel, "writeBinderList", 1, v));
+ } else {
+ // parcel.writeTypedListXX(arg);
+ addTo->Add(new MethodCall(parcel, "writeTypedList", 1, v));
+ }
+}
+
+void GenericListType::CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ if (m_creator == m_types->StringType()->CreatorName()) {
+ addTo->Add(
+ new Assignment(v, new MethodCall(parcel, "createStringArrayList", 0)));
+ } else if (m_creator == m_types->IBinderType()->CreatorName()) {
+ addTo->Add(
+ new Assignment(v, new MethodCall(parcel, "createBinderArrayList", 0)));
+ } else {
+ // v = _data.readTypedArrayList(XXX.creator);
+ addTo->Add(
+ new Assignment(v, new MethodCall(parcel, "createTypedArrayList", 1,
+ new LiteralExpression(m_creator))));
+ }
+}
+
+void GenericListType::ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable**) const {
+ if (m_creator == m_types->StringType()->CreatorName()) {
+ addTo->Add(new MethodCall(parcel, "readStringList", 1, v));
+ } else if (m_creator == m_types->IBinderType()->CreatorName()) {
+ addTo->Add(new MethodCall(parcel, "readBinderList", 1, v));
+ } else {
+ // v = _data.readTypedList(v, XXX.creator);
+ addTo->Add(new MethodCall(parcel, "readTypedList", 2, v,
+ new LiteralExpression(m_creator)));
+ }
+}
+
+// ================================================================
+
+ClassLoaderType::ClassLoaderType(const JavaTypeNamespace* types)
+ : Type(types, "java.lang", "ClassLoader", ValidatableType::KIND_BUILT_IN,
+ false, false) {}
+
+// ================================================================
+
+void JavaTypeNamespace::Init() {
+ Add(new BasicType(this, "void", "XXX", "XXX", "XXX", "XXX", "XXX"));
+
+ m_bool_type = new BooleanType(this);
+ Add(m_bool_type);
+
+ Add(new BasicType(this, "byte", "writeByte", "readByte", "writeByteArray",
+ "createByteArray", "readByteArray"));
+
+ Add(new CharType(this));
+
+ m_int_type = new BasicType(this, "int", "writeInt", "readInt",
+ "writeIntArray", "createIntArray", "readIntArray");
+ Add(m_int_type);
+
+ Add(new BasicType(this, "long", "writeLong", "readLong", "writeLongArray",
+ "createLongArray", "readLongArray"));
+
+ Add(new BasicType(this, "float", "writeFloat", "readFloat", "writeFloatArray",
+ "createFloatArray", "readFloatArray"));
+
+ Add(new BasicType(this, "double", "writeDouble", "readDouble",
+ "writeDoubleArray", "createDoubleArray",
+ "readDoubleArray"));
+
+ m_string_type = new class StringType(this, "java.lang", "String");
+ Add(m_string_type);
+ Add(new class StringType(this, ::android::aidl::kAidlReservedTypePackage,
+ ::android::aidl::kUtf8InCppStringClass));
+
+ Add(new Type(this, "java.lang", "Object", ValidatableType::KIND_BUILT_IN,
+ false, false));
+
+ Add(new FileDescriptorType(this));
+
+ Add(new CharSequenceType(this));
+
+ Add(new MapType(this));
+
+ Add(new ListType(this));
+
+ m_text_utils_type =
+ new Type(this, "android.text", "TextUtils",
+ ValidatableType::KIND_BUILT_IN, false, false);
+ Add(m_text_utils_type);
+
+ m_remote_exception_type = new class RemoteExceptionType(this);
+ Add(m_remote_exception_type);
+
+ m_runtime_exception_type = new class RuntimeExceptionType(this);
+ Add(m_runtime_exception_type);
+
+ m_ibinder_type = new class IBinderType(this);
+ Add(m_ibinder_type);
+
+ m_iinterface_type = new class IInterfaceType(this);
+ Add(m_iinterface_type);
+
+ m_binder_native_type = new class BinderType(this);
+ Add(m_binder_native_type);
+
+ m_binder_proxy_type = new class BinderProxyType(this);
+ Add(m_binder_proxy_type);
+
+ m_parcel_type = new class ParcelType(this);
+ Add(m_parcel_type);
+
+ m_parcelable_interface_type = new class ParcelableInterfaceType(this);
+ Add(m_parcelable_interface_type);
+
+ m_context_type = new class Type(this, "android.content", "Context",
+ ValidatableType::KIND_BUILT_IN, false, false);
+ Add(m_context_type);
+
+ m_classloader_type = new class ClassLoaderType(this);
+ Add(m_classloader_type);
+
+ NULL_VALUE = new LiteralExpression("null");
+ THIS_VALUE = new LiteralExpression("this");
+ SUPER_VALUE = new LiteralExpression("super");
+ TRUE_VALUE = new LiteralExpression("true");
+ FALSE_VALUE = new LiteralExpression("false");
+}
+
+bool JavaTypeNamespace::AddParcelableType(const AidlParcelable& p,
+ const std::string& filename) {
+ Type* type =
+ new UserDataType(this, p.GetPackage(), p.GetName(), false,
+ true, filename, p.GetLine());
+ return Add(type);
+}
+
+bool JavaTypeNamespace::AddBinderType(const AidlInterface& b,
+ const std::string& filename) {
+ // for interfaces, add the stub, proxy, and interface types.
+ Type* stub = new Type(this, b.GetPackage(),
+ b.GetName() + ".Stub", ValidatableType::KIND_GENERATED,
+ false, false, filename, b.GetLine());
+ Type* proxy = new Type(this, b.GetPackage(),
+ b.GetName() + ".Stub.Proxy",
+ ValidatableType::KIND_GENERATED,
+ false, false, filename, b.GetLine());
+ Type* type =
+ new InterfaceType(this, b.GetPackage(), b.GetName(), false,
+ b.IsOneway(), filename, b.GetLine(), stub, proxy);
+
+ bool success = true;
+ success &= Add(type);
+ success &= Add(stub);
+ success &= Add(proxy);
+ return success;
+}
+
+bool JavaTypeNamespace::AddListType(const std::string& contained_type_name) {
+ const Type* contained_type = FindTypeByCanonicalName(contained_type_name);
+ if (!contained_type) {
+ return false;
+ }
+ Add(new GenericListType(this, contained_type));
+ return true;
+}
+
+bool JavaTypeNamespace::AddMapType(const string& key_type_name,
+ const string& value_type_name) {
+ LOG(ERROR) << "Don't know how to create a Map<K,V> container.";
+ return false;
+}
+
+} // namespace java
+} // namespace aidl
+} // namespace android
diff --git a/aidl/type_java.h b/aidl/type_java.h
new file mode 100644
index 0000000..6d35f2a
--- /dev/null
+++ b/aidl/type_java.h
@@ -0,0 +1,498 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_TYPE_JAVA_H_
+#define AIDL_TYPE_JAVA_H_
+
+#include <string>
+#include <vector>
+
+#include "ast_java.h"
+#include "type_namespace.h"
+
+namespace android {
+namespace aidl {
+namespace java {
+
+class JavaTypeNamespace;
+
+class Type : public ValidatableType {
+ public:
+ // WriteToParcel flags
+ enum { PARCELABLE_WRITE_RETURN_VALUE = 0x0001 };
+
+ Type(const JavaTypeNamespace* types, const std::string& name, int kind,
+ bool canWriteToParcel, bool canBeOut);
+ Type(const JavaTypeNamespace* types, const std::string& package,
+ const std::string& name, int kind, bool canWriteToParcel, bool canBeOut,
+ const std::string& declFile = "", int declLine = -1);
+ virtual ~Type() = default;
+
+ bool CanBeOutParameter() const override { return m_canBeOut; }
+ bool CanWriteToParcel() const override { return m_canWriteToParcel; }
+
+ const ValidatableType* ArrayType() const override { return m_array_type.get(); }
+ const ValidatableType* NullableType() const override { return nullptr; }
+
+ virtual std::string JavaType() const { return m_javaType; }
+ virtual std::string CreatorName() const;
+ virtual std::string InstantiableName() const;
+
+ virtual void WriteToParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, int flags) const;
+ virtual void CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const;
+ virtual void ReadFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const;
+
+ protected:
+ Expression* BuildWriteToParcelFlags(int flags) const;
+
+ const JavaTypeNamespace* m_types;
+
+ std::unique_ptr<Type> m_array_type;
+
+ private:
+ Type();
+ Type(const Type&);
+
+ std::string m_javaType;
+ std::string m_declFile;
+ bool m_canWriteToParcel;
+ bool m_canBeOut;
+};
+
+class BasicArrayType : public Type {
+ public:
+ BasicArrayType(const JavaTypeNamespace* types, const std::string& name,
+ const std::string& writeArrayParcel,
+ const std::string& createArrayParcel,
+ const std::string& readArrayParcel);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ void ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+
+ private:
+ std::string m_writeArrayParcel;
+ std::string m_createArrayParcel;
+ std::string m_readArrayParcel;
+};
+
+class BasicType : public Type {
+ public:
+ BasicType(const JavaTypeNamespace* types, const std::string& name,
+ const std::string& marshallParcel,
+ const std::string& unmarshallParcel,
+ const std::string& writeArrayParcel,
+ const std::string& createArrayParcel,
+ const std::string& readArrayParcel);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+
+ private:
+ std::string m_marshallParcel;
+ std::string m_unmarshallParcel;
+};
+
+class FileDescriptorArrayType : public Type {
+ public:
+ explicit FileDescriptorArrayType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const override;
+ void ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class FileDescriptorType : public Type {
+ public:
+ explicit FileDescriptorType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+};
+
+class BooleanArrayType : public Type {
+ public:
+ explicit BooleanArrayType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const override;
+ void ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class BooleanType : public Type {
+ public:
+ explicit BooleanType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+};
+
+class CharArrayType : public Type {
+ public:
+ explicit CharArrayType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const override;
+ void ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class CharType : public Type {
+ public:
+ explicit CharType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+};
+
+class StringArrayType : public Type {
+ public:
+ explicit StringArrayType(const JavaTypeNamespace* types);
+
+ std::string CreatorName() const override;
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const override;
+ void ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class StringType : public Type {
+ public:
+ StringType(const JavaTypeNamespace* types, const std::string& package,
+ const std::string& class_name);
+
+ std::string JavaType() const override { return "java.lang.String"; }
+ std::string CreatorName() const override;
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class CharSequenceType : public Type {
+ public:
+ explicit CharSequenceType(const JavaTypeNamespace* types);
+
+ std::string CreatorName() const override;
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+};
+
+class RemoteExceptionType : public Type {
+ public:
+ explicit RemoteExceptionType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+};
+
+class RuntimeExceptionType : public Type {
+ public:
+ explicit RuntimeExceptionType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+};
+
+class IBinderArrayType : public Type {
+ public:
+ explicit IBinderArrayType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const override;
+ void ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class IBinderType : public Type {
+ public:
+ explicit IBinderType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class IInterfaceType : public Type {
+ public:
+ explicit IInterfaceType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+};
+
+class BinderType : public Type {
+ public:
+ explicit BinderType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+};
+
+class BinderProxyType : public Type {
+ public:
+ explicit BinderProxyType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+};
+
+class ParcelType : public Type {
+ public:
+ explicit ParcelType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class ParcelableInterfaceType : public Type {
+ public:
+ explicit ParcelableInterfaceType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+};
+
+class MapType : public Type {
+ public:
+ explicit MapType(const JavaTypeNamespace* types);
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ void ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class ListType : public Type {
+ public:
+ explicit ListType(const JavaTypeNamespace* types);
+
+ std::string InstantiableName() const override;
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ void ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class UserDataArrayType : public Type {
+ public:
+ UserDataArrayType(const JavaTypeNamespace* types, const std::string& package,
+ const std::string& name, bool builtIn,
+ bool canWriteToParcel, const std::string& declFile = "",
+ int declLine = -1);
+
+ std::string CreatorName() const override;
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v,
+ Variable* parcel, Variable** cl) const override;
+ void ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class UserDataType : public Type {
+ public:
+ UserDataType(const JavaTypeNamespace* types, const std::string& package,
+ const std::string& name, bool builtIn, bool canWriteToParcel,
+ const std::string& declFile = "", int declLine = -1);
+
+ std::string CreatorName() const override;
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ void ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+};
+
+class InterfaceType : public Type {
+ public:
+ InterfaceType(const JavaTypeNamespace* types, const std::string& package,
+ const std::string& name, bool builtIn, bool oneway,
+ const std::string& declFile, int declLine, const Type* stub,
+ const Type* proxy);
+
+ bool OneWay() const;
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+ const Type* GetStub() const { return stub_; }
+ const Type* GetProxy() const { return proxy_; }
+
+ private:
+ bool m_oneway;
+ const Type* stub_;
+ const Type* proxy_;
+};
+
+class ClassLoaderType : public Type {
+ public:
+ explicit ClassLoaderType(const JavaTypeNamespace* types);
+};
+
+class GenericListType : public Type {
+ public:
+ GenericListType(const JavaTypeNamespace* types, const Type* arg);
+
+ std::string CreatorName() const override;
+ std::string InstantiableName() const override;
+ std::string JavaType() const override {
+ return "java.util.List<" + m_contained_type->JavaType() + ">";
+ }
+
+ void WriteToParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ int flags) const override;
+ void CreateFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ void ReadFromParcel(StatementBlock* addTo, Variable* v, Variable* parcel,
+ Variable** cl) const override;
+ const ValidatableType* NullableType() const override { return this; }
+
+ private:
+ const Type* m_contained_type;
+ const std::string m_creator;
+};
+
+class JavaTypeNamespace : public LanguageTypeNamespace<Type> {
+ public:
+ JavaTypeNamespace() = default;
+ virtual ~JavaTypeNamespace() = default;
+
+ void Init() override;
+ bool AddParcelableType(const AidlParcelable& p,
+ const std::string& filename) override;
+ bool AddBinderType(const AidlInterface& b,
+ const std::string& filename) override;
+ bool AddListType(const std::string& contained_type_name) override;
+ bool AddMapType(const std::string& key_type_name,
+ const std::string& value_type_name) override;
+
+ const Type* BoolType() const { return m_bool_type; }
+ const Type* IntType() const { return m_int_type; }
+ const Type* StringType() const { return m_string_type; }
+ const Type* TextUtilsType() const { return m_text_utils_type; }
+ const Type* RemoteExceptionType() const { return m_remote_exception_type; }
+ const Type* RuntimeExceptionType() const { return m_runtime_exception_type; }
+ const Type* IBinderType() const { return m_ibinder_type; }
+ const Type* IInterfaceType() const { return m_iinterface_type; }
+ const Type* BinderNativeType() const { return m_binder_native_type; }
+ const Type* BinderProxyType() const { return m_binder_proxy_type; }
+ const Type* ParcelType() const { return m_parcel_type; }
+ const Type* ParcelableInterfaceType() const {
+ return m_parcelable_interface_type;
+ }
+ const Type* ContextType() const { return m_context_type; }
+ const Type* ClassLoaderType() const { return m_classloader_type; }
+
+ private:
+ const Type* m_bool_type{nullptr};
+ const Type* m_int_type{nullptr};
+ const Type* m_string_type{nullptr};
+ const Type* m_text_utils_type{nullptr};
+ const Type* m_remote_exception_type{nullptr};
+ const Type* m_runtime_exception_type{nullptr};
+ const Type* m_ibinder_type{nullptr};
+ const Type* m_iinterface_type{nullptr};
+ const Type* m_binder_native_type{nullptr};
+ const Type* m_binder_proxy_type{nullptr};
+ const Type* m_parcel_type{nullptr};
+ const Type* m_parcelable_interface_type{nullptr};
+ const Type* m_context_type{nullptr};
+ const Type* m_classloader_type{nullptr};
+
+ DISALLOW_COPY_AND_ASSIGN(JavaTypeNamespace);
+};
+
+extern Expression* NULL_VALUE;
+extern Expression* THIS_VALUE;
+extern Expression* SUPER_VALUE;
+extern Expression* TRUE_VALUE;
+extern Expression* FALSE_VALUE;
+
+} // namespace java
+} // namespace aidl
+} // namespace android
+
+#endif // AIDL_TYPE_JAVA_H_
diff --git a/aidl/type_java_unittest.cpp b/aidl/type_java_unittest.cpp
new file mode 100644
index 0000000..a9df134
--- /dev/null
+++ b/aidl/type_java_unittest.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 <memory>
+
+#include <gtest/gtest.h>
+
+#include "aidl_language.h"
+#include "type_java.h"
+
+using std::unique_ptr;
+
+namespace android {
+namespace aidl {
+namespace java {
+
+class JavaTypeNamespaceTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ types_.Init();
+ }
+ JavaTypeNamespace types_;
+};
+
+TEST_F(JavaTypeNamespaceTest, HasSomeBasicTypes) {
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("void"));
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("int"));
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("java.lang.String"));
+}
+
+TEST_F(JavaTypeNamespaceTest, ContainerTypeCreation) {
+ // We start with no knowledge of parcelables or lists of them.
+ EXPECT_FALSE(types_.HasTypeByCanonicalName("Foo"));
+ EXPECT_FALSE(types_.HasTypeByCanonicalName("java.util.List<a.goog.Foo>"));
+ unique_ptr<AidlParcelable> parcelable(
+ new AidlParcelable(new AidlQualifiedName("Foo", ""), 0, {"a", "goog"}));
+ // Add the parcelable type we care about.
+ EXPECT_TRUE(types_.AddParcelableType(*parcelable.get(), __FILE__));
+ // Now we can find the parcelable type, but not the List of them.
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("a.goog.Foo"));
+ EXPECT_FALSE(types_.HasTypeByCanonicalName("java.util.List<a.goog.Foo>"));
+ // But after we add the list explicitly...
+ AidlType container_type("List<Foo>", 0, "", false /* not array */);
+ EXPECT_TRUE(types_.MaybeAddContainerType(container_type));
+ // This should work.
+ EXPECT_TRUE(types_.HasTypeByCanonicalName("java.util.List<a.goog.Foo>"));
+}
+
+} // namespace java
+} // namespace android
+} // namespace aidl
diff --git a/aidl/type_namespace.cpp b/aidl/type_namespace.cpp
new file mode 100644
index 0000000..d3f4aaf
--- /dev/null
+++ b/aidl/type_namespace.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 "type_namespace.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "aidl_language.h"
+#include "logging.h"
+
+using android::base::StringPrintf;
+using android::base::Split;
+using android::base::Trim;
+using std::string;
+using std::vector;
+
+namespace android {
+namespace aidl {
+
+// Since packages cannot contain '-' normally, we cannot be asked
+// to create a type that conflicts with these strings.
+const char kAidlReservedTypePackage[] = "aidl-internal";
+const char kUtf8StringClass[] = "Utf8String";
+const char kUtf8InCppStringClass[] = "Utf8InCppString";
+
+// These *must* match the package and class names above.
+const char kUtf8StringCanonicalName[] = "aidl-internal.Utf8String";
+const char kUtf8InCppStringCanonicalName[] = "aidl-internal.Utf8InCppString";
+
+const char kStringCanonicalName[] = "java.lang.String";
+
+const char kUtf8Annotation[] = "@utf8";
+const char kUtf8InCppAnnotation[] = "@utfInCpp";
+
+namespace {
+
+bool is_java_keyword(const char* str) {
+ static const std::vector<std::string> kJavaKeywords{
+ "abstract", "assert", "boolean", "break", "byte",
+ "case", "catch", "char", "class", "const",
+ "continue", "default", "do", "double", "else",
+ "enum", "extends", "final", "finally", "float",
+ "for", "goto", "if", "implements", "import",
+ "instanceof", "int", "interface", "long", "native",
+ "new", "package", "private", "protected", "public",
+ "return", "short", "static", "strictfp", "super",
+ "switch", "synchronized", "this", "throw", "throws",
+ "transient", "try", "void", "volatile", "while",
+ "true", "false", "null",
+ };
+ return std::find(kJavaKeywords.begin(), kJavaKeywords.end(), str) !=
+ kJavaKeywords.end();
+}
+
+} // namespace
+
+ValidatableType::ValidatableType(
+ int kind, const string& package, const string& type_name,
+ const string& decl_file, int decl_line)
+ : kind_(kind),
+ type_name_(type_name),
+ canonical_name_((package.empty()) ? type_name
+ : package + "." + type_name),
+ origin_file_(decl_file),
+ origin_line_(decl_line) {}
+
+string ValidatableType::HumanReadableKind() const {
+ switch (Kind()) {
+ case ValidatableType::KIND_BUILT_IN:
+ return "a built in";
+ case ValidatableType::KIND_PARCELABLE:
+ return "a parcelable";
+ case ValidatableType::KIND_INTERFACE:
+ return "an interface";
+ case ValidatableType::KIND_GENERATED:
+ return "a generated";
+ }
+ return "unknown";
+}
+
+bool TypeNamespace::IsValidPackage(const string& /* package */) const {
+ return true;
+}
+
+const ValidatableType* TypeNamespace::GetReturnType(
+ const AidlType& raw_type, const string& filename,
+ const AidlInterface& interface) const {
+ string error_msg;
+ const ValidatableType* return_type = GetValidatableType(raw_type, &error_msg,
+ interface);
+ if (return_type == nullptr) {
+ LOG(ERROR) << StringPrintf("In file %s line %d return type %s:\n ",
+ filename.c_str(), raw_type.GetLine(),
+ raw_type.ToString().c_str())
+ << error_msg;
+ return nullptr;
+ }
+
+ return return_type;
+}
+
+const ValidatableType* TypeNamespace::GetArgType(
+ const AidlArgument& a, int arg_index, const string& filename,
+ const AidlInterface& interface) const {
+ string error_prefix = StringPrintf(
+ "In file %s line %d parameter %s (argument %d):\n ",
+ filename.c_str(), a.GetLine(), a.GetName().c_str(), arg_index);
+
+ // check the arg type
+ string error_msg;
+ const ValidatableType* t = GetValidatableType(a.GetType(), &error_msg,
+ interface);
+ if (t == nullptr) {
+ LOG(ERROR) << error_prefix << error_msg;
+ return nullptr;
+ }
+
+ if (!a.DirectionWasSpecified() && t->CanBeOutParameter()) {
+ LOG(ERROR) << error_prefix << StringPrintf(
+ "'%s' can be an out type, so you must declare it as in,"
+ " out or inout.",
+ a.GetType().ToString().c_str());
+ return nullptr;
+ }
+
+ if (a.GetDirection() != AidlArgument::IN_DIR &&
+ !t->CanBeOutParameter()) {
+ LOG(ERROR) << error_prefix << StringPrintf(
+ "'%s' can only be an in parameter.",
+ a.ToString().c_str());
+ return nullptr;
+ }
+
+ // check that the name doesn't match a keyword
+ if (is_java_keyword(a.GetName().c_str())) {
+ LOG(ERROR) << error_prefix << "Argument name is a Java or aidl keyword";
+ return nullptr;
+ }
+
+ // Reserve a namespace for internal use
+ if (a.GetName().substr(0, 5) == "_aidl") {
+ LOG(ERROR) << error_prefix << "Argument name cannot begin with '_aidl'";
+ return nullptr;
+ }
+
+ return t;
+}
+
+} // namespace aidl
+} // namespace android
diff --git a/aidl/type_namespace.h b/aidl/type_namespace.h
new file mode 100644
index 0000000..7defd24
--- /dev/null
+++ b/aidl/type_namespace.h
@@ -0,0 +1,496 @@
+/*
+ * 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.
+ */
+
+#ifndef AIDL_TYPE_NAMESPACE_H_
+#define AIDL_TYPE_NAMESPACE_H_
+
+#include <memory>
+#include <string>
+
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "aidl_language.h"
+#include "logging.h"
+
+namespace android {
+namespace aidl {
+
+// Special reserved type names.
+extern const char kAidlReservedTypePackage[];
+extern const char kUtf8StringClass[]; // UTF8 wire format string
+extern const char kUtf8InCppStringClass[]; // UTF16 wire format, UTF8 in C++
+
+// Helpful aliases defined to be <kAidlReservedTypePackage>.<class name>
+extern const char kUtf8StringCanonicalName[];
+extern const char kUtf8InCppStringCanonicalName[];
+
+// We sometimes special case this class.
+extern const char kStringCanonicalName[];
+
+// Note that these aren't the strings recognized by the parser, we just keep
+// here for the sake of logging a common string constant.
+extern const char kUtf8Annotation[];
+extern const char kUtf8InCppAnnotation[];
+
+class ValidatableType {
+ public:
+ enum {
+ KIND_BUILT_IN,
+ KIND_PARCELABLE,
+ KIND_INTERFACE,
+ KIND_GENERATED,
+ };
+
+ ValidatableType(int kind,
+ const std::string& package, const std::string& type_name,
+ const std::string& decl_file, int decl_line);
+ virtual ~ValidatableType() = default;
+
+ virtual bool CanBeArray() const { return ArrayType() != nullptr; }
+ virtual bool CanBeOutParameter() const = 0;
+ virtual bool CanWriteToParcel() const = 0;
+
+ virtual const ValidatableType* ArrayType() const = 0;
+ virtual const ValidatableType* NullableType() const = 0;
+
+ // ShortName() is the class name without a package.
+ std::string ShortName() const { return type_name_; }
+ // CanonicalName() returns the canonical AIDL type, with packages.
+ std::string CanonicalName() const { return canonical_name_; }
+
+ int Kind() const { return kind_; }
+ std::string HumanReadableKind() const;
+ std::string DeclFile() const { return origin_file_; }
+ int DeclLine() const { return origin_line_; }
+
+ private:
+ const int kind_;
+ const std::string type_name_;
+ const std::string canonical_name_;
+ const std::string origin_file_;
+ const int origin_line_;
+
+ DISALLOW_COPY_AND_ASSIGN(ValidatableType);
+};
+
+class TypeNamespace {
+ public:
+ // Load the TypeNamespace with built in types. Don't do work in the
+ // constructor because many of the useful methods are virtual.
+ virtual void Init() = 0;
+
+ // Load this TypeNamespace with user defined types.
+ virtual bool AddParcelableType(const AidlParcelable& p,
+ const std::string& filename) = 0;
+ virtual bool AddBinderType(const AidlInterface& b,
+ const std::string& filename) = 0;
+ // Add a container type to this namespace. Returns false only
+ // on error. Silently discards requests to add non-container types.
+ virtual bool MaybeAddContainerType(const AidlType& aidl_type) = 0;
+
+ // Returns true iff this has a type for |import|.
+ virtual bool HasImportType(const AidlImport& import) const = 0;
+
+ // Returns true iff |package| is a valid package name.
+ virtual bool IsValidPackage(const std::string& package) const;
+
+ // Returns a pointer to a type corresponding to |raw_type| or nullptr
+ // if this is an invalid return type.
+ virtual const ValidatableType* GetReturnType(
+ const AidlType& raw_type,
+ const std::string& filename,
+ const AidlInterface& interface) const;
+
+ // Returns a pointer to a type corresponding to |a| or nullptr if |a|
+ // has an invalid argument type.
+ virtual const ValidatableType* GetArgType(
+ const AidlArgument& a,
+ int arg_index,
+ const std::string& filename,
+ const AidlInterface& interface) const;
+
+ // Returns a pointer to a type corresponding to |interface|.
+ virtual const ValidatableType* GetInterfaceType(
+ const AidlInterface& interface) const = 0;
+
+ protected:
+ TypeNamespace() = default;
+ virtual ~TypeNamespace() = default;
+
+ virtual const ValidatableType* GetValidatableType(
+ const AidlType& type, std::string* error_msg,
+ const AidlInterface& interface) const = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TypeNamespace);
+};
+
+template<typename T>
+class LanguageTypeNamespace : public TypeNamespace {
+ public:
+ LanguageTypeNamespace() = default;
+ virtual ~LanguageTypeNamespace() = default;
+
+ // Get a pointer to an existing type. Searches first by fully-qualified
+ // name, and then class name (dropping package qualifiers).
+ const T* Find(const AidlType& aidl_type) const;
+
+ // Find a type by its |name|. If |name| refers to a container type (e.g.
+ // List<String>) you must turn it into a canonical name first (e.g.
+ // java.util.List<java.lang.String>).
+ const T* FindTypeByCanonicalName(const std::string& name) const;
+ bool HasTypeByCanonicalName(const std::string& type_name) const {
+ return FindTypeByCanonicalName(type_name) != nullptr;
+ }
+ bool HasImportType(const AidlImport& import) const override {
+ return HasTypeByCanonicalName(import.GetNeededClass());
+ }
+ const ValidatableType* GetInterfaceType(
+ const AidlInterface& interface) const override {
+ return FindTypeByCanonicalName(interface.GetCanonicalName());
+ }
+
+ bool MaybeAddContainerType(const AidlType& aidl_type) override;
+ // We dynamically create container types as we discover them in the parse
+ // tree. Returns false if the contained types cannot be canonicalized.
+ virtual bool AddListType(const std::string& contained_type_name) = 0;
+ virtual bool AddMapType(const std::string& key_type_name,
+ const std::string& value_type_name) = 0;
+
+ protected:
+ bool Add(const T* type);
+
+ private:
+ // Returns true iff the name can be canonicalized to a container type.
+ virtual bool CanonicalizeContainerType(
+ const AidlType& aidl_type,
+ std::vector<std::string>* container_class,
+ std::vector<std::string>* contained_type_names) const;
+
+ // Returns true if this is a container type, rather than a normal type.
+ bool IsContainerType(const std::string& type_name) const;
+
+ const ValidatableType* GetValidatableType(
+ const AidlType& type, std::string* error_msg,
+ const AidlInterface& interface) const override;
+
+ std::vector<std::unique_ptr<const T>> types_;
+
+ DISALLOW_COPY_AND_ASSIGN(LanguageTypeNamespace);
+}; // class LanguageTypeNamespace
+
+template<typename T>
+bool LanguageTypeNamespace<T>::Add(const T* type) {
+ const T* existing = FindTypeByCanonicalName(type->CanonicalName());
+ if (!existing) {
+ types_.emplace_back(type);
+ return true;
+ }
+
+ if (existing->Kind() == ValidatableType::KIND_BUILT_IN) {
+ LOG(ERROR) << type->DeclFile() << ":" << type->DeclLine()
+ << " attempt to redefine built in class "
+ << type->CanonicalName();
+ return false;
+ }
+
+ if (type->Kind() != existing->Kind()) {
+ LOG(ERROR) << type->DeclFile() << ":" << type->DeclLine()
+ << " attempt to redefine " << type->CanonicalName()
+ << " as " << type->HumanReadableKind();
+ LOG(ERROR) << existing->DeclFile() << ":" << existing->DeclLine()
+ << " previously defined here as "
+ << existing->HumanReadableKind();
+ return false;
+ }
+
+ return true;
+}
+
+template<typename T>
+const T* LanguageTypeNamespace<T>::Find(const AidlType& aidl_type) const {
+ using std::string;
+ using std::vector;
+ using android::base::Join;
+ using android::base::Trim;
+
+ string name = Trim(aidl_type.GetName());
+ if (IsContainerType(name)) {
+ vector<string> container_class;
+ vector<string> contained_type_names;
+ if (!CanonicalizeContainerType(aidl_type, &container_class,
+ &contained_type_names)) {
+ return nullptr;
+ }
+ name = Join(container_class, '.') +
+ "<" + Join(contained_type_names, ',') + ">";
+ }
+ // Here, we know that we have the canonical name for this container.
+ return FindTypeByCanonicalName(name);
+}
+
+template<typename T>
+const T* LanguageTypeNamespace<T>::FindTypeByCanonicalName(
+ const std::string& raw_name) const {
+ using android::base::Trim;
+
+ std::string name = Trim(raw_name);
+ const T* ret = nullptr;
+ for (const auto& type : types_) {
+ // Always prefer a exact match if possible.
+ // This works for primitives and class names qualified with a package.
+ if (type->CanonicalName() == name) {
+ ret = type.get();
+ break;
+ }
+ // We allow authors to drop packages when refering to a class name.
+ if (type->ShortName() == name) {
+ ret = type.get();
+ }
+ }
+
+ return ret;
+}
+
+template<typename T>
+bool LanguageTypeNamespace<T>::MaybeAddContainerType(
+ const AidlType& aidl_type) {
+ using android::base::Join;
+
+ const std::string& type_name = aidl_type.GetName();
+ if (!IsContainerType(type_name)) {
+ return true;
+ }
+
+ std::vector<std::string> container_class;
+ std::vector<std::string> contained_type_names;
+ if (!CanonicalizeContainerType(aidl_type, &container_class,
+ &contained_type_names)) {
+ return false;
+ }
+
+ const std::string canonical_name = Join(container_class, ".") +
+ "<" + Join(contained_type_names, ",") + ">";
+ if (HasTypeByCanonicalName(canonical_name)) {
+ return true;
+ }
+
+
+ // We only support two types right now and this type is one of them.
+ switch (contained_type_names.size()) {
+ case 1:
+ return AddListType(contained_type_names[0]);
+ case 2:
+ return AddMapType(contained_type_names[0], contained_type_names[1]);
+ default:
+ break; // Should never get here, will FATAL below.
+ }
+
+ LOG(FATAL) << "aidl internal error";
+ return false;
+}
+
+template<typename T>
+bool LanguageTypeNamespace<T>::IsContainerType(
+ const std::string& type_name) const {
+ const size_t opening_brace = type_name.find('<');
+ const size_t closing_brace = type_name.find('>');
+ if (opening_brace != std::string::npos ||
+ closing_brace != std::string::npos) {
+ return true; // Neither < nor > appear in normal AIDL types.
+ }
+ return false;
+}
+
+template<typename T>
+bool LanguageTypeNamespace<T>::CanonicalizeContainerType(
+ const AidlType& aidl_type,
+ std::vector<std::string>* container_class,
+ std::vector<std::string>* contained_type_names) const {
+ using android::base::Trim;
+ using android::base::Split;
+
+ std::string name = Trim(aidl_type.GetName());
+ const size_t opening_brace = name.find('<');
+ const size_t closing_brace = name.find('>');
+ if (opening_brace == std::string::npos ||
+ closing_brace == std::string::npos) {
+ return false;
+ }
+
+ if (opening_brace != name.rfind('<') ||
+ closing_brace != name.rfind('>') ||
+ closing_brace != name.length() - 1) {
+ // Nested/invalid templates are forbidden.
+ LOG(ERROR) << "Invalid template type '" << name << "'";
+ return false;
+ }
+
+ std::string container = Trim(name.substr(0, opening_brace));
+ std::string remainder = name.substr(opening_brace + 1,
+ (closing_brace - opening_brace) - 1);
+ std::vector<std::string> args = Split(remainder, ",");
+ for (auto& type_name: args) {
+ // Here, we are relying on FindTypeByCanonicalName to do its best when
+ // given a non-canonical name for non-compound type (i.e. not another
+ // container).
+ const T* arg_type = FindTypeByCanonicalName(type_name);
+ if (!arg_type) {
+ return false;
+ }
+
+ // Now get the canonical names for these contained types, remapping them if
+ // necessary.
+ type_name = arg_type->CanonicalName();
+ if (aidl_type.IsUtf8() && type_name == "java.lang.String") {
+ type_name = kUtf8StringCanonicalName;
+ } else if (aidl_type.IsUtf8InCpp() && type_name == "java.lang.String") {
+ type_name = kUtf8InCppStringCanonicalName;
+ }
+ }
+
+ // Map the container name to its canonical form for supported containers.
+ if ((container == "List" || container == "java.util.List") &&
+ args.size() == 1) {
+ *container_class = {"java", "util", "List"};
+ *contained_type_names = args;
+ return true;
+ }
+ if ((container == "Map" || container == "java.util.Map") &&
+ args.size() == 2) {
+ *container_class = {"java", "util", "Map"};
+ *contained_type_names = args;
+ return true;
+ }
+
+ LOG(ERROR) << "Unknown find container with name " << container
+ << " and " << args.size() << "contained types.";
+ return false;
+}
+
+template<typename T>
+const ValidatableType* LanguageTypeNamespace<T>::GetValidatableType(
+ const AidlType& aidl_type, std::string* error_msg,
+ const AidlInterface& interface) const {
+ using android::base::StringPrintf;
+
+ const ValidatableType* type = Find(aidl_type);
+ if (type == nullptr) {
+ *error_msg = "unknown type";
+ return nullptr;
+ }
+
+ if (aidl_type.GetName() == "void") {
+ if (aidl_type.IsArray()) {
+ *error_msg = "void type cannot be an array";
+ return nullptr;
+ }
+ if (aidl_type.IsNullable() || aidl_type.IsUtf8() ||
+ aidl_type.IsUtf8InCpp()) {
+ *error_msg = "void type cannot be annotated";
+ return nullptr;
+ }
+ // We have no more special handling for void.
+ return type;
+ }
+
+ // No type may be annotated with both these annotations.
+ if (aidl_type.IsUtf8() && aidl_type.IsUtf8InCpp()) {
+ *error_msg = StringPrintf("Type cannot be marked as both %s and %s.",
+ kUtf8Annotation, kUtf8InCppAnnotation);
+ return nullptr;
+ }
+
+ bool utf8 = aidl_type.IsUtf8();
+ bool utf8InCpp = aidl_type.IsUtf8InCpp();
+
+ // Strings inside containers get remapped to appropriate utf8 versions when
+ // we convert the container name to its canonical form and the look up the
+ // type. However, for non-compound types (i.e. those not in a container) we
+ // must patch them up here.
+ if (IsContainerType(type->CanonicalName())) {
+ utf8 = false;
+ utf8InCpp = false;
+ } else if (aidl_type.GetName() == "String" ||
+ aidl_type.GetName() == "java.lang.String") {
+ utf8 = utf8 || interface.IsUtf8();
+ utf8InCpp = utf8InCpp || interface.IsUtf8InCpp();
+ } else if (utf8 || utf8InCpp) {
+ const char* annotation_literal =
+ (utf8) ? kUtf8Annotation : kUtf8InCppAnnotation;
+ *error_msg = StringPrintf("type '%s' may not be annotated as %s.",
+ aidl_type.GetName().c_str(),
+ annotation_literal);
+ return nullptr;
+ }
+
+ if (utf8) {
+ type = FindTypeByCanonicalName(kUtf8StringCanonicalName);
+ } else if (utf8InCpp) {
+ type = FindTypeByCanonicalName(kUtf8InCppStringCanonicalName);
+ }
+
+ // One of our UTF8 transforms made type null
+ if (type == nullptr) {
+ const char* annotation_literal =
+ (utf8) ? kUtf8Annotation : kUtf8InCppAnnotation;
+ *error_msg = StringPrintf(
+ "%s is unsupported when generating code for this language.",
+ annotation_literal);
+ return nullptr;
+ }
+
+ if (!type->CanWriteToParcel()) {
+ *error_msg = "type cannot be marshalled";
+ return nullptr;
+ }
+
+ if (aidl_type.IsArray()) {
+ type = type->ArrayType();
+ if (!type) {
+ *error_msg = StringPrintf("type '%s' cannot be an array",
+ aidl_type.GetName().c_str());
+ return nullptr;
+ }
+ }
+
+ if (interface.IsNullable()) {
+ const ValidatableType* nullableType = type->NullableType();
+
+ if (nullableType) {
+ return nullableType;
+ }
+ }
+
+ if (aidl_type.IsNullable()) {
+ type = type->NullableType();
+ if (!type) {
+ *error_msg = StringPrintf("type '%s%s' cannot be marked as possibly null",
+ aidl_type.GetName().c_str(),
+ (aidl_type.IsArray()) ? "[]" : "");
+ return nullptr;
+ }
+ }
+
+ return type;
+}
+
+} // namespace aidl
+} // namespace android
+
+#endif // AIDL_TYPE_NAMESPACE_H_