Log supporting for binder ndk

Move log code Generating code aidl_to_cpp -> aidl_to_common
Add dependency of libjsoncpp(_ndk)
 -ndk: libjsoncpp_ndk
 -ndk_platform: libjsoncpp

Bug: 126501406
Test: m
Test: ./runtests.sh

Change-Id: Ife69f146ce313564082d070e5e047ec8e1111093
Merged-In: Ife69f146ce313564082d070e5e047ec8e1111093
diff --git a/Android.bp b/Android.bp
index 268ece1..e60468e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -251,3 +251,16 @@
         },
     },
 }
+
+aidl_interface {
+    name: "aidl_test_loggable_interface_ndk",
+    local_include_dir: "tests",
+    srcs: [
+        "tests/android/aidl/loggable/ILoggableInterfaceNdk.aidl",
+    ],
+    backend: {
+        ndk: {
+            gen_log: true,
+        },
+    },
+}
\ No newline at end of file
diff --git a/aidl_to_cpp.cpp b/aidl_to_cpp.cpp
index d5a9f02..61917e9 100644
--- a/aidl_to_cpp.cpp
+++ b/aidl_to_cpp.cpp
@@ -35,119 +35,6 @@
   return raw_value;
 };
 
-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(const CodeGeneratorContext& c, const string& var_name)> toJsonValueExpr;
-};
-
-const static std::unordered_map<std::string, TypeInfo> kTypeInfoMap = {
-    {"void", {"void", nullptr}},
-    {"boolean",
-     {
-         "bool",
-         [](const CodeGeneratorContext& c, const string& var_name) {
-           c.writer << "Json::Value(" << var_name << "? \"true\" : \"false\")";
-         },
-     }},
-    {"byte",
-     {
-         "int8_t",
-         [](const CodeGeneratorContext& c, const string& var_name) {
-           c.writer << "Json::Value(" << var_name << ")";
-         },
-     }},
-    {"char",
-     {
-         "char16_t",
-         [](const CodeGeneratorContext& c, const string& var_name) {
-           c.writer << "Json::Value(std::string(android::String8(&" << var_name << ", 1)))";
-         },
-     }},
-    {"int",
-     {
-         "int32_t",
-         [](const CodeGeneratorContext& c, const string& var_name) {
-           c.writer << "Json::Value(" << var_name << ")";
-         },
-     }},
-    {"long",
-     {
-         "int64_t",
-         [](const CodeGeneratorContext& c, const string& var_name) {
-           c.writer << "Json::Value(static_cast<Json::Int64>(" << var_name << "))";
-         },
-     }},
-    {"float",
-     {
-         "float",
-         [](const CodeGeneratorContext& c, const string& var_name) {
-           c.writer << "Json::Value(" << var_name << ")";
-         },
-     }},
-    {"double",
-     {
-         "double",
-         [](const CodeGeneratorContext& c, const string& var_name) {
-           c.writer << "Json::Value(" << var_name << ")";
-         },
-     }},
-    {"String",
-     {
-         "std::string",
-         [](const CodeGeneratorContext& c, const string& var_name) {
-           c.writer << "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(const CodeGeneratorContext& c) {
-  const TypeInfo info = GetTypeInfo(c.type);
-  if (!CanWriteLog(info)) {
-    return;
-  }
-
-  const string var_object_expr = ((c.isPointer ? "*" : "")) + c.name;
-  if (c.type.IsArray()) {
-    c.writer << c.log << " = Json::Value(Json::arrayValue);\n";
-    c.writer << "for (const auto& v: " << var_object_expr << ") " << c.log << ".append(";
-    info.toJsonValueExpr(c, "v");
-    c.writer << ");";
-  } else {
-    c.writer << c.log << " = ";
-    info.toJsonValueExpr(c, var_object_expr);
-    c.writer << ";";
-  }
-  c.writer << "\n";
-}
-
 std::string GetTransactionIdFor(const AidlMethod& method) {
   ostringstream output;
 
diff --git a/aidl_to_cpp.h b/aidl_to_cpp.h
index ca5a6cd..5c936c9 100644
--- a/aidl_to_cpp.h
+++ b/aidl_to_cpp.h
@@ -35,10 +35,6 @@
   const string log;               // name of the variable of type Json::Value to write the log into
 };
 
-// Emits code that writes name and value of an argument or the return value to
-// the Json object
-void WriteLogFor(const CodeGeneratorContext& c);
-bool CanWriteLog(const AidlTypeSpecifier& aidl);
 std::string GetTransactionIdFor(const AidlMethod& method);
 }  // namespace cpp
 }  // namespace aidl
diff --git a/aidl_to_cpp_common.cpp b/aidl_to_cpp_common.cpp
index 9dbcadd..3208e72 100644
--- a/aidl_to_cpp_common.cpp
+++ b/aidl_to_cpp_common.cpp
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-#include "aidl_to_cpp_common.h"
+#include <unordered_map>
 
+#include "aidl_to_cpp_common.h"
+#include "logging.h"
 #include "os.h"
 
 namespace android {
@@ -84,6 +86,271 @@
   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_getMessage("
+              << 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
diff --git a/aidl_to_cpp_common.h b/aidl_to_cpp_common.h
index 3a6e2e6..381a8c9 100644
--- a/aidl_to_cpp_common.h
+++ b/aidl_to_cpp_common.h
@@ -49,7 +49,11 @@
 void LeaveNamespace(CodeWriter& out, const AidlDefinedType& defined_type);
 
 string BuildVarName(const AidlArgument& a);
-
+const string GenLogBeforeExecute(const string className, const AidlMethod& method, bool isServer,
+                                 bool isNdk);
+const string GenLogAfterExecute(const string className, const AidlInterface& interface,
+                                const AidlMethod& method, const string& statusVarName,
+                                const string& returnVarName, bool isServer, bool isNdk);
 }  // namespace cpp
 }  // namespace aidl
 }  // namespace android
