blob: 4fa54d865ff462c8229c25f4153807e768fa0aa0 [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 "AST.h"
#include "Coordinator.h"
#include "Interface.h"
#include "Method.h"
#include "Scope.h"
#include <hidl-util/Formatter.h>
#include <android-base/logging.h>
namespace android {
void AST::emitJavaReaderWriter(
Formatter &out,
const std::string &parcelObj,
const TypedVar *arg,
bool isReader) const {
if (isReader) {
out << arg->type().getJavaType()
<< " "
<< arg->name()
<< " = ";
}
arg->type().emitJavaReaderWriter(out, parcelObj, arg->name(), isReader);
}
status_t AST::generateJavaTypes(
const std::string &outputPath, const std::string &limitToType) const {
// Splits types.hal up into one java file per declared type.
for (const auto &type : mRootScope->getSubTypes()) {
std::string typeName = type->localName();
if (type->isTypeDef()) {
continue;
}
if (!limitToType.empty() && typeName != limitToType) {
continue;
}
std::string path = outputPath;
path.append(mCoordinator->convertPackageRootToPath(mPackage));
path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
path.append(typeName);
path.append(".java");
CHECK(Coordinator::MakeParentHierarchy(path));
FILE *file = fopen(path.c_str(), "w");
if (file == NULL) {
return -errno;
}
Formatter out(file);
std::vector<std::string> packageComponents;
getPackageAndVersionComponents(
&packageComponents, true /* cpp_compatible */);
out << "package " << mPackage.javaPackage() << ";\n\n";
out << "import android.os.HwBlob;\n";
out << "import android.os.HwParcel;\n\n";
out << "import java.util.Arrays;\n";
out << "import java.util.ArrayList;\n\n";
for (const auto &item : mImportedNamesForJava) {
out << "import " << item.javaName() << ";\n";
}
out << "\n";
status_t err =
type->emitJavaTypeDeclarations(out, true /* atTopLevel */);
if (err != OK) {
return err;
}
}
return OK;
}
status_t AST::generateJava(
const std::string &outputPath, const std::string &limitToType) const {
if (!isJavaCompatible()) {
fprintf(stderr,
"ERROR: This interface is not Java compatible. The Java backend"
" does NOT support union types nor native handles. "
"In addition, vectors of arrays are limited to at most "
"one-dimensional arrays and vectors of {vectors,interfaces} are"
" not supported.\n");
return UNKNOWN_ERROR;
}
std::string ifaceName;
if (!AST::isInterface(&ifaceName)) {
return generateJavaTypes(outputPath, limitToType);
}
const Interface *iface = mRootScope->getInterface();
const std::string baseName = iface->getBaseName();
std::string path = outputPath;
path.append(mCoordinator->convertPackageRootToPath(mPackage));
path.append(mCoordinator->getPackagePath(mPackage, true /* relative */));
path.append(ifaceName);
path.append(".java");
CHECK(Coordinator::MakeParentHierarchy(path));
FILE *file = fopen(path.c_str(), "w");
if (file == NULL) {
return -errno;
}
Formatter out(file);
std::vector<std::string> packageComponents;
getPackageAndVersionComponents(
&packageComponents, true /* cpp_compatible */);
out << "package " << mPackage.javaPackage() << ";\n\n";
out << "import android.os.IHwBinder;\n";
out << "import android.os.IHwInterface;\n";
out << "import android.os.HwBinder;\n";
out << "import android.os.HwBlob;\n";
out << "import android.os.HwParcel;\n\n";
out << "import java.util.Arrays;\n";
out << "import java.util.ArrayList;\n\n";
for (const auto &item : mImportedNamesForJava) {
out << "import " << item.javaName() << ";\n";
}
if (!mImportedNamesForJava.empty()) {
out << "\n";
}
out.setNamespace(mPackage.javaPackage() + ".");
const Interface *superType = iface->superType();
out << "public interface " << ifaceName << " extends ";
if (superType != NULL) {
out << superType->fullJavaName();
} else {
out << "IHwInterface";
}
out << " {\n";
out.indent();
out << "public static final String kInterfaceName = \""
<< mPackage.string()
<< "::"
<< ifaceName
<< "\";\n\n";
out << "public static "
<< ifaceName
<< " asInterface(IHwBinder binder) {\n";
out.indent();
out << "if (binder == null) {\n";
out.indent();
out << "return null;\n";
out.unindent();
out << "}\n\n";
out << "IHwInterface iface =\n";
out.indent();
out.indent();
out << "binder.queryLocalInterface(kInterfaceName);\n\n";
out.unindent();
out.unindent();
out << "if ((iface != null) && (iface instanceof "
<< ifaceName
<< ")) {\n";
out.indent();
out << "return (" << ifaceName << ")iface;\n";
out.unindent();
out << "}\n\n";
out << "return new " << ifaceName << ".Proxy(binder);\n";
out.unindent();
out << "}\n\n";
out << "public IHwBinder asBinder();\n\n";
out << "public static "
<< ifaceName
<< " getService(String serviceName) {\n";
out.indent();
out << "return "
<< ifaceName
<< ".asInterface(HwBinder.getService(\""
<< iface->fqName().string()
<< "\",serviceName));\n";
out.unindent();
out << "}\n\n";
status_t err = emitJavaTypeDeclarations(out);
if (err != OK) {
return err;
}
for (const auto &method : iface->methods()) {
const bool returnsValue = !method->results().empty();
const bool needsCallback = method->results().size() > 1;
if (needsCallback) {
out << "\npublic abstract class "
<< method->name()
<< "Callback {\n";
out.indent();
out << "public abstract void onValues("
<< Method::GetJavaArgSignature(method->results())
<< ");\n";
out.unindent();
out << "}\n\n";
}
if (returnsValue && !needsCallback) {
out << method->results()[0]->type().getJavaType();
} else {
out << "void";
}
out << " "
<< method->name()
<< "("
<< Method::GetJavaArgSignature(method->args());
if (needsCallback) {
if (!method->args().empty()) {
out << ", ";
}
out << method->name()
<< "Callback cb";
}
out << ");\n";
}
out << "\npublic static final class Proxy implements "
<< ifaceName
<< " {\n";
out.indent();
out << "private IHwBinder mRemote;\n\n";
out << "public Proxy(IHwBinder remote) {\n";
out.indent();
out << "mRemote = remote;\n";
out.unindent();
out << "}\n\n";
out << "public IHwBinder asBinder() {\n";
out.indent();
out << "return mRemote;\n";
out.unindent();
out << "}\n\n";
const Interface *prevInterface = nullptr;
for (const auto &tuple : iface->allMethodsFromRoot()) {
const Method *method = tuple.method();
const Interface *superInterface = tuple.interface();
if (prevInterface != superInterface) {
out << "// Methods from "
<< superInterface->fullName()
<< " follow.\n";
prevInterface = superInterface;
}
const bool returnsValue = !method->results().empty();
const bool needsCallback = method->results().size() > 1;
out << "public ";
if (returnsValue && !needsCallback) {
out << method->results()[0]->type().getJavaType();
} else {
out << "void";
}
out << " "
<< method->name()
<< "("
<< Method::GetJavaArgSignature(method->args());
if (needsCallback) {
if (!method->args().empty()) {
out << ", ";
}
out << method->name()
<< "Callback cb";
}
out << ") {\n";
out.indent();
out << "HwParcel request = new HwParcel();\n";
out << "request.writeInterfaceToken("
<< superInterface->fullJavaName()
<< ".kInterfaceName);\n";
for (const auto &arg : method->args()) {
emitJavaReaderWriter(
out,
"request",
arg,
false /* isReader */);
}
out << "\nHwParcel reply = new HwParcel();\n"
<< "mRemote.transact("
<< method->getSerialId()
<< " /* "
<< method->name()
<< " */, request, reply, ";
if (method->isOneway()) {
out << "IHwBinder.FLAG_ONEWAY";
} else {
out << "0 /* flags */";
}
out << ");\n";
if (!method->isOneway()) {
out << "reply.verifySuccess();\n";
} else {
CHECK(!returnsValue);
}
out << "request.releaseTemporaryStorage();\n";
if (returnsValue) {
out << "\n";
for (const auto &arg : method->results()) {
emitJavaReaderWriter(
out,
"reply",
arg,
true /* isReader */);
}
if (needsCallback) {
out << "cb.onValues(";
bool firstField = true;
for (const auto &arg : method->results()) {
if (!firstField) {
out << ", ";
}
out << arg->name();
firstField = false;
}
out << ");\n";
} else {
const std::string returnName = method->results()[0]->name();
out << "return " << returnName << ";\n";
}
}
out.unindent();
out << "}\n\n";
}
out.unindent();
out << "}\n";
////////////////////////////////////////////////////////////////////////////
out << "\npublic static abstract class Stub extends HwBinder "
<< "implements "
<< ifaceName << " {\n";
out.indent();
out << "public IHwBinder asBinder() {\n";
out.indent();
out << "return this;\n";
out.unindent();
out << "}\n\n";
// b/32383557 this is a hack. We need to change this if we have more reserved methods.
for (Method *method : iface->hidlReservedMethods()) {
out << "public final "
<< method->results()[0]->type().getJavaType()
<< " "
<< method->name()
<< "() {\n";
out.indent();
method->javaImpl(out);
out.unindent();
out << "\n}\n\n";
}
out << "public IHwInterface queryLocalInterface(String descriptor) {\n";
out.indent();
// XXX what about potential superClasses?
out << "if (kInterfaceName.equals(descriptor)) {\n";
out.indent();
out << "return this;\n";
out.unindent();
out << "}\n";
out << "return null;\n";
out.unindent();
out << "}\n\n";
out << "public void registerAsService(String serviceName) {\n";
out.indent();
out << "registerService(interfaceChain(), serviceName);\n";
out.unindent();
out << "}\n\n";
out << "public void onTransact("
<< "int code, HwParcel request, final HwParcel reply, "
<< "int flags) {\n";
out.indent();
out << "switch (code) {\n";
out.indent();
for (const auto &tuple : iface->allMethodsFromRoot()) {
const Method *method = tuple.method();
const Interface *superInterface = tuple.interface();
const bool returnsValue = !method->results().empty();
const bool needsCallback = method->results().size() > 1;
out << "case "
<< method->getSerialId()
<< " /* "
<< method->name()
<< " */:\n{\n";
out.indent();
out << "request.enforceInterface("
<< superInterface->fullJavaName()
<< ".kInterfaceName);\n\n";
for (const auto &arg : method->args()) {
emitJavaReaderWriter(
out,
"request",
arg,
true /* isReader */);
}
if (!needsCallback && returnsValue) {
const TypedVar *returnArg = method->results()[0];
out << returnArg->type().getJavaType()
<< " "
<< returnArg->name()
<< " = ";
}
out << method->name()
<< "(";
bool firstField = true;
for (const auto &arg : method->args()) {
if (!firstField) {
out << ", ";
}
out << arg->name();
firstField = false;
}
if (needsCallback) {
if (!firstField) {
out << ", ";
}
out << "new " << method->name() << "Callback() {\n";
out.indent();
out << "@Override\n"
<< "public void onValues("
<< Method::GetJavaArgSignature(method->results())
<< ") {\n";
out.indent();
out << "reply.writeStatus(HwParcel.STATUS_SUCCESS);\n";
for (const auto &arg : method->results()) {
emitJavaReaderWriter(
out,
"reply",
arg,
false /* isReader */);
}
out << "reply.send();\n"
<< "}}";
out.unindent();
out.unindent();
}
out << ");\n";
if (!needsCallback) {
out << "reply.writeStatus(HwParcel.STATUS_SUCCESS);\n";
if (returnsValue) {
const TypedVar *returnArg = method->results()[0];
emitJavaReaderWriter(
out,
"reply",
returnArg,
false /* isReader */);
}
out << "reply.send();\n";
}
out << "break;\n";
out.unindent();
out << "}\n\n";
}
out.unindent();
out << "}\n";
out.unindent();
out << "}\n";
out.unindent();
out << "}\n";
out.unindent();
out << "}\n";
return OK;
}
status_t AST::emitJavaTypeDeclarations(Formatter &out) const {
return mRootScope->emitJavaTypeDeclarations(out, false /* atTopLevel */);
}
} // namespace android