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