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_