/*
* 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