diff --git a/build/aidl_interface.go b/build/aidl_interface.go
index 89594d2..dcda58f 100644
--- a/build/aidl_interface.go
+++ b/build/aidl_interface.go
@@ -559,6 +559,10 @@
 			// Whether to generate C++ code using NDK binder APIs
 			// Default: true
 			Enabled *bool
+			// Whether to generate additional code for gathering information
+			// about the transactions
+			// Default: false
+			Gen_log *bool
 		}
 	}
 }
@@ -751,6 +755,8 @@
 	genLog := false
 	if lang == langCpp {
 		genLog = proptools.Bool(i.properties.Backend.Cpp.Gen_log)
+	} else if lang == langNdk || lang == langNdkPlatform {
+		genLog = proptools.Bool(i.properties.Backend.Ndk.Gen_log)
 	}
 
 	mctx.CreateModule(android.ModuleFactoryAdaptor(aidlGenFactory), &nameProperties{
@@ -767,25 +773,30 @@
 
 	importExportDependencies := wrap("", i.properties.Imports, "-"+lang)
 	var libJSONCppDependency []string
+	var staticLibDependency []string
 	var sdkVersion *string
 	var stl *string
 	var cpp_std *string
-
 	if lang == langCpp {
 		importExportDependencies = append(importExportDependencies, "libbinder", "libutils")
 		if genLog {
 			libJSONCppDependency = []string{"libjsoncpp"}
-			importExportDependencies = append(importExportDependencies, "libbase")
 		}
 		sdkVersion = nil
 		stl = nil
 		cpp_std = nil
 	} else if lang == langNdk {
 		importExportDependencies = append(importExportDependencies, "libbinder_ndk")
+		if genLog {
+			staticLibDependency = []string{"libjsoncpp_ndk"}
+		}
 		sdkVersion = proptools.StringPtr("current")
 		stl = proptools.StringPtr("c++_shared")
 	} else if lang == langNdkPlatform {
 		importExportDependencies = append(importExportDependencies, "libbinder_ndk")
+		if genLog {
+			libJSONCppDependency = []string{"libjsoncpp"}
+		}
 	} else {
 		panic("Unrecognized language: " + lang)
 	}
@@ -800,6 +811,7 @@
 		Export_generated_headers:  []string{cppSourceGen},
 		Static:                    staticLib{Whole_static_libs: libJSONCppDependency},
 		Shared:                    sharedLib{Shared_libs: libJSONCppDependency, Export_shared_lib_headers: libJSONCppDependency},
+		Static_libs:               staticLibDependency,
 		Shared_libs:               importExportDependencies,
 		Export_shared_lib_headers: importExportDependencies,
 		Sdk_version:               sdkVersion,
diff --git a/build/properties.go b/build/properties.go
index 13f1705..f0256b8 100644
--- a/build/properties.go
+++ b/build/properties.go
@@ -34,6 +34,7 @@
 	Generated_headers         []string
 	Shared                    sharedLib
 	Static                    staticLib
+	Static_libs               []string
 	Shared_libs               []string
 	Export_shared_lib_headers []string
 	Export_generated_headers  []string
diff --git a/generate_cpp.cpp b/generate_cpp.cpp
index 61552c1..6d08eac 100644
--- a/generate_cpp.cpp
+++ b/generate_cpp.cpp
@@ -225,140 +225,6 @@
   return ret;
 }
 
-void WriteLogForArguments(CodeWriterPtr& writer, const AidlArgument& a, const TypeNamespace& types,
-                          bool isServer, string logVarName) {
-  if (!CanWriteLog(a.GetType())) {
-    return;
-  }
-  string logElementVarName = "_log_arg_element";
-  (*writer) << "{\n";
-  (*writer).Indent();
-  (*writer) << "Json::Value " << logElementVarName << "(Json::objectValue);\n";
-  string varName = isServer ? BuildVarName(a) : a.GetName();
-  (*writer) << logElementVarName << "[\"name\"] = \"" << varName << "\";\n";
-
-  bool isPointer = a.IsOut() && !isServer;
-  WriteLogFor({*(writer.get()), types.typenames_, a.GetType(), varName, isPointer,
-               logElementVarName + "[\"value\"]"});
-  (*writer) << logVarName << ".append(" << logElementVarName << ");\n";
-  (*writer) << "}\n";
-  (*writer).Dedent();
-}
-
-const string GenLogBeforeExecute(const string className, const AidlMethod& method,
-                                 const TypeNamespace& types, bool isServer) {
-  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, types, isServer, "_log_input_args");
-    }
-  }
-
-  (*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 TypeNamespace& types,
-                                const string& statusVarName, const string& returnVarName,
-                                bool isServer) {
-  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") << "\"] = "
-            << "Json::Value(android::base::StringPrintf(\"0x%%p\", this));\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";
-  (*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, types, isServer, "_log_output_args");
-  }
-
-  (*writer) << "_log_transaction[\"output_args\"] = _log_output_args;\n";
-
-  if (method.GetType().GetName() != "void") {
-    WriteLogFor({*(writer.get()), types.typenames_, method.GetType(), returnVarName, !isServer,
-                 "_log_transaction[\"" + returnVarName + "\"]"});
-  }
-
-  // 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;
-}
-
 unique_ptr<Declaration> DefineClientTransaction(const TypeNamespace& types,
                                                 const AidlInterface& interface,
                                                 const AidlMethod& method, const Options& options) {
@@ -388,7 +254,8 @@
   }
 
   if (options.GenLog()) {
-    b->AddLiteral(GenLogBeforeExecute(bp_name, method, types, false), false /* no semicolon */);
+    b->AddLiteral(GenLogBeforeExecute(bp_name, method, false /* isServer */, false /* isNdk */),
+                  false /* no semicolon */);
   }
 
   // Add the name of the interface we're hoping to call.
