/* * Copyright (C) 2018, 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_to_java.h" #include "aidl_language.h" #include "aidl_typenames.h" #include "logging.h" #include <android-base/strings.h> #include <cassert> #include <functional> #include <iostream> #include <map> #include <string> #include <vector> namespace android { namespace aidl { namespace java { using android::base::Join; using std::endl; using std::function; using std::map; using std::string; using std::vector; std::string ConstantValueDecorator(const AidlTypeSpecifier& /*type*/, const std::string& raw_value) { // no difference return raw_value; }; const string& JavaNameOf(const AidlTypeSpecifier& aidl) { CHECK(aidl.IsResolved()) << aidl.ToString(); // map from AIDL built-in type name to the corresponding Java type name static map<string, string> m = { {"void", "void"}, {"boolean", "boolean"}, {"byte", "byte"}, {"char", "char"}, {"int", "int"}, {"long", "long"}, {"float", "float"}, {"double", "double"}, {"String", "java.lang.String"}, {"List", "java.util.List"}, {"Map", "java.util.Map"}, {"IBinder", "android.os.IBinder"}, {"FileDescriptor", "java.io.FileDescriptor"}, {"CharSequence", "java.lang.CharSequence"}, {"ParcelFileDescriptor", "android.os.ParcelFileDescriptor"}, }; const string& aidl_name = aidl.GetName(); if (m.find(aidl_name) != m.end()) { CHECK(AidlTypenames::IsBuiltinTypename(aidl_name)); return m[aidl_name]; } else { // 'foo.bar.IFoo' in AIDL maps to 'foo.bar.IFoo' in Java return aidl_name; } } string JavaSignatureOf(const AidlTypeSpecifier& aidl) { string ret = JavaNameOf(aidl); if (aidl.IsGeneric()) { vector<string> arg_names; for (const auto& ta : aidl.GetTypeParameters()) { arg_names.emplace_back(JavaSignatureOf(*ta)); } ret += "<" + Join(arg_names, ",") + ">"; } if (aidl.IsArray()) { ret += "[]"; } return ret; } string DefaultJavaValueOf(const AidlTypeSpecifier& aidl) { static map<string, string> m = { {"boolean", "false"}, {"byte", "0"}, {"char", R"('\u0000')"}, {"int", "0"}, {"long", "0L"}, {"float", "0.0f"}, {"double", "0.0d"}, }; const string& name = aidl.GetName(); assert(name != "void"); if (!aidl.IsArray() && m.find(name) != m.end()) { CHECK(AidlTypenames::IsBuiltinTypename(name)); return m[name]; } else { return "null"; } } // These are supported by AIDL syntax, but are unsupported by the AIDL compiler static bool IsMarshallingUnsupportedFor(const AidlTypeSpecifier& aidl, const AidlTypenames& typenames) { const string name = aidl.GetName(); // List<T> is support only for String, Binder, ParcelFileDescriptor and Parcelable. if (name == "List" && aidl.IsGeneric()) { const string& contained_type = aidl.GetTypeParameters().at(0)->GetName(); if (AidlTypenames::IsBuiltinTypename(contained_type)) { if (contained_type != "String" && contained_type != "IBinder" && contained_type != "ParcelFileDescriptor") { return true; } } else { const AidlDefinedType* t = typenames.TryGetDefinedType(contained_type); if (t != nullptr && t->AsInterface() != nullptr) { return true; } } } // List[], Map[], CharSequence[] are not supported. if (AidlTypenames::IsBuiltinTypename(name) && aidl.IsArray()) { if (name == "List" || name == "Map" || name == "CharSequence") { return true; } } // T[] is not supported for interfaces const AidlDefinedType* t = typenames.TryGetDefinedType(name); if (aidl.IsArray() && t != nullptr && t->AsInterface() != nullptr) { return true; } return false; } static bool EnsureCodegenIsSupported(const CodeGeneratorContext& c) { if (IsMarshallingUnsupportedFor(c.type, c.typenames)) { AIDL_ERROR(c.type) << c.type.ToString() << "' is not yet supported."; return false; } return true; } static string GetFlagFor(const CodeGeneratorContext& c) { if (c.is_return_value) { return "android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE"; } else { return "0"; } } bool WriteToParcelFor(const CodeGeneratorContext& c) { if (!EnsureCodegenIsSupported(c)) { return false; } static map<string, function<void(const CodeGeneratorContext&)>> method_map{ {"boolean", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeInt(((" << c.var << ")?(1):(0)));\n"; }}, {"boolean[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeBooleanArray(" << c.var << ");\n"; }}, {"byte", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeByte(" << c.var << ");\n"; }}, {"byte[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeByteArray(" << c.var << ");\n"; }}, {"char", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeInt(((int)" << c.var << "));\n"; }}, {"char[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeCharArray(" << c.var << ");\n"; }}, {"int", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeInt(" << c.var << ");\n"; }}, {"int[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeIntArray(" << c.var << ");\n"; }}, {"long", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeLong(" << c.var << ");\n"; }}, {"long[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeLongArray(" << c.var << ");\n"; }}, {"float", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeFloat(" << c.var << ");\n"; }}, {"float[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeFloatArray(" << c.var << ");\n"; }}, {"double", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeDouble(" << c.var << ");\n"; }}, {"double[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeDoubleArray(" << c.var << ");\n"; }}, {"String", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeString(" << c.var << ");\n"; }}, {"String[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeStringArray(" << c.var << ");\n"; }}, {"List", [](const CodeGeneratorContext& c) { if (c.type.IsGeneric()) { const string& contained_type = c.type.GetTypeParameters().at(0)->GetName(); if (AidlTypenames::IsBuiltinTypename(contained_type)) { if (contained_type == "String") { c.writer << c.parcel << ".writeStringList(" << c.var << ");\n"; } else if (contained_type == "IBinder") { c.writer << c.parcel << ".writeBinderList(" << c.var << ");\n"; } } else { const AidlDefinedType* t = c.typenames.TryGetDefinedType(contained_type); CHECK(t != nullptr) << "Unknown type: " << contained_type << endl; if (t->AsParcelable() != nullptr) { c.writer << c.parcel << ".writeTypedList(" << c.var << ");\n"; } } } else { c.writer << c.parcel << ".writeList(" << c.var << ");\n"; } }}, {"Map", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeMap(" << c.var << ");\n"; }}, {"IBinder", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeStrongBinder(" << c.var << ");\n"; }}, {"IBinder[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeBinderArray(" << c.var << ");\n"; }}, {"FileDescriptor", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeRawFileDescriptor(" << c.var << ");\n"; }}, {"FileDescriptor[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeRawFileDescriptorArray(" << c.var << ");\n"; }}, {"ParcelFileDescriptor", [](const CodeGeneratorContext& c) { // This is same as writeTypedObject which was introduced with SDK 23. // Keeping below code so that the generated code is buildable with older SDK. c.writer << "if ((" << c.var << "!=null)) {\n"; c.writer.Indent(); c.writer << c.parcel << ".writeInt(1);\n"; c.writer << c.var << ".writeToParcel(" << c.parcel << ", " << GetFlagFor(c) << ");\n"; c.writer.Dedent(); c.writer << "}\n"; c.writer << "else {\n"; c.writer.Indent(); c.writer << c.parcel << ".writeInt(0);\n"; c.writer.Dedent(); c.writer << "}\n"; }}, {"ParcelFileDescriptor[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".writeTypedArray(" << c.var << ", " << GetFlagFor(c) << ");\n"; }}, {"CharSequence", [](const CodeGeneratorContext& c) { // TextUtils.writeToParcel does not accept null. So, we need to handle // the case here. c.writer << "if (" << c.var << "!=null) {\n"; c.writer.Indent(); c.writer << c.parcel << ".writeInt(1);\n"; c.writer << "android.text.TextUtils.writeToParcel(" << c.var << ", " << c.parcel << ", " << GetFlagFor(c) << ");\n"; c.writer.Dedent(); c.writer << "}\n"; c.writer << "else {\n"; c.writer.Indent(); c.writer << c.parcel << ".writeInt(0);\n"; c.writer.Dedent(); c.writer << "}\n"; }}, }; const string type_name = c.type.GetName() + (c.type.IsArray() ? "[]" : ""); const auto found = method_map.find(type_name); if (found != method_map.end()) { found->second(c); } else { const AidlDefinedType* t = c.typenames.TryGetDefinedType(c.type.GetName()); CHECK(t != nullptr) << "Unknown type: " << c.type.GetName() << endl; if (t->AsInterface() != nullptr) { if (!c.type.IsArray()) { // Why don't we use writeStrongInterface which does the exact same thing? // Keeping below code just not to break unit tests. c.writer << c.parcel << ".writeStrongBinder((((" << c.var << "!=null))?" << "(" << c.var << ".asBinder()):(null)));\n"; } } else if (t->AsParcelable() != nullptr) { if (c.type.IsArray()) { c.writer << c.parcel << ".writeTypedArray(" << c.var << ", " << GetFlagFor(c) << ");\n"; } else { // This is same as writeTypedObject. // Keeping below code just not to break tests. c.writer << "if ((" << c.var << "!=null)) {\n"; c.writer.Indent(); c.writer << c.parcel << ".writeInt(1);\n"; c.writer << c.var << ".writeToParcel(" << c.parcel << ", " << GetFlagFor(c) << ");\n"; c.writer.Dedent(); c.writer << "}\n"; c.writer << "else {\n"; c.writer.Indent(); c.writer << c.parcel << ".writeInt(0);\n"; c.writer.Dedent(); c.writer << "}\n"; } } } return true; } // Ensures that a variable is initialized to refer to the classloader // of the current object and returns the name of the variable. static string EnsureAndGetClassloader(CodeGeneratorContext& c) { CHECK(c.is_classloader_created != nullptr); if (!*(c.is_classloader_created)) { c.writer << "java.lang.ClassLoader cl = " << "(java.lang.ClassLoader)this.getClass().getClassLoader();\n"; *(c.is_classloader_created) = true; } return "cl"; } bool CreateFromParcelFor(const CodeGeneratorContext& c) { if (!EnsureCodegenIsSupported(c)) { return false; } static map<string, function<void(const CodeGeneratorContext&)>> method_map{ {"boolean", [](const CodeGeneratorContext& c) { c.writer << c.var << " = (0!=" << c.parcel << ".readInt());\n"; }}, {"boolean[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createBooleanArray();\n"; }}, {"byte", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".readByte();\n"; }}, {"byte[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createByteArray();\n"; }}, {"char", [](const CodeGeneratorContext& c) { c.writer << c.var << " = (char)" << c.parcel << ".readInt();\n"; }}, {"char[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createCharArray();\n"; }}, {"int", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".readInt();\n"; }}, {"int[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createIntArray();\n"; }}, {"long", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".readLong();\n"; }}, {"long[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createLongArray();\n"; }}, {"float", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".readFloat();\n"; }}, {"float[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createFloatArray();\n"; }}, {"double", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".readDouble();\n"; }}, {"double[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createDoubleArray();\n"; }}, {"String", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".readString();\n"; }}, {"String[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createStringArray();\n"; }}, {"List", [](const CodeGeneratorContext& c) { if (c.type.IsGeneric()) { const string& contained_type = c.type.GetTypeParameters().at(0)->GetName(); if (AidlTypenames::IsBuiltinTypename(contained_type)) { if (contained_type == "String") { c.writer << c.var << " = " << c.parcel << ".createStringArrayList();\n"; } else if (contained_type == "IBinder") { c.writer << c.var << " = " << c.parcel << ".createBinderArrayList();\n"; } } else { const AidlDefinedType* t = c.typenames.TryGetDefinedType(contained_type); CHECK(t != nullptr) << "Unknown type: " << contained_type << endl; if (t->AsParcelable() != nullptr) { c.writer << c.var << " = " << c.parcel << ".createTypedArrayList(" << JavaNameOf(*(c.type.GetTypeParameters().at(0))) << ".CREATOR);\n"; } } } else { const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c)); c.writer << c.var << " = " << c.parcel << ".readArrayList(" << classloader << ");\n"; } }}, {"Map", [](const CodeGeneratorContext& c) { const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c)); c.writer << c.var << " = " << c.parcel << ".readHashMap(" << classloader << ");\n"; }}, {"IBinder", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".readStrongBinder();\n"; }}, {"IBinder[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createBinderArray();\n"; }}, {"FileDescriptor", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".readRawFileDescriptor();\n"; }}, {"FileDescriptor[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createRawFileDescriptorArray();\n"; }}, {"ParcelFileDescriptor", [](const CodeGeneratorContext& c) { // This is same as readTypedObject which was introduced with SDK 23. // Keeping below code so that the generated code is buildable with older SDK. c.writer << "if ((0!=" << c.parcel << ".readInt())) {\n"; c.writer.Indent(); c.writer << c.var << " = " << "android.os.ParcelFileDescriptor.CREATOR.createFromParcel(" << c.parcel << ");\n"; c.writer.Dedent(); c.writer << "}\n"; c.writer << "else {\n"; c.writer.Indent(); c.writer << c.var << " = null;\n"; c.writer.Dedent(); c.writer << "}\n"; }}, {"ParcelFileDescriptor[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createTypedArray(android.os.ParcelFileDescriptor.CREATOR);\n"; }}, {"CharSequence", [](const CodeGeneratorContext& c) { // We have written 0 for null CharSequence. c.writer << "if (0!=" << c.parcel << ".readInt()) {\n"; c.writer.Indent(); c.writer << c.var << " = android.text.TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(" << c.parcel << ");\n"; c.writer.Dedent(); c.writer << "}\n"; c.writer << "else {\n"; c.writer.Indent(); c.writer << c.var << " = null;\n"; c.writer.Dedent(); c.writer << "}\n"; }}, }; const string type_name = c.type.GetName() + (c.type.IsArray() ? "[]" : ""); const auto found = method_map.find(type_name); if (found != method_map.end()) { found->second(c); } else { const AidlDefinedType* t = c.typenames.TryGetDefinedType(c.type.GetName()); CHECK(t != nullptr) << "Unknown type: " << c.type.GetName() << endl; if (t->AsInterface() != nullptr) { if (!c.type.IsArray()) { c.writer << c.var << " = " << c.type.GetName() << ".Stub.asInterface(" << c.parcel << ".readStrongBinder());\n"; } } else if (t->AsParcelable() != nullptr || t->AsStructuredParcelable() != nullptr) { if (c.type.IsArray()) { c.writer << c.var << " = " << c.parcel << ".createTypedArray(" << JavaNameOf(c.type) << ".CREATOR);\n"; } else { // This is same as readTypedObject. // Keeping below code just not to break unit tests. c.writer << "if ((0!=" << c.parcel << ".readInt())) {\n"; c.writer.Indent(); c.writer << c.var << " = " << c.type.GetName() << ".CREATOR.createFromParcel(" << c.parcel << ");\n"; c.writer.Dedent(); c.writer << "}\n"; c.writer << "else {\n"; c.writer.Indent(); c.writer << c.var << " = null;\n"; c.writer.Dedent(); c.writer << "}\n"; } } } return true; } bool ReadFromParcelFor(const CodeGeneratorContext& c) { if (!EnsureCodegenIsSupported(c)) { return false; } static map<string, function<void(const CodeGeneratorContext&)>> method_map{ {"boolean[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".readBooleanArray(" << c.var << ");\n"; }}, {"byte[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".readByteArray(" << c.var << ");\n"; }}, {"char[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".readCharArray(" << c.var << ");\n"; }}, {"int[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".readIntArray(" << c.var << ");\n"; }}, {"long[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".readLongArray(" << c.var << ");\n"; }}, {"float[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".readFloatArray(" << c.var << ");\n"; }}, {"double[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".readDoubleArray(" << c.var << ");\n"; }}, {"String[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".readStringArray(" << c.var << ");\n"; }}, {"List", [](const CodeGeneratorContext& c) { if (c.type.IsGeneric()) { const string& contained_type = c.type.GetTypeParameters().at(0)->GetName(); if (AidlTypenames::IsBuiltinTypename(contained_type)) { if (contained_type == "String") { c.writer << c.parcel << ".readStringList(" << c.var << ");\n"; } else if (contained_type == "IBinder") { c.writer << c.parcel << ".readBinderList(" << c.var << ");\n"; } } else { const AidlDefinedType* t = c.typenames.TryGetDefinedType(contained_type); CHECK(t != nullptr) << "Unknown type: " << contained_type << endl; if (t->AsParcelable() != nullptr) { c.writer << c.parcel << ".readTypedList(" << c.var << ", " << JavaNameOf(*(c.type.GetTypeParameters().at(0))) << ".CREATOR);\n"; } } } else { const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c)); c.writer << c.parcel << ".readList(" << c.var << ", " << classloader << ");\n"; } }}, {"Map", [](const CodeGeneratorContext& c) { const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c)); c.writer << c.var << " = " << c.parcel << ".readHashMap(" << classloader << ");\n"; }}, {"IBinder[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createBinderArray();\n"; }}, {"FileDescriptor[]", [](const CodeGeneratorContext& c) { c.writer << c.var << " = " << c.parcel << ".createRawFileDescriptorArray();\n"; }}, {"ParcelFileDescriptor", [](const CodeGeneratorContext& c) { c.writer << "if ((0!=" << c.parcel << ".readInt())) {\n"; c.writer.Indent(); c.writer << c.var << " = " << "android.os.ParcelFileDescriptor.CREATOR.createFromParcel(" << c.parcel << ");\n"; c.writer.Dedent(); c.writer << "}\n"; }}, {"ParcelFileDescriptor[]", [](const CodeGeneratorContext& c) { c.writer << c.parcel << ".readTypedArray(" << c.var << ", android.os.ParcelFileDescriptor.CREATOR);\n"; }}, }; const string type_name = c.type.GetName() + (c.type.IsArray() ? "[]" : ""); const auto& found = method_map.find(type_name); if (found != method_map.end()) { found->second(c); } else { const AidlDefinedType* t = c.typenames.TryGetDefinedType(c.type.GetName()); CHECK(t != nullptr) << "Unknown type: " << c.type.GetName() << endl; if (t->AsParcelable() != nullptr) { if (c.type.IsArray()) { c.writer << c.parcel << ".readTypedArray(" << c.var << ", " << c.type.GetName() << ".CREATOR);\n"; } else { c.writer << "if ((0!=" << c.parcel << ".readInt())) {\n"; c.writer.Indent(); c.writer << c.var << ".readFromParcel(" << c.parcel << ");\n"; c.writer.Dedent(); c.writer << "}\n"; } } } return true; } } // namespace java } // namespace aidl } // namespace android