blob: e5561ff51661bdb770ee3d5cc0d24ebd1362506b [file] [log] [blame]
/*
* 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_language.h"
#include "aidl_to_common.h"
#include "aidl_to_java.h"
#include "aidl_typenames.h"
#include "ast_java.h"
#include "code_writer.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::vector<std::shared_ptr<IfStatement>> transact_if_statements_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 MakeConstructors(const AidlInterface* interfaceType);
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());
MakeConstructors(interfaceType);
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() || options_.GenTraces()) {
// 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_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);
}
// Build the if/else chain for the meta methods. There at most 3 different
// statements so if/else is more efficient than a switch statement.
// Meta transactions are looked up prior to user-defined transactions.
AIDL_FATAL_IF(this->transact_if_statements_meta.size() == 0, AIDL_LOCATION_HERE)
<< "Expecting to have meta methods and found none.";
AIDL_FATAL_IF(this->transact_if_statements_meta.size() > 3, AIDL_LOCATION_HERE)
<< "Expecting to have at most 3 meta methods and found "
<< this->transact_if_statements_meta.size();
auto ifStatement = this->transact_if_statements_meta[0];
std::shared_ptr<IfStatement> currentIfStatement = ifStatement;
for (size_t i = 1; i < transact_if_statements_meta.size(); i++) {
currentIfStatement->elseif = this->transact_if_statements_meta[i];
currentIfStatement = currentIfStatement->elseif;
}
transact_statements->Add(ifStatement);
transact_statements->Add(this->transact_switch_user);
// getTransactionName
if (options_.GenTransactionNames() || options_.GenTraces()) {
// 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::MakeConstructors(const AidlInterface* interfaceType) {
string ctors_code;
CodeWriterPtr writer = CodeWriter::ForString(&ctors_code);
CodeWriter& code = *writer;
if (interfaceType->UsesPermissions()) {
code << "private final android.os.PermissionEnforcer mEnforcer;\n";
code << "/** Construct the stub using the Enforcer provided. */\n";
code << "public Stub(android.os.PermissionEnforcer enforcer)\n";
} else {
code << "/** Construct the stub at attach it to the interface. */\n";
code << "@SuppressWarnings(\"this-escape\")\n";
code << "public Stub()\n";
}
code << "{\n";
code.Indent();
if (interfaceType->IsVintfStability()) {
code << "this.markVintfStability();\n";
}
code << "this.attachInterface(this, DESCRIPTOR);\n";
if (interfaceType->UsesPermissions()) {
code << "if (enforcer == null) {\n";
code.Indent();
code << "throw new IllegalArgumentException(\"enforcer cannot be null\");\n";
code.Dedent();
code << "}\n";
code << "mEnforcer = enforcer;\n";
}
code.Dedent();
code << "}\n";
// Setup a default constructor for permissions interfaces.
if (interfaceType->UsesPermissions()) {
code << "@Deprecated\n";
code << "/** Default constructor. */\n";
code << "public Stub() {\n";
code.Indent();
code << "this(android.os.PermissionEnforcer.fromContext(\n";
code << " android.app.ActivityThread.currentActivityThread().getSystemContext()));\n";
code.Dedent();
code << "}\n";
}
code.Close();
this->elements.push_back(std::make_shared<LiteralClassElement>(ctors_code));
}
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(CodeWriter& out, const AidlTypenames& typenames,
const AidlTypeSpecifier& type, const std::string& parcel,
const std::string& var, uint32_t min_sdk_version,
bool is_return_value) {
WriteToParcelFor(CodeGeneratorContext{
.writer = out,
.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",
});
}
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;
GenerateWriteToParcel(*CodeWriter::ForString(&code), typenames, type, parcel, var,
min_sdk_version, is_return_value);
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 AidlInterface& iface,
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);
// If the interface has some permission annotation, add it to the method.
if (auto iface_annotation = JavaPermissionAnnotation(iface); iface_annotation) {
decl->annotations.push_back(*iface_annotation);
}
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.
class PermissionVisitor {
public:
PermissionVisitor(CodeWriter* code, const AidlMethod& method) : code_(code), method_(method) {}
~PermissionVisitor() {
code_->Dedent();
*code_ << "}\n";
}
string Credentials() const { return "getCallingPid(), getCallingUid()"; }
void Prologue() {
*code_ << "/** Helper method to enforce permissions for " << method_.GetName() << " */\n";
*code_ << "protected void " << method_.GetName() << "_enforcePermission() "
<< "throws SecurityException {\n";
code_->Indent();
}
void AddStaticArrayPermissions(const std::vector<std::string>& permissions) {
*code_ << "static final String[] PERMISSIONS_" << method_.GetName() << " = {"
<< Join(permissions, ", ") << "};\n";
}
void operator()(const perm::AllOf& quantifier) {
std::vector<std::string> permissions;
permissions.reserve(quantifier.operands.size());
for (auto const& permission : quantifier.operands) {
permissions.push_back(android::aidl::perm::JavaFullName(permission));
}
AddStaticArrayPermissions(permissions);
Prologue();
*code_ << "mEnforcer.enforcePermissionAllOf(PERMISSIONS_" << method_.GetName() << ", "
<< Credentials() << ");\n";
}
void operator()(const perm::AnyOf& quantifier) {
std::vector<std::string> permissions;
permissions.reserve(quantifier.operands.size());
for (auto const& permission : quantifier.operands) {
permissions.push_back(android::aidl::perm::JavaFullName(permission));
}
AddStaticArrayPermissions(permissions);
Prologue();
*code_ << "mEnforcer.enforcePermissionAnyOf(PERMISSIONS_" << method_.GetName() << ", "
<< Credentials() << ");\n";
}
void operator()(const std::string& permission) {
auto permissionName = android::aidl::perm::JavaFullName(permission);
Prologue();
*code_ << "mEnforcer.enforcePermission(" << permissionName << ", " << Credentials() << ");\n";
}
private:
CodeWriter* code_;
const AidlMethod& method_;
};
static void GeneratePermissionMethod(const AidlInterface& iface, const AidlMethod& method,
const std::shared_ptr<Class>& addTo) {
string code;
CodeWriterPtr writer = CodeWriter::ForString(&code);
if (auto ifacePermExpr = iface.EnforceExpression(); ifacePermExpr) {
std::visit(PermissionVisitor(writer.get(), method), *ifacePermExpr.get());
} else if (auto methodPermExpr = method.GetType().EnforceExpression(); methodPermExpr) {
std::visit(PermissionVisitor(writer.get(), method), *methodPermExpr.get());
}
writer->Close();
addTo->elements.push_back(std::make_shared<LiteralClassElement>(code));
}
static void GenerateStubCode(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& statements = statement_block;
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;
if (method.IsNew() && ShouldForceDowngradeFor(CommunicationSide::READ)) {
auto if_statement = std::make_shared<IfStatement>();
if_statement->expression = std::make_shared<LiteralExpression>("true");
if_statement->statements = std::make_shared<StatementBlock>();
if_statement->statements->Add(
std::make_shared<LiteralExpression>("throw new android.os.RemoteException(\"Method " +
method.GetName() + " is unimplemented.\")"));
statements->Add(if_statement);
}
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";
// if impossibly large array requested, return false
(*writer) << "if (" << var_length << " > 1000000) {\n";
(*writer) << " return false;\n";
(*writer) << "} else 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"));
}
// 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 AidlMethod& method, const std::string& transactCodeName,
bool oneway, const std::shared_ptr<StubClass>& stubClass,
const AidlTypenames& typenames, const Options& options) {
auto c = std::make_shared<Case>(transactCodeName);
GenerateStubCode(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 AidlMethod& method, const std::string& transactCodeName,
bool oneway, const 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(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);
}
}
template <typename Formatter>
static std::string ArgList(const AidlMethod& method, Formatter formatter) {
vector<string> args;
for (const auto& arg : method.GetArguments()) {
args.push_back(std::invoke(formatter, *arg));
}
return Join(args, ", ");
}
static std::string FormatArgForDecl(const AidlArgument& arg) {
return JavaSignatureOf(arg.GetType()) + " " + arg.GetName();
}
static void GenerateProxyMethod(CodeWriter& out, const AidlInterface& iface,
const AidlMethod& method, const std::string& transactCodeName,
bool oneway, const AidlTypenames& typenames,
const Options& options) {
bool is_void = method.GetType().GetName() == "void";
out << GenerateComments(method);
out << "@Override public " << JavaSignatureOf(method.GetType()) << " " << method.GetName() << "("
<< ArgList(method, FormatArgForDecl) << ") throws android.os.RemoteException\n{\n";
out.Indent();
if (method.IsNew() && ShouldForceDowngradeFor(CommunicationSide::WRITE)) {
out << "if (true) {\n";
out << " throw new android.os.RemoteException(\"Method " + method.GetName() +
" is unimplemented.\");\n";
out << "}\n";
}
// the parcels
if (options.GenRpc()) {
out << "android.os.Parcel _data = android.os.Parcel.obtain(asBinder());\n";
} else {
out << "android.os.Parcel _data = android.os.Parcel.obtain();\n";
}
if (iface.IsSensitiveData()) {
out << "_data.markSensitive();\n";
}
if (!oneway) {
out << "android.os.Parcel _reply = android.os.Parcel.obtain();\n";
}
// the return value
if (!is_void) {
out << JavaSignatureOf(method.GetType()) << " _result;\n";
}
out << "try {\n";
out.Indent();
// the interface identifier token: the DESCRIPTOR constant, marshalled as a
// string
out << "_data.writeInterfaceToken(DESCRIPTOR);\n";
// the parameters
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
AidlArgument::Direction dir = arg->GetDirection();
if (dir == AidlArgument::OUT_DIR && arg->GetType().IsDynamicArray()) {
// In Java we pass a pre-allocated array for an 'out' argument. For transaction,
// we pass the size of the array so that the remote can allocate the array with the same size.
out << "_data.writeInt(" << arg->GetName() << ".length);\n";
} else if (dir & AidlArgument::IN_DIR) {
GenerateWriteToParcel(out, typenames, arg->GetType(), "_data", arg->GetName(),
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
out << "boolean _status = mRemote.transact(Stub." << transactCodeName << ", _data, "
<< (oneway ? "null" : "_reply") << ", " << (flags.empty() ? "0" : Join(flags, " | "))
<< ");\n";
// TODO(b/151102494): annotation is applied on the return type
if (method.GetType().IsPropagateAllowBlocking() && !oneway) {
if (options.GetMinSdkVersion() < JAVA_PROPAGATE_VERSION) {
out << "if (android.os.Build.VERSION.SDK_INT >= " + std::to_string(JAVA_PROPAGATE_VERSION) +
") { _reply.setPropagateAllowBlocking(); }\n";
} else {
out << "_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.
if (iface.IsJavaDefault() || options.Version() > 0) {
out << "if (!_status) {\n";
out.Indent();
if (iface.IsJavaDefault()) {
out << "if (getDefaultImpl() != null) {\n";
out.Indent();
if (is_void) {
out << "getDefaultImpl()." << method.GetName() << "("
<< ArgList(method, &AidlArgument::GetName) << ");\n";
out << "return;\n";
} else {
out << "return getDefaultImpl()." << method.GetName() << "("
<< ArgList(method, &AidlArgument::GetName) << ");\n";
}
out.Dedent();
out << "}\n";
}
// TODO(b/274144762): we shouldn't have different behavior for versioned interfaces
// also this set to false for all exceptions, not just unimplemented methods.
if (options.Version() > 0) {
out << "throw new android.os.RemoteException(\"Method " << method.GetName()
<< " is unimplemented.\");\n";
}
out.Dedent();
out << "}\n";
}
if (!oneway) {
// keep this across return value and arguments in order to create the
// classloader at most once.
bool is_classloader_created = false;
// throw back exceptions.
out << "_reply.readException();\n";
if (!is_void) {
CreateFromParcelFor(CodeGeneratorContext{.writer = out,
.typenames = typenames,
.type = method.GetType(),
.parcel = "_reply",
.var = "_result",
.min_sdk_version = options.GetMinSdkVersion(),
.is_classloader_created = &is_classloader_created});
}
// the out/inout parameters
for (const std::unique_ptr<AidlArgument>& arg : method.GetArguments()) {
if (arg->GetDirection() & AidlArgument::OUT_DIR) {
ReadFromParcelFor(CodeGeneratorContext{.writer = out,
.typenames = typenames,
.type = arg->GetType(),
.parcel = "_reply",
.var = arg->GetName(),
.min_sdk_version = options.GetMinSdkVersion(),
.is_classloader_created = &is_classloader_created});
}
}
}
out.Dedent();
out << "}\nfinally {\n";
out.Indent();
// returning and cleanup
if (!oneway) {
out << "_reply.recycle();\n";
}
out << "_data.recycle();\n";
out.Dedent();
out << "}\n"; // finally
if (!is_void) {
out << "return _result;\n";
}
out.Dedent();
out << "}\n"; // method body
}
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() || options.GenTraces()) {
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(iface, 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(method, transactCodeName, oneway, stubClass, typenames, options);
} else {
GenerateStubCase(method, transactCodeName, oneway, stubClass, typenames, options);
}
if (iface.EnforceExpression() || method.GetType().EnforceExpression()) {
GeneratePermissionMethod(iface, method, stubClass);
}
} else {
if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
auto ifStatement = std::make_shared<IfStatement>();
ifStatement->expression = std::make_shared<LiteralExpression>("code == " + transactCodeName);
std::ostringstream code;
code << "reply.writeNoException();\n"
<< "reply.writeInt(" << kGetInterfaceVersion << "());\n"
<< "return true;\n";
ifStatement->statements->Add(std::make_shared<LiteralStatement>(code.str()));
stubClass->transact_if_statements_meta.push_back(ifStatement);
}
if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
auto ifStatement = std::make_shared<IfStatement>();
ifStatement->expression = std::make_shared<LiteralExpression>("code == " + transactCodeName);
std::ostringstream code;
code << "reply.writeNoException();\n"
<< "reply.writeString(" << kGetInterfaceHash << "());\n"
<< "return true;\n";
ifStatement->statements->Add(std::make_shared<LiteralStatement>(code.str()));
stubClass->transact_if_statements_meta.push_back(ifStatement);
}
}
// == the proxy method ===================================================
string proxy_code;
CodeWriterPtr writer = CodeWriter::ForString(&proxy_code);
CodeWriter& code = *writer;
if (method.IsUserDefined()) {
GenerateProxyMethod(code, iface, method, transactCodeName, oneway, typenames, options);
} else {
if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
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";
}
if (method.GetName() == kGetInterfaceHash && !options.Hash().empty()) {
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";
}
}
code.Close();
if (!proxy_code.empty()) {
proxyClass->elements.push_back(std::make_shared<LiteralClassElement>(proxy_code));
}
}
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 ifStatement = std::make_shared<IfStatement>();
ifStatement->expression = std::make_shared<LiteralExpression>("code == INTERFACE_TRANSACTION");
ifStatement->statements->Add(std::make_shared<MethodCall>(
stub->transact_reply, "writeString",
std::vector<std::shared_ptr<Expression>>{stub->GetTransactDescriptor(nullptr)}));
ifStatement->statements->Add(std::make_shared<ReturnStatement>(TRUE_VALUE));
stub->transact_if_statements_meta.push_back(ifStatement);
// 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"));
descriptor->comment = "/** @hide */";
std::string name = iface->GetDescriptor();
// TODO(b/242862858): avoid differentiating behahavior. This is currently blocked
// to fix because of a metalava lint which disallows running code to generate
// static fields.
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 = ";
if (options.IsLatestUnfrozenVersion()) {
code << "true ? " << options.PreviousVersion() << " : " << options.Version() << ";\n";
} else {
code << options.Version() << ";\n";
}
interface->elements.emplace_back(std::make_shared<LiteralClassElement>(code.str()));
}
if (!options.Hash().empty() || options.IsLatestUnfrozenVersion()) {
std::ostringstream code;
if (options.IsLatestUnfrozenVersion()) {
code << "public static final String HASH = \"" << options.PreviousHash() << "\";\n";
} else {
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
int max_transaction_id = 0;
for (const auto& item : iface->GetMethods()) {
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() || options.GenTraces()) {
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, typenames, 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