@@ -517,8 +384,8 @@
                    kAndroidStatusVarName));
 
   if (options.GenLog()) {
-    b->AddLiteral(GenLogAfterExecute(bp_name, interface, method, types, kStatusVarName,
-                                     kReturnVarName, false),
+    b->AddLiteral(GenLogAfterExecute(bp_name, interface, method, kStatusVarName, kReturnVarName,
+                                     false /* isServer */, false /* isNdk */),
                   false /* no semicolon */);
   }
 
@@ -571,7 +438,6 @@
     include_list.emplace_back("chrono");
     include_list.emplace_back("functional");
     include_list.emplace_back("json/value.h");
-    include_list.emplace_back("android-base/stringprintf.h");
   }
   vector<unique_ptr<Declaration>> file_decls;
 
@@ -675,7 +541,8 @@
   }
   const string bn_name = ClassName(interface, ClassNames::SERVER);
   if (options.GenLog()) {
-    b->AddLiteral(GenLogBeforeExecute(bn_name, method, types, true), false);
+    b->AddLiteral(GenLogBeforeExecute(bn_name, method, true /* isServer */, false /* isNdk */),
+                  false);
   }
   // Call the actual method.  This is implemented by the subclass.
   vector<unique_ptr<AstNode>> status_args;
@@ -692,9 +559,9 @@
   }
 
   if (options.GenLog()) {
-    b->AddLiteral(
-        GenLogAfterExecute(bn_name, interface, method, types, kStatusVarName, kReturnVarName, true),
-        false);
+    b->AddLiteral(GenLogAfterExecute(bn_name, interface, method, kStatusVarName, kReturnVarName,
+                                     true /* isServer */, false /* isNdk */),
+                  false);
   }
 
   // Write exceptions during transaction handling to parcel.
@@ -766,7 +633,6 @@
     include_list.emplace_back("chrono");
     include_list.emplace_back("functional");
     include_list.emplace_back("json/value.h");
