1443 lines
62 KiB
C++
1443 lines
62 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 "aidl.h"
|
|
#include "aidl_to_java.h"
|
|
#include "aidl_typenames.h"
|
|
#include "ast_java.h"
|
|
#include "generate_java.h"
|
|
#include "logging.h"
|
|
#include "options.h"
|
|
#include "parser.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <algorithm>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#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;
|
|
|
|
explicit VariableFactory(const std::string& base) : base_(base), index_(0) {}
|
|
std::shared_ptr<Variable> Get(const AidlTypeSpecifier& type) {
|
|
auto v = std::make_shared<Variable>(JavaSignatureOf(type),
|
|
StringPrintf("%s%d", base_.c_str(), index_));
|
|
vars_.push_back(v);
|
|
index_++;
|
|
return v;
|
|
}
|
|
|
|
std::shared_ptr<Variable> Get(int index) { return vars_[index]; }
|
|
|
|
private:
|
|
std::vector<std::shared_ptr<Variable>> vars_;
|
|
std::string base_;
|
|
int index_;
|
|
};
|
|
|
|
// =================================================
|
|
class StubClass : public Class {
|
|
public:
|
|
StubClass(const AidlInterface* interfaceType, const Options& options);
|
|
~StubClass() override = default;
|
|
|
|
// non-copyable, non-movable
|
|
StubClass(const StubClass&) = delete;
|
|
StubClass(StubClass&&) = delete;
|
|
StubClass& operator=(const StubClass&) = delete;
|
|
StubClass& operator=(StubClass&&) = delete;
|
|
|
|
std::shared_ptr<Variable> transact_code;
|
|
std::shared_ptr<Variable> transact_data;
|
|
std::shared_ptr<Variable> transact_reply;
|
|
std::shared_ptr<Variable> transact_flags;
|
|
std::shared_ptr<SwitchStatement> transact_switch_meta;
|
|
std::shared_ptr<SwitchStatement> transact_switch_user;
|
|
std::shared_ptr<StatementBlock> transact_statements;
|
|
std::shared_ptr<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();
|
|
|
|
std::shared_ptr<Expression> GetTransactDescriptor(const AidlMethod* method);
|
|
|
|
private:
|
|
void MakeAsInterface(const AidlInterface* interfaceType);
|
|
|
|
std::shared_ptr<Variable> transact_descriptor;
|
|
const Options& options_;
|
|
};
|
|
|
|
StubClass::StubClass(const AidlInterface* interfaceType, 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 = interfaceType->GetCanonicalName() + ".Stub";
|
|
this->extends = "android.os.Binder";
|
|
this->interfaces.push_back(interfaceType->GetCanonicalName());
|
|
|
|
// ctor
|
|
auto ctor = std::make_shared<Method>();
|
|
ctor->modifiers = PUBLIC;
|
|
ctor->comment =
|
|
"/** Construct the stub at attach it to the "
|
|
"interface. */";
|
|
ctor->name = "Stub";
|
|
ctor->statements = std::make_shared<StatementBlock>();
|
|
if (interfaceType->IsVintfStability()) {
|
|
auto stability = std::make_shared<LiteralStatement>("this.markVintfStability();\n");
|
|
ctor->statements->Add(stability);
|
|
}
|
|
auto attach = std::make_shared<MethodCall>(
|
|
THIS_VALUE, "attachInterface",
|
|
std::vector<std::shared_ptr<Expression>>{THIS_VALUE,
|
|
std::make_shared<LiteralExpression>("DESCRIPTOR")});
|
|
ctor->statements->Add(attach);
|
|
this->elements.push_back(ctor);
|
|
|
|
// asInterface
|
|
MakeAsInterface(interfaceType);
|
|
|
|
// asBinder
|
|
auto asBinder = std::make_shared<Method>();
|
|
asBinder->modifiers = PUBLIC | OVERRIDE;
|
|
asBinder->returnType = "android.os.IBinder";
|
|
asBinder->name = "asBinder";
|
|
asBinder->statements = std::make_shared<StatementBlock>();
|
|
asBinder->statements->Add(std::make_shared<ReturnStatement>(THIS_VALUE));
|
|
this->elements.push_back(asBinder);
|
|
|
|
if (options_.GenTransactionNames()) {
|
|
// getDefaultTransactionName
|
|
auto getDefaultTransactionName = std::make_shared<Method>();
|
|
getDefaultTransactionName->comment = "/** @hide */";
|
|
getDefaultTransactionName->modifiers = PUBLIC | STATIC;
|
|
getDefaultTransactionName->returnType = "java.lang.String";
|
|
getDefaultTransactionName->name = "getDefaultTransactionName";
|
|
auto code = std::make_shared<Variable>("int", "transactionCode");
|
|
getDefaultTransactionName->parameters.push_back(code);
|
|
getDefaultTransactionName->statements = std::make_shared<StatementBlock>();
|
|
this->code_to_method_name_switch = std::make_shared<SwitchStatement>(code);
|
|
getDefaultTransactionName->statements->Add(this->code_to_method_name_switch);
|
|
this->elements.push_back(getDefaultTransactionName);
|
|
|
|
// getTransactionName
|
|
auto getTransactionName = std::make_shared<Method>();
|
|
getTransactionName->comment = "/** @hide */";
|
|
getTransactionName->modifiers = PUBLIC;
|
|
getTransactionName->returnType = "java.lang.String";
|
|
getTransactionName->name = "getTransactionName";
|
|
auto code2 = std::make_shared<Variable>("int", "transactionCode");
|
|
getTransactionName->parameters.push_back(code2);
|
|
getTransactionName->statements = std::make_shared<StatementBlock>();
|
|
getTransactionName->statements->Add(std::make_shared<ReturnStatement>(
|
|
std::make_shared<MethodCall>(THIS_VALUE, "getDefaultTransactionName",
|
|
std::vector<std::shared_ptr<Expression>>{code2})));
|
|
this->elements.push_back(getTransactionName);
|
|
}
|
|
|
|
// onTransact
|
|
this->transact_code = std::make_shared<Variable>("int", "code");
|
|
this->transact_data = std::make_shared<Variable>("android.os.Parcel", "data");
|
|
this->transact_reply = std::make_shared<Variable>("android.os.Parcel", "reply");
|
|
this->transact_flags = std::make_shared<Variable>("int", "flags");
|
|
auto onTransact = std::make_shared<Method>();
|
|
onTransact->modifiers = PUBLIC | OVERRIDE;
|
|
onTransact->returnType = "boolean";
|
|
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 = std::make_shared<StatementBlock>();
|
|
transact_statements = onTransact->statements;
|
|
onTransact->exceptions.push_back("android.os.RemoteException");
|
|
this->elements.push_back(onTransact);
|
|
this->transact_switch_meta = std::make_shared<SwitchStatement>(this->transact_code);
|
|
this->transact_switch_user = std::make_shared<SwitchStatement>(this->transact_code);
|
|
}
|
|
|
|
void StubClass::Finish() {
|
|
auto default_case = std::make_shared<Case>();
|
|
|
|
auto superCall = std::make_shared<MethodCall>(
|
|
SUPER_VALUE, "onTransact",
|
|
std::vector<std::shared_ptr<Expression>>{this->transact_code, this->transact_data,
|
|
this->transact_reply, this->transact_flags});
|
|
default_case->statements->Add(std::make_shared<ReturnStatement>(superCall));
|
|
|
|
auto case_count = transact_switch_user->cases.size();
|
|
transact_switch_user->cases.push_back(default_case);
|
|
|
|
// Interface token validation is done for user-defined transactions.
|
|
if (case_count > 0) {
|
|
auto ifStatement = std::make_shared<IfStatement>();
|
|
ifStatement->expression = std::make_shared<LiteralExpression>(
|
|
"code >= android.os.IBinder.FIRST_CALL_TRANSACTION && "
|
|
"code <= android.os.IBinder.LAST_CALL_TRANSACTION");
|
|
ifStatement->statements = std::make_shared<StatementBlock>();
|
|
ifStatement->statements->Add(std::make_shared<MethodCall>(
|
|
this->transact_data, "enforceInterface",
|
|
std::vector<std::shared_ptr<Expression>>{this->GetTransactDescriptor(nullptr)}));
|
|
transact_statements->Add(ifStatement);
|
|
}
|
|
|
|
// Meta transactions are looked up prior to user-defined transactions.
|
|
transact_statements->Add(this->transact_switch_meta);
|
|
transact_statements->Add(this->transact_switch_user);
|
|
|
|
// 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.
|
|
auto code_switch_default_case = std::make_shared<Case>();
|
|
code_switch_default_case->statements->Add(std::make_shared<ReturnStatement>(NULL_VALUE));
|
|
this->code_to_method_name_switch->cases.push_back(code_switch_default_case);
|
|
}
|
|
|
|
// There will be at least one statement for the default, but if we emit a
|
|
// return true after that default, it will be unreachable.
|
|
if (case_count > 0) {
|
|
transact_statements->Add(std::make_shared<ReturnStatement>(TRUE_VALUE));
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
std::shared_ptr<Expression> StubClass::GetTransactDescriptor(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 std::make_shared<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 std::make_shared<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 = std::make_shared<Variable>("java.lang.String", "descriptor");
|
|
transact_statements->Add(std::make_shared<VariableDeclaration>(
|
|
transact_descriptor, std::make_shared<LiteralExpression>("DESCRIPTOR")));
|
|
}
|
|
return transact_descriptor;
|
|
}
|
|
|
|
void StubClass::MakeAsInterface(const AidlInterface* interfaceType) {
|
|
auto obj = std::make_shared<Variable>("android.os.IBinder", "obj");
|
|
|
|
auto m = std::make_shared<Method>();
|
|
m->comment = "/**\n * Cast an IBinder object into an ";
|
|
m->comment += interfaceType->GetCanonicalName();
|
|
m->comment += " interface,\n";
|
|
m->comment += " * generating a proxy if needed.\n */";
|
|
m->modifiers = PUBLIC | STATIC;
|
|
m->returnType = interfaceType->GetCanonicalName();
|
|
m->name = "asInterface";
|
|
m->parameters.push_back(obj);
|
|
m->statements = std::make_shared<StatementBlock>();
|
|
|
|
auto ifstatement = std::make_shared<IfStatement>();
|
|
ifstatement->expression = std::make_shared<Comparison>(obj, "==", NULL_VALUE);
|
|
ifstatement->statements = std::make_shared<StatementBlock>();
|
|
ifstatement->statements->Add(std::make_shared<ReturnStatement>(NULL_VALUE));
|
|
m->statements->Add(ifstatement);
|
|
|
|
// IInterface iin = obj.queryLocalInterface(DESCRIPTOR)
|
|
auto queryLocalInterface = std::make_shared<MethodCall>(obj, "queryLocalInterface");
|
|
queryLocalInterface->arguments.push_back(std::make_shared<LiteralExpression>("DESCRIPTOR"));
|
|
auto iin = std::make_shared<Variable>("android.os.IInterface", "iin");
|
|
auto iinVd = std::make_shared<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;
|
|
auto iinNotNull = std::make_shared<Comparison>(iin, "!=", NULL_VALUE);
|
|
auto instOfCheck = std::make_shared<Comparison>(
|
|
iin, " instanceof ", std::make_shared<LiteralExpression>(interfaceType->GetCanonicalName()));
|
|
auto instOfStatement = std::make_shared<IfStatement>();
|
|
instOfStatement->expression = std::make_shared<Comparison>(iinNotNull, "&&", instOfCheck);
|
|
instOfStatement->statements = std::make_shared<StatementBlock>();
|
|
instOfStatement->statements->Add(std::make_shared<ReturnStatement>(
|
|
std::make_shared<Cast>(interfaceType->GetCanonicalName(), iin)));
|
|
m->statements->Add(instOfStatement);
|
|
|
|
auto ne = std::make_shared<NewExpression>(interfaceType->GetCanonicalName() + ".Stub.Proxy");
|
|
ne->arguments.push_back(obj);
|
|
m->statements->Add(std::make_shared<ReturnStatement>(ne));
|
|
|
|
this->elements.push_back(m);
|
|
}
|
|
|
|
// =================================================
|
|
class ProxyClass : public Class {
|
|
public:
|
|
ProxyClass(const AidlInterface* interfaceType, const Options& options);
|
|
~ProxyClass() override;
|
|
|
|
std::shared_ptr<Variable> mRemote;
|
|
};
|
|
|
|
ProxyClass::ProxyClass(const AidlInterface* interfaceType, const Options& options) : Class() {
|
|
this->modifiers = PRIVATE | STATIC;
|
|
this->what = Class::CLASS;
|
|
this->type = interfaceType->GetCanonicalName() + ".Stub.Proxy";
|
|
this->interfaces.push_back(interfaceType->GetCanonicalName());
|
|
|
|
// IBinder mRemote
|
|
mRemote = std::make_shared<Variable>("android.os.IBinder", "mRemote");
|
|
this->elements.push_back(std::make_shared<Field>(PRIVATE, mRemote));
|
|
|
|
// Proxy()
|
|
auto remote = std::make_shared<Variable>("android.os.IBinder", "remote");
|
|
auto ctor = std::make_shared<Method>();
|
|
ctor->name = "Proxy";
|
|
ctor->statements = std::make_shared<StatementBlock>();
|
|
ctor->parameters.push_back(remote);
|
|
ctor->statements->Add(std::make_shared<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(std::make_shared<LiteralClassElement>(code.str()));
|
|
}
|
|
if (!options.Hash().empty()) {
|
|
std::ostringstream code;
|
|
code << "private String mCachedHash = \"-1\";\n";
|
|
this->elements.emplace_back(std::make_shared<LiteralClassElement>(code.str()));
|
|
}
|
|
|
|
// IBinder asBinder()
|
|
auto asBinder = std::make_shared<Method>();
|
|
asBinder->modifiers = PUBLIC | OVERRIDE;
|
|
asBinder->returnType = "android.os.IBinder";
|
|
asBinder->name = "asBinder";
|
|
asBinder->statements = std::make_shared<StatementBlock>();
|
|
asBinder->statements->Add(std::make_shared<ReturnStatement>(mRemote));
|
|
this->elements.push_back(asBinder);
|
|
}
|
|
|
|
ProxyClass::~ProxyClass() {}
|
|
|
|
// =================================================
|
|
|
|
static void GenerateWriteToParcel(std::shared_ptr<StatementBlock> addTo,
|
|
const AidlTypenames& typenames, const AidlTypeSpecifier& type,
|
|
const std::string& parcel, const std::string& var,
|
|
uint32_t min_sdk_version, bool is_return_value) {
|
|
string code;
|
|
CodeWriterPtr writer = CodeWriter::ForString(&code);
|
|
CodeGeneratorContext context{
|
|
.writer = *(writer.get()),
|
|
.typenames = typenames,
|
|
.type = type,
|
|
.parcel = parcel,
|
|
.var = var,
|
|
.min_sdk_version = min_sdk_version,
|
|
.write_to_parcel_flag =
|
|
is_return_value ? "android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE" : "0",
|
|
};
|
|
WriteToParcelFor(context);
|
|
writer->Close();
|
|
addTo->Add(std::make_shared<LiteralStatement>(code));
|
|
}
|
|
|
|
void GenerateConstantDeclarations(CodeWriter& out, const AidlDefinedType& type) {
|
|
for (const auto& constant : type.GetConstantDeclarations()) {
|
|
const AidlTypeSpecifier& type = constant->GetType();
|
|
out << GenerateComments(*constant);
|
|
out << GenerateAnnotations(*constant);
|
|
out << "public static final " << type.Signature() << " " << constant->GetName() << " = "
|
|
<< constant->ValueString(ConstantValueDecorator) << ";\n";
|
|
}
|
|
}
|
|
|
|
static std::shared_ptr<Method> GenerateInterfaceMethod(const AidlMethod& method) {
|
|
auto decl = std::make_shared<Method>();
|
|
decl->comment = GenerateComments(method);
|
|
decl->modifiers = PUBLIC;
|
|
decl->returnType = JavaSignatureOf(method.GetType());
|
|
decl->name = method.GetName();
|
|
decl->annotations = JavaAnnotationsFor(method);
|
|
|
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
|
auto var = std::make_shared<Variable>(JavaSignatureOf(arg->GetType()), arg->GetName());
|
|
var->annotations = JavaAnnotationsFor(arg->GetType());
|
|
decl->parameters.push_back(var);
|
|
}
|
|
|
|
decl->exceptions.push_back("android.os.RemoteException");
|
|
|
|
return decl;
|
|
}
|
|
|
|
// Visitor for the permission declared in the @EnforcePermission annotation.
|
|
struct PermissionVisitor {
|
|
shared_ptr<Expression> operator()(const perm::AllOf& quantifier) {
|
|
std::shared_ptr<Expression> result;
|
|
for (const auto& operand : quantifier.operands) {
|
|
auto expr = (*this)(operand);
|
|
if (result) {
|
|
result = std::make_shared<Comparison>(result, "&&", expr);
|
|
} else {
|
|
result = expr;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
shared_ptr<Expression> operator()(const perm::AnyOf& quantifier) {
|
|
std::shared_ptr<Expression> result;
|
|
for (const auto& operand : quantifier.operands) {
|
|
auto expr = (*this)(operand);
|
|
if (result) {
|
|
result = std::make_shared<Comparison>(result, "||", expr);
|
|
} else {
|
|
result = expr;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
shared_ptr<Expression> operator()(const std::string& permission) {
|
|
auto attributionSource =
|
|
std::string("new android.content.AttributionSource(getCallingUid(), null, null)");
|
|
for (size_t i = 0; i < method_.GetArguments().size(); i++) {
|
|
const auto& arg = method_.GetArguments()[i];
|
|
if (arg->GetType().GetName() == "android.content.AttributionSource") {
|
|
attributionSource = android::base::StringPrintf("_arg%zu", i);
|
|
break;
|
|
}
|
|
}
|
|
auto permissionName = android::aidl::perm::JavaFullName(permission);
|
|
auto checkPermission =
|
|
std::make_shared<MethodCall>(THIS_VALUE, "permissionCheckerWrapper",
|
|
std::vector<std::shared_ptr<Expression>>{
|
|
std::make_shared<LiteralExpression>(permissionName),
|
|
std::make_shared<MethodCall>(THIS_VALUE, "getCallingPid"),
|
|
std::make_shared<LiteralExpression>(attributionSource)});
|
|
return checkPermission;
|
|
}
|
|
|
|
const AidlMethod& method_;
|
|
};
|
|
|
|
static void GeneratePermissionWrapper(Class* stubClass) {
|
|
// TODO(b/208707422) avoid generating platform-specific API calls.
|
|
std::string permissionCheckerWrapperCode =
|
|
"private boolean permissionCheckerWrapper(\n"
|
|
" String permission, int pid, android.content.AttributionSource attributionSource) {\n"
|
|
" android.content.Context ctx =\n"
|
|
" android.app.ActivityThread.currentActivityThread().getSystemContext();\n"
|
|
" return (android.content.PermissionChecker.checkPermissionForDataDelivery(\n"
|
|
" ctx, permission, pid, attributionSource, \"\" /*message*/) ==\n"
|
|
" android.content.PermissionChecker.PERMISSION_GRANTED);\n"
|
|
"}\n";
|
|
auto permissionCheckerWrapper =
|
|
std::make_shared<LiteralClassElement>(permissionCheckerWrapperCode);
|
|
stubClass->elements.push_back(permissionCheckerWrapper);
|
|
}
|
|
|
|
static void GeneratePermissionCheck(const AidlMethod& method, const perm::Expression& expr,
|
|
std::shared_ptr<StatementBlock> addTo) {
|
|
auto ifstatement = std::make_shared<IfStatement>();
|
|
auto combinedExpr = std::visit(PermissionVisitor{method}, expr);
|
|
ifstatement->expression = std::make_shared<Comparison>(combinedExpr, "!=", TRUE_VALUE);
|
|
ifstatement->statements = std::make_shared<StatementBlock>();
|
|
ifstatement->statements->Add(std::make_shared<LiteralStatement>(
|
|
android::base::StringPrintf("throw new SecurityException(\"Access denied, requires: %s\");\n",
|
|
perm::AsJavaAnnotation(expr).c_str())));
|
|
addTo->Add(ifstatement);
|
|
}
|
|
|
|
static void GeneratePermissionChecks(const AidlInterface& iface, const AidlMethod& method,
|
|
std::shared_ptr<StatementBlock> addTo) {
|
|
auto ifacePermExpr = iface.EnforceExpression();
|
|
if (ifacePermExpr) {
|
|
GeneratePermissionCheck(method, *ifacePermExpr.get(), addTo);
|
|
}
|
|
auto methodPermExpr = method.GetType().EnforceExpression();
|
|
if (methodPermExpr) {
|
|
GeneratePermissionCheck(method, *methodPermExpr.get(), addTo);
|
|
}
|
|
}
|
|
|
|
static void GenerateStubCode(const AidlInterface& iface, const AidlMethod& method, bool oneway,
|
|
std::shared_ptr<Variable> transact_data,
|
|
std::shared_ptr<Variable> transact_reply,
|
|
const AidlTypenames& typenames,
|
|
std::shared_ptr<StatementBlock> statement_block,
|
|
const Options& options) {
|
|
// try and finally
|
|
auto tryStatement = std::make_shared<TryStatement>();
|
|
auto finallyStatement = std::make_shared<FinallyStatement>();
|
|
auto& statements = statement_block;
|
|
|
|
if (options.GenTraces()) {
|
|
statements->Add(tryStatement);
|
|
statements->Add(finallyStatement);
|
|
statements = tryStatement->statements;
|
|
tryStatement->statements->Add(std::make_shared<MethodCall>(
|
|
std::make_shared<LiteralExpression>("android.os.Trace"), "traceBegin",
|
|
std::vector<std::shared_ptr<Expression>>{
|
|
std::make_shared<LiteralExpression>("android.os.Trace.TRACE_TAG_AIDL"),
|
|
std::make_shared<StringLiteralExpression>("AIDL::java::" + iface.GetName() +
|
|
"::" + method.GetName() + "::server")}));
|
|
finallyStatement->statements->Add(std::make_shared<MethodCall>(
|
|
std::make_shared<LiteralExpression>("android.os.Trace"), "traceEnd",
|
|
std::vector<std::shared_ptr<Expression>>{
|
|
std::make_shared<LiteralExpression>("android.os.Trace.TRACE_TAG_AIDL")}));
|
|
}
|
|
|
|
auto realCall = std::make_shared<MethodCall>(THIS_VALUE, method.GetName());
|
|
|
|
// 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()) {
|
|
std::shared_ptr<Variable> v = stubArgs.Get(arg->GetType());
|
|
|
|
statements->Add(std::make_shared<VariableDeclaration>(v));
|
|
|
|
string code;
|
|
CodeWriterPtr writer = CodeWriter::ForString(&code);
|
|
if (arg->GetDirection() & AidlArgument::IN_DIR) {
|
|
// "in/inout" parameter should be created from parcel.
|
|
CodeGeneratorContext context{.writer = *(writer.get()),
|
|
.typenames = typenames,
|
|
.type = arg->GetType(),
|
|
.parcel = transact_data->name,
|
|
.var = v->name,
|
|
.min_sdk_version = options.GetMinSdkVersion(),
|
|
.is_classloader_created = &is_classloader_created};
|
|
CreateFromParcelFor(context);
|
|
} else {
|
|
// "out" parameter should be instantiated before calling the real impl.
|
|
string java_type = InstantiableJavaSignatureOf(arg->GetType());
|
|
|
|
if (arg->GetType().IsDynamicArray()) {
|
|
// dynamic array should be created with a passed length.
|
|
string var_length = v->name + "_length";
|
|
(*writer) << "int " << var_length << " = data.readInt();\n";
|
|
(*writer) << "if (" << var_length << " < 0) {\n";
|
|
(*writer) << " " << v->name << " = null;\n";
|
|
(*writer) << "} else {\n";
|
|
(*writer) << " " << v->name << " = new " << java_type << "[" << var_length << "];\n";
|
|
(*writer) << "}\n";
|
|
} else if (arg->GetType().IsFixedSizeArray()) {
|
|
// fixed-size array can be created with a known size
|
|
string dimensions;
|
|
for (auto dim : arg->GetType().GetFixedSizeArrayDimensions()) {
|
|
dimensions += "[" + std::to_string(dim) + "]";
|
|
}
|
|
(*writer) << v->name << " = new " << java_type << dimensions << ";\n";
|
|
} else {
|
|
// otherwise, create a new instance with a default constructor
|
|
(*writer) << v->name << " = new " << java_type << "();\n";
|
|
}
|
|
}
|
|
writer->Close();
|
|
statements->Add(std::make_shared<LiteralStatement>(code));
|
|
|
|
realCall->arguments.push_back(v);
|
|
}
|
|
}
|
|
|
|
// EOF check
|
|
if (!method.GetArguments().empty() && options.GetMinSdkVersion() >= 32u) {
|
|
statements->Add(std::make_shared<MethodCall>(transact_data, "enforceNoDataAvail"));
|
|
}
|
|
|
|
GeneratePermissionChecks(iface, method, statements);
|
|
|
|
// the real call
|
|
if (method.GetType().GetName() == "void") {
|
|
statements->Add(realCall);
|
|
|
|
if (!oneway) {
|
|
// report that there were no exceptions
|
|
auto ex = std::make_shared<MethodCall>(transact_reply, "writeNoException");
|
|
statements->Add(ex);
|
|
}
|
|
} else {
|
|
auto _result = std::make_shared<Variable>(JavaSignatureOf(method.GetType()), "_result");
|
|
statements->Add(std::make_shared<VariableDeclaration>(_result, realCall));
|
|
|
|
if (!oneway) {
|
|
// report that there were no exceptions
|
|
auto ex = std::make_shared<MethodCall>(transact_reply, "writeNoException");
|
|
statements->Add(ex);
|
|
}
|
|
|
|
// marshall the return value
|
|
GenerateWriteToParcel(statements, typenames, method.GetType(), transact_reply->name,
|
|
_result->name, options.GetMinSdkVersion(), /*is_return_value=*/true);
|
|
}
|
|
|
|
// out parameters
|
|
int i = 0;
|
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
|
std::shared_ptr<Variable> v = stubArgs.Get(i++);
|
|
if (arg->GetDirection() & AidlArgument::OUT_DIR) {
|
|
GenerateWriteToParcel(statements, typenames, arg->GetType(), transact_reply->name, v->name,
|
|
options.GetMinSdkVersion(), /*is_return_value=*/true);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void GenerateStubCase(const AidlInterface& iface, const AidlMethod& method,
|
|
const std::string& transactCodeName, bool oneway,
|
|
std::shared_ptr<StubClass> stubClass, const AidlTypenames& typenames,
|
|
const Options& options) {
|
|
auto c = std::make_shared<Case>(transactCodeName);
|
|
|
|
GenerateStubCode(iface, method, oneway, stubClass->transact_data, stubClass->transact_reply,
|
|
typenames, c->statements, options);
|
|
c->statements->Add(std::make_shared<BreakStatement>());
|
|
|
|
stubClass->transact_switch_user->cases.push_back(c);
|
|
}
|
|
|
|
static void GenerateStubCaseOutline(const AidlInterface& iface, const AidlMethod& method,
|
|
const std::string& transactCodeName, bool oneway,
|
|
std::shared_ptr<StubClass> stubClass,
|
|
const AidlTypenames& typenames, const Options& options) {
|
|
std::string outline_name = "onTransact$" + method.GetName() + "$";
|
|
// Generate an "outlined" method with the actual code.
|
|
{
|
|
auto transact_data = std::make_shared<Variable>("android.os.Parcel", "data");
|
|
auto transact_reply = std::make_shared<Variable>("android.os.Parcel", "reply");
|
|
auto onTransact_case = std::make_shared<Method>();
|
|
onTransact_case->modifiers = PRIVATE;
|
|
onTransact_case->returnType = "boolean";
|
|
onTransact_case->name = outline_name;
|
|
onTransact_case->parameters.push_back(transact_data);
|
|
onTransact_case->parameters.push_back(transact_reply);
|
|
onTransact_case->statements = std::make_shared<StatementBlock>();
|
|
onTransact_case->exceptions.push_back("android.os.RemoteException");
|
|
stubClass->elements.push_back(onTransact_case);
|
|
|
|
GenerateStubCode(iface, method, oneway, transact_data, transact_reply, typenames,
|
|
onTransact_case->statements, options);
|
|
onTransact_case->statements->Add(std::make_shared<ReturnStatement>(TRUE_VALUE));
|
|
}
|
|
|
|
// Generate the case dispatch.
|
|
{
|
|
auto c = std::make_shared<Case>(transactCodeName);
|
|
|
|
auto helper_call =
|
|
std::make_shared<MethodCall>(THIS_VALUE, outline_name,
|
|
std::vector<std::shared_ptr<Expression>>{
|
|
stubClass->transact_data, stubClass->transact_reply});
|
|
c->statements->Add(std::make_shared<ReturnStatement>(helper_call));
|
|
|
|
stubClass->transact_switch_user->cases.push_back(c);
|
|
}
|
|
}
|
|
|
|
static std::shared_ptr<Method> GenerateProxyMethod(const AidlInterface& iface,
|
|
const AidlMethod& method,
|
|
const std::string& transactCodeName, bool oneway,
|
|
std::shared_ptr<ProxyClass> proxyClass,
|
|
const AidlTypenames& typenames,
|
|
const Options& options) {
|
|
auto proxy = std::make_shared<Method>();
|
|
proxy->comment = GenerateComments(method);
|
|
proxy->modifiers = PUBLIC | OVERRIDE;
|
|
proxy->returnType = JavaSignatureOf(method.GetType());
|
|
proxy->name = method.GetName();
|
|
proxy->statements = std::make_shared<StatementBlock>();
|
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
|
proxy->parameters.push_back(
|
|
std::make_shared<Variable>(JavaSignatureOf(arg->GetType()), arg->GetName()));
|
|
}
|
|
proxy->exceptions.push_back("android.os.RemoteException");
|
|
|
|
// the parcels
|
|
auto _data = std::make_shared<Variable>("android.os.Parcel", "_data");
|
|
if (options.GenRpc()) {
|
|
proxy->statements->Add(std::make_shared<LiteralStatement>(
|
|
"android.os.Parcel _data = android.os.Parcel.obtain(asBinder());\n"));
|
|
} else {
|
|
proxy->statements->Add(std::make_shared<LiteralStatement>(
|
|
"android.os.Parcel _data = android.os.Parcel.obtain();\n"));
|
|
}
|
|
|
|
if (iface.IsSensitiveData()) {
|
|
proxy->statements->Add(std::make_shared<LiteralStatement>("_data.markSensitive();\n"));
|
|
}
|
|
|
|
std::shared_ptr<Variable> _reply = nullptr;
|
|
if (!oneway) {
|
|
_reply = std::make_shared<Variable>("android.os.Parcel", "_reply");
|
|
proxy->statements->Add(std::make_shared<VariableDeclaration>(
|
|
_reply, std::make_shared<MethodCall>("android.os.Parcel", "obtain")));
|
|
}
|
|
|
|
// the return value
|
|
std::shared_ptr<Variable> _result = nullptr;
|
|
if (method.GetType().GetName() != "void") {
|
|
_result = std::make_shared<Variable>(*proxy->returnType, "_result");
|
|
proxy->statements->Add(std::make_shared<VariableDeclaration>(_result));
|
|
}
|
|
|
|
// try and finally
|
|
auto tryStatement = std::make_shared<TryStatement>();
|
|
proxy->statements->Add(tryStatement);
|
|
auto finallyStatement = std::make_shared<FinallyStatement>();
|
|
proxy->statements->Add(finallyStatement);
|
|
|
|
if (options.GenTraces()) {
|
|
tryStatement->statements->Add(std::make_shared<MethodCall>(
|
|
std::make_shared<LiteralExpression>("android.os.Trace"), "traceBegin",
|
|
std::vector<std::shared_ptr<Expression>>{
|
|
std::make_shared<LiteralExpression>("android.os.Trace.TRACE_TAG_AIDL"),
|
|
std::make_shared<StringLiteralExpression>("AIDL::java::" + iface.GetName() +
|
|
"::" + method.GetName() + "::client")}));
|
|
}
|
|
|
|
// the interface identifier token: the DESCRIPTOR constant, marshalled as a
|
|
// string
|
|
tryStatement->statements->Add(std::make_shared<MethodCall>(
|
|
_data, "writeInterfaceToken",
|
|
std::vector<std::shared_ptr<Expression>>{std::make_shared<LiteralExpression>("DESCRIPTOR")}));
|
|
|
|
// the parameters
|
|
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
|
|
auto v = std::make_shared<Variable>(JavaSignatureOf(arg->GetType()), arg->GetName());
|
|
AidlArgument::Direction dir = arg->GetDirection();
|
|
if (dir == AidlArgument::OUT_DIR && arg->GetType().IsDynamicArray()) {
|
|
auto checklen = std::make_shared<IfStatement>();
|
|
checklen->expression = std::make_shared<Comparison>(v, "==", NULL_VALUE);
|
|
checklen->statements->Add(std::make_shared<MethodCall>(
|
|
_data, "writeInt",
|
|
std::vector<std::shared_ptr<Expression>>{std::make_shared<LiteralExpression>("-1")}));
|
|
checklen->elseif = std::make_shared<IfStatement>();
|
|
checklen->elseif->statements->Add(std::make_shared<MethodCall>(
|
|
_data, "writeInt",
|
|
std::vector<std::shared_ptr<Expression>>{std::make_shared<FieldVariable>(v, "length")}));
|
|
tryStatement->statements->Add(checklen);
|
|
} else if (dir & AidlArgument::IN_DIR) {
|
|
GenerateWriteToParcel(tryStatement->statements, typenames, arg->GetType(), _data->name,
|
|
v->name, options.GetMinSdkVersion(), /*is_return_value=*/false);
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> flags;
|
|
if (oneway) flags.push_back("android.os.IBinder.FLAG_ONEWAY");
|
|
if (iface.IsSensitiveData()) flags.push_back("android.os.IBinder.FLAG_CLEAR_BUF");
|
|
|
|
// the transact call
|
|
auto call = std::make_shared<MethodCall>(
|
|
proxyClass->mRemote, "transact",
|
|
std::vector<std::shared_ptr<Expression>>{
|
|
std::make_shared<LiteralExpression>("Stub." + transactCodeName), _data,
|
|
_reply ? _reply : NULL_VALUE,
|
|
std::make_shared<LiteralExpression>(flags.empty() ? "0" : Join(flags, " | "))});
|
|
auto _status = std::make_shared<Variable>("boolean", "_status");
|
|
tryStatement->statements->Add(std::make_shared<VariableDeclaration>(_status, call));
|
|
|
|
// TODO(b/151102494): annotation is applied on the return type
|
|
if (method.GetType().IsPropagateAllowBlocking()) {
|
|
if (options.GetMinSdkVersion() < JAVA_PROPAGATE_VERSION) {
|
|
tryStatement->statements->Add(std::make_shared<LiteralStatement>(
|
|
"if (android.os.Build.VERSION.SDK_INT >= " + std::to_string(JAVA_PROPAGATE_VERSION) +
|
|
") { _reply.setPropagateAllowBlocking(); }\n"));
|
|
} else {
|
|
tryStatement->statements->Add(
|
|
std::make_shared<LiteralStatement>("_reply.setPropagateAllowBlocking();\n"));
|
|
}
|
|
}
|
|
|
|
// If the transaction returns false, which means UNKNOWN_TRANSACTION, fall back to the local
|
|
// method in the default impl, if set before. Otherwise, throw a RuntimeException if the interface
|
|
// is versioned. We can't throw the exception for unversioned interface because that would be an
|
|
// app breaking change.
|
|
vector<string> arg_names;
|
|
for (const auto& arg : method.GetArguments()) {
|
|
arg_names.emplace_back(arg->GetName());
|
|
}
|
|
bool has_return_type = method.GetType().GetName() != "void";
|
|
|
|
auto checkDefaultImpl = std::make_shared<IfStatement>();
|
|
checkDefaultImpl->expression = std::make_shared<LiteralExpression>("getDefaultImpl() != null");
|
|
if (has_return_type) {
|
|
checkDefaultImpl->statements->Add(std::make_shared<LiteralStatement>(
|
|
android::base::StringPrintf("return getDefaultImpl().%s(%s);\n", method.GetName().c_str(),
|
|
Join(arg_names, ", ").c_str())));
|
|
} else {
|
|
checkDefaultImpl->statements->Add(std::make_shared<LiteralStatement>(
|
|
android::base::StringPrintf("getDefaultImpl().%s(%s);\n", method.GetName().c_str(),
|
|
Join(arg_names, ", ").c_str())));
|
|
checkDefaultImpl->statements->Add(std::make_shared<LiteralStatement>("return;\n"));
|
|
}
|
|
|
|
auto checkTransactionError = std::make_shared<IfStatement>();
|
|
checkTransactionError->expression = std::make_shared<LiteralExpression>("!_status");
|
|
|
|
if (iface.IsJavaDefault()) {
|
|
checkTransactionError->statements->Add(checkDefaultImpl);
|
|
}
|
|
|
|
if (options.Version() > 0) {
|
|
checkTransactionError->statements->Add(
|
|
std::make_shared<LiteralStatement>(android::base::StringPrintf(
|
|
"throw new android.os.RemoteException(\"Method %s is unimplemented.\");\n",
|
|
method.GetName().c_str())));
|
|
}
|
|
|
|
if (iface.IsJavaDefault() || options.Version() > 0) {
|
|
tryStatement->statements->Add(checkTransactionError);
|
|
}
|
|
|
|
// throw back exceptions.
|
|
if (_reply) {
|
|
auto ex = std::make_shared<MethodCall>(_reply, "readException");
|
|
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 = typenames,
|
|
.type = method.GetType(),
|
|
.parcel = _reply->name,
|
|
.var = _result->name,
|
|
.min_sdk_version = options.GetMinSdkVersion(),
|
|
.is_classloader_created = &is_classloader_created};
|
|
CreateFromParcelFor(context);
|
|
writer->Close();
|
|
tryStatement->statements->Add(std::make_shared<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 = typenames,
|
|
.type = arg->GetType(),
|
|
.parcel = _reply->name,
|
|
.var = arg->GetName(),
|
|
.min_sdk_version = options.GetMinSdkVersion(),
|
|
.is_classloader_created = &is_classloader_created};
|
|
ReadFromParcelFor(context);
|
|
writer->Close();
|
|
tryStatement->statements->Add(std::make_shared<LiteralStatement>(code));
|
|
}
|
|
}
|
|
|
|
finallyStatement->statements->Add(std::make_shared<MethodCall>(_reply, "recycle"));
|
|
}
|
|
finallyStatement->statements->Add(std::make_shared<MethodCall>(_data, "recycle"));
|
|
|
|
if (options.GenTraces()) {
|
|
finallyStatement->statements->Add(std::make_shared<MethodCall>(
|
|
std::make_shared<LiteralExpression>("android.os.Trace"), "traceEnd",
|
|
std::vector<std::shared_ptr<Expression>>{
|
|
std::make_shared<LiteralExpression>("android.os.Trace.TRACE_TAG_AIDL")}));
|
|
}
|
|
|
|
if (_result != nullptr) {
|
|
proxy->statements->Add(std::make_shared<ReturnStatement>(_result));
|
|
}
|
|
|
|
return proxy;
|
|
}
|
|
|
|
static void GenerateMethods(const AidlInterface& iface, const AidlMethod& method, Class* interface,
|
|
std::shared_ptr<StubClass> stubClass,
|
|
std::shared_ptr<ProxyClass> proxyClass, int index,
|
|
const AidlTypenames& typenames, const Options& options) {
|
|
const bool oneway = method.IsOneway();
|
|
|
|
// == the TRANSACT_ constant =============================================
|
|
string transactCodeName = "TRANSACTION_";
|
|
transactCodeName += method.GetName();
|
|
|
|
auto transactCode =
|
|
std::make_shared<Field>(STATIC | FINAL, std::make_shared<Variable>("int", transactCodeName));
|
|
transactCode->value =
|
|
StringPrintf("(android.os.IBinder.FIRST_CALL_TRANSACTION + %d)", index);
|
|
stubClass->elements.push_back(transactCode);
|
|
|
|
// getTransactionName
|
|
if (options.GenTransactionNames()) {
|
|
auto c = std::make_shared<Case>(transactCodeName);
|
|
c->statements->Add(std::make_shared<ReturnStatement>(
|
|
std::make_shared<StringLiteralExpression>(method.GetName())));
|
|
stubClass->code_to_method_name_switch->cases.push_back(c);
|
|
}
|
|
|
|
// == the declaration in the interface ===================================
|
|
std::shared_ptr<ClassElement> decl;
|
|
if (method.IsUserDefined()) {
|
|
decl = GenerateInterfaceMethod(method);
|
|
} else {
|
|
if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
|
|
std::ostringstream code;
|
|
code << "public int " << kGetInterfaceVersion << "() "
|
|
<< "throws android.os.RemoteException;\n";
|
|
decl = std::make_shared<LiteralClassElement>(code.str());
|
|
}
|
|
if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
|
|
std::ostringstream code;
|
|
code << "public String " << kGetInterfaceHash << "() "
|
|
<< "throws android.os.RemoteException;\n";
|
|
decl = std::make_shared<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) {
|
|
GenerateStubCaseOutline(iface, method, transactCodeName, oneway, stubClass, typenames,
|
|
options);
|
|
} else {
|
|
GenerateStubCase(iface, method, transactCodeName, oneway, stubClass, typenames, options);
|
|
}
|
|
} else {
|
|
if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
|
|
auto c = std::make_shared<Case>(transactCodeName);
|
|
std::ostringstream code;
|
|
code << "reply.writeNoException();\n"
|
|
<< "reply.writeInt(" << kGetInterfaceVersion << "());\n"
|
|
<< "return true;\n";
|
|
c->statements->Add(std::make_shared<LiteralStatement>(code.str()));
|
|
stubClass->transact_switch_meta->cases.push_back(c);
|
|
}
|
|
if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
|
|
auto c = std::make_shared<Case>(transactCodeName);
|
|
std::ostringstream code;
|
|
code << "reply.writeNoException();\n"
|
|
<< "reply.writeString(" << kGetInterfaceHash << "());\n"
|
|
<< "return true;\n";
|
|
c->statements->Add(std::make_shared<LiteralStatement>(code.str()));
|
|
stubClass->transact_switch_meta->cases.push_back(c);
|
|
}
|
|
}
|
|
|
|
// == the proxy method ===================================================
|
|
std::shared_ptr<ClassElement> proxy = nullptr;
|
|
if (method.IsUserDefined()) {
|
|
proxy = GenerateProxyMethod(iface, method, transactCodeName, oneway, proxyClass, typenames,
|
|
options);
|
|
|
|
} 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";
|
|
if (options.GenRpc()) {
|
|
code << " android.os.Parcel data = android.os.Parcel.obtain(asBinder());\n";
|
|
} else {
|
|
code << " android.os.Parcel data = android.os.Parcel.obtain();\n";
|
|
}
|
|
code << " android.os.Parcel reply = android.os.Parcel.obtain();\n"
|
|
<< " try {\n"
|
|
<< " data.writeInterfaceToken(DESCRIPTOR);\n"
|
|
<< " boolean _status = mRemote.transact(Stub." << transactCodeName << ", "
|
|
<< "data, reply, 0);\n";
|
|
if (iface.IsJavaDefault()) {
|
|
code << " if (!_status) {\n"
|
|
<< " if (getDefaultImpl() != null) {\n"
|
|
<< " return getDefaultImpl().getInterfaceVersion();\n"
|
|
<< " }\n"
|
|
<< " }\n";
|
|
}
|
|
code << " reply.readException();\n"
|
|
<< " mCachedVersion = reply.readInt();\n"
|
|
<< " } finally {\n"
|
|
<< " reply.recycle();\n"
|
|
<< " data.recycle();\n"
|
|
<< " }\n"
|
|
<< " }\n"
|
|
<< " return mCachedVersion;\n"
|
|
<< "}\n";
|
|
proxy = std::make_shared<LiteralClassElement>(code.str());
|
|
}
|
|
if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
|
|
std::ostringstream code;
|
|
code << "@Override\n"
|
|
<< "public synchronized String " << kGetInterfaceHash << "()"
|
|
<< " throws "
|
|
<< "android.os.RemoteException {\n"
|
|
<< " if (\"-1\".equals(mCachedHash)) {\n";
|
|
if (options.GenRpc()) {
|
|
code << " android.os.Parcel data = android.os.Parcel.obtain(asBinder());\n";
|
|
} else {
|
|
code << " android.os.Parcel data = android.os.Parcel.obtain();\n";
|
|
}
|
|
code << " android.os.Parcel reply = android.os.Parcel.obtain();\n"
|
|
<< " try {\n"
|
|
<< " data.writeInterfaceToken(DESCRIPTOR);\n"
|
|
<< " boolean _status = mRemote.transact(Stub." << transactCodeName << ", "
|
|
<< "data, reply, 0);\n";
|
|
if (iface.IsJavaDefault()) {
|
|
code << " if (!_status) {\n"
|
|
<< " if (getDefaultImpl() != null) {\n"
|
|
<< " return getDefaultImpl().getInterfaceHash();\n"
|
|
<< " }\n"
|
|
<< " }\n";
|
|
}
|
|
code << " reply.readException();\n"
|
|
<< " mCachedHash = reply.readString();\n"
|
|
<< " } finally {\n"
|
|
<< " reply.recycle();\n"
|
|
<< " data.recycle();\n"
|
|
<< " }\n"
|
|
<< " }\n"
|
|
<< " return mCachedHash;\n"
|
|
<< "}\n";
|
|
proxy = std::make_shared<LiteralClassElement>(code.str());
|
|
}
|
|
}
|
|
if (proxy != nullptr) {
|
|
proxyClass->elements.push_back(proxy);
|
|
}
|
|
}
|
|
|
|
static void GenerateInterfaceDescriptors(const Options& options, const AidlInterface* iface,
|
|
Class* interface, std::shared_ptr<StubClass> stub,
|
|
std::shared_ptr<ProxyClass> proxy) {
|
|
// the interface descriptor transaction handler
|
|
auto c = std::make_shared<Case>("INTERFACE_TRANSACTION");
|
|
c->statements->Add(std::make_shared<MethodCall>(
|
|
stub->transact_reply, "writeString",
|
|
std::vector<std::shared_ptr<Expression>>{stub->GetTransactDescriptor(nullptr)}));
|
|
c->statements->Add(std::make_shared<ReturnStatement>(TRUE_VALUE));
|
|
stub->transact_switch_meta->cases.push_back(c);
|
|
|
|
// and the proxy-side method returning the descriptor directly
|
|
auto getDesc = std::make_shared<Method>();
|
|
getDesc->modifiers = PUBLIC;
|
|
getDesc->returnType = "java.lang.String";
|
|
getDesc->name = "getInterfaceDescriptor";
|
|
getDesc->statements = std::make_shared<StatementBlock>();
|
|
getDesc->statements->Add(
|
|
std::make_shared<ReturnStatement>(std::make_shared<LiteralExpression>("DESCRIPTOR")));
|
|
proxy->elements.push_back(getDesc);
|
|
|
|
// add the DESCRIPTOR field to the interface class
|
|
Class* classToAddDescriptor = interface;
|
|
static std::set<std::string> greylist = {
|
|
#include "hiddenapi-greylist"
|
|
};
|
|
if (greylist.find(iface->GetCanonicalName()) != greylist.end()) {
|
|
// For app compatibility, we keep DESCRIPTOR to the stub class for
|
|
// the interfaces that are in the greylist.
|
|
classToAddDescriptor = stub.get();
|
|
}
|
|
auto descriptor = std::make_shared<Field>(
|
|
STATIC | FINAL | PUBLIC, std::make_shared<Variable>("java.lang.String", "DESCRIPTOR"));
|
|
std::string name = iface->GetDescriptor();
|
|
if (options.IsStructured()) {
|
|
// mangle the interface name at build time and demangle it at runtime, to avoid
|
|
// being renamed by jarjar. See b/153843174
|
|
std::replace(name.begin(), name.end(), '.', '$');
|
|
descriptor->value = "\"" + name + "\".replace('$', '.')";
|
|
} else {
|
|
descriptor->value = "\"" + name + "\"";
|
|
}
|
|
classToAddDescriptor->elements.push_back(descriptor);
|
|
}
|
|
|
|
// 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 ComputeOutlineMethods(const AidlInterface* iface, const std::shared_ptr<StubClass> stub,
|
|
size_t outline_threshold, size_t non_outline_count) {
|
|
AIDL_FATAL_IF(non_outline_count > outline_threshold, iface);
|
|
// 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 auto& 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 shared_ptr<ClassElement> GenerateDefaultImplMethod(const AidlMethod& method) {
|
|
auto default_method = std::make_shared<Method>();
|
|
default_method->comment = GenerateComments(method);
|
|
default_method->modifiers = PUBLIC | OVERRIDE;
|
|
default_method->returnType = JavaSignatureOf(method.GetType());
|
|
default_method->name = method.GetName();
|
|
default_method->statements = std::make_shared<StatementBlock>();
|
|
for (const auto& arg : method.GetArguments()) {
|
|
default_method->parameters.push_back(
|
|
std::make_shared<Variable>(JavaSignatureOf(arg->GetType()), arg->GetName()));
|
|
}
|
|
default_method->exceptions.push_back("android.os.RemoteException");
|
|
|
|
if (method.GetType().GetName() != "void") {
|
|
const string& defaultValue = DefaultJavaValueOf(method.GetType());
|
|
default_method->statements->Add(
|
|
std::make_shared<LiteralStatement>(StringPrintf("return %s;\n", defaultValue.c_str())));
|
|
}
|
|
return default_method;
|
|
}
|
|
|
|
static shared_ptr<Class> GenerateDefaultImplClass(const AidlInterface& iface,
|
|
const Options& options) {
|
|
auto default_class = std::make_shared<Class>();
|
|
default_class->comment = "/** Default implementation for " + iface.GetName() + ". */";
|
|
default_class->modifiers = PUBLIC | STATIC;
|
|
default_class->what = Class::CLASS;
|
|
default_class->type = iface.GetCanonicalName() + ".Default";
|
|
default_class->interfaces.emplace_back(iface.GetCanonicalName());
|
|
|
|
for (const auto& m : iface.GetMethods()) {
|
|
if (m->IsUserDefined()) {
|
|
default_class->elements.emplace_back(GenerateDefaultImplMethod(*m));
|
|
} else {
|
|
// These are called only when the remote side does not implement these
|
|
// methods, which is normally impossible, because these methods are
|
|
// automatically declared in the interface class and not implementing
|
|
// them on the remote side causes a compilation error. But if the remote
|
|
// side somehow managed to not implement it, that's an error and we
|
|
// report the case by returning an invalid value here.
|
|
if (m->GetName() == kGetInterfaceVersion && options.Version() > 0) {
|
|
std::ostringstream code;
|
|
code << "@Override\n"
|
|
<< "public int " << kGetInterfaceVersion << "() {\n"
|
|
<< " return 0;\n"
|
|
<< "}\n";
|
|
default_class->elements.emplace_back(std::make_shared<LiteralClassElement>(code.str()));
|
|
}
|
|
if (m->GetName() == kGetInterfaceHash && !options.Hash().empty()) {
|
|
std::ostringstream code;
|
|
code << "@Override\n"
|
|
<< "public String " << kGetInterfaceHash << "() {\n"
|
|
<< " return \"\";\n"
|
|
<< "}\n";
|
|
default_class->elements.emplace_back(std::make_shared<LiteralClassElement>(code.str()));
|
|
}
|
|
}
|
|
}
|
|
|
|
default_class->elements.emplace_back(
|
|
std::make_shared<LiteralClassElement>("@Override\n"
|
|
"public android.os.IBinder asBinder() {\n"
|
|
" return null;\n"
|
|
"}\n"));
|
|
|
|
return default_class;
|
|
}
|
|
|
|
static shared_ptr<ClassElement> GenerateDelegatorMethod(const AidlMethod& method) {
|
|
auto delegator_method = std::make_shared<Method>();
|
|
delegator_method->comment = GenerateComments(method);
|
|
delegator_method->modifiers = PUBLIC | OVERRIDE;
|
|
delegator_method->returnType = JavaSignatureOf(method.GetType());
|
|
delegator_method->name = method.GetName();
|
|
delegator_method->statements = std::make_shared<StatementBlock>();
|
|
std::vector<std::string> argNames;
|
|
for (const auto& arg : method.GetArguments()) {
|
|
delegator_method->parameters.push_back(
|
|
std::make_shared<Variable>(JavaSignatureOf(arg->GetType()), arg->GetName()));
|
|
argNames.push_back(arg->GetName());
|
|
}
|
|
delegator_method->exceptions.push_back("android.os.RemoteException");
|
|
|
|
std::string return_str;
|
|
if (method.GetType().GetName() != "void") {
|
|
return_str = "return ";
|
|
}
|
|
delegator_method->statements->Add(
|
|
std::make_shared<LiteralStatement>(return_str + "mImpl." + method.GetName() + "(" +
|
|
android::base::Join(argNames, ",") + ");\n"));
|
|
return delegator_method;
|
|
}
|
|
|
|
static shared_ptr<Class> GenerateDelegatorClass(const AidlInterface& iface,
|
|
const Options& options) {
|
|
auto delegator_class = std::make_shared<Class>();
|
|
delegator_class->comment = "/** Delegator implementation for " + iface.GetName() + ". */";
|
|
delegator_class->modifiers = PUBLIC | STATIC;
|
|
delegator_class->what = Class::CLASS;
|
|
delegator_class->type = iface.GetCanonicalName() + ".Delegator";
|
|
delegator_class->extends = iface.GetCanonicalName() + ".Stub";
|
|
|
|
// constructor
|
|
delegator_class->elements.emplace_back(
|
|
std::make_shared<LiteralClassElement>("public Delegator(" + iface.GetCanonicalName() +
|
|
" impl) {\n"
|
|
" this.mImpl = impl;\n"
|
|
"}\n"));
|
|
// meta methods
|
|
if (!options.Hash().empty()) {
|
|
delegator_class->elements.emplace_back(
|
|
std::make_shared<LiteralClassElement>("@Override\n"
|
|
"public String " +
|
|
kGetInterfaceHash +
|
|
"() throws android.os.RemoteException {\n"
|
|
" return mImpl." +
|
|
kGetInterfaceHash +
|
|
"();\n"
|
|
"}\n"));
|
|
}
|
|
if (options.Version() > 0) {
|
|
delegator_class->elements.emplace_back(
|
|
std::make_shared<LiteralClassElement>("@Override\n"
|
|
"public int " +
|
|
kGetInterfaceVersion +
|
|
"() throws android.os.RemoteException {\n"
|
|
" int implVer = mImpl." +
|
|
kGetInterfaceVersion +
|
|
"();\n"
|
|
" return VERSION < implVer ? VERSION : implVer;\n"
|
|
"}\n"));
|
|
}
|
|
|
|
// user defined methods
|
|
for (const auto& m : iface.GetMethods()) {
|
|
if (m->IsUserDefined()) {
|
|
delegator_class->elements.emplace_back(GenerateDelegatorMethod(*m));
|
|
}
|
|
}
|
|
|
|
delegator_class->elements.emplace_back(
|
|
std::make_shared<LiteralClassElement>(iface.GetCanonicalName() + " mImpl;\n"));
|
|
|
|
return delegator_class;
|
|
}
|
|
|
|
static shared_ptr<ClassElement> GenerateMaxTransactionId(int max_transaction_id) {
|
|
auto getMaxTransactionId = std::make_shared<Method>();
|
|
getMaxTransactionId->comment = "/** @hide */";
|
|
getMaxTransactionId->modifiers = PUBLIC;
|
|
getMaxTransactionId->returnType = "int";
|
|
getMaxTransactionId->name = "getMaxTransactionId";
|
|
getMaxTransactionId->statements = std::make_shared<StatementBlock>();
|
|
getMaxTransactionId->statements->Add(std::make_shared<ReturnStatement>(
|
|
std::make_shared<LiteralExpression>(std::to_string(max_transaction_id))));
|
|
return getMaxTransactionId;
|
|
}
|
|
|
|
std::unique_ptr<Class> GenerateInterfaceClass(const AidlInterface* iface,
|
|
const AidlTypenames& typenames,
|
|
const Options& options) {
|
|
// the interface class
|
|
auto interface = std::make_unique<Class>();
|
|
interface->comment = GenerateComments(*iface);
|
|
interface->modifiers = PUBLIC;
|
|
interface->what = Class::INTERFACE;
|
|
interface->type = iface->GetCanonicalName();
|
|
interface->interfaces.push_back("android.os.IInterface");
|
|
interface->annotations = JavaAnnotationsFor(*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(std::make_shared<LiteralClassElement>(code.str()));
|
|
}
|
|
if (!options.Hash().empty()) {
|
|
std::ostringstream code;
|
|
code << "public static final String HASH = \"" << options.Hash() << "\";\n";
|
|
interface->elements.emplace_back(std::make_shared<LiteralClassElement>(code.str()));
|
|
}
|
|
|
|
// the default impl class
|
|
auto default_impl = GenerateDefaultImplClass(*iface, options);
|
|
interface->elements.emplace_back(default_impl);
|
|
|
|
// the delegator class
|
|
if (iface->IsJavaDelegator()) {
|
|
auto delegator = GenerateDelegatorClass(*iface, options);
|
|
interface->elements.emplace_back(delegator);
|
|
}
|
|
|
|
// the stub inner class
|
|
auto stub = std::make_shared<StubClass>(iface, options);
|
|
interface->elements.push_back(stub);
|
|
|
|
ComputeOutlineMethods(iface, stub, options.onTransact_outline_threshold_,
|
|
options.onTransact_non_outline_count_);
|
|
|
|
// the proxy inner class
|
|
auto proxy = std::make_shared<ProxyClass>(iface, options);
|
|
stub->elements.push_back(proxy);
|
|
|
|
// stub and proxy support for getInterfaceDescriptor()
|
|
GenerateInterfaceDescriptors(options, iface, interface.get(), stub, proxy);
|
|
|
|
// all the declared constants of the interface
|
|
string constants;
|
|
GenerateConstantDeclarations(*CodeWriter::ForString(&constants), *iface);
|
|
interface->elements.push_back(std::make_shared<LiteralClassElement>(constants));
|
|
|
|
// all the declared methods of the interface
|
|
bool permissionWrapperGenerated = false;
|
|
int max_transaction_id = 0;
|
|
for (const auto& item : iface->GetMethods()) {
|
|
if ((iface->EnforceExpression() || item->GetType().EnforceExpression()) &&
|
|
!permissionWrapperGenerated) {
|
|
GeneratePermissionWrapper(stub.get());
|
|
permissionWrapperGenerated = true;
|
|
}
|
|
GenerateMethods(*iface, *item, interface.get(), stub, proxy, item->GetId(), typenames, options);
|
|
max_transaction_id = std::max(max_transaction_id, item->GetId());
|
|
}
|
|
|
|
// getMaxTransactionId
|
|
if (options.GenTransactionNames()) {
|
|
stub->elements.push_back(GenerateMaxTransactionId(max_transaction_id));
|
|
}
|
|
|
|
// all the nested types
|
|
string code;
|
|
auto writer = CodeWriter::ForString(&code);
|
|
for (const auto& nested : iface->GetNestedTypes()) {
|
|
GenerateClass(*writer, *nested, typenames, options);
|
|
}
|
|
GenerateParcelHelpers(*writer, *iface, options);
|
|
writer->Close();
|
|
interface->elements.push_back(std::make_shared<LiteralClassElement>(code));
|
|
|
|
if (iface->IsJavaDefault()) {
|
|
// 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 = iface->GetCanonicalName();
|
|
stub->elements.emplace_back(std::make_shared<LiteralClassElement>(
|
|
StringPrintf("public static boolean setDefaultImpl(%s impl) {\n"
|
|
" // Only one user of this interface can use this function\n"
|
|
" // at a time. This is a heuristic to detect if two different\n"
|
|
" // users in the same process use this function.\n"
|
|
" if (Stub.Proxy.sDefaultImpl != null) {\n"
|
|
" throw new IllegalStateException(\"setDefaultImpl() called twice\");\n"
|
|
" }\n"
|
|
" if (impl != null) {\n"
|
|
" Stub.Proxy.sDefaultImpl = impl;\n"
|
|
" return true;\n"
|
|
" }\n"
|
|
" return false;\n"
|
|
"}\n",
|
|
i_name.c_str())));
|
|
stub->elements.emplace_back(
|
|
std::make_shared<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(std::make_shared<LiteralClassElement>(
|
|
StringPrintf("public static %s sDefaultImpl;\n", i_name.c_str())));
|
|
}
|
|
|
|
stub->Finish();
|
|
|
|
return interface;
|
|
}
|
|
|
|
} // namespace java
|
|
} // namespace aidl
|
|
} // namespace android
|