/*
|
* 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.h"
|
#include "aidl_to_java.h"
|
#include "generate_java.h"
|
#include "options.h"
|
#include "type_java.h"
|
|
#include <stdio.h>
|
#include <stdlib.h>
|
#include <string.h>
|
|
#include <algorithm>
|
#include <unordered_set>
|
#include <utility>
|
#include <vector>
|
|
#include <android-base/macros.h>
|
#include <android-base/stringprintf.h>
|
|
using android::base::Join;
|
using android::base::StringPrintf;
|
|
using std::string;
|
using std::unique_ptr;
|
using std::vector;
|
|
namespace android {
|
namespace aidl {
|
namespace java {
|
|
// =================================================
|
class VariableFactory {
|
public:
|
using Variable = ::android::aidl::java::Variable;
|
using Type = ::android::aidl::java::Type;
|
|
explicit VariableFactory(const std::string& base) : base_(base), index_(0) {}
|
Variable* Get(const Type* type) {
|
Variable* v = new Variable(type->JavaType(), StringPrintf("%s%d", base_.c_str(), index_));
|
vars_.push_back(v);
|
index_++;
|
return v;
|
}
|
|
Variable* Get(int index) { return vars_[index]; }
|
|
private:
|
std::vector<Variable*> vars_;
|
std::string base_;
|
int index_;
|
|
DISALLOW_COPY_AND_ASSIGN(VariableFactory);
|
};
|
|
// =================================================
|
class StubClass : public Class {
|
public:
|
StubClass(const Type* type, const InterfaceType* interfaceType, JavaTypeNamespace* types,
|
const Options& options);
|
~StubClass() override = default;
|
|
Variable* transact_code;
|
Variable* transact_data;
|
Variable* transact_reply;
|
Variable* transact_flags;
|
SwitchStatement* transact_switch;
|
StatementBlock* transact_statements;
|
SwitchStatement* code_to_method_name_switch;
|
|
// Where onTransact cases should be generated as separate methods.
|
bool transact_outline;
|
// Specific methods that should be outlined when transact_outline is true.
|
std::unordered_set<const AidlMethod*> outline_methods;
|
// Number of all methods.
|
size_t all_method_count;
|
|
// Finish generation. This will add a default case to the switch.
|
void finish();
|
|
Expression* get_transact_descriptor(const JavaTypeNamespace* types,
|
const AidlMethod* method);
|
|
private:
|
void make_as_interface(const InterfaceType* interfaceType,
|
JavaTypeNamespace* types);
|
|
Variable* transact_descriptor;
|
const Options& options_;
|
|
DISALLOW_COPY_AND_ASSIGN(StubClass);
|
};
|
|
StubClass::StubClass(const Type* type, const InterfaceType* interfaceType, JavaTypeNamespace* types,
|
const Options& options)
|
: Class(), options_(options) {
|
transact_descriptor = nullptr;
|
transact_outline = false;
|
all_method_count = 0; // Will be set when outlining may be enabled.
|
|
this->comment = "/** Local-side IPC implementation stub class. */";
|
this->modifiers = PUBLIC | ABSTRACT | STATIC;
|
this->what = Class::CLASS;
|
this->type = type->JavaType();
|
this->extends = types->BinderNativeType()->JavaType();
|
this->interfaces.push_back(interfaceType->JavaType());
|
|
// descriptor
|
Field* descriptor = new Field(STATIC | FINAL | PRIVATE,
|
new Variable(types->StringType()->JavaType(), "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()->JavaType();
|
asBinder->name = "asBinder";
|
asBinder->statements = new StatementBlock;
|
asBinder->statements->Add(new ReturnStatement(THIS_VALUE));
|
this->elements.push_back(asBinder);
|
|
if (options_.GenTransactionNames()) {
|
// getDefaultTransactionName
|
Method* getDefaultTransactionName = new Method;
|
getDefaultTransactionName->comment = "/** @hide */";
|
getDefaultTransactionName->modifiers = PUBLIC | STATIC;
|
getDefaultTransactionName->returnType = types->StringType()->JavaType();
|
getDefaultTransactionName->name = "getDefaultTransactionName";
|
Variable* code = new Variable(types->IntType()->JavaType(), "transactionCode");
|
getDefaultTransactionName->parameters.push_back(code);
|
getDefaultTransactionName->statements = new StatementBlock;
|
this->code_to_method_name_switch = new SwitchStatement(code);
|
getDefaultTransactionName->statements->Add(this->code_to_method_name_switch);
|
this->elements.push_back(getDefaultTransactionName);
|
|
// getTransactionName
|
Method* getTransactionName = new Method;
|
getTransactionName->comment = "/** @hide */";
|
getTransactionName->modifiers = PUBLIC;
|
getTransactionName->returnType = types->StringType()->JavaType();
|
getTransactionName->name = "getTransactionName";
|
Variable* code2 = new Variable(types->IntType()->JavaType(), "transactionCode");
|
getTransactionName->parameters.push_back(code2);
|
getTransactionName->statements = new StatementBlock;
|
getTransactionName->statements->Add(
|
new ReturnStatement(new MethodCall(THIS_VALUE, "getDefaultTransactionName", 1, code2)));
|
this->elements.push_back(getTransactionName);
|
}
|
|
// onTransact
|
this->transact_code = new Variable(types->IntType()->JavaType(), "code");
|
this->transact_data = new Variable(types->ParcelType()->JavaType(), "data");
|
this->transact_reply = new Variable(types->ParcelType()->JavaType(), "reply");
|
this->transact_flags = new Variable(types->IntType()->JavaType(), "flags");
|
Method* onTransact = new Method;
|
onTransact->modifiers = PUBLIC | OVERRIDE;
|
onTransact->returnType = types->BoolType()->JavaType();
|
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;
|
transact_statements = onTransact->statements;
|
onTransact->exceptions.push_back(types->RemoteExceptionType()->JavaType());
|
this->elements.push_back(onTransact);
|
this->transact_switch = new SwitchStatement(this->transact_code);
|
}
|
|
void StubClass::finish() {
|
Case* default_case = new Case;
|
|
MethodCall* superCall = new MethodCall(
|
SUPER_VALUE, "onTransact", 4, this->transact_code, this->transact_data,
|
this->transact_reply, this->transact_flags);
|
default_case->statements->Add(new ReturnStatement(superCall));
|
transact_switch->cases.push_back(default_case);
|
|
transact_statements->Add(this->transact_switch);
|
|
// getTransactionName
|
if (options_.GenTransactionNames()) {
|
// Some transaction codes are common, e.g. INTERFACE_TRANSACTION or DUMP_TRANSACTION.
|
// Common transaction codes will not be resolved to a string by getTransactionName. The method
|
// will return NULL in this case.
|
Case* code_switch_default_case = new Case;
|
code_switch_default_case->statements->Add(new ReturnStatement(NULL_VALUE));
|
this->code_to_method_name_switch->cases.push_back(code_switch_default_case);
|
}
|
}
|
|
// The the expression for the interface's descriptor to be used when
|
// generating code for the given method. Null is acceptable for method
|
// and stands for synthetic cases.
|
Expression* StubClass::get_transact_descriptor(const JavaTypeNamespace* types,
|
const AidlMethod* method) {
|
if (transact_outline) {
|
if (method != nullptr) {
|
// When outlining, each outlined method needs its own literal.
|
if (outline_methods.count(method) != 0) {
|
return new LiteralExpression("DESCRIPTOR");
|
}
|
} else {
|
// Synthetic case. A small number is assumed. Use its own descriptor
|
// if there are only synthetic cases.
|
if (outline_methods.size() == all_method_count) {
|
return new LiteralExpression("DESCRIPTOR");
|
}
|
}
|
}
|
|
// When not outlining, store the descriptor literal into a local variable, in
|
// an effort to save const-string instructions in each switch case.
|
if (transact_descriptor == nullptr) {
|
transact_descriptor = new Variable(types->StringType()->JavaType(), "descriptor");
|
transact_statements->Add(
|
new VariableDeclaration(transact_descriptor,
|
new LiteralExpression("DESCRIPTOR")));
|
}
|
return transact_descriptor;
|
}
|
|
void StubClass::make_as_interface(const InterfaceType* interfaceType,
|
JavaTypeNamespace* types) {
|
Variable* obj = new Variable(types->IBinderType()->JavaType(), "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->JavaType();
|
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(types);
|
Variable* iin = new Variable(iinType.JavaType(), "iin");
|
VariableDeclaration* iinVd = new VariableDeclaration(iin, queryLocalInterface);
|
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->JavaType(), iin)));
|
m->statements->Add(instOfStatement);
|
|
NewExpression* ne = new NewExpression(interfaceType->GetProxy()->InstantiableName());
|
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,
|
const Options& options);
|
~ProxyClass() override;
|
|
Variable* mRemote;
|
};
|
|
ProxyClass::ProxyClass(const JavaTypeNamespace* types, const Type* type,
|
const InterfaceType* interfaceType, const Options& options)
|
: Class() {
|
this->modifiers = PRIVATE | STATIC;
|
this->what = Class::CLASS;
|
this->type = type->JavaType();
|
this->interfaces.push_back(interfaceType->JavaType());
|
|
// IBinder mRemote
|
mRemote = new Variable(types->IBinderType()->JavaType(), "mRemote");
|
this->elements.push_back(new Field(PRIVATE, mRemote));
|
|
// Proxy()
|
Variable* remote = new Variable(types->IBinderType()->JavaType(), "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);
|
|
if (options.Version() > 0) {
|
std::ostringstream code;
|
code << "private int mCachedVersion = -1;\n";
|
this->elements.emplace_back(new LiteralClassElement(code.str()));
|
}
|
|
// IBinder asBinder()
|
Method* asBinder = new Method;
|
asBinder->modifiers = PUBLIC | OVERRIDE;
|
asBinder->returnType = types->IBinderType()->JavaType();
|
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()->JavaType(), 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->JavaType(), len)));
|
addTo->Add(lencheck);
|
}
|
|
static void generate_write_to_parcel(const AidlTypeSpecifier& type, StatementBlock* addTo,
|
Variable* v, Variable* parcel, bool is_return_value,
|
const AidlTypenames& typenames) {
|
string code;
|
CodeWriterPtr writer = CodeWriter::ForString(&code);
|
CodeGeneratorContext context{
|
.writer = *(writer.get()),
|
.typenames = typenames,
|
.type = type,
|
.var = v->name,
|
.parcel = parcel->name,
|
.is_return_value = is_return_value,
|
};
|
WriteToParcelFor(context);
|
writer->Close();
|
addTo->Add(new LiteralStatement(code));
|
}
|
|
static void generate_int_constant(Class* interface, const std::string& name,
|
const std::string& value) {
|
auto code = StringPrintf("public static final int %s = %s;\n", name.c_str(), value.c_str());
|
interface->elements.push_back(new LiteralClassElement(code));
|
}
|
|
static void generate_string_constant(Class* interface, const std::string& name,
|
const std::string& value) {
|
auto code = StringPrintf("public static final String %s = %s;\n", name.c_str(), value.c_str());
|
interface->elements.push_back(new LiteralClassElement(code));
|
}
|
|
static std::unique_ptr<Method> generate_interface_method(
|
const AidlMethod& method, JavaTypeNamespace* types) {
|
std::unique_ptr<Method> decl(new Method);
|
decl->comment = method.GetComments();
|
decl->modifiers = PUBLIC;
|
decl->returnType = method.GetType().GetLanguageType<Type>()->JavaType();
|
decl->returnTypeDimension = method.GetType().IsArray() ? 1 : 0;
|
decl->name = method.GetName();
|
decl->annotations = generate_java_annotations(method.GetType());
|
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
decl->parameters.push_back(new Variable(arg->GetType().GetLanguageType<Type>()->JavaType(),
|
arg->GetName(), arg->GetType().IsArray() ? 1 : 0));
|
}
|
|
decl->exceptions.push_back(types->RemoteExceptionType()->JavaType());
|
|
return decl;
|
}
|
|
static void generate_stub_code(const AidlInterface& iface, const AidlMethod& method, bool oneway,
|
Variable* transact_data, Variable* transact_reply,
|
JavaTypeNamespace* types, StatementBlock* statements,
|
StubClass* stubClass, const Options& options) {
|
TryStatement* tryStatement = nullptr;
|
FinallyStatement* finallyStatement = nullptr;
|
MethodCall* realCall = new MethodCall(THIS_VALUE, method.GetName());
|
|
// interface token validation is the very first thing we do
|
statements->Add(new MethodCall(transact_data,
|
"enforceInterface", 1,
|
stubClass->get_transact_descriptor(types,
|
&method)));
|
|
// args
|
VariableFactory stubArgs("_arg");
|
{
|
// keep this across different args in order to create the classloader
|
// at most once.
|
bool is_classloader_created = false;
|
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;
|
|
statements->Add(new VariableDeclaration(v));
|
|
if (arg->GetDirection() & AidlArgument::IN_DIR) {
|
string code;
|
CodeWriterPtr writer = CodeWriter::ForString(&code);
|
CodeGeneratorContext context{.writer = *(writer.get()),
|
.typenames = types->typenames_,
|
.type = arg->GetType(),
|
.var = v->name,
|
.parcel = transact_data->name,
|
.is_classloader_created = &is_classloader_created};
|
CreateFromParcelFor(context);
|
writer->Close();
|
statements->Add(new LiteralStatement(code));
|
} else {
|
if (!arg->GetType().IsArray()) {
|
statements->Add(new Assignment(v, new NewExpression(t->InstantiableName())));
|
} else {
|
generate_new_array(t, statements, v, transact_data, types);
|
}
|
}
|
|
realCall->arguments.push_back(v);
|
}
|
}
|
|
if (options.GenTraces()) {
|
// try and finally, but only when generating trace code
|
tryStatement = new TryStatement();
|
finallyStatement = new FinallyStatement();
|
|
tryStatement->statements->Add(new MethodCall(
|
new LiteralExpression("android.os.Trace"), "traceBegin", 2,
|
new LiteralExpression("android.os.Trace.TRACE_TAG_AIDL"),
|
new StringLiteralExpression(iface.GetName() + "::"
|
+ method.GetName() + "::server")));
|
|
finallyStatement->statements->Add(new MethodCall(
|
new LiteralExpression("android.os.Trace"), "traceEnd", 1,
|
new LiteralExpression("android.os.Trace.TRACE_TAG_AIDL")));
|
}
|
|
// the real call
|
if (method.GetType().GetName() == "void") {
|
if (options.GenTraces()) {
|
statements->Add(tryStatement);
|
tryStatement->statements->Add(realCall);
|
statements->Add(finallyStatement);
|
} else {
|
statements->Add(realCall);
|
}
|
|
if (!oneway) {
|
// report that there were no exceptions
|
MethodCall* ex =
|
new MethodCall(transact_reply, "writeNoException", 0);
|
statements->Add(ex);
|
}
|
} else {
|
Variable* _result = new Variable(method.GetType().GetLanguageType<Type>()->JavaType(),
|
"_result", method.GetType().IsArray() ? 1 : 0);
|
if (options.GenTraces()) {
|
statements->Add(new VariableDeclaration(_result));
|
statements->Add(tryStatement);
|
tryStatement->statements->Add(new Assignment(_result, realCall));
|
statements->Add(finallyStatement);
|
} else {
|
statements->Add(new VariableDeclaration(_result, realCall));
|
}
|
|
if (!oneway) {
|
// report that there were no exceptions
|
MethodCall* ex =
|
new MethodCall(transact_reply, "writeNoException", 0);
|
statements->Add(ex);
|
}
|
|
// marshall the return value
|
generate_write_to_parcel(method.GetType(), statements, _result, transact_reply, true,
|
types->typenames_);
|
}
|
|
// out parameters
|
int i = 0;
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
Variable* v = stubArgs.Get(i++);
|
|
if (arg->GetDirection() & AidlArgument::OUT_DIR) {
|
generate_write_to_parcel(arg->GetType(), statements, v, transact_reply, true,
|
types->typenames_);
|
}
|
}
|
|
// return true
|
statements->Add(new ReturnStatement(TRUE_VALUE));
|
}
|
|
static void generate_stub_case(const AidlInterface& iface, const AidlMethod& method,
|
const std::string& transactCodeName, bool oneway,
|
StubClass* stubClass, JavaTypeNamespace* types,
|
const Options& options) {
|
Case* c = new Case(transactCodeName);
|
|
generate_stub_code(iface, method, oneway, stubClass->transact_data, stubClass->transact_reply,
|
types, c->statements, stubClass, options);
|
|
stubClass->transact_switch->cases.push_back(c);
|
}
|
|
static void generate_stub_case_outline(const AidlInterface& iface, const AidlMethod& method,
|
const std::string& transactCodeName, bool oneway,
|
StubClass* stubClass, JavaTypeNamespace* types,
|
const Options& options) {
|
std::string outline_name = "onTransact$" + method.GetName() + "$";
|
// Generate an "outlined" method with the actual code.
|
{
|
Variable* transact_data = new Variable(types->ParcelType()->JavaType(), "data");
|
Variable* transact_reply = new Variable(types->ParcelType()->JavaType(), "reply");
|
Method* onTransact_case = new Method;
|
onTransact_case->modifiers = PRIVATE;
|
onTransact_case->returnType = types->BoolType()->JavaType();
|
onTransact_case->name = outline_name;
|
onTransact_case->parameters.push_back(transact_data);
|
onTransact_case->parameters.push_back(transact_reply);
|
onTransact_case->statements = new StatementBlock;
|
onTransact_case->exceptions.push_back(types->RemoteExceptionType()->JavaType());
|
stubClass->elements.push_back(onTransact_case);
|
|
generate_stub_code(iface, method, oneway, transact_data, transact_reply, types,
|
onTransact_case->statements, stubClass, options);
|
}
|
|
// Generate the case dispatch.
|
{
|
Case* c = new Case(transactCodeName);
|
|
MethodCall* helper_call = new MethodCall(THIS_VALUE,
|
outline_name,
|
2,
|
stubClass->transact_data,
|
stubClass->transact_reply);
|
c->statements->Add(new ReturnStatement(helper_call));
|
|
stubClass->transact_switch->cases.push_back(c);
|
}
|
}
|
|
static std::unique_ptr<Method> generate_proxy_method(
|
const AidlInterface& iface, const AidlMethod& method, const std::string& transactCodeName,
|
bool oneway, ProxyClass* proxyClass, JavaTypeNamespace* types, const Options& options) {
|
std::unique_ptr<Method> proxy(new Method);
|
proxy->comment = method.GetComments();
|
proxy->modifiers = PUBLIC | OVERRIDE;
|
proxy->returnType = method.GetType().GetLanguageType<Type>()->JavaType();
|
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>()->JavaType(),
|
arg->GetName(), arg->GetType().IsArray() ? 1 : 0));
|
}
|
proxy->exceptions.push_back(types->RemoteExceptionType()->JavaType());
|
|
// the parcels
|
Variable* _data = new Variable(types->ParcelType()->JavaType(), "_data");
|
proxy->statements->Add(
|
new VariableDeclaration(_data, new MethodCall(types->ParcelType()->JavaType(), "obtain")));
|
Variable* _reply = nullptr;
|
if (!oneway) {
|
_reply = new Variable(types->ParcelType()->JavaType(), "_reply");
|
proxy->statements->Add(
|
new VariableDeclaration(_reply, new MethodCall(types->ParcelType()->JavaType(), "obtain")));
|
}
|
|
// the return value
|
Variable* _result = nullptr;
|
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);
|
|
if (options.GenTraces()) {
|
tryStatement->statements->Add(new MethodCall(
|
new LiteralExpression("android.os.Trace"), "traceBegin", 2,
|
new LiteralExpression("android.os.Trace.TRACE_TAG_AIDL"),
|
new StringLiteralExpression(iface.GetName() + "::" +
|
method.GetName() + "::client")));
|
}
|
|
// 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->JavaType(), 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(arg->GetType(), tryStatement->statements, v, _data, false,
|
types->typenames_);
|
} else {
|
delete v;
|
}
|
}
|
|
// the transact call
|
unique_ptr<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")));
|
unique_ptr<Variable> _status(new Variable(types->BoolType()->JavaType(), "_status"));
|
tryStatement->statements->Add(new VariableDeclaration(_status.release(), call.release()));
|
|
// If the transaction returns false, which means UNKNOWN_TRANSACTION, fall
|
// back to the local method in the default impl, if set before.
|
vector<string> arg_names;
|
for (const auto& arg : method.GetArguments()) {
|
arg_names.emplace_back(arg->GetName());
|
}
|
bool has_return_type = method.GetType().GetName() != "void";
|
tryStatement->statements->Add(new LiteralStatement(
|
android::base::StringPrintf(has_return_type ? "if (!_status && getDefaultImpl() != null) {\n"
|
" return getDefaultImpl().%s(%s);\n"
|
"}\n"
|
: "if (!_status && getDefaultImpl() != null) {\n"
|
" getDefaultImpl().%s(%s);\n"
|
" return;\n"
|
"}\n",
|
method.GetName().c_str(), Join(arg_names, ", ").c_str())));
|
|
// throw back exceptions.
|
if (_reply) {
|
MethodCall* ex = new MethodCall(_reply, "readException", 0);
|
tryStatement->statements->Add(ex);
|
}
|
|
// returning and cleanup
|
if (_reply != nullptr) {
|
// keep this across return value and arguments in order to create the
|
// classloader at most once.
|
bool is_classloader_created = false;
|
if (_result != nullptr) {
|
string code;
|
CodeWriterPtr writer = CodeWriter::ForString(&code);
|
CodeGeneratorContext context{.writer = *(writer.get()),
|
.typenames = types->typenames_,
|
.type = method.GetType(),
|
.var = _result->name,
|
.parcel = _reply->name,
|
.is_classloader_created = &is_classloader_created};
|
CreateFromParcelFor(context);
|
writer->Close();
|
tryStatement->statements->Add(new LiteralStatement(code));
|
}
|
|
// the out/inout parameters
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
if (arg->GetDirection() & AidlArgument::OUT_DIR) {
|
string code;
|
CodeWriterPtr writer = CodeWriter::ForString(&code);
|
CodeGeneratorContext context{.writer = *(writer.get()),
|
.typenames = types->typenames_,
|
.type = arg->GetType(),
|
.var = arg->GetName(),
|
.parcel = _reply->name,
|
.is_classloader_created = &is_classloader_created};
|
ReadFromParcelFor(context);
|
writer->Close();
|
tryStatement->statements->Add(new LiteralStatement(code));
|
}
|
}
|
|
finallyStatement->statements->Add(new MethodCall(_reply, "recycle"));
|
}
|
finallyStatement->statements->Add(new MethodCall(_data, "recycle"));
|
|
if (options.GenTraces()) {
|
finallyStatement->statements->Add(new MethodCall(
|
new LiteralExpression("android.os.Trace"), "traceEnd", 1,
|
new LiteralExpression("android.os.Trace.TRACE_TAG_AIDL")));
|
}
|
|
if (_result != nullptr) {
|
proxy->statements->Add(new ReturnStatement(_result));
|
}
|
|
return proxy;
|
}
|
|
static void generate_methods(const AidlInterface& iface, const AidlMethod& method, Class* interface,
|
StubClass* stubClass, ProxyClass* proxyClass, int index,
|
JavaTypeNamespace* types, const Options& options) {
|
const bool oneway = method.IsOneway();
|
|
// == the TRANSACT_ constant =============================================
|
string transactCodeName = "TRANSACTION_";
|
transactCodeName += method.GetName();
|
|
Field* transactCode =
|
new Field(STATIC | FINAL, new Variable(types->IntType()->JavaType(), transactCodeName));
|
transactCode->value =
|
StringPrintf("(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)", index);
|
stubClass->elements.push_back(transactCode);
|
|
// getTransactionName
|
if (options.GenTransactionNames()) {
|
Case* c = new Case(transactCodeName);
|
c->statements->Add(new ReturnStatement(new StringLiteralExpression(method.GetName())));
|
stubClass->code_to_method_name_switch->cases.push_back(c);
|
}
|
|
// == the declaration in the interface ===================================
|
ClassElement* decl;
|
if (method.IsUserDefined()) {
|
decl = generate_interface_method(method, types).release();
|
} else {
|
if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
|
std::ostringstream code;
|
code << "public int " << kGetInterfaceVersion << "() "
|
<< "throws android.os.RemoteException;\n";
|
decl = new LiteralClassElement(code.str());
|
}
|
}
|
interface->elements.push_back(decl);
|
|
// == the stub method ====================================================
|
if (method.IsUserDefined()) {
|
bool outline_stub =
|
stubClass->transact_outline && stubClass->outline_methods.count(&method) != 0;
|
if (outline_stub) {
|
generate_stub_case_outline(iface, method, transactCodeName, oneway, stubClass, types,
|
options);
|
} else {
|
generate_stub_case(iface, method, transactCodeName, oneway, stubClass, types, options);
|
}
|
} else {
|
if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
|
Case* c = new Case(transactCodeName);
|
std::ostringstream code;
|
code << "data.enforceInterface(descriptor);\n"
|
<< "reply.writeNoException();\n"
|
<< "reply.writeInt(" << kGetInterfaceVersion << "());\n"
|
<< "return true;\n";
|
c->statements->Add(new LiteralStatement(code.str()));
|
stubClass->transact_switch->cases.push_back(c);
|
}
|
}
|
|
// == the proxy method ===================================================
|
ClassElement* proxy = nullptr;
|
if (method.IsUserDefined()) {
|
proxy =
|
generate_proxy_method(iface, method, transactCodeName, oneway, proxyClass, types, options)
|
.release();
|
|
} else {
|
if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
|
std::ostringstream code;
|
code << "@Override\n"
|
<< "public int " << kGetInterfaceVersion << "()"
|
<< " throws "
|
<< "android.os.RemoteException {\n"
|
<< " if (mCachedVersion == -1) {\n"
|
<< " android.os.Parcel data = android.os.Parcel.obtain();\n"
|
<< " android.os.Parcel reply = android.os.Parcel.obtain();\n"
|
<< " try {\n"
|
<< " data.writeInterfaceToken(DESCRIPTOR);\n"
|
<< " mRemote.transact(Stub." << transactCodeName << ", "
|
<< "data, reply, 0);\n"
|
<< " reply.readException();\n"
|
<< " mCachedVersion = reply.readInt();\n"
|
<< " } finally {\n"
|
<< " reply.recycle();\n"
|
<< " data.recycle();\n"
|
<< " }\n"
|
<< " }\n"
|
<< " return mCachedVersion;\n"
|
<< "}\n";
|
proxy = new LiteralClassElement(code.str());
|
}
|
}
|
if (proxy != nullptr) {
|
proxyClass->elements.push_back(proxy);
|
}
|
}
|
|
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,
|
stub->get_transact_descriptor(types,
|
nullptr)));
|
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()->JavaType();
|
getDesc->returnTypeDimension = 0;
|
getDesc->name = "getInterfaceDescriptor";
|
getDesc->statements = new StatementBlock;
|
getDesc->statements->Add(
|
new ReturnStatement(new LiteralExpression("DESCRIPTOR")));
|
proxy->elements.push_back(getDesc);
|
}
|
|
// Check whether (some) methods in this interface should be "outlined," that
|
// is, have specific onTransact methods for certain cases. Set up StubClass
|
// metadata accordingly.
|
//
|
// Outlining will be enabled if the interface has more than outline_threshold
|
// methods. In that case, the methods are sorted by number of arguments
|
// (so that more "complex" methods come later), and the first non_outline_count
|
// number of methods not outlined (are kept in the onTransact() method).
|
//
|
// Requirements: non_outline_count <= outline_threshold.
|
static void compute_outline_methods(const AidlInterface* iface,
|
StubClass* stub,
|
size_t outline_threshold,
|
size_t non_outline_count) {
|
CHECK_LE(non_outline_count, outline_threshold);
|
// We'll outline (create sub methods) if there are more than min_methods
|
// cases.
|
stub->transact_outline = iface->GetMethods().size() > outline_threshold;
|
if (stub->transact_outline) {
|
stub->all_method_count = iface->GetMethods().size();
|
std::vector<const AidlMethod*> methods;
|
methods.reserve(iface->GetMethods().size());
|
for (const std::unique_ptr<AidlMethod>& ptr : iface->GetMethods()) {
|
methods.push_back(ptr.get());
|
}
|
|
std::stable_sort(
|
methods.begin(),
|
methods.end(),
|
[](const AidlMethod* m1, const AidlMethod* m2) {
|
return m1->GetArguments().size() < m2->GetArguments().size();
|
});
|
|
stub->outline_methods.insert(methods.begin() + non_outline_count,
|
methods.end());
|
}
|
}
|
|
static unique_ptr<ClassElement> generate_default_impl_method(const AidlMethod& method) {
|
unique_ptr<Method> default_method(new Method);
|
default_method->comment = method.GetComments();
|
default_method->modifiers = PUBLIC | OVERRIDE;
|
default_method->returnType = method.GetType().GetLanguageType<Type>()->JavaType();
|
default_method->returnTypeDimension = method.GetType().IsArray() ? 1 : 0;
|
default_method->name = method.GetName();
|
default_method->statements = new StatementBlock;
|
for (const auto& arg : method.GetArguments()) {
|
default_method->parameters.push_back(
|
new Variable(arg->GetType().GetLanguageType<Type>()->JavaType(), arg->GetName(),
|
arg->GetType().IsArray() ? 1 : 0));
|
}
|
default_method->exceptions.push_back(method.GetType()
|
.GetLanguageType<Type>()
|
->GetTypeNamespace()
|
->RemoteExceptionType()
|
->JavaType());
|
|
if (method.GetType().GetName() != "void") {
|
const string& defaultValue = DefaultJavaValueOf(method.GetType());
|
default_method->statements->Add(
|
new LiteralStatement(StringPrintf("return %s;\n", defaultValue.c_str())));
|
}
|
return default_method;
|
}
|
|
static unique_ptr<Class> generate_default_impl_class(const AidlInterface& iface,
|
const Options& options) {
|
unique_ptr<Class> default_class(new Class);
|
default_class->comment = "/** Default implementation for " + iface.GetName() + ". */";
|
default_class->modifiers = PUBLIC | STATIC;
|
default_class->what = Class::CLASS;
|
default_class->type = iface.GetLanguageType<InterfaceType>()->GetDefaultImpl()->JavaType();
|
default_class->interfaces.emplace_back(iface.GetLanguageType<InterfaceType>()->JavaType());
|
|
for (const auto& m : iface.GetMethods()) {
|
if (m->IsUserDefined()) {
|
default_class->elements.emplace_back(generate_default_impl_method(*(m.get())).release());
|
} else {
|
if (m->GetName() == kGetInterfaceVersion && options.Version() > 0) {
|
// This is called only when the remote side is not implementing this
|
// method, which is impossible in normal case, because this method is
|
// automatically declared in the interface class and not implementing
|
// it in the remote side is causing compilation error. But if the remote
|
// side somehow managed to not implement it, that's an error and we
|
// report the case by returning -1 here.
|
std::ostringstream code;
|
code << "@Override\n"
|
<< "public int " << kGetInterfaceVersion << "() {\n"
|
<< " return -1;\n"
|
<< "}\n";
|
default_class->elements.emplace_back(new LiteralClassElement(code.str()));
|
}
|
}
|
}
|
|
default_class->elements.emplace_back(
|
new LiteralClassElement("@Override\n"
|
"public android.os.IBinder asBinder() {\n"
|
" return null;\n"
|
"}\n"));
|
|
return default_class;
|
}
|
|
Class* generate_binder_interface_class(const AidlInterface* iface, JavaTypeNamespace* types,
|
const Options& options) {
|
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->JavaType();
|
interface->interfaces.push_back(types->IInterfaceType()->JavaType());
|
interface->annotations = generate_java_annotations(*iface);
|
|
if (options.Version()) {
|
std::ostringstream code;
|
code << "/**\n"
|
<< " * The version of this interface that the caller is built against.\n"
|
<< " * This might be different from what {@link #getInterfaceVersion()\n"
|
<< " * getInterfaceVersion} returns as that is the version of the interface\n"
|
<< " * that the remote object is implementing.\n"
|
<< " */\n"
|
<< "public static final int VERSION = " << options.Version() << ";\n";
|
interface->elements.emplace_back(new LiteralClassElement(code.str()));
|
}
|
|
// the default impl class
|
Class* default_impl = generate_default_impl_class(*iface, options).release();
|
interface->elements.emplace_back(default_impl);
|
|
// the stub inner class
|
StubClass* stub =
|
new StubClass(interfaceType->GetStub(), interfaceType, types, options);
|
interface->elements.push_back(stub);
|
|
compute_outline_methods(iface,
|
stub,
|
options.onTransact_outline_threshold_,
|
options.onTransact_non_outline_count_);
|
|
// the proxy inner class
|
ProxyClass* proxy = new ProxyClass(types, interfaceType->GetProxy(), interfaceType, options);
|
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& constant : iface->GetConstantDeclarations()) {
|
const AidlConstantValue& value = constant->GetValue();
|
|
switch (value.GetType()) {
|
case AidlConstantValue::Type::STRING: {
|
generate_string_constant(interface, constant->GetName(),
|
constant->ValueString(ConstantValueDecorator));
|
break;
|
}
|
case AidlConstantValue::Type::INTEGRAL:
|
case AidlConstantValue::Type::HEXIDECIMAL: {
|
generate_int_constant(interface, constant->GetName(),
|
constant->ValueString(ConstantValueDecorator));
|
break;
|
}
|
default: {
|
LOG(FATAL) << "Unrecognized constant type: " << static_cast<int>(value.GetType());
|
}
|
}
|
}
|
|
// all the declared methods of the interface
|
|
for (const auto& item : iface->GetMethods()) {
|
generate_methods(*iface,
|
*item,
|
interface,
|
stub,
|
proxy,
|
item->GetId(),
|
types,
|
options);
|
}
|
|
// additional static methods for the default impl set/get to the
|
// stub class. Can't add them to the interface as the generated java files
|
// may be compiled with Java < 1.7 where static interface method isn't
|
// supported.
|
// TODO(b/111417145) make this conditional depending on the Java language
|
// version requested
|
const string i_name = interfaceType->JavaType();
|
stub->elements.emplace_back(new LiteralClassElement(
|
StringPrintf("public static boolean setDefaultImpl(%s impl) {\n"
|
" if (Stub.Proxy.sDefaultImpl == null && impl != null) {\n"
|
" Stub.Proxy.sDefaultImpl = impl;\n"
|
" return true;\n"
|
" }\n"
|
" return false;\n"
|
"}\n",
|
i_name.c_str())));
|
stub->elements.emplace_back(
|
new LiteralClassElement(StringPrintf("public static %s getDefaultImpl() {\n"
|
" return Stub.Proxy.sDefaultImpl;\n"
|
"}\n",
|
i_name.c_str())));
|
|
// the static field is defined in the proxy class, not in the interface class
|
// because all fields in an interface class are by default final.
|
proxy->elements.emplace_back(new LiteralClassElement(
|
StringPrintf("public static %s sDefaultImpl;\n", i_name.c_str())));
|
|
stub->finish();
|
|
return interface;
|
}
|
|
} // namespace java
|
} // namespace android
|
} // namespace aidl
|