-    include_list.emplace_back("android-base/stringprintf.h");
   }
   unique_ptr<MethodImpl> on_transact{new MethodImpl{
       kAndroidStatusLiteral, bn_name, "onTransact",
diff --git a/generate_ndk.cpp b/generate_ndk.cpp
index cafd794..f7a2b45 100644
--- a/generate_ndk.cpp
+++ b/generate_ndk.cpp
@@ -233,9 +233,11 @@
   return "(FIRST_CALL_TRANSACTION + " + std::to_string(m.GetId()) + " /*" + m.GetName() + "*/)";
 }
 
-static void GenerateClientMethodDefinition(
-    CodeWriter& out, const AidlTypenames& types, const AidlInterface& defined_type,
-    const AidlMethod& method, const std::optional<std::string> return_value_cached_to) {
+static void GenerateClientMethodDefinition(CodeWriter& out, const AidlTypenames& types,
+                                           const AidlInterface& defined_type,
+                                           const AidlMethod& method,
+                                           const std::optional<std::string> return_value_cached_to,
+                                           const Options& options) {
   const std::string clazz = ClassName(defined_type, ClassNames::CLIENT);
 
   out << NdkMethodDecl(types, method, clazz) << " {\n";
@@ -256,6 +258,11 @@
   out << "::ndk::ScopedAParcel _aidl_out;\n";
   out << "\n";
 
+  if (options.GenLog()) {
+    out << cpp::GenLogBeforeExecute(ClassName(defined_type, ClassNames::CLIENT), method,
+                                    false /* isServer */, true /* isNdk */);
+  }
+
   out << "_aidl_ret_status = AIBinder_prepareTransaction(asBinder().get(), _aidl_in.getR());\n";
   StatusCheckGoto(out);
 
@@ -320,14 +327,19 @@
 
   out << "_aidl_error:\n";
   out << "_aidl_status.set(AStatus_fromStatus(_aidl_ret_status));\n";
+  if (options.GenLog()) {
+    out << cpp::GenLogAfterExecute(ClassName(defined_type, ClassNames::CLIENT), defined_type,
+                                   method, "_aidl_status", "_aidl_return", false /* isServer */,
+                                   true /* isNdk */);
+  }
   out << "return _aidl_status;\n";
   out.Dedent();
   out << "}\n";
 }
 
 static void GenerateServerCaseDefinition(CodeWriter& out, const AidlTypenames& types,
-                                         const AidlInterface& /*defined_type*/,
-                                         const AidlMethod& method) {
+                                         const AidlInterface& defined_type,
+                                         const AidlMethod& method, const Options& options) {
   out << "case " << MethodId(method) << ": {\n";
   out.Indent();
   for (const auto& arg : method.GetArguments()) {
@@ -351,7 +363,10 @@
       out << "_aidl_ret_status = ::ndk::AParcel_resizeVector(_aidl_in, &" << var_name << ");\n";
     }
   }
-
+  if (options.GenLog()) {
+    out << cpp::GenLogBeforeExecute(ClassName(defined_type, ClassNames::SERVER), method,
+                                    true /* isServer */, true /* isNdk */);
+  }
   out << "::ndk::ScopedAStatus _aidl_status = _aidl_impl->" << method.GetName() << "("
       << NdkArgList(types, method, FormatArgForCall) << ");\n";
 
@@ -378,14 +393,18 @@
       StatusCheckBreak(out);
     }
   }
