You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
357 lines
11 KiB
357 lines
11 KiB
/* |
|
* Copyright (C) 2018, 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 <unordered_map> |
|
|
|
#include "aidl_to_cpp_common.h" |
|
#include "logging.h" |
|
#include "os.h" |
|
|
|
namespace android { |
|
namespace aidl { |
|
namespace cpp { |
|
|
|
string ClassName(const AidlDefinedType& defined_type, ClassNames type) { |
|
string base_name = defined_type.GetName(); |
|
if (base_name.length() >= 2 && base_name[0] == 'I' && isupper(base_name[1])) { |
|
base_name = base_name.substr(1); |
|
} |
|
|
|
switch (type) { |
|
case ClassNames::CLIENT: |
|
return "Bp" + base_name; |
|
case ClassNames::SERVER: |
|
return "Bn" + base_name; |
|
case ClassNames::INTERFACE: |
|
return "I" + base_name; |
|
case ClassNames::DEFAULT_IMPL: |
|
return "I" + base_name + "Default"; |
|
case ClassNames::BASE: |
|
return base_name; |
|
case ClassNames::RAW: |
|
[[fallthrough]]; |
|
default: |
|
return defined_type.GetName(); |
|
} |
|
} |
|
|
|
std::string HeaderFile(const AidlDefinedType& defined_type, ClassNames class_type, |
|
bool use_os_sep) { |
|
std::string file_path = defined_type.GetPackage(); |
|
for (char& c : file_path) { |
|
if (c == '.') { |
|
c = (use_os_sep) ? OS_PATH_SEPARATOR : '/'; |
|
} |
|
} |
|
if (!file_path.empty()) { |
|
file_path += (use_os_sep) ? OS_PATH_SEPARATOR : '/'; |
|
} |
|
file_path += ClassName(defined_type, class_type); |
|
file_path += ".h"; |
|
|
|
return file_path; |
|
} |
|
|
|
void EnterNamespace(CodeWriter& out, const AidlDefinedType& defined_type) { |
|
const std::vector<std::string> packages = defined_type.GetSplitPackage(); |
|
for (const std::string& package : packages) { |
|
out << "namespace " << package << " {\n"; |
|
} |
|
} |
|
void LeaveNamespace(CodeWriter& out, const AidlDefinedType& defined_type) { |
|
const std::vector<std::string> packages = defined_type.GetSplitPackage(); |
|
for (auto it = packages.rbegin(); it != packages.rend(); ++it) { |
|
out << "} // namespace " << *it << "\n"; |
|
} |
|
} |
|
|
|
string BuildVarName(const AidlArgument& a) { |
|
string prefix = "out_"; |
|
if (a.GetDirection() & AidlArgument::IN_DIR) { |
|
prefix = "in_"; |
|
} |
|
return prefix + a.GetName(); |
|
} |
|
|
|
struct TypeInfo { |
|
// name of the type in C++ output |
|
std::string cpp_name; |
|
|
|
// function that writes an expression to convert a variable to a Json::Value |
|
// object |
|
std::function<void(CodeWriter& w, const string& var_name, bool isNdk)> toJsonValueExpr; |
|
}; |
|
|
|
const static std::unordered_map<std::string, TypeInfo> kTypeInfoMap = { |
|
{"void", {"void", nullptr}}, |
|
{"boolean", |
|
{ |
|
"bool", |
|
[](CodeWriter& c, const string& var_name, bool) { |
|
c << "Json::Value(" << var_name << "? \"true\" : \"false\")"; |
|
}, |
|
}}, |
|
{"byte", |
|
{ |
|
"int8_t", |
|
[](CodeWriter& c, const string& var_name, bool) { |
|
c << "Json::Value(" << var_name << ")"; |
|
}, |
|
}}, |
|
{"char", |
|
{ |
|
"char16_t", |
|
[](CodeWriter& c, const string& var_name, bool isNdk) { |
|
if (isNdk) { |
|
c << "Json::Value(" << var_name << ")"; |
|
} else { |
|
c << "Json::Value(std::string(android::String8(&" << var_name << ", 1)))"; |
|
} |
|
}, |
|
}}, |
|
{"int", |
|
{ |
|
"int32_t", |
|
[](CodeWriter& c, const string& var_name, bool) { |
|
c << "Json::Value(" << var_name << ")"; |
|
}, |
|
}}, |
|
{"long", |
|
{ |
|
"int64_t", |
|
[](CodeWriter& c, const string& var_name, bool) { |
|
c << "Json::Value(static_cast<Json::Int64>(" << var_name << "))"; |
|
}, |
|
}}, |
|
{"float", |
|
{ |
|
"float", |
|
[](CodeWriter& c, const string& var_name, bool) { |
|
c << "Json::Value(" << var_name << ")"; |
|
}, |
|
}}, |
|
{"double", |
|
{ |
|
"double", |
|
[](CodeWriter& c, const string& var_name, bool) { |
|
c << "Json::Value(" << var_name << ")"; |
|
}, |
|
}}, |
|
{"String", |
|
{ |
|
"std::string", |
|
[](CodeWriter& c, const string& var_name, bool) { |
|
c << "Json::Value(" << var_name << ")"; |
|
}, |
|
}} |
|
// missing List, Map, ParcelFileDescriptor, IBinder |
|
}; |
|
|
|
TypeInfo GetTypeInfo(const AidlTypeSpecifier& aidl) { |
|
CHECK(aidl.IsResolved()) << aidl.ToString(); |
|
const string& aidl_name = aidl.GetName(); |
|
|
|
TypeInfo info; |
|
if (AidlTypenames::IsBuiltinTypename(aidl_name)) { |
|
auto it = kTypeInfoMap.find(aidl_name); |
|
if (it != kTypeInfoMap.end()) { |
|
info = it->second; |
|
} |
|
} |
|
// Missing interface and parcelable type |
|
return info; |
|
} |
|
|
|
inline bool CanWriteLog(const TypeInfo& t) { |
|
return t.cpp_name != ""; |
|
} |
|
|
|
bool CanWriteLog(const AidlTypeSpecifier& aidl) { |
|
return CanWriteLog(GetTypeInfo(aidl)); |
|
} |
|
|
|
void WriteLogFor(CodeWriter& writer, const AidlTypeSpecifier& type, const std::string& name, |
|
bool isPointer, const std::string& log, bool isNdk) { |
|
const TypeInfo info = GetTypeInfo(type); |
|
if (!CanWriteLog(info)) { |
|
return; |
|
} |
|
|
|
const string var_object_expr = ((isPointer ? "*" : "")) + name; |
|
if (type.IsArray()) { |
|
writer << log << " = Json::Value(Json::arrayValue);\n"; |
|
writer << "for (const auto& v: " << var_object_expr << ") " << log << ".append("; |
|
info.toJsonValueExpr(writer, "v", isNdk); |
|
writer << ");"; |
|
} else { |
|
writer << log << " = "; |
|
info.toJsonValueExpr(writer, var_object_expr, isNdk); |
|
writer << ";"; |
|
} |
|
writer << "\n"; |
|
} |
|
|
|
void WriteLogForArguments(CodeWriterPtr& writer, const AidlArgument& a, bool isServer, |
|
string logVarName, bool isNdk) { |
|
if (!CanWriteLog(a.GetType())) { |
|
return; |
|
} |
|
string logElementVarName = "_log_arg_element"; |
|
(*writer) << "{\n"; |
|
(*writer).Indent(); |
|
(*writer) << "Json::Value " << logElementVarName << "(Json::objectValue);\n"; |
|
string varName = isServer || isNdk ? BuildVarName(a) : a.GetName(); |
|
(*writer) << logElementVarName << "[\"name\"] = \"" << varName << "\";\n"; |
|
|
|
bool isPointer = a.IsOut() && !isServer; |
|
WriteLogFor(*(writer.get()), a.GetType(), varName, isPointer, logElementVarName + "[\"value\"]", |
|
isNdk); |
|
(*writer) << logVarName << ".append(" << logElementVarName << ");\n"; |
|
(*writer) << "}\n"; |
|
(*writer).Dedent(); |
|
} |
|
|
|
const string GenLogBeforeExecute(const string className, const AidlMethod& method, bool isServer, |
|
bool isNdk) { |
|
string code; |
|
CodeWriterPtr writer = CodeWriter::ForString(&code); |
|
(*writer) << "Json::Value _log_input_args(Json::arrayValue);\n"; |
|
|
|
(*writer) << "if (" << className << "::logFunc != nullptr) {\n"; |
|
(*writer).Indent(); |
|
|
|
for (const auto& a : method.GetArguments()) { |
|
if (a->IsIn()) { |
|
WriteLogForArguments(writer, *a, isServer, "_log_input_args", isNdk); |
|
} |
|
} |
|
|
|
(*writer).Dedent(); |
|
(*writer) << "}\n"; |
|
|
|
(*writer) << "auto _log_start = std::chrono::steady_clock::now();\n"; |
|
writer->Close(); |
|
return code; |
|
} |
|
|
|
const string GenLogAfterExecute(const string className, const AidlInterface& interface, |
|
const AidlMethod& method, const string& statusVarName, |
|
const string& returnVarName, bool isServer, bool isNdk) { |
|
string code; |
|
CodeWriterPtr writer = CodeWriter::ForString(&code); |
|
|
|
(*writer) << "if (" << className << "::logFunc != nullptr) {\n"; |
|
(*writer).Indent(); |
|
|
|
// Write the log as a Json object. For example, |
|
// |
|
// Json log object for following interface description |
|
// |
|
// package foo.bar; |
|
// interface IFoo { |
|
// String TestMethod(int arg1, inout String[] arg2, out double arg3); |
|
// } |
|
// |
|
// would be: |
|
// |
|
// { |
|
// duration_ms: 100.42, |
|
// interface_name: "foo.bar.IFoo", |
|
// method_name: "TestMethod", |
|
// (proxy|stub)_address: "0x12345678", |
|
// input_args: [ |
|
// {name: "arg1", value: 30,}, |
|
// {name: "arg2", value: ["apple", "grape"],}, |
|
// ], |
|
// output_args: [ |
|
// {name: "arg2", value: ["mango", "banana"],}, |
|
// {name: "arg3", value: "10.5",}, |
|
// ], |
|
// _aidl_return: "ok", |
|
// binder_status: { |
|
// exception_code: -8, |
|
// exception_message: "Something wrong", |
|
// transaction_error: 0, |
|
// service_specific_error_code: -42, |
|
// }, |
|
// } |
|
(*writer) << "auto _log_end = std::chrono::steady_clock::now();\n"; |
|
(*writer) << "Json::Value _log_transaction(Json::objectValue);\n"; |
|
(*writer) << "_log_transaction[\"duration_ms\"] = " |
|
<< "std::chrono::duration<double, std::milli>(_log_end - " |
|
"_log_start).count();\n"; |
|
(*writer) << "_log_transaction[\"interface_name\"] = " |
|
<< "Json::Value(\"" << interface.GetCanonicalName() << "\");\n"; |
|
(*writer) << "_log_transaction[\"method_name\"] = " |
|
<< "Json::Value(\"" << method.GetName() << "\");\n"; |
|
|
|
(*writer) << "_log_transaction[\"" << (isServer ? "stub_address" : "proxy_address") << "\"] = "; |
|
(*writer) << "Json::Value(" |
|
<< "(std::ostringstream() << " |
|
<< (isNdk && isServer ? "_aidl_impl" : "static_cast<const void*>(this)") << ").str()" |
|
<< ");\n"; |
|
(*writer) << "_log_transaction[\"input_args\"] = _log_input_args;\n"; |
|
(*writer) << "Json::Value _log_output_args(Json::arrayValue);\n"; |
|
|
|
(*writer) << "Json::Value _log_status(Json::objectValue);\n"; |
|
if (isNdk) { |
|
(*writer) << "_log_status[\"exception_code\"] = Json::Value(AStatus_getExceptionCode(" |
|
<< statusVarName << ".get()));\n"; |
|
(*writer) << "_log_status[\"exception_message\"] = Json::Value(AStatus_getMessage(" |
|
<< statusVarName << ".get()));\n"; |
|
(*writer) << "_log_status[\"transaction_error\"] = Json::Value(AStatus_getStatus(" |
|
<< statusVarName << ".get()));\n"; |
|
(*writer) << "_log_status[\"service_specific_error_code\"] = " |
|
"Json::Value(AStatus_getServiceSpecificError(" |
|
<< statusVarName << ".get()));\n"; |
|
} else { |
|
(*writer) << "_log_status[\"exception_code\"] = Json::Value(" << statusVarName |
|
<< ".exceptionCode());\n"; |
|
(*writer) << "_log_status[\"exception_message\"] = Json::Value(" << statusVarName |
|
<< ".exceptionMessage());\n"; |
|
(*writer) << "_log_status[\"transaction_error\"] = Json::Value(" << statusVarName |
|
<< ".transactionError());\n"; |
|
(*writer) << "_log_status[\"service_specific_error_code\"] = Json::Value(" << statusVarName |
|
<< ".serviceSpecificErrorCode());\n"; |
|
} |
|
|
|
(*writer) << "_log_transaction[\"binder_status\"] = _log_status;\n"; |
|
|
|
for (const auto& a : method.GetOutArguments()) { |
|
WriteLogForArguments(writer, *a, isServer, "_log_output_args", isNdk); |
|
} |
|
|
|
(*writer) << "_log_transaction[\"output_args\"] = _log_output_args;\n"; |
|
|
|
if (method.GetType().GetName() != "void") { |
|
WriteLogFor(*(writer.get()), method.GetType(), returnVarName, !isServer, |
|
"_log_transaction[\"" + returnVarName + "\"]", isNdk); |
|
} |
|
|
|
// call the user-provided function with the Json object for the entire |
|
// transaction |
|
(*writer) << className << "::logFunc(_log_transaction);\n"; |
|
|
|
(*writer).Dedent(); |
|
(*writer) << "}\n"; |
|
|
|
writer->Close(); |
|
return code; |
|
} |
|
|
|
} // namespace cpp |
|
} // namespace aidl |
|
} // namespace android
|
|
|