554 lines
20 KiB
C++
554 lines
20 KiB
C++
/*
|
|
* Copyright (C) 2016, The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "generate_java.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <string.h>
|
|
|
|
#include <android-base/macros.h>
|
|
|
|
#include "type_java.h"
|
|
|
|
using std::string;
|
|
|
|
namespace android {
|
|
namespace aidl {
|
|
namespace java {
|
|
|
|
// =================================================
|
|
class StubClass : public Class {
|
|
public:
|
|
StubClass(const Type* type, const InterfaceType* interfaceType,
|
|
JavaTypeNamespace* types);
|
|
virtual ~StubClass() = default;
|
|
|
|
Variable* transact_code;
|
|
Variable* transact_data;
|
|
Variable* transact_reply;
|
|
Variable* transact_flags;
|
|
SwitchStatement* transact_switch;
|
|
|
|
private:
|
|
void make_as_interface(const InterfaceType* interfaceType,
|
|
JavaTypeNamespace* types);
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(StubClass);
|
|
};
|
|
|
|
StubClass::StubClass(const Type* type, const InterfaceType* interfaceType,
|
|
JavaTypeNamespace* types)
|
|
: Class() {
|
|
this->comment = "/** Local-side IPC implementation stub class. */";
|
|
this->modifiers = PUBLIC | ABSTRACT | STATIC;
|
|
this->what = Class::CLASS;
|
|
this->type = type;
|
|
this->extends = types->BinderNativeType();
|
|
this->interfaces.push_back(interfaceType);
|
|
|
|
// descriptor
|
|
Field* descriptor =
|
|
new Field(STATIC | FINAL | PRIVATE,
|
|
new Variable(types->StringType(), "DESCRIPTOR"));
|
|
descriptor->value = "\"" + interfaceType->JavaType() + "\"";
|
|
this->elements.push_back(descriptor);
|
|
|
|
// ctor
|
|
Method* ctor = new Method;
|
|
ctor->modifiers = PUBLIC;
|
|
ctor->comment =
|
|
"/** Construct the stub at attach it to the "
|
|
"interface. */";
|
|
ctor->name = "Stub";
|
|
ctor->statements = new StatementBlock;
|
|
MethodCall* attach =
|
|
new MethodCall(THIS_VALUE, "attachInterface", 2, THIS_VALUE,
|
|
new LiteralExpression("DESCRIPTOR"));
|
|
ctor->statements->Add(attach);
|
|
this->elements.push_back(ctor);
|
|
|
|
// asInterface
|
|
make_as_interface(interfaceType, types);
|
|
|
|
// asBinder
|
|
Method* asBinder = new Method;
|
|
asBinder->modifiers = PUBLIC | OVERRIDE;
|
|
asBinder->returnType = types->IBinderType();
|
|
asBinder->name = "asBinder";
|
|
asBinder->statements = new StatementBlock;
|
|
asBinder->statements->Add(new ReturnStatement(THIS_VALUE));
|
|
this->elements.push_back(asBinder);
|
|
|
|
// onTransact
|
|
this->transact_code = new Variable(types->IntType(), "code");
|
|
this->transact_data = new Variable(types->ParcelType(), "data");
|
|
this->transact_reply = new Variable(types->ParcelType(), "reply");
|
|
this->transact_flags = new Variable(types->IntType(), "flags");
|
|
Method* onTransact = new Method;
|
|
onTransact->modifiers = PUBLIC | OVERRIDE;
|
|
onTransact->returnType = types->BoolType();
|
|
onTransact->name = "onTransact";
|
|
onTransact->parameters.push_back(this->transact_code);
|
|
onTransact->parameters.push_back(this->transact_data);
|
|
onTransact->parameters.push_back(this->transact_reply);
|
|
onTransact->parameters.push_back(this->transact_flags);
|
|
onTransact->statements = new StatementBlock;
|
|
onTransact->exceptions.push_back(types->RemoteExceptionType());
|
|
this->elements.push_back(onTransact);
|
|
this->transact_switch = new SwitchStatement(this->transact_code);
|
|
|
|
onTransact->statements->Add(this->transact_switch);
|
|
MethodCall* superCall = new MethodCall(
|
|
SUPER_VALUE, "onTransact", 4, this->transact_code, this->transact_data,
|
|
this->transact_reply, this->transact_flags);
|
|
onTransact->statements->Add(new ReturnStatement(superCall));
|
|
}
|
|
|
|
void StubClass::make_as_interface(const InterfaceType* interfaceType,
|
|
JavaTypeNamespace* types) {
|
|
Variable* obj = new Variable(types->IBinderType(), "obj");
|
|
|
|
Method* m = new Method;
|
|
m->comment = "/**\n * Cast an IBinder object into an ";
|
|
m->comment += interfaceType->JavaType();
|
|
m->comment += " interface,\n";
|
|
m->comment += " * generating a proxy if needed.\n */";
|
|
m->modifiers = PUBLIC | STATIC;
|
|
m->returnType = interfaceType;
|
|
m->name = "asInterface";
|
|
m->parameters.push_back(obj);
|
|
m->statements = new StatementBlock;
|
|
|
|
IfStatement* ifstatement = new IfStatement();
|
|
ifstatement->expression = new Comparison(obj, "==", NULL_VALUE);
|
|
ifstatement->statements = new StatementBlock;
|
|
ifstatement->statements->Add(new ReturnStatement(NULL_VALUE));
|
|
m->statements->Add(ifstatement);
|
|
|
|
// IInterface iin = obj.queryLocalInterface(DESCRIPTOR)
|
|
MethodCall* queryLocalInterface = new MethodCall(obj, "queryLocalInterface");
|
|
queryLocalInterface->arguments.push_back(new LiteralExpression("DESCRIPTOR"));
|
|
IInterfaceType* iinType = new IInterfaceType(types);
|
|
Variable* iin = new Variable(iinType, "iin");
|
|
VariableDeclaration* iinVd =
|
|
new VariableDeclaration(iin, queryLocalInterface, NULL);
|
|
m->statements->Add(iinVd);
|
|
|
|
// Ensure the instance type of the local object is as expected.
|
|
// One scenario where this is needed is if another package (with a
|
|
// different class loader) runs in the same process as the service.
|
|
|
|
// if (iin != null && iin instanceof <interfaceType>) return (<interfaceType>)
|
|
// iin;
|
|
Comparison* iinNotNull = new Comparison(iin, "!=", NULL_VALUE);
|
|
Comparison* instOfCheck =
|
|
new Comparison(iin, " instanceof ",
|
|
new LiteralExpression(interfaceType->JavaType()));
|
|
IfStatement* instOfStatement = new IfStatement();
|
|
instOfStatement->expression = new Comparison(iinNotNull, "&&", instOfCheck);
|
|
instOfStatement->statements = new StatementBlock;
|
|
instOfStatement->statements->Add(
|
|
new ReturnStatement(new Cast(interfaceType, iin)));
|
|
m->statements->Add(instOfStatement);
|
|
|
|
NewExpression* ne = new NewExpression(interfaceType->GetProxy());
|
|
ne->arguments.push_back(obj);
|
|
m->statements->Add(new ReturnStatement(ne));
|
|
|
|
this->elements.push_back(m);
|
|
}
|
|
|
|
// =================================================
|
|
class ProxyClass : public Class {
|
|
public:
|
|
ProxyClass(const JavaTypeNamespace* types, const Type* type,
|
|
const InterfaceType* interfaceType);
|
|
virtual ~ProxyClass();
|
|
|
|
Variable* mRemote;
|
|
bool mOneWay;
|
|
};
|
|
|
|
ProxyClass::ProxyClass(const JavaTypeNamespace* types, const Type* type,
|
|
const InterfaceType* interfaceType)
|
|
: Class() {
|
|
this->modifiers = PRIVATE | STATIC;
|
|
this->what = Class::CLASS;
|
|
this->type = type;
|
|
this->interfaces.push_back(interfaceType);
|
|
|
|
mOneWay = interfaceType->OneWay();
|
|
|
|
// IBinder mRemote
|
|
mRemote = new Variable(types->IBinderType(), "mRemote");
|
|
this->elements.push_back(new Field(PRIVATE, mRemote));
|
|
|
|
// Proxy()
|
|
Variable* remote = new Variable(types->IBinderType(), "remote");
|
|
Method* ctor = new Method;
|
|
ctor->name = "Proxy";
|
|
ctor->statements = new StatementBlock;
|
|
ctor->parameters.push_back(remote);
|
|
ctor->statements->Add(new Assignment(mRemote, remote));
|
|
this->elements.push_back(ctor);
|
|
|
|
// IBinder asBinder()
|
|
Method* asBinder = new Method;
|
|
asBinder->modifiers = PUBLIC | OVERRIDE;
|
|
asBinder->returnType = types->IBinderType();
|
|
asBinder->name = "asBinder";
|
|
asBinder->statements = new StatementBlock;
|
|
asBinder->statements->Add(new ReturnStatement(mRemote));
|
|
this->elements.push_back(asBinder);
|
|
}
|
|
|
|
ProxyClass::~ProxyClass() {}
|
|
|
|
// =================================================
|
|
static void generate_new_array(const Type* t, StatementBlock* addTo,
|
|
Variable* v, Variable* parcel,
|
|
JavaTypeNamespace* types) {
|
|
Variable* len = new Variable(types->IntType(), v->name + "_length");
|
|
addTo->Add(new VariableDeclaration(len, new MethodCall(parcel, "readInt")));
|
|
IfStatement* lencheck = new IfStatement();
|
|
lencheck->expression = new Comparison(len, "<", new LiteralExpression("0"));
|
|
lencheck->statements->Add(new Assignment(v, NULL_VALUE));
|
|
lencheck->elseif = new IfStatement();
|
|
lencheck->elseif->statements->Add(
|
|
new Assignment(v, new NewArrayExpression(t, len)));
|
|
addTo->Add(lencheck);
|
|
}
|
|
|
|
static void generate_write_to_parcel(const Type* t, StatementBlock* addTo,
|
|
Variable* v, Variable* parcel, int flags) {
|
|
t->WriteToParcel(addTo, v, parcel, flags);
|
|
}
|
|
|
|
static void generate_create_from_parcel(const Type* t, StatementBlock* addTo,
|
|
Variable* v, Variable* parcel,
|
|
Variable** cl) {
|
|
t->CreateFromParcel(addTo, v, parcel, cl);
|
|
}
|
|
|
|
static void generate_int_constant(const AidlIntConstant& constant,
|
|
Class* interface) {
|
|
IntConstant* decl = new IntConstant(constant.GetName(), constant.GetValue());
|
|
interface->elements.push_back(decl);
|
|
}
|
|
|
|
static void generate_string_constant(const AidlStringConstant& constant,
|
|
Class* interface) {
|
|
StringConstant* decl = new StringConstant(constant.GetName(),
|
|
constant.GetValue());
|
|
interface->elements.push_back(decl);
|
|
}
|
|
|
|
static void generate_method(const AidlMethod& method, Class* interface,
|
|
StubClass* stubClass, ProxyClass* proxyClass,
|
|
int index, JavaTypeNamespace* types) {
|
|
int i;
|
|
|
|
const bool oneway = proxyClass->mOneWay || method.IsOneway();
|
|
|
|
// == the TRANSACT_ constant =============================================
|
|
string transactCodeName = "TRANSACTION_";
|
|
transactCodeName += method.GetName();
|
|
|
|
char transactCodeValue[60];
|
|
sprintf(transactCodeValue, "(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)",
|
|
index);
|
|
|
|
Field* transactCode = new Field(
|
|
STATIC | FINAL, new Variable(types->IntType(), transactCodeName));
|
|
transactCode->value = transactCodeValue;
|
|
stubClass->elements.push_back(transactCode);
|
|
|
|
// == the declaration in the interface ===================================
|
|
Method* decl = new Method;
|
|
decl->comment = method.GetComments();
|
|
decl->modifiers = PUBLIC;
|
|
decl->returnType = method.GetType().GetLanguageType<Type>();
|
|
decl->returnTypeDimension = method.GetType().IsArray() ? 1 : 0;
|
|
decl->name = method.GetName();
|
|
|
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
|
decl->parameters.push_back(
|
|
new Variable(arg->GetType().GetLanguageType<Type>(), arg->GetName(),
|
|
arg->GetType().IsArray() ? 1 : 0));
|
|
}
|
|
|
|
decl->exceptions.push_back(types->RemoteExceptionType());
|
|
|
|
interface->elements.push_back(decl);
|
|
|
|
// == the stub method ====================================================
|
|
|
|
Case* c = new Case(transactCodeName);
|
|
|
|
MethodCall* realCall = new MethodCall(THIS_VALUE, method.GetName());
|
|
|
|
// interface token validation is the very first thing we do
|
|
c->statements->Add(new MethodCall(stubClass->transact_data,
|
|
"enforceInterface", 1,
|
|
new LiteralExpression("DESCRIPTOR")));
|
|
|
|
// args
|
|
Variable* cl = NULL;
|
|
VariableFactory stubArgs("_arg");
|
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
|
const Type* t = arg->GetType().GetLanguageType<Type>();
|
|
Variable* v = stubArgs.Get(t);
|
|
v->dimension = arg->GetType().IsArray() ? 1 : 0;
|
|
|
|
c->statements->Add(new VariableDeclaration(v));
|
|
|
|
if (arg->GetDirection() & AidlArgument::IN_DIR) {
|
|
generate_create_from_parcel(t, c->statements, v, stubClass->transact_data,
|
|
&cl);
|
|
} else {
|
|
if (!arg->GetType().IsArray()) {
|
|
c->statements->Add(new Assignment(v, new NewExpression(v->type)));
|
|
} else {
|
|
generate_new_array(v->type, c->statements, v, stubClass->transact_data,
|
|
types);
|
|
}
|
|
}
|
|
|
|
realCall->arguments.push_back(v);
|
|
}
|
|
|
|
cl = NULL;
|
|
|
|
// the real call
|
|
Variable* _result = NULL;
|
|
if (method.GetType().GetName() == "void") {
|
|
c->statements->Add(realCall);
|
|
|
|
if (!oneway) {
|
|
// report that there were no exceptions
|
|
MethodCall* ex =
|
|
new MethodCall(stubClass->transact_reply, "writeNoException", 0);
|
|
c->statements->Add(ex);
|
|
}
|
|
} else {
|
|
_result =
|
|
new Variable(decl->returnType, "_result", decl->returnTypeDimension);
|
|
c->statements->Add(new VariableDeclaration(_result, realCall));
|
|
|
|
if (!oneway) {
|
|
// report that there were no exceptions
|
|
MethodCall* ex =
|
|
new MethodCall(stubClass->transact_reply, "writeNoException", 0);
|
|
c->statements->Add(ex);
|
|
}
|
|
|
|
// marshall the return value
|
|
generate_write_to_parcel(decl->returnType, c->statements, _result,
|
|
stubClass->transact_reply,
|
|
Type::PARCELABLE_WRITE_RETURN_VALUE);
|
|
}
|
|
|
|
// out parameters
|
|
i = 0;
|
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
|
const Type* t = arg->GetType().GetLanguageType<Type>();
|
|
Variable* v = stubArgs.Get(i++);
|
|
|
|
if (arg->GetDirection() & AidlArgument::OUT_DIR) {
|
|
generate_write_to_parcel(t, c->statements, v, stubClass->transact_reply,
|
|
Type::PARCELABLE_WRITE_RETURN_VALUE);
|
|
}
|
|
}
|
|
|
|
// return true
|
|
c->statements->Add(new ReturnStatement(TRUE_VALUE));
|
|
stubClass->transact_switch->cases.push_back(c);
|
|
|
|
// == the proxy method ===================================================
|
|
Method* proxy = new Method;
|
|
proxy->comment = method.GetComments();
|
|
proxy->modifiers = PUBLIC | OVERRIDE;
|
|
proxy->returnType = method.GetType().GetLanguageType<Type>();
|
|
proxy->returnTypeDimension = method.GetType().IsArray() ? 1 : 0;
|
|
proxy->name = method.GetName();
|
|
proxy->statements = new StatementBlock;
|
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
|
proxy->parameters.push_back(
|
|
new Variable(arg->GetType().GetLanguageType<Type>(), arg->GetName(),
|
|
arg->GetType().IsArray() ? 1 : 0));
|
|
}
|
|
proxy->exceptions.push_back(types->RemoteExceptionType());
|
|
proxyClass->elements.push_back(proxy);
|
|
|
|
// the parcels
|
|
Variable* _data = new Variable(types->ParcelType(), "_data");
|
|
proxy->statements->Add(new VariableDeclaration(
|
|
_data, new MethodCall(types->ParcelType(), "obtain")));
|
|
Variable* _reply = NULL;
|
|
if (!oneway) {
|
|
_reply = new Variable(types->ParcelType(), "_reply");
|
|
proxy->statements->Add(new VariableDeclaration(
|
|
_reply, new MethodCall(types->ParcelType(), "obtain")));
|
|
}
|
|
|
|
// the return value
|
|
_result = NULL;
|
|
if (method.GetType().GetName() != "void") {
|
|
_result = new Variable(proxy->returnType, "_result",
|
|
method.GetType().IsArray() ? 1 : 0);
|
|
proxy->statements->Add(new VariableDeclaration(_result));
|
|
}
|
|
|
|
// try and finally
|
|
TryStatement* tryStatement = new TryStatement();
|
|
proxy->statements->Add(tryStatement);
|
|
FinallyStatement* finallyStatement = new FinallyStatement();
|
|
proxy->statements->Add(finallyStatement);
|
|
|
|
// the interface identifier token: the DESCRIPTOR constant, marshalled as a
|
|
// string
|
|
tryStatement->statements->Add(new MethodCall(
|
|
_data, "writeInterfaceToken", 1, new LiteralExpression("DESCRIPTOR")));
|
|
|
|
// the parameters
|
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
|
const Type* t = arg->GetType().GetLanguageType<Type>();
|
|
Variable* v =
|
|
new Variable(t, arg->GetName(), arg->GetType().IsArray() ? 1 : 0);
|
|
AidlArgument::Direction dir = arg->GetDirection();
|
|
if (dir == AidlArgument::OUT_DIR && arg->GetType().IsArray()) {
|
|
IfStatement* checklen = new IfStatement();
|
|
checklen->expression = new Comparison(v, "==", NULL_VALUE);
|
|
checklen->statements->Add(
|
|
new MethodCall(_data, "writeInt", 1, new LiteralExpression("-1")));
|
|
checklen->elseif = new IfStatement();
|
|
checklen->elseif->statements->Add(
|
|
new MethodCall(_data, "writeInt", 1, new FieldVariable(v, "length")));
|
|
tryStatement->statements->Add(checklen);
|
|
} else if (dir & AidlArgument::IN_DIR) {
|
|
generate_write_to_parcel(t, tryStatement->statements, v, _data, 0);
|
|
} else {
|
|
delete v;
|
|
}
|
|
}
|
|
|
|
// the transact call
|
|
MethodCall* call = new MethodCall(
|
|
proxyClass->mRemote, "transact", 4,
|
|
new LiteralExpression("Stub." + transactCodeName), _data,
|
|
_reply ? _reply : NULL_VALUE,
|
|
new LiteralExpression(oneway ? "android.os.IBinder.FLAG_ONEWAY" : "0"));
|
|
tryStatement->statements->Add(call);
|
|
|
|
// throw back exceptions.
|
|
if (_reply) {
|
|
MethodCall* ex = new MethodCall(_reply, "readException", 0);
|
|
tryStatement->statements->Add(ex);
|
|
}
|
|
|
|
// returning and cleanup
|
|
if (_reply != NULL) {
|
|
if (_result != NULL) {
|
|
generate_create_from_parcel(proxy->returnType, tryStatement->statements,
|
|
_result, _reply, &cl);
|
|
}
|
|
|
|
// the out/inout parameters
|
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
|
const Type* t = arg->GetType().GetLanguageType<Type>();
|
|
if (arg->GetDirection() & AidlArgument::OUT_DIR) {
|
|
Variable* v =
|
|
new Variable(t, arg->GetName(), arg->GetType().IsArray() ? 1 : 0);
|
|
t->ReadFromParcel(tryStatement->statements, v, _reply, &cl);
|
|
}
|
|
}
|
|
|
|
finallyStatement->statements->Add(new MethodCall(_reply, "recycle"));
|
|
}
|
|
finallyStatement->statements->Add(new MethodCall(_data, "recycle"));
|
|
|
|
if (_result != NULL) {
|
|
proxy->statements->Add(new ReturnStatement(_result));
|
|
}
|
|
}
|
|
|
|
static void generate_interface_descriptors(StubClass* stub, ProxyClass* proxy,
|
|
const JavaTypeNamespace* types) {
|
|
// the interface descriptor transaction handler
|
|
Case* c = new Case("INTERFACE_TRANSACTION");
|
|
c->statements->Add(new MethodCall(stub->transact_reply, "writeString", 1,
|
|
new LiteralExpression("DESCRIPTOR")));
|
|
c->statements->Add(new ReturnStatement(TRUE_VALUE));
|
|
stub->transact_switch->cases.push_back(c);
|
|
|
|
// and the proxy-side method returning the descriptor directly
|
|
Method* getDesc = new Method;
|
|
getDesc->modifiers = PUBLIC;
|
|
getDesc->returnType = types->StringType();
|
|
getDesc->returnTypeDimension = 0;
|
|
getDesc->name = "getInterfaceDescriptor";
|
|
getDesc->statements = new StatementBlock;
|
|
getDesc->statements->Add(
|
|
new ReturnStatement(new LiteralExpression("DESCRIPTOR")));
|
|
proxy->elements.push_back(getDesc);
|
|
}
|
|
|
|
Class* generate_binder_interface_class(const AidlInterface* iface,
|
|
JavaTypeNamespace* types) {
|
|
const InterfaceType* interfaceType = iface->GetLanguageType<InterfaceType>();
|
|
|
|
// the interface class
|
|
Class* interface = new Class;
|
|
interface->comment = iface->GetComments();
|
|
interface->modifiers = PUBLIC;
|
|
interface->what = Class::INTERFACE;
|
|
interface->type = interfaceType;
|
|
interface->interfaces.push_back(types->IInterfaceType());
|
|
|
|
// the stub inner class
|
|
StubClass* stub =
|
|
new StubClass(interfaceType->GetStub(), interfaceType, types);
|
|
interface->elements.push_back(stub);
|
|
|
|
// the proxy inner class
|
|
ProxyClass* proxy =
|
|
new ProxyClass(types, interfaceType->GetProxy(), interfaceType);
|
|
stub->elements.push_back(proxy);
|
|
|
|
// stub and proxy support for getInterfaceDescriptor()
|
|
generate_interface_descriptors(stub, proxy, types);
|
|
|
|
// all the declared constants of the interface
|
|
for (const auto& item : iface->GetIntConstants()) {
|
|
generate_int_constant(*item, interface);
|
|
}
|
|
for (const auto& item : iface->GetStringConstants()) {
|
|
generate_string_constant(*item, interface);
|
|
}
|
|
|
|
// all the declared methods of the interface
|
|
for (const auto& item : iface->GetMethods()) {
|
|
generate_method(*item, interface, stub, proxy, item->GetId(), types);
|
|
}
|
|
|
|
return interface;
|
|
}
|
|
|
|
} // namespace java
|
|
} // namespace android
|
|
} // namespace aidl
|