-
+  if (options.GenLog()) {
+    out << cpp::GenLogAfterExecute(ClassName(defined_type, ClassNames::SERVER), defined_type,
+                                   method, "_aidl_status", "_aidl_return", true /* isServer */,
+                                   true /* isNdk */);
+  }
   out << "break;\n";
   out.Dedent();
   out << "}\n";
 }
 
 void GenerateClassSource(CodeWriter& out, const AidlTypenames& types,
-                         const AidlInterface& defined_type, const Options& /*options*/) {
+                         const AidlInterface& defined_type, const Options& options) {
   const std::string clazz = ClassName(defined_type, ClassNames::INTERFACE);
   const std::string bn_clazz = ClassName(defined_type, ClassNames::SERVER);
 
@@ -405,7 +424,7 @@
     out << "switch (_aidl_code) {\n";
     out.Indent();
     for (const auto& method : defined_type.GetMethods()) {
-      GenerateServerCaseDefinition(out, types, defined_type, *method);
+      GenerateServerCaseDefinition(out, types, defined_type, *method, options);
     }
     out.Dedent();
     out << "}\n";
@@ -426,6 +445,9 @@
 
   out << clazz << "::" << clazz << "(const ::ndk::SpAIBinder& binder) : BpCInterface(binder) {}\n";
   out << clazz << "::~" << clazz << "() {}\n";
+  if (options.GenLog()) {
+    out << "std::function<void(const Json::Value&)> " << clazz << "::logFunc;\n";
+  }
   out << "\n";
   for (const auto& method : defined_type.GetMethods()) {
     // Only getInterfaceVersion can use cache.
@@ -433,7 +455,8 @@
                            options.Version() > 0;
     const auto return_value_cached_to =
         cacheable ? std::make_optional<std::string>(kCacheVariable) : std::nullopt;
-    GenerateClientMethodDefinition(out, types, defined_type, *method, return_value_cached_to);
+    GenerateClientMethodDefinition(out, types, defined_type, *method, return_value_cached_to,
+                                   options);
   }
 }
 void GenerateServerSource(CodeWriter& out, const AidlTypenames& types,
@@ -444,7 +467,9 @@
   out << "// Source for " << clazz << "\n";
   out << clazz << "::" << clazz << "() {}\n";
   out << clazz << "::~" << clazz << "() {}\n";
-
+  if (options.GenLog()) {
+    out << "std::function<void(const Json::Value&)> " << clazz << "::logFunc;\n";
+  }
   out << "::ndk::SpAIBinder " << clazz << "::createBinder() {\n";
   out.Indent();
   out << "AIBinder* binder = AIBinder_new(" << kClazz << ", static_cast<void*>(this));\n";
@@ -585,6 +610,12 @@
       << "\"\n";
   out << "\n";
   out << "#include <android/binder_ibinder.h>\n";
+  if (options.GenLog()) {
+    out << "#include <json/value.h>\n";
+    out << "#include <functional>\n";
+    out << "#include <chrono>\n";
+    out << "#include <sstream>\n";
+  }
   out << "\n";
   EnterNdkNamespace(out, defined_type);
   out << "class " << clazz << " : public ::ndk::BpCInterface<"
@@ -601,7 +632,9 @@
   if (options.Version() > 0) {
     out << "int32_t " << kCacheVariable << " = -1;\n";
   }
-
+  if (options.GenLog()) {
+    out << "static std::function<void(const Json::Value&)> logFunc;\n";
+  }
   out.Dedent();
   out << "};\n";
   LeaveNdkNamespace(out, defined_type);
@@ -635,7 +668,9 @@
       AIDL_FATAL(defined_type) << "Meta method '" << method->GetName() << "' is unimplemented.";
     }
   }
-
+  if (options.GenLog()) {
+    out << "static std::function<void(const Json::Value&)> logFunc;\n";
+  }
   out.Dedent();
   out << "protected:\n";
   out.Indent();
@@ -653,6 +688,12 @@
 
   out << "#pragma once\n\n";
   out << "#include <android/binder_interface_utils.h>\n";
+  if (options.GenLog()) {
+    out << "#include <json/value.h>\n";
+    out << "#include <functional>\n";
+    out << "#include <chrono>\n";
+    out << "#include <sstream>\n";
+  }
   out << "\n";
 
   GenerateHeaderIncludes(out, types, defined_type);
diff --git a/options.cpp b/options.cpp
index 166e8ac..351b4f6 100644
--- a/options.cpp
+++ b/options.cpp
@@ -408,8 +408,8 @@
                      << "file." << endl;
       return;
     }
-    if (gen_log_ && language_ != Options::Language::CPP) {
-      error_message_ << "--log is currently supported only for --lang=cpp" << endl;
+    if (gen_log_ && (language_ != Options::Language::CPP && language_ != Options::Language::NDK)) {
+      error_message_ << "--log is currently supported for either --lang=cpp or --lang=ndk" << endl;
       return;
     }
   }
diff --git a/tests/android/aidl/loggable/ILoggableInterfaceNdk.aidl b/tests/android/aidl/loggable/ILoggableInterfaceNdk.aidl
new file mode 100644
index 0000000..b3d2de6
--- /dev/null
+++ b/tests/android/aidl/loggable/ILoggableInterfaceNdk.aidl
@@ -0,0 +1,14 @@
+package android.aidl.loggable;
+
+interface ILoggableInterfaceNdk {
+    String[] LogThis(boolean boolValue, inout boolean[] boolArray,
+                   byte byteValue, inout byte[] byteArray,
+                   char charValue, inout char[] charArray,
+                   int intValue, inout int[] intArray,
+                   long longValue, inout long[] longArray,
+                   float floatValue, inout float[] floatArray,
+                   double doubleValue, inout double[] doubleArray,
+                   String stringValue, inout String[] stringArray,
+                   IBinder binderValue,
+                   inout ParcelFileDescriptor pfdValue);
+}