960 lines
36 KiB
C++
960 lines
36 KiB
C++
/*
|
|
* 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 <functional>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace android {
|
|
namespace aidl {
|
|
namespace java {
|
|
|
|
using android::base::Join;
|
|
|
|
using std::function;
|
|
using std::map;
|
|
using std::string;
|
|
using std::vector;
|
|
|
|
std::string ConstantValueDecorator(
|
|
const AidlTypeSpecifier& type,
|
|
const std::variant<std::string, std::vector<std::string>>& raw_value) {
|
|
if (type.IsArray()) {
|
|
const auto& values = std::get<std::vector<std::string>>(raw_value);
|
|
return "{" + Join(values, ", ") + "}";
|
|
}
|
|
const std::string& value = std::get<std::string>(raw_value);
|
|
if (type.GetName() == "long") {
|
|
return value + "L";
|
|
}
|
|
if (auto defined_type = type.GetDefinedType(); defined_type) {
|
|
auto enum_type = defined_type->AsEnumDeclaration();
|
|
AIDL_FATAL_IF(!enum_type, type) << "Invalid type for \"" << value << "\"";
|
|
return type.GetName() + "." + value.substr(value.find_last_of('.') + 1);
|
|
}
|
|
return value;
|
|
};
|
|
|
|
const string& JavaNameOf(const AidlTypeSpecifier& aidl, bool instantiable = false,
|
|
bool boxing = false) {
|
|
AIDL_FATAL_IF(!aidl.IsResolved(), aidl) << aidl.ToString();
|
|
|
|
if (instantiable) {
|
|
// An instantiable type is used in only out type(not even inout type),
|
|
// And instantiable type has to be either the type in List, Map, ParcelFileDescriptor or
|
|
// user-defined type.
|
|
|
|
static map<string, string> instantiable_m = {
|
|
{"List", "java.util.ArrayList"},
|
|
{"Map", "java.util.HashMap"},
|
|
{"ParcelFileDescriptor", "android.os.ParcelFileDescriptor"},
|
|
};
|
|
const string& aidl_name = aidl.GetName();
|
|
|
|
if (instantiable_m.find(aidl_name) != instantiable_m.end()) {
|
|
return instantiable_m[aidl_name];
|
|
}
|
|
}
|
|
|
|
// 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"},
|
|
{"ParcelableHolder", "android.os.ParcelableHolder"},
|
|
};
|
|
|
|
// map from primitive types to the corresponding boxing types
|
|
static map<string, string> boxing_types = {
|
|
{"void", "Void"}, {"boolean", "Boolean"}, {"byte", "Byte"}, {"char", "Character"},
|
|
{"int", "Integer"}, {"long", "Long"}, {"float", "Float"}, {"double", "Double"},
|
|
};
|
|
|
|
// Enums in Java are represented by their backing type when
|
|
// referenced in parcelables, methods, etc.
|
|
const auto defined_type = aidl.GetDefinedType();
|
|
if (defined_type && defined_type->AsEnumDeclaration()) {
|
|
const auto enum_decl = defined_type->AsEnumDeclaration();
|
|
const string& backing_type_name = enum_decl->GetBackingType().GetName();
|
|
AIDL_FATAL_IF(m.find(backing_type_name) == m.end(), enum_decl);
|
|
AIDL_FATAL_IF(!AidlTypenames::IsBuiltinTypename(backing_type_name), enum_decl);
|
|
if (boxing) {
|
|
return boxing_types[backing_type_name];
|
|
} else {
|
|
return m[backing_type_name];
|
|
}
|
|
}
|
|
|
|
const string& aidl_name = aidl.GetName();
|
|
if (boxing && AidlTypenames::IsPrimitiveTypename(aidl_name)) {
|
|
// Every primitive type must have the corresponding boxing type
|
|
AIDL_FATAL_IF(boxing_types.find(aidl_name) == m.end(), aidl);
|
|
return boxing_types[aidl_name];
|
|
}
|
|
if (m.find(aidl_name) != m.end()) {
|
|
AIDL_FATAL_IF(!AidlTypenames::IsBuiltinTypename(aidl_name), aidl);
|
|
return m[aidl_name];
|
|
} else {
|
|
// 'foo.bar.IFoo' in AIDL maps to 'foo.bar.IFoo' in Java
|
|
return aidl_name;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
string JavaSignatureOfInternal(const AidlTypeSpecifier& aidl, bool instantiable, bool omit_array,
|
|
bool boxing) {
|
|
string ret = JavaNameOf(aidl, instantiable, boxing && !aidl.IsArray());
|
|
if (aidl.IsGeneric()) {
|
|
vector<string> arg_names;
|
|
for (const auto& ta : aidl.GetTypeParameters()) {
|
|
arg_names.emplace_back(JavaSignatureOfInternal(*ta, /*instantiable=*/false,
|
|
/*omit_array=*/false, /*boxing=*/true));
|
|
}
|
|
ret += "<" + Join(arg_names, ",") + ">";
|
|
}
|
|
if (aidl.IsArray() && !omit_array) {
|
|
if (aidl.IsFixedSizeArray()) {
|
|
ret += Join(std::vector<std::string>(aidl.GetFixedSizeArrayDimensions().size(), "[]"), "");
|
|
} else {
|
|
ret += "[]";
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Returns the name of the backing type for the specified type.
|
|
// Note: Do not use the result in the generated code! It's supposed to be used as a key.
|
|
// This returns type names as used in AIDL, not a Java signature.
|
|
// For enums, this is the enum's backing type.
|
|
// For all other types, this is the type itself.
|
|
string AidlBackingTypeName(const AidlTypeSpecifier& type) {
|
|
string type_name;
|
|
const auto defined_type = type.GetDefinedType();
|
|
if (defined_type && defined_type->AsEnumDeclaration()) {
|
|
const AidlEnumDeclaration* enum_decl = defined_type->AsEnumDeclaration();
|
|
type_name = enum_decl->GetBackingType().GetName();
|
|
} else {
|
|
type_name = type.GetName();
|
|
}
|
|
if (type.IsArray()) {
|
|
if (type.IsFixedSizeArray()) {
|
|
for (const auto& dim : type.GetFixedSizeArrayDimensions()) {
|
|
type_name += "[" + std::to_string(dim) + "]";
|
|
}
|
|
} else {
|
|
type_name += "[]";
|
|
}
|
|
}
|
|
return type_name;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
string JavaSignatureOf(const AidlTypeSpecifier& aidl) {
|
|
return JavaSignatureOfInternal(aidl, /*instantiable=*/false, /*omit_array=*/false,
|
|
/*boxing=*/false);
|
|
}
|
|
|
|
// Used for "new" expression. Ignore arrays because "new" expression handles it.
|
|
string InstantiableJavaSignatureOf(const AidlTypeSpecifier& aidl) {
|
|
return JavaSignatureOfInternal(aidl, /*instantiable=*/true, /*omit_array=*/true,
|
|
/*boxing=*/false);
|
|
}
|
|
|
|
string JavaBoxingTypeOf(const AidlTypeSpecifier& aidl) {
|
|
AIDL_FATAL_IF(!AidlTypenames::IsPrimitiveTypename(aidl.GetName()), aidl);
|
|
return JavaSignatureOfInternal(aidl, /*instantiable=*/false, /*omit_array=*/false,
|
|
/*boxing=*/true);
|
|
}
|
|
|
|
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 = AidlBackingTypeName(aidl);
|
|
AIDL_FATAL_IF(name == "void", aidl);
|
|
|
|
if (!aidl.IsArray() && m.find(name) != m.end()) {
|
|
AIDL_FATAL_IF(!AidlTypenames::IsBuiltinTypename(name), aidl);
|
|
return m[name];
|
|
} else {
|
|
return "null";
|
|
}
|
|
}
|
|
|
|
typedef void (*ParcelHelperGenerator)(CodeWriter&, const Options&);
|
|
|
|
static void GenerateTypedObjectHelper(CodeWriter& out, const Options&) {
|
|
// Note that the name is inconsistent here because Parcel.java defines readTypedObject as if it
|
|
// "creates" a new value from a parcel. "in-place" read function is not necessary because
|
|
// user-defined parcelable defines its readFromParcel.
|
|
out << R"(static private <T> T readTypedObject(
|
|
android.os.Parcel parcel,
|
|
android.os.Parcelable.Creator<T> c) {
|
|
if (parcel.readInt() != 0) {
|
|
return c.createFromParcel(parcel);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
static private <T extends android.os.Parcelable> void writeTypedObject(
|
|
android.os.Parcel parcel, T value, int parcelableFlags) {
|
|
if (value != null) {
|
|
parcel.writeInt(1);
|
|
value.writeToParcel(parcel, parcelableFlags);
|
|
} else {
|
|
parcel.writeInt(0);
|
|
}
|
|
}
|
|
)";
|
|
}
|
|
|
|
void GenerateParcelHelpers(CodeWriter& out, const AidlDefinedType& defined_type,
|
|
const Options& options) {
|
|
// root-level type contains all necessary helpers
|
|
if (defined_type.GetParentType()) {
|
|
return;
|
|
}
|
|
// visits method parameters and parcelable fields to collect types which
|
|
// requires read/write/create helpers.
|
|
struct Visitor : AidlVisitor {
|
|
const Options& options;
|
|
set<ParcelHelperGenerator> helpers;
|
|
Visitor(const Options& options) : options(options) {}
|
|
void Visit(const AidlTypeSpecifier& type) override {
|
|
auto name = type.GetName();
|
|
if (auto defined_type = type.GetDefinedType(); defined_type) {
|
|
if (defined_type->AsParcelable() != nullptr && !type.IsArray()) {
|
|
// TypedObjects are supported since 23. So we don't need helpers.
|
|
if (options.GetMinSdkVersion() < 23u) {
|
|
helpers.insert(&GenerateTypedObjectHelper);
|
|
}
|
|
}
|
|
} else {
|
|
// There's parcelable-like built-in types as well.
|
|
if (name == "ParcelFileDescriptor" || name == "CharSequence") {
|
|
if (!type.IsArray()) {
|
|
// TypedObjects are supported since 23. So we don't need helpers.
|
|
if (options.GetMinSdkVersion() < 23u) {
|
|
helpers.insert(&GenerateTypedObjectHelper);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} v{options};
|
|
|
|
VisitTopDown(v, defined_type);
|
|
if (!v.helpers.empty()) {
|
|
// Nested class (_Parcel) is used to contain static helper methods because some targets are
|
|
// still using Java 7 which doesn't allow interfaces to have static methods.
|
|
// Helpers shouldn't bother API checks, but in case where AIDL types are not marked `@hide`
|
|
// explicitly marks the helper class as @hide.
|
|
out << "/** @hide */\n";
|
|
out << "static class _Parcel {\n";
|
|
out.Indent();
|
|
for (const auto& helper : v.helpers) {
|
|
helper(out, options);
|
|
}
|
|
out.Dedent();
|
|
out << "}\n";
|
|
}
|
|
}
|
|
|
|
void WriteToParcelFor(const CodeGeneratorContext& c) {
|
|
static map<string, function<void(const CodeGeneratorContext&)>> method_map{
|
|
{"boolean",
|
|
[](const CodeGeneratorContext& c) {
|
|
if (c.min_sdk_version >= 29u) {
|
|
c.writer << c.parcel << ".writeBoolean(" << c.var << ");\n";
|
|
} else {
|
|
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()) {
|
|
AIDL_FATAL_IF(c.type.GetTypeParameters().size() != 1, c.type);
|
|
const auto& element_type = *c.type.GetTypeParameters().at(0);
|
|
const auto& element_type_name = element_type.GetName();
|
|
if (element_type_name == "String") {
|
|
c.writer << c.parcel << ".writeStringList(" << c.var << ");\n";
|
|
} else if (element_type_name == "IBinder") {
|
|
c.writer << c.parcel << ".writeBinderList(" << c.var << ");\n";
|
|
} else if (c.typenames.IsParcelable(element_type_name)) {
|
|
c.writer << c.parcel << ".writeTypedList(" << c.var << ");\n";
|
|
} else if (c.typenames.GetInterface(element_type)) {
|
|
c.writer << c.parcel << ".writeInterfaceList(" << c.var << ");\n";
|
|
} else {
|
|
AIDL_FATAL(c.type) << "write: NOT IMPLEMENTED for " << element_type_name;
|
|
}
|
|
} else {
|
|
c.writer << c.parcel << ".writeList(" << c.var << ");\n";
|
|
}
|
|
}},
|
|
{"Map",
|
|
[](const CodeGeneratorContext& c) {
|
|
if (c.type.IsGeneric()) {
|
|
c.writer << "if (" << c.var << " == null) {\n";
|
|
c.writer.Indent();
|
|
c.writer << c.parcel << ".writeInt(-1);\n";
|
|
c.writer.Dedent();
|
|
c.writer << "} else {\n";
|
|
c.writer.Indent();
|
|
c.writer << c.parcel << ".writeInt(" << c.var << ".size());\n";
|
|
c.writer << c.var << ".forEach((k, v) -> {\n";
|
|
c.writer.Indent();
|
|
c.writer << c.parcel << ".writeString(k);\n";
|
|
|
|
CodeGeneratorContext value_context{
|
|
c.writer,
|
|
c.typenames,
|
|
*c.type.GetTypeParameters()[1].get(),
|
|
c.parcel,
|
|
"v",
|
|
c.min_sdk_version,
|
|
c.write_to_parcel_flag,
|
|
c.is_classloader_created,
|
|
};
|
|
WriteToParcelFor(value_context);
|
|
c.writer.Dedent();
|
|
c.writer << "});\n";
|
|
|
|
c.writer.Dedent();
|
|
c.writer << "}\n";
|
|
} else {
|
|
c.writer << c.parcel << ".writeMap(" << c.var << ");\n";
|
|
}
|
|
}},
|
|
{"IBinder",
|
|
[](const CodeGeneratorContext& c) {
|
|
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) {
|
|
if (c.min_sdk_version >= 23u) {
|
|
c.writer << c.parcel << ".writeTypedObject(" << c.var << ", " << c.write_to_parcel_flag
|
|
<< ");\n";
|
|
} else {
|
|
c.writer << "_Parcel.writeTypedObject(" << c.parcel << ", " << c.var << ", "
|
|
<< c.write_to_parcel_flag << ");\n";
|
|
}
|
|
}},
|
|
{"ParcelFileDescriptor[]",
|
|
[](const CodeGeneratorContext& c) {
|
|
c.writer << c.parcel << ".writeTypedArray(" << c.var << ", " << c.write_to_parcel_flag
|
|
<< ");\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 << ", "
|
|
<< c.write_to_parcel_flag << ");\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";
|
|
}},
|
|
{"ParcelableHolder",
|
|
[](const CodeGeneratorContext& c) {
|
|
c.writer << c.parcel << ".writeTypedObject(" << c.var << ", 0);\n";
|
|
}},
|
|
};
|
|
const string type_name = AidlBackingTypeName(c.type);
|
|
const auto found = method_map.find(type_name);
|
|
if (found != method_map.end()) {
|
|
found->second(c);
|
|
} else if (c.type.IsFixedSizeArray()) {
|
|
std::vector<std::string> args = {c.var, c.write_to_parcel_flag};
|
|
for (auto dim : c.type.GetFixedSizeArrayDimensions()) {
|
|
args.push_back(std::to_string(dim));
|
|
}
|
|
c.writer << c.parcel << ".writeFixedArray(" << Join(args, ", ") << ");\n";
|
|
} else {
|
|
const AidlDefinedType* t = c.typenames.TryGetDefinedType(c.type.GetName());
|
|
AIDL_FATAL_IF(t == nullptr, c.type) << "Unknown type: " << c.type.GetName();
|
|
if (t->AsInterface() != nullptr) {
|
|
if (c.type.IsArray()) {
|
|
c.writer << c.parcel << ".writeInterfaceArray(" << c.var << ");\n";
|
|
} else {
|
|
c.writer << c.parcel << ".writeStrongInterface(" << c.var << ");\n";
|
|
}
|
|
} else if (t->AsParcelable() != nullptr) {
|
|
if (c.type.IsArray()) {
|
|
c.writer << c.parcel << ".writeTypedArray(" << c.var << ", " << c.write_to_parcel_flag
|
|
<< ");\n";
|
|
} else {
|
|
if (c.min_sdk_version >= 23u) {
|
|
c.writer << c.parcel << ".writeTypedObject(" << c.var << ", " << c.write_to_parcel_flag
|
|
<< ");\n";
|
|
} else {
|
|
c.writer << "_Parcel.writeTypedObject(" << c.parcel << ", " << c.var << ", "
|
|
<< c.write_to_parcel_flag << ");\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
AIDL_FATAL_IF(c.is_classloader_created == nullptr, AIDL_LOCATION_HERE);
|
|
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) {
|
|
static map<string, function<void(const CodeGeneratorContext&)>> method_map{
|
|
{"boolean",
|
|
[](const CodeGeneratorContext& c) {
|
|
if (c.min_sdk_version >= 29u) {
|
|
c.writer << c.var << " = " << c.parcel << ".readBoolean();\n";
|
|
} else {
|
|
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()) {
|
|
AIDL_FATAL_IF(c.type.GetTypeParameters().size() != 1, c.type);
|
|
const auto& element_type = *c.type.GetTypeParameters().at(0);
|
|
const auto& element_type_name = element_type.GetName();
|
|
if (element_type_name == "String") {
|
|
c.writer << c.var << " = " << c.parcel << ".createStringArrayList();\n";
|
|
} else if (element_type_name == "IBinder") {
|
|
c.writer << c.var << " = " << c.parcel << ".createBinderArrayList();\n";
|
|
} else if (c.typenames.IsParcelable(element_type_name)) {
|
|
c.writer << c.var << " = " << c.parcel << ".createTypedArrayList("
|
|
<< JavaNameOf(element_type) << ".CREATOR);\n";
|
|
} else if (c.typenames.GetInterface(element_type)) {
|
|
auto as_interface = element_type_name + ".Stub::asInterface";
|
|
c.writer << c.var << " = " << c.parcel << ".createInterfaceArrayList(" << as_interface
|
|
<< ");\n";
|
|
} else {
|
|
AIDL_FATAL(c.type) << "create: NOT IMPLEMENTED for " << element_type_name;
|
|
}
|
|
} else {
|
|
const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
|
|
c.writer << c.var << " = " << c.parcel << ".readArrayList(" << classloader << ");\n";
|
|
}
|
|
}},
|
|
{"Map",
|
|
[](const CodeGeneratorContext& c) {
|
|
if (c.type.IsGeneric()) {
|
|
c.writer << "{\n";
|
|
c.writer.Indent();
|
|
c.writer << "int N = " << c.parcel << ".readInt();\n";
|
|
c.writer << c.var << " = N < 0 ? null : new java.util.HashMap<>();\n";
|
|
|
|
auto creator = JavaNameOf(*(c.type.GetTypeParameters().at(1))) + ".CREATOR";
|
|
c.writer << "java.util.stream.IntStream.range(0, N).forEach(i -> {\n";
|
|
c.writer.Indent();
|
|
c.writer << "String k = " << c.parcel << ".readString();\n";
|
|
c.writer << JavaSignatureOf(*(c.type.GetTypeParameters().at(1))) << " v;\n";
|
|
CodeGeneratorContext value_context{
|
|
c.writer,
|
|
c.typenames,
|
|
*c.type.GetTypeParameters()[1].get(),
|
|
c.parcel,
|
|
"v",
|
|
c.min_sdk_version,
|
|
c.write_to_parcel_flag,
|
|
c.is_classloader_created,
|
|
};
|
|
CreateFromParcelFor(value_context);
|
|
c.writer << c.var << ".put(k, v);\n";
|
|
|
|
c.writer.Dedent();
|
|
c.writer << "});\n";
|
|
|
|
c.writer.Dedent();
|
|
c.writer << "}\n";
|
|
} else {
|
|
const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
|
|
c.writer << c.var << " = " << c.parcel << ".readHashMap(" << classloader << ");\n";
|
|
}
|
|
}},
|
|
{"IBinder",
|
|
[](const CodeGeneratorContext& c) {
|
|
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) {
|
|
if (c.min_sdk_version >= 23u) {
|
|
c.writer << c.var << " = " << c.parcel
|
|
<< ".readTypedObject(android.os.ParcelFileDescriptor.CREATOR);\n";
|
|
} else {
|
|
c.writer << c.var << " = _Parcel.readTypedObject(" << c.parcel
|
|
<< ", android.os.ParcelFileDescriptor.CREATOR);\n";
|
|
}
|
|
}},
|
|
{"ParcelFileDescriptor[]",
|
|
[](const CodeGeneratorContext& c) {
|
|
c.writer << c.var << " = " << c.parcel
|
|
<< ".createTypedArray(android.os.ParcelFileDescriptor.CREATOR);\n";
|
|
}},
|
|
{"CharSequence",
|
|
[](const CodeGeneratorContext& c) {
|
|
if (c.min_sdk_version >= 23u) {
|
|
c.writer << c.var << " = " << c.parcel
|
|
<< ".readTypedObject(android.text.TextUtils.CHAR_SEQUENCE_CREATOR);\n";
|
|
} else {
|
|
c.writer << c.var << " = _Parcel.readTypedObject(" << c.parcel
|
|
<< ", android.text.TextUtils.CHAR_SEQUENCE_CREATOR);\n";
|
|
}
|
|
}},
|
|
{"ParcelableHolder",
|
|
[](const CodeGeneratorContext& c) {
|
|
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";
|
|
}},
|
|
};
|
|
const auto found = method_map.find(AidlBackingTypeName(c.type));
|
|
if (found != method_map.end()) {
|
|
found->second(c);
|
|
} else if (c.type.IsFixedSizeArray()) {
|
|
std::vector<std::string> args = {JavaSignatureOf(c.type) + ".class"};
|
|
if (c.typenames.IsParcelable(c.type.GetName())) {
|
|
args.push_back(JavaNameOf(c.type) + ".CREATOR");
|
|
} else if (c.typenames.GetInterface(c.type)) {
|
|
args.push_back(c.type.GetName() + ".Stub::asInterface");
|
|
}
|
|
for (auto dim : c.type.GetFixedSizeArrayDimensions()) {
|
|
args.push_back(std::to_string(dim));
|
|
}
|
|
c.writer << c.var << " = " << c.parcel << ".createFixedArray(" << Join(args, ", ") << ");\n";
|
|
} else {
|
|
const AidlDefinedType* t = c.typenames.TryGetDefinedType(c.type.GetName());
|
|
AIDL_FATAL_IF(t == nullptr, c.type) << "Unknown type: " << c.type.GetName();
|
|
if (t->AsInterface() != nullptr) {
|
|
auto name = c.type.GetName();
|
|
if (c.type.IsArray()) {
|
|
auto new_array = name + "[]::new";
|
|
auto as_interface = name + ".Stub::asInterface";
|
|
c.writer << c.var << " = " << c.parcel << ".createInterfaceArray(" << new_array << ", "
|
|
<< as_interface << ");\n";
|
|
} else {
|
|
c.writer << c.var << " = " << name << ".Stub.asInterface(" << c.parcel
|
|
<< ".readStrongBinder());\n";
|
|
}
|
|
} else if (t->AsParcelable() != nullptr) {
|
|
if (c.type.IsArray()) {
|
|
c.writer << c.var << " = " << c.parcel << ".createTypedArray(" << JavaNameOf(c.type)
|
|
<< ".CREATOR);\n";
|
|
} else {
|
|
if (c.min_sdk_version >= 23u) {
|
|
c.writer << c.var << " = " << c.parcel << ".readTypedObject(" << c.type.GetName()
|
|
<< ".CREATOR);\n";
|
|
} else {
|
|
c.writer << c.var << " = _Parcel.readTypedObject(" << c.parcel << ", " << c.type.GetName()
|
|
<< ".CREATOR);\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ReadFromParcelFor(const CodeGeneratorContext& c) {
|
|
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()) {
|
|
AIDL_FATAL_IF(c.type.GetTypeParameters().size() != 1, c.type);
|
|
const auto& element_type = *c.type.GetTypeParameters().at(0);
|
|
const auto& element_type_name = element_type.GetName();
|
|
if (element_type_name == "String") {
|
|
c.writer << c.parcel << ".readStringList(" << c.var << ");\n";
|
|
} else if (element_type_name == "IBinder") {
|
|
c.writer << c.parcel << ".readBinderList(" << c.var << ");\n";
|
|
} else if (c.typenames.IsParcelable(element_type_name)) {
|
|
c.writer << c.parcel << ".readTypedList(" << c.var << ", "
|
|
<< JavaNameOf(*(c.type.GetTypeParameters().at(0))) << ".CREATOR);\n";
|
|
} else if (c.typenames.GetInterface(element_type)) {
|
|
auto as_interface = element_type_name + ".Stub::asInterface";
|
|
c.writer << c.parcel << ".readInterfaceList(" << c.var << ", " << as_interface
|
|
<< ");\n";
|
|
} else {
|
|
AIDL_FATAL(c.type) << "read: NOT IMPLEMENTED for " << element_type_name;
|
|
}
|
|
} else {
|
|
const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
|
|
c.writer << c.parcel << ".readList(" << c.var << ", " << classloader << ");\n";
|
|
}
|
|
}},
|
|
{"Map",
|
|
[](const CodeGeneratorContext& c) {
|
|
if (c.type.IsGeneric()) {
|
|
c.writer << "if (" << c.var << " != null) " << c.var << ".clear();\n";
|
|
c.writer << "java.util.stream.IntStream.range(0, " << c.parcel
|
|
<< ".readInt()).forEach(i -> {\n";
|
|
c.writer.Indent();
|
|
c.writer << "String k = " << c.parcel << ".readString();\n";
|
|
c.writer << JavaSignatureOf(*(c.type.GetTypeParameters().at(1))) << " v;\n";
|
|
CodeGeneratorContext value_context{
|
|
c.writer,
|
|
c.typenames,
|
|
*c.type.GetTypeParameters()[1].get(),
|
|
c.parcel,
|
|
"v",
|
|
c.min_sdk_version,
|
|
c.write_to_parcel_flag,
|
|
c.is_classloader_created,
|
|
};
|
|
CreateFromParcelFor(value_context);
|
|
c.writer << c.var << ".put(k, v);\n";
|
|
|
|
c.writer.Dedent();
|
|
c.writer << "});\n";
|
|
} else {
|
|
const string classloader = EnsureAndGetClassloader(const_cast<CodeGeneratorContext&>(c));
|
|
c.writer << c.var << " = " << c.parcel << ".readHashMap(" << classloader << ");\n";
|
|
}
|
|
}},
|
|
{"IBinder[]",
|
|
[](const CodeGeneratorContext& c) {
|
|
c.writer << c.parcel << ".readBinderArray(" << c.var << ");\n";
|
|
}},
|
|
{"FileDescriptor[]",
|
|
[](const CodeGeneratorContext& c) {
|
|
c.writer << c.parcel << ".readRawFileDescriptorArray(" << c.var << ");\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 auto& found = method_map.find(AidlBackingTypeName(c.type));
|
|
if (found != method_map.end()) {
|
|
found->second(c);
|
|
} else if (c.type.IsFixedSizeArray()) {
|
|
std::vector<std::string> args = {c.var};
|
|
if (c.typenames.IsParcelable(c.type.GetName())) {
|
|
args.push_back(c.type.GetName() + ".CREATOR");
|
|
} else if (c.typenames.GetInterface(c.type)) {
|
|
args.push_back(c.type.GetName() + ".Stub::asInterface");
|
|
}
|
|
c.writer << c.parcel << ".readFixedArray(" << Join(args, ", ") << ");\n";
|
|
} else {
|
|
const AidlDefinedType* t = c.typenames.TryGetDefinedType(c.type.GetName());
|
|
AIDL_FATAL_IF(t == nullptr, c.type) << "Unknown type: " << c.type.GetName();
|
|
if (t->AsParcelable() != nullptr || t->AsUnionDeclaration() != 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";
|
|
}
|
|
} else if (t->AsInterface()) {
|
|
AIDL_FATAL_IF(!c.type.IsArray(), c.type) << "readFromParcel(interface) doesn't make sense.";
|
|
auto as_interface = c.type.GetName() + ".Stub::asInterface";
|
|
c.writer << c.parcel << ".readInterfaceArray(" << c.var << ", " << as_interface << ");\n";
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ToStringFor(const CodeGeneratorContext& c) {
|
|
// Use derived toString() for enum type annotated with @JavaDerive(toString=true)
|
|
if (auto t = c.type.GetDefinedType();
|
|
t != nullptr && t->AsEnumDeclaration() && t->JavaDerive("toString")) {
|
|
if (c.type.IsArray()) {
|
|
c.writer << c.type.GetName() << ".$.arrayToString(" << c.var << ")";
|
|
} else {
|
|
c.writer << c.type.GetName() << ".$.toString(" << c.var << ")";
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (c.type.IsArray()) {
|
|
if (c.type.IsDynamicArray() || c.type.GetFixedSizeArrayDimensions().size() == 1) {
|
|
c.writer << "java.util.Arrays.toString(" << c.var << ")";
|
|
} else {
|
|
c.writer << "java.util.Arrays.deepToString(" << c.var << ")";
|
|
}
|
|
return;
|
|
}
|
|
|
|
const std::string name = c.type.GetName();
|
|
|
|
if (AidlTypenames::IsPrimitiveTypename(name)) {
|
|
c.writer << c.var;
|
|
return;
|
|
}
|
|
|
|
const AidlDefinedType* t = c.typenames.TryGetDefinedType(name);
|
|
if (t != nullptr && t->AsEnumDeclaration()) {
|
|
c.writer << c.var;
|
|
return;
|
|
}
|
|
|
|
// FileDescriptor doesn't have a good toString() impl.
|
|
if (name == "FileDescriptor") {
|
|
c.writer << c.var << " == null ? \"null\" : ";
|
|
c.writer << c.var << ".getInt$()";
|
|
return;
|
|
}
|
|
|
|
// Rest of the built-in types have reasonable toString() impls.
|
|
if (AidlTypenames::IsBuiltinTypename(name)) {
|
|
c.writer << "java.util.Objects.toString(" << c.var << ")";
|
|
return;
|
|
}
|
|
|
|
// For user-defined types, we also use toString() that we are generating here, but just make sure
|
|
// that they are actually user-defined types.
|
|
AIDL_FATAL_IF(t == nullptr, c.type) << "Unknown type";
|
|
if (t->AsInterface() != nullptr || t->AsParcelable() != nullptr) {
|
|
c.writer << "java.util.Objects.toString(" << c.var << ")";
|
|
return;
|
|
}
|
|
|
|
AIDL_FATAL(AIDL_LOCATION_HERE) << "Unhandled typename: " << name;
|
|
}
|
|
|
|
} // namespace java
|
|
} // namespace aidl
|
|
} // namespace android
|