blob: 23c0ae647dd645feb3e2de105e34d8bd39fa3af5 [file] [log] [blame]
/*
* Copyright (C) 2022, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "generate_cpp_analyzer.h"
#include <string>
#include "aidl.h"
#include "aidl_language.h"
#include "aidl_to_common.h"
#include "aidl_to_cpp.h"
#include "code_writer.h"
#include "logging.h"
using std::string;
using std::unique_ptr;
namespace android {
namespace aidl {
namespace cpp {
namespace {
const char kAndroidStatusVarName[] = "_aidl_ret_status";
const char kReturnVarName[] = "_aidl_return";
const char kDataVarName[] = "_aidl_data";
const char kReplyVarName[] = "_aidl_reply";
void GenerateAnalyzerTransaction(CodeWriter& out, const AidlInterface& interface,
const AidlMethod& method, const AidlTypenames& typenames,
const Options& options) {
// Reading past the interface descriptor and reply binder status
out << "_aidl_ret_status = ::android::OK;\n";
out.Write("if (!(%s.enforceInterface(android::String16(\"%s\")))) {\n", kDataVarName,
interface.GetDescriptor().c_str());
out.Write(" %s = ::android::BAD_TYPE;\n", kAndroidStatusVarName);
out << " std::cout << \" Failure: Parcel interface does not match.\" << std::endl;\n"
<< " break;\n"
<< "}\n";
// Declare parameters
for (const unique_ptr<AidlArgument>& a : method.GetArguments()) {
out.Write("%s %s;\n", CppNameOf(a->GetType(), typenames).c_str(), BuildVarName(*a).c_str());
}
out << "::android::binder::Status binderStatus;\n";
// Declare and read the return value.
// Read past the binder status.
out.Write("binderStatus.readFromParcel(%s);\n", kReplyVarName);
if (method.GetType().GetName() != "void") {
out.Write("%s %s;\n", CppNameOf(method.GetType(), typenames).c_str(), kReturnVarName);
out.Write("bool returnError = false;\n");
}
// Read Reply
if (method.GetType().GetName() != "void") {
out.Write("%s = %s.%s(%s);\n", kAndroidStatusVarName, kReplyVarName,
ParcelReadMethodOf(method.GetType(), typenames).c_str(),
ParcelReadCastOf(method.GetType(), typenames, string("&") + kReturnVarName).c_str());
out.Write("if (((%s) != (android::NO_ERROR))) {\n", kAndroidStatusVarName);
out.Indent();
out.Write(
"std::cerr << \"Failure: error in reading return value from Parcel.\" << std::endl;\n");
out.Write("returnError = true;\n");
out.Dedent();
out.Write("}\n");
}
// Reading arguments
out << "do { // Single-pass loop to break if argument reading fails\n";
out.Indent();
for (const auto& a : method.GetArguments()) {
out.Write("%s = %s.%s(%s);\n", kAndroidStatusVarName, kDataVarName,
ParcelReadMethodOf(a->GetType(), typenames).c_str(),
ParcelReadCastOf(a->GetType(), typenames, "&" + BuildVarName(*a)).c_str());
out.Write("if (((%s) != (android::NO_ERROR))) {\n", kAndroidStatusVarName);
out.Indent();
out.Write("std::cerr << \"Failure: error in reading argument %s from Parcel.\" << std::endl;\n",
a->GetName().c_str());
out.Dedent();
out.Write(" break;\n}\n");
}
out.Dedent();
out << "} while(false);\n";
if (!method.GetArguments().empty() && options.GetMinSdkVersion() >= SDK_VERSION_Tiramisu) {
out.Write(
"if (!%s.enforceNoDataAvail().isOk()) {\n %s = android::BAD_VALUE;\n std::cout << \" "
"Failure: Parcel has too much data.\" << std::endl;\n break;\n}\n",
kDataVarName, kAndroidStatusVarName);
}
// Arguments
out.Write("std::cout << \" arguments: \" << std::endl;\n");
for (const auto& a : method.GetArguments()) {
out.Write(
"std::cout << \" %s: \" << ::android::internal::ToString(%s) "
"<< std::endl;\n",
a->GetName().c_str(), BuildVarName(*a).c_str());
}
// Return Value
if (method.GetType().GetName() != "void") {
out.Write("if (returnError) {\n");
out.Indent();
out.Write("std::cout << \" return: <error>\" << std::endl;\n");
out.Dedent();
out.Write("} else {");
out.Indent();
out.Write("std::cout << \" return: \" << ::android::internal::ToString(%s) << std::endl;\n",
kReturnVarName);
out.Dedent();
out.Write("}\n");
} else {
out.Write("std::cout << \" return: void\" << std::endl;\n");
}
}
void GenerateAnalyzerSource(CodeWriter& out, const AidlDefinedType& defined_type,
const AidlTypenames& typenames, const Options& options) {
auto interface = AidlCast<AidlInterface>(defined_type);
string q_name = GetQualifiedName(*interface, ClassNames::INTERFACE);
string canonicalName = defined_type.GetCanonicalName();
string interfaceName = defined_type.GetName();
// Includes
vector<string> include_list{
"iostream", "binder/Parcel.h", "android/binder_to_string.h",
HeaderFile(*interface, ClassNames::RAW, false),
// HeaderFile(*interface, ClassNames::INTERFACE, false),
};
for (const auto& include : include_list) {
out << "#include <" << include << ">\n";
}
out << "namespace {\n";
// Function Start
out.Write(
"android::status_t analyze%s(uint32_t _aidl_code, const android::Parcel& %s, const "
"android::Parcel& %s) {\n",
q_name.c_str(), kDataVarName, kReplyVarName);
out.Indent();
out.Write("android::status_t %s;\nswitch(_aidl_code) {\n", kAndroidStatusVarName);
out.Indent();
// Main Switch Statement
for (const auto& method : interface->GetMethods()) {
out.Write("case ::android::IBinder::FIRST_CALL_TRANSACTION + %d:\n{\n", (method->GetId()));
out.Indent();
out.Write("std::cout << \"%s.%s()\" << std::endl;\n", interfaceName.c_str(),
method->GetName().c_str());
GenerateAnalyzerTransaction(out, *interface, *method, typenames, options);
out.Dedent();
out << "}\n";
out << "break;\n";
}
out << "default:\n{\n std::cout << \" Transaction code \" << _aidl_code << \" not known.\" << "
"std::endl;\n";
out.Write("%s = android::UNKNOWN_TRANSACTION;\n}\n", kAndroidStatusVarName);
out.Dedent();
out.Write("}\nreturn %s;\n", kAndroidStatusVarName);
out << "// To prevent unused variable warnings\n";
out.Write("(void)%s; (void)%s; (void)%s;\n", kAndroidStatusVarName, kDataVarName, kReplyVarName);
out.Dedent();
out << "}\n\n} // namespace\n";
out << "\n#include <Analyzer.h>\nusing android::aidl::Analyzer;\n";
out.Write(
"__attribute__((constructor)) static void addAnalyzer() {\n"
" Analyzer::installAnalyzer(std::make_unique<Analyzer>(\"%s\", \"%s\", &analyze%s));\n}\n",
canonicalName.c_str(), interfaceName.c_str(), q_name.c_str());
}
void GenerateAnalyzerPlaceholder(CodeWriter& out, const AidlDefinedType& /*defined_type*/,
const AidlTypenames& /*typenames*/, const Options& /*options*/) {
out << "// This file is intentionally left blank as placeholder for building an analyzer.\n";
}
} // namespace
bool GenerateCppAnalyzer(const string& output_file, const Options& options,
const AidlTypenames& typenames, const AidlDefinedType& defined_type,
const IoDelegate& io_delegate) {
if (!ValidateOutputFilePath(output_file, options, defined_type)) {
return false;
}
using GenFn = void (*)(CodeWriter & out, const AidlDefinedType& defined_type,
const AidlTypenames& typenames, const Options& options);
auto gen = [&](auto file, GenFn fn) {
unique_ptr<CodeWriter> writer(io_delegate.GetCodeWriter(file));
GenerateAutoGenHeader(*writer, options);
fn(*writer, defined_type, typenames, options);
AIDL_FATAL_IF(!writer->Close(), defined_type) << "I/O Error!";
return true;
};
if (AidlCast<AidlInterface>(defined_type)) {
return gen(output_file, &GenerateAnalyzerSource);
} else {
return gen(output_file, &GenerateAnalyzerPlaceholder);
}
}
} // namespace cpp
} // namespace aidl
} // namespace android