Version query mechanism and default impl for NDK backend

The version query mechanism (getInterfaceVersion()) and default impl
(set/getDefaultImpl()) are added to NDK backend.

This CL also fixes problems in other backends (Java/C++) found during
the implementation.

1) getInterfaceVersion() always writes and checks the interface token.
This wasn't needed because the binder library for Java and C++ didn't
mandate it. However, since libbinder_ndk is mandating it (in
AIBinder_prepareTransaction), other backends were all updated for
interoperability with the NDK backend.

2) aidl_interface module type now collectly invokes the aidl compiler
with --version flag when the 'versions' property is set.

3) Meta transactions (like getInterfaceVersion()) are now within
[FIRST_CALL_TRANSACTION, LAST_CALL_TRANSACTION]. They are numbered
backward from LAST_CALL_TRANSACTION.

Bug: 110760419
Bug: 113550618
Test: atest android.binder.cts.NdkBinderTest
Change-Id: Ib6c0989a6aec7cf4f4e534a68223f18cd6980470
diff --git a/aidl.cpp b/aidl.cpp
index 5c6fa5d..ebd8c7c 100644
--- a/aidl.cpp
+++ b/aidl.cpp
@@ -67,16 +67,29 @@
 namespace aidl {
 namespace {
 
-// The following are gotten as the offset from the allowable id's between
-// android.os.IBinder.FIRST_CALL_TRANSACTION=1 and
-// android.os.IBinder.LAST_CALL_TRANSACTION=16777215
-const int kMinUserSetMethodId = 0;
-const int kMaxUserSetMethodId = 0x00ffffff;
+// Copied from android.is.IBinder.[FIRST|LAST]_CALL_TRANSACTION
+const int kFirstCallTransaction = 1;
+const int kLastCallTransaction = 0x00ffffff;
+
+// Following IDs are all offsets from  kFirstCallTransaction
 
 // IDs for meta transactions. Most of the meta transactions are implemented in
 // the framework side (Binder.java or Binder.cpp). But these are the ones that
 // are auto-implemented by the AIDL compiler.
-const int kGetInterfaceVersionId = ('_' << 24) | ('V' << 16) | ('E' << 8) | 'R';
+const int kFirstMetaMethodId = kLastCallTransaction - kFirstCallTransaction;
+const int kGetInterfaceVersionId = kFirstMetaMethodId;
+// Additional meta transactions implemented by AIDL should use
+// kFirstMetaMethodId -1, -2, ...and so on.
+
+// Reserve 100 IDs for meta methods, which is more than enough. If we don't reserve,
+// in the future, a newly added meta transaction ID will have a chance to
+// collide with the user-defined methods that were added in the past. So,
+// let's prevent users from using IDs in this range from the beginning.
+const int kLastMetaMethodId = kFirstMetaMethodId - 99;
+
+// Range of IDs that is allowed for user-defined methods.
+const int kMinUserSetMethodId = 0;
+const int kMaxUserSetMethodId = kLastMetaMethodId - 1;
 
 bool check_filename(const std::string& filename, const AidlDefinedType& defined_type) {
     const char* p;
diff --git a/aidl_to_ndk.cpp b/aidl_to_ndk.cpp
index cfd23e1..582f798 100644
--- a/aidl_to_ndk.cpp
+++ b/aidl_to_ndk.cpp
@@ -312,33 +312,22 @@
   aspect.read_func(c);
 }
 
-std::string NdkArgListOf(const AidlTypenames& types, const AidlMethod& method) {
+std::string NdkArgList(
+    const AidlTypenames& types, const AidlMethod& method,
+    std::function<std::string(const std::string& type, const std::string& name, bool isOut)>
+        formatter) {
   std::vector<std::string> method_arguments;
   for (const auto& a : method.GetArguments()) {
     StorageMode mode = a->IsOut() ? StorageMode::OUT_ARGUMENT : StorageMode::ARGUMENT;
     std::string type = NdkNameOf(types, a->GetType(), mode);
     std::string name = cpp::BuildVarName(*a);
-    method_arguments.emplace_back(type + " " + name);
+    method_arguments.emplace_back(formatter(type, name, a->IsOut()));
   }
 
   if (method.GetType().GetName() != "void") {
-    std::string return_type = NdkNameOf(types, method.GetType(), StorageMode::OUT_ARGUMENT);
-    method_arguments.emplace_back(return_type + " _aidl_return");
-  }
-
-  return Join(method_arguments, ", ");
-}
-
-std::string NdkCallListFor(const AidlMethod& method) {
-  std::vector<std::string> method_arguments;
-  for (const auto& a : method.GetArguments()) {
-    std::string reference_prefix = a->IsOut() ? "&" : "";
-    std::string name = cpp::BuildVarName(*a);
-    method_arguments.emplace_back(reference_prefix + name);
-  }
-
-  if (method.GetType().GetName() != "void") {
-    method_arguments.emplace_back("&_aidl_return");
+    std::string type = NdkNameOf(types, method.GetType(), StorageMode::OUT_ARGUMENT);
+    std::string name = "_aidl_return";
+    method_arguments.emplace_back(formatter(type, name, true));
   }
 
   return Join(method_arguments, ", ");
@@ -348,7 +337,7 @@
                           const std::string& clazz) {
   std::string class_prefix = clazz.empty() ? "" : (clazz + "::");
   return "::ndk::ScopedAStatus " + class_prefix + method.GetName() + "(" +
-         NdkArgListOf(types, method) + ")";
+         NdkArgList(types, method, FormatArgForDecl) + ")";
 }
 
 }  // namespace ndk
diff --git a/aidl_to_ndk.h b/aidl_to_ndk.h
index 54de198..50fb880 100644
--- a/aidl_to_ndk.h
+++ b/aidl_to_ndk.h
@@ -51,11 +51,32 @@
 void WriteToParcelFor(const CodeGeneratorContext& c);
 void ReadFromParcelFor(const CodeGeneratorContext& c);
 
-// -> 'type name, type name, type name' for a method
-std::string NdkArgListOf(const AidlTypenames& types, const AidlMethod& method);
+// Returns argument list of a method where each arg is formatted by the fomatter
+std::string NdkArgList(
+    const AidlTypenames& types, const AidlMethod& method,
+    std::function<std::string(const std::string& type, const std::string& name, bool isOut)>
+        formatter);
 
-// -> 'name, name, name' for a method where out arguments are '&name'
-std::string NdkCallListFor(const AidlMethod& method);
+inline std::string FormatArgForDecl(const std::string& type, const std::string& name,
+                                    bool /*isOut*/) {
+  return type + " " + name;
+}
+
+inline std::string FormatArgNameUnused(const std::string& type, const std::string& name,
+                                       bool /*isOut*/) {
+  return type + " /*" + name + "*/";
+}
+
+inline std::string FormatArgForCall(const std::string& /*type*/, const std::string& name,
+                                    bool isOut) {
+  std::string reference_prefix = isOut ? "&" : "";
+  return reference_prefix + name;
+}
+
+inline std::string FormatArgNameOnly(const std::string& /*type*/, const std::string& name,
+                                     bool /*isOut*/) {
+  return name;
+}
 
 // -> 'status (class::)name(type name, ...)' for a method
 std::string NdkMethodDecl(const AidlTypenames& types, const AidlMethod& method,
diff --git a/build/aidl_interface.go b/build/aidl_interface.go
index 09c3013..b458737 100644
--- a/build/aidl_interface.go
+++ b/build/aidl_interface.go
@@ -15,6 +15,11 @@
 package aidl
 
 import (
+	"android/soong/android"
+	"android/soong/cc"
+	"android/soong/genrule"
+	"android/soong/java"
+	"android/soong/phony"
 	"fmt"
 	"io"
 	"path/filepath"
@@ -25,12 +30,6 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
 	"github.com/google/blueprint/proptools"
-
-	"android/soong/android"
-	"android/soong/cc"
-	"android/soong/genrule"
-	"android/soong/java"
-	"android/soong/phony"
 )
 
 var (
@@ -39,6 +38,7 @@
 	langCpp             = "cpp"
 	langJava            = "java"
 	langNdk             = "ndk"
+	futureVersion       = "10000"
 
 	pctx = android.NewPackageContext("android/aidl")
 
@@ -55,13 +55,13 @@
 
 	aidlJavaRule = pctx.StaticRule("aidlJavaRule", blueprint.RuleParams{
 		Command: `rm -rf "${outDir}" && mkdir -p "${outDir}" && ` +
-			`${aidlCmd} --lang=java --structured --ninja -d ${out}.d ` +
+			`${aidlCmd} --lang=java ${optionalFlags} --structured --ninja -d ${out}.d ` +
 			`-o ${outDir} ${imports} ${in}`,
 		Depfile:     "${out}.d",
 		Deps:        blueprint.DepsGCC,
 		CommandDeps: []string{"${aidlCmd}"},
 		Description: "AIDL Java ${in}",
-	}, "imports", "outDir")
+	}, "imports", "outDir", "optionalFlags")
 
 	aidlDumpApiRule = pctx.StaticRule("aidlDumpApiRule", blueprint.RuleParams{
 		Command: `rm -rf "${out}" && mkdir -p "${out}" && ` +
@@ -120,6 +120,7 @@
 	Lang     string // target language [java|cpp|ndk]
 	BaseName string
 	GenLog   bool
+	Version  string
 }
 
 type aidlGenRule struct {
@@ -166,6 +167,11 @@
 
 	imports := strings.Join(wrap("-I", importPaths, ""), " ")
 
+	var optionalFlags []string
+	if g.properties.Version != "" {
+		optionalFlags = append(optionalFlags, "--version "+g.properties.Version)
+	}
+
 	if g.properties.Lang == langJava {
 		ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 			Rule:      aidlJavaRule,
@@ -173,8 +179,9 @@
 			Implicits: checkApiTimestamps,
 			Outputs:   g.genOutputs,
 			Args: map[string]string{
-				"imports": imports,
-				"outDir":  outDir.String(),
+				"imports":       imports,
+				"outDir":        outDir.String(),
+				"optionalFlags": strings.Join(optionalFlags, " "),
 			},
 		})
 	} else {
@@ -203,10 +210,10 @@
 		headers = append(headers, g.genHeaderDir.Join(ctx, prefix, packagePath,
 			"Bn"+baseName+".h"))
 
-		var optionalFlags []string
 		if g.properties.GenLog {
 			optionalFlags = append(optionalFlags, "--log")
 		}
+
 		ctx.ModuleBuild(pctx, android.ModuleBuildParams{
 			Rule:            aidlCppRule,
 			Input:           input,
@@ -555,14 +562,14 @@
 
 func (i *aidlInterface) versionedName(version string) string {
 	name := i.ModuleBase.Name()
-	if version != "" {
+	if version != futureVersion && version != "" {
 		name = name + "-V" + version
 	}
 	return name
 }
 
 func (i *aidlInterface) srcsForVersion(mctx android.LoadHookContext, version string) (srcs []string, base string) {
-	if version == "" {
+	if version == futureVersion || version == "" {
 		return i.rawSrcs, i.properties.Local_include_dir
 	} else {
 		var apiDir string
@@ -600,21 +607,26 @@
 
 	var libs []string
 
+	currentVersion := ""
+	if len(i.properties.Versions) > 0 {
+		currentVersion = futureVersion
+	}
+
 	if i.shouldGenerateCppBackend() {
-		libs = append(libs, addCppLibrary(mctx, i, "", langCpp))
+		libs = append(libs, addCppLibrary(mctx, i, currentVersion, langCpp))
 		for _, version := range i.properties.Versions {
 			addCppLibrary(mctx, i, version, langCpp)
 		}
 	}
 
 	if i.shouldGenerateNdkBackend() {
-		libs = append(libs, addCppLibrary(mctx, i, "", langNdk))
+		libs = append(libs, addCppLibrary(mctx, i, currentVersion, langNdk))
 		for _, version := range i.properties.Versions {
 			addCppLibrary(mctx, i, version, langNdk)
 		}
 	}
 
-	libs = append(libs, addJavaLibrary(mctx, i, ""))
+	libs = append(libs, addJavaLibrary(mctx, i, currentVersion))
 	for _, version := range i.properties.Versions {
 		addJavaLibrary(mctx, i, version)
 	}
@@ -660,6 +672,7 @@
 			Lang:     lang,
 			BaseName: i.ModuleBase.Name(),
 			GenLog:   genLog,
+			Version:  version,
 		})
 		cppGeneratedSources = append(cppGeneratedSources, cppSourceGenName)
 	}
@@ -728,6 +741,7 @@
 			Imports:  concat(i.properties.Imports, []string{i.ModuleBase.Name()}),
 			Lang:     langJava,
 			BaseName: i.ModuleBase.Name(),
+			Version:  version,
 		})
 		javaGeneratedSources = append(javaGeneratedSources, javaSourceGenName)
 	}
diff --git a/generate_cpp.cpp b/generate_cpp.cpp
index 34fc136..edec267 100644
--- a/generate_cpp.cpp
+++ b/generate_cpp.cpp
@@ -490,9 +490,10 @@
     // give write the same value to cached_version_.
     std::ostringstream code;
     code << "int32_t " << proxy << "::" << kGetInterfaceVersion << "() {\n"
-         << "  if (cached_version_ != -1) {\n"
+         << "  if (cached_version_ == -1) {\n"
          << "    ::android::Parcel data;\n"
          << "    ::android::Parcel reply;\n"
+         << "    data.writeInterfaceToken(getInterfaceDescriptor());\n"
          << "    ::android::status_t err = remote()->transact(" << GetTransactionIdFor(method)
          << ", data, &reply);\n"
          << "    if (err == ::android::OK) {\n"
@@ -684,7 +685,8 @@
 
   if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
     std::ostringstream code;
-    code << "_aidl_reply->writeInt32(" << ClassName(interface, ClassNames::INTERFACE)
+    code << "_aidl_data.checkInterface(this);\n"
+         << "_aidl_reply->writeInt32(" << ClassName(interface, ClassNames::INTERFACE)
          << "::VERSION)";
     b->AddLiteral(code.str());
     return true;
diff --git a/generate_java_binder.cpp b/generate_java_binder.cpp
index 7fbb472..3714f3d 100644
--- a/generate_java_binder.cpp
+++ b/generate_java_binder.cpp
@@ -814,7 +814,9 @@
     if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
       Case* c = new Case(transactCodeName);
       std::ostringstream code;
-      code << "reply.writeInt(" << kGetInterfaceVersion << "());\n"
+      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);
@@ -839,6 +841,7 @@
            << "    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"
            << "      mCachedVersion = reply.readInt();\n"
diff --git a/generate_ndk.cpp b/generate_ndk.cpp
index ea42a5a..3c09f09 100644
--- a/generate_ndk.cpp
+++ b/generate_ndk.cpp
@@ -16,6 +16,7 @@
 
 #include "generate_ndk.h"
 
+#include "aidl.h"
 #include "aidl_language.h"
 #include "aidl_to_cpp_common.h"
 #include "aidl_to_ndk.h"
@@ -26,6 +27,10 @@
 namespace aidl {
 namespace ndk {
 
+static constexpr const char* kClazz = "clazz";
+static constexpr const char* kDescriptor = "descriptor";
+static constexpr const char* kVersion = "version";
+
 using namespace internals;
 using cpp::ClassNames;
 
@@ -257,6 +262,18 @@
   out << "_aidl_out.getR(),\n";
   out << (method.IsOneway() ? "FLAG_ONEWAY" : "0") << ");\n";
   out.Dedent();
+
+  // If the method is not implmented in the server side but the client has
+  // provided the default implementation, call it instead of failing hard.
+  const std::string iface = ClassName(defined_type, ClassNames::INTERFACE);
+  out << "if (_aidl_ret_status == STATUS_UNKNOWN_TRANSACTION && ";
+  out << iface << "::getDefaultImpl()) {\n";
+  out.Indent();
+  out << "return " << iface << "::getDefaultImpl()->" << method.GetName() << "(";
+  out << NdkArgList(types, method, FormatArgNameOnly) << ");\n";
+  out.Dedent();
+  out << "}\n";
+
   StatusCheckGoto(out);
 
   if (!method.IsOneway()) {
@@ -286,6 +303,63 @@
   out << "}\n";
 }
 
+static void GenerateClientMetaMethodDefinition(CodeWriter& out, const AidlTypenames& types,
+                                               const AidlInterface& defined_type,
+                                               const AidlMethod& method, const Options& options) {
+  CHECK(!method.IsUserDefined());
+  if (method.GetName() == kGetInterfaceVersion && options.Version() > 0) {
+    // Client-side implementation for getInterfaceVersion. The version is cached
+    // in the proxy object.
+    const std::string clazz = ClassName(defined_type, ClassNames::CLIENT);
+
+    out << NdkMethodDecl(types, method, clazz) << " {\n";
+    out.Indent();
+    out << "::ndk::ScopedAStatus _aidl_status;\n";
+    out << "binder_status_t _aidl_ret_status = STATUS_OK;\n";
+    out << "if (cached_version_ == -1) {\n";
+    out.Indent();
+    out << "::ndk::ScopedAParcel _aidl_in;\n";
+    out << "::ndk::ScopedAParcel _aidl_out;\n";
+    out << "\n";
+
+    out << "_aidl_ret_status = AIBinder_prepareTransaction(asBinder().get(), _aidl_in.getR());\n";
+    StatusCheckGoto(out);
+
+    out << "_aidl_ret_status = AIBinder_transact(\n";
+    out.Indent();
+    out << "asBinder().get(),\n";
+    out << MethodId(method) << ",\n";
+    out << "_aidl_in.getR(),\n";
+    out << "_aidl_out.getR(),\n";
+    out << "0);\n";
+    out.Dedent();
+    StatusCheckGoto(out);
+
+    out << "_aidl_ret_status = AParcel_readStatusHeader(_aidl_out.get(), _aidl_status.getR());\n";
+    StatusCheckGoto(out);
+
+    out << "if (!AStatus_isOk(_aidl_status.get())) return _aidl_status;\n\n";
+
+    out << "_aidl_ret_status = AParcel_readInt32(_aidl_out.get(), _aidl_return);\n";
+    StatusCheckGoto(out);
+
+    out << "cached_version_ = *_aidl_return;\n";
+    out << "_aidl_status.set(AStatus_fromStatus(_aidl_ret_status));\n";
+    out << "return _aidl_status;\n";
+
+    out.Dedent();
+    out << "}\n";
+
+    // Shortcut. If cached, just return it without doing the transaction.
+    out << "*_aidl_return = cached_version_;\n";
+    out << "_aidl_error:\n";
+    out << "_aidl_status.set(AStatus_fromStatus(_aidl_ret_status));\n";
+    out << "return _aidl_status;\n";
+    out.Dedent();
+    out << "}\n";
+  }
+}
+
 static void GenerateServerCaseDefinition(CodeWriter& out, const AidlTypenames& types,
                                          const AidlInterface& /*defined_type*/,
                                          const AidlMethod& method) {
@@ -314,7 +388,7 @@
   }
 
   out << "::ndk::ScopedAStatus _aidl_status = _aidl_impl->" << method.GetName() << "("
-      << NdkCallListFor(method) << ");\n";
+      << NdkArgList(types, method, FormatArgForCall) << ");\n";
 
   if (method.IsOneway()) {
     // For a oneway transaction, the kernel will have already returned a result. This is for the
@@ -356,7 +430,7 @@
 
   out << "struct " << data_clazz << " {\n";
   out.Indent();
-  out << "static AIBinder_Class* clazz;\n";
+  out << "static AIBinder_Class* " << kClazz << ";\n";
   out << "std::shared_ptr<" << bn_clazz << "> instance;\n";
   out.Dedent();
   out << "};\n\n";
@@ -400,11 +474,12 @@
   out.Dedent();
   out << "};\n\n";
 
-  out << "AIBinder_Class* " << data_clazz << "::clazz = AIBinder_Class_define(" << clazz
-      << "::descriptor, " << on_create << ", " << on_destroy << ", " << on_transact << ");\n\n";
+  out << "AIBinder_Class* " << data_clazz << ":: " << kClazz << " = AIBinder_Class_define(" << clazz
+      << "::" << kDescriptor << ", " << on_create << ", " << on_destroy << ", " << on_transact
+      << ");\n\n";
 }
 void GenerateClientSource(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::CLIENT);
   const std::string data_clazz = DataClassFor(defined_type);
 
@@ -412,8 +487,8 @@
   out << "std::shared_ptr<" << clazz << "> " << clazz
       << "::associate(const ::ndk::SpAIBinder& binder) {\n";
   out.Indent();
-  out << "if (!AIBinder_associateClass(binder.get(), " << data_clazz
-      << "::clazz)) { return nullptr; }\n";
+  out << "if (!AIBinder_associateClass(binder.get(), " << data_clazz << "::" << kClazz
+      << ")) { return nullptr; }\n";
   out << "return (new " << clazz << "(binder))->ref<" << clazz << ">();\n";
   out.Dedent();
   out << "}\n\n";
@@ -422,12 +497,17 @@
   out << clazz << "::~" << clazz << "() {}\n";
   out << "\n";
   for (const auto& method : defined_type.GetMethods()) {
-    GenerateClientMethodDefinition(out, types, defined_type, *method);
+    if (method->IsUserDefined()) {
+      GenerateClientMethodDefinition(out, types, defined_type, *method);
+    } else {
+      GenerateClientMetaMethodDefinition(out, types, defined_type, *method, options);
+    }
   }
 }
-void GenerateServerSource(CodeWriter& out, const AidlTypenames& /*types*/,
-                          const AidlInterface& defined_type, const Options& /*options*/) {
+void GenerateServerSource(CodeWriter& out, const AidlTypenames& types,
+                          const AidlInterface& defined_type, const Options& options) {
   const std::string clazz = ClassName(defined_type, ClassNames::SERVER);
+  const std::string iface = ClassName(defined_type, ClassNames::INTERFACE);
   const std::string data_clazz = DataClassFor(defined_type);
 
   out << "// Source for " << clazz << "\n";
@@ -436,20 +516,35 @@
 
   out << "::ndk::SpAIBinder " << clazz << "::createBinder() {\n";
   out.Indent();
-  out << "AIBinder* binder = AIBinder_new(" << data_clazz
-      << "::clazz, static_cast<void*>(this));\n";
+  out << "AIBinder* binder = AIBinder_new(" << data_clazz << "::" << kClazz
+      << ", static_cast<void*>(this));\n";
   out << "return ::ndk::SpAIBinder(binder);\n";
   out.Dedent();
   out << "}\n";
+
+  // Implement the meta methods
+  for (const auto& method : defined_type.GetMethods()) {
+    if (method->IsUserDefined()) {
+      continue;
+    }
+    if (method->GetName() == kGetInterfaceVersion && options.Version() > 0) {
+      out << NdkMethodDecl(types, *method, clazz) << " {\n";
+      out.Indent();
+      out << "*_aidl_return = " << iface << "::" << kVersion << ";\n";
+      out << "return ::ndk::ScopedAStatus(AStatus_newOk());\n";
+      out.Dedent();
+      out << "}\n";
+    }
+  }
 }
-void GenerateInterfaceSource(CodeWriter& out, const AidlTypenames& /*types*/,
-                             const AidlInterface& defined_type, const Options& /*options*/) {
+void GenerateInterfaceSource(CodeWriter& out, const AidlTypenames& types,
+                             const AidlInterface& defined_type, const Options& options) {
   const std::string clazz = ClassName(defined_type, ClassNames::INTERFACE);
   const std::string data_clazz = DataClassFor(defined_type);
 
   out << "// Source for " << clazz << "\n";
-  out << "const char* " << clazz << "::descriptor = \"" << defined_type.GetCanonicalName()
-      << "\";\n";
+  out << "const char* " << clazz << "::" << kDescriptor << " = \""
+      << defined_type.GetCanonicalName() << "\";\n";
   out << clazz << "::" << clazz << "() {}\n";
   out << clazz << "::~" << clazz << "() {}\n";
   out << "\n";
@@ -485,9 +580,70 @@
   out << "return STATUS_OK;\n";
   out.Dedent();
   out << "}\n";
+
+  // defintion for static member setDefaultImpl
+  out << "bool " << clazz << "::setDefaultImpl(std::shared_ptr<" << clazz << "> impl) {\n";
+  out.Indent();
+  out << "if (!" << clazz << "::default_impl && impl) {\n";
+  out.Indent();
+  out << clazz << "::default_impl = impl;\n";
+  out << "return true;\n";
+  out.Dedent();
+  out << "}\n";
+  out << "return false;\n";
+  out.Dedent();
+  out << "}\n";
+
+  // definition for static member getDefaultImpl
+  out << "const std::shared_ptr<" << clazz << ">& " << clazz << "::getDefaultImpl() {\n";
+  out.Indent();
+  out << "return " << clazz << "::default_impl;\n";
+  out.Dedent();
+  out << "}\n";
+
+  // definition for the static field default_impl
+  out << "std::shared_ptr<" << clazz << "> " << clazz << "::default_impl = nullptr;\n";
+
+  // default implementation for the <Name>Default class members
+  const std::string defaultClazz = clazz + "Default";
+  for (const auto& method : defined_type.GetMethods()) {
+    if (method->IsUserDefined()) {
+      out << "::ndk::ScopedAStatus " << defaultClazz << "::" << method->GetName() << "("
+          << NdkArgList(types, *method, FormatArgNameUnused) << ") {\n";
+      out.Indent();
+      out << "::ndk::ScopedAStatus _aidl_status;\n";
+      out << "_aidl_status.set(AStatus_fromStatus(STATUS_UNKNOWN_TRANSACTION));\n";
+      out << "return _aidl_status;\n";
+      out.Dedent();
+      out << "}\n";
+    } else {
+      if (method->GetName() == kGetInterfaceVersion && options.Version() > 0) {
+        out << "::ndk::ScopedAStatus " << defaultClazz << "::" << method->GetName() << "("
+            << "int32_t* _aidl_return) {\n";
+        out.Indent();
+        out << "*_aidl_return = 0;\n";
+        out << "return ::ndk::ScopedAStatus(AStatus_newOk());\n";
+        out.Dedent();
+        out << "}\n";
+      }
+    }
+  }
+
+  out << "::ndk::SpAIBinder " << defaultClazz << "::asBinder() {\n";
+  out.Indent();
+  out << "return ::ndk::SpAIBinder();\n";
+  out.Dedent();
+  out << "}\n";
+
+  out << "bool " << defaultClazz << "::isRemote() {\n";
+  out.Indent();
+  out << "return false;\n";
+  out.Dedent();
+  out << "}\n";
 }
+
 void GenerateClientHeader(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::CLIENT);
 
   out << "#pragma once\n\n";
@@ -511,13 +667,19 @@
   out << "private:\n";
   out.Indent();
   out << clazz << "(const ::ndk::SpAIBinder& binder);\n";
+
+  if (options.Version() > 0) {
+    out << "int32_t cached_version_ = -1;\n";
+  }
+
   out.Dedent();
   out << "};\n";
   LeaveNdkNamespace(out, defined_type);
 }
-void GenerateServerHeader(CodeWriter& out, const AidlTypenames& /*types*/,
-                          const AidlInterface& defined_type, const Options& /*options*/) {
+void GenerateServerHeader(CodeWriter& out, const AidlTypenames& types,
+                          const AidlInterface& defined_type, const Options& options) {
   const std::string clazz = ClassName(defined_type, ClassNames::SERVER);
+  const std::string iface = ClassName(defined_type, ClassNames::INTERFACE);
 
   out << "#pragma once\n\n";
   out << "#include \"" << NdkHeaderFile(defined_type, ClassNames::INTERFACE, false /*use_os_sep*/)
@@ -526,12 +688,24 @@
   out << "#include <android/binder_ibinder.h>\n";
   out << "\n";
   EnterNdkNamespace(out, defined_type);
-  out << "class " << clazz << " : public ::ndk::BnCInterface<"
-      << ClassName(defined_type, ClassNames::INTERFACE) << "> {\n";
+  out << "class " << clazz << " : public ::ndk::BnCInterface<" << iface << "> {\n";
   out << "public:\n";
   out.Indent();
   out << clazz << "();\n";
   out << "virtual ~" << clazz << "();\n";
+
+  // Declare the meta methods
+  for (const auto& method : defined_type.GetMethods()) {
+    if (method->IsUserDefined()) {
+      continue;
+    }
+    if (method->GetName() == kGetInterfaceVersion && options.Version() > 0) {
+      out << NdkMethodDecl(types, *method) << " final override;\n";
+    } else {
+      AIDL_FATAL(defined_type) << "Meta method '" << method->GetName() << "' is unimplemented.";
+    }
+  }
+
   out.Dedent();
   out << "protected:\n";
   out.Indent();
@@ -544,7 +718,7 @@
   LeaveNdkNamespace(out, defined_type);
 }
 void GenerateInterfaceHeader(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);
 
   out << "#pragma once\n\n";
@@ -558,12 +732,16 @@
   out << "class " << clazz << " : public ::ndk::ICInterface {\n";
   out << "public:\n";
   out.Indent();
-  out << "static AIBinder_Class* clazz;\n";
-  out << "static const char* descriptor;\n";
+  out << "static AIBinder_Class* " << kClazz << ";\n";
+  out << "static const char* " << kDescriptor << ";\n";
   out << clazz << "();\n";
   out << "virtual ~" << clazz << "();\n";
   out << "\n";
   GenerateConstantDeclarations(out, defined_type);
+  if (options.Version() > 0) {
+    out << "static const int32_t " << kVersion << " = " << std::to_string(options.Version())
+        << ";\n";
+  }
   out << "\n";
   out << "static binder_status_t writeToParcel(AParcel* parcel, const std::shared_ptr<" << clazz
       << ">& instance);";
@@ -571,11 +749,37 @@
   out << "static binder_status_t readFromParcel(const AParcel* parcel, std::shared_ptr<" << clazz
       << ">* instance);";
   out << "\n";
+  out << "static bool setDefaultImpl(std::shared_ptr<" << clazz << "> impl);";
+  out << "\n";
+  out << "static const std::shared_ptr<" << clazz << ">& getDefaultImpl();";
+  out << "\n";
   for (const auto& method : defined_type.GetMethods()) {
     out << "virtual " << NdkMethodDecl(types, *method) << " = 0;\n";
   }
   out.Dedent();
+  out << "private:\n";
+  out.Indent();
+  out << "static std::shared_ptr<" << clazz << "> default_impl;\n";
+  out.Dedent();
   out << "};\n";
+
+  const std::string defaultClazz = clazz + "Default";
+
+  out << "class " << defaultClazz << " : public " << clazz << " {\n";
+  out << "public:\n";
+  out.Indent();
+  for (const auto& method : defined_type.GetMethods()) {
+    if (method->IsUserDefined()) {
+      out << NdkMethodDecl(types, *method) << " override;\n";
+    } else if (method->GetName() == kGetInterfaceVersion && options.Version() > 0) {
+      out << NdkMethodDecl(types, *method) << " override;\n";
+    }
+  }
+  out << "::ndk::SpAIBinder asBinder() override;\n";
+  out << "bool isRemote() override;\n";
+  out.Dedent();
+  out << "};\n";
+
   LeaveNdkNamespace(out, defined_type);
 }
 void GenerateParcelHeader(CodeWriter& out, const AidlTypenames& types,
@@ -620,8 +824,8 @@
   GenerateSourceIncludes(out, types, defined_type);
   out << "\n";
   EnterNdkNamespace(out, defined_type);
-  out << "const char* " << clazz << "::descriptor = \"" << defined_type.GetCanonicalName()
-      << "\";\n";
+  out << "const char* " << clazz << "::" << kDescriptor << " = \""
+      << defined_type.GetCanonicalName() << "\";\n";
   out << "\n";
 
   out << "binder_status_t " << clazz << "::readFromParcel(const AParcel* parcel) {\n";
diff --git a/tests/test_data_example_interface.cpp b/tests/test_data_example_interface.cpp
index 8b324e4..b6cc349 100644
--- a/tests/test_data_example_interface.cpp
+++ b/tests/test_data_example_interface.cpp
@@ -2118,7 +2118,7 @@
 )";
 
 const char kExpectedJavaOutputWithVersion[] =
-R"(/*
+    R"(/*
  * This file is auto-generated.  DO NOT MODIFY.
  * Original file: android/test/IExampleInterface.aidl
  */
@@ -2272,6 +2272,8 @@
         }
         case TRANSACTION_getInterfaceVersion:
         {
+          data.enforceInterface(descriptor);
+          reply.writeNoException();
           reply.writeInt(getInterfaceVersion());
           return true;
         }
@@ -2509,6 +2511,7 @@
           android.os.Parcel data = android.os.Parcel.obtain();
           android.os.Parcel reply = android.os.Parcel.obtain();
           try {
+            data.writeInterfaceToken(DESCRIPTOR);
             mRemote.transact(Stub.TRANSACTION_getInterfaceVersion, data, reply, 0);
             mCachedVersion = reply.readInt();
           } finally {
@@ -2609,7 +2612,7 @@
       }
       return true;
     }
-    static final int TRANSACTION_getInterfaceVersion = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1599489362);
+    static final int TRANSACTION_getInterfaceVersion = (android.os.IBinder.FIRST_CALL_TRANSACTION + 16777214);
     public static boolean setDefaultImpl(android.test.IExampleInterface impl) {
       if (Stub.Proxy.sDefaultImpl == null && impl != null) {
         Stub.Proxy.sDefaultImpl = impl;
diff --git a/tests/test_data_ping_responder.cpp b/tests/test_data_ping_responder.cpp
index f6731cb..b2cc14f 100644
--- a/tests/test_data_ping_responder.cpp
+++ b/tests/test_data_ping_responder.cpp
@@ -686,10 +686,11 @@
 }
 
 int32_t BpPingResponder::getInterfaceVersion() {
-  if (cached_version_ != -1) {
+  if (cached_version_ == -1) {
     ::android::Parcel data;
     ::android::Parcel reply;
-    ::android::status_t err = remote()->transact(1599489362 /* getInterfaceVersion */, data, &reply);
+    data.writeInterfaceToken(getInterfaceDescriptor());
+    ::android::status_t err = remote()->transact(16777214 /* getInterfaceVersion */, data, &reply);
     if (err == ::android::OK) {
       cached_version_ = reply.readInt32();
     }
@@ -814,8 +815,9 @@
     }
   }
   break;
-  case 1599489362 /* getInterfaceVersion */:
+  case 16777214 /* getInterfaceVersion */:
   {
+    _aidl_data.checkInterface(this);
     _aidl_reply->writeInt32(IPingResponder::VERSION);
   }
   break;
diff --git a/tests/test_data_string_constants.cpp b/tests/test_data_string_constants.cpp
index 79046f0..41155cc 100644
--- a/tests/test_data_string_constants.cpp
+++ b/tests/test_data_string_constants.cpp
@@ -224,7 +224,7 @@
 )";
 
 const char kExpectedJavaOutputWithVersion[] =
-R"(/*
+    R"(/*
  * This file is auto-generated.  DO NOT MODIFY.
  * Original file: android/os/IStringConstants.aidl
  */
@@ -290,6 +290,8 @@
         }
         case TRANSACTION_getInterfaceVersion:
         {
+          data.enforceInterface(descriptor);
+          reply.writeNoException();
           reply.writeInt(getInterfaceVersion());
           return true;
         }
@@ -321,6 +323,7 @@
           android.os.Parcel data = android.os.Parcel.obtain();
           android.os.Parcel reply = android.os.Parcel.obtain();
           try {
+            data.writeInterfaceToken(DESCRIPTOR);
             mRemote.transact(Stub.TRANSACTION_getInterfaceVersion, data, reply, 0);
             mCachedVersion = reply.readInt();
           } finally {
@@ -332,7 +335,7 @@
       }
       public static android.os.IStringConstants sDefaultImpl;
     }
-    static final int TRANSACTION_getInterfaceVersion = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1599489362);
+    static final int TRANSACTION_getInterfaceVersion = (android.os.IBinder.FIRST_CALL_TRANSACTION + 16777214);
     public static boolean setDefaultImpl(android.os.IStringConstants impl) {
       if (Stub.Proxy.sDefaultImpl == null && impl != null) {
         Stub.Proxy.sDefaultImpl = impl;
@@ -424,10 +427,11 @@
 }
 
 int32_t BpStringConstants::getInterfaceVersion() {
-  if (cached_version_ != -1) {
+  if (cached_version_ == -1) {
     ::android::Parcel data;
     ::android::Parcel reply;
-    ::android::status_t err = remote()->transact(1599489362 /* getInterfaceVersion */, data, &reply);
+    data.writeInterfaceToken(getInterfaceDescriptor());
+    ::android::status_t err = remote()->transact(16777214 /* getInterfaceVersion */, data, &reply);
     if (err == ::android::OK) {
       cached_version_ = reply.readInt32();
     }
@@ -448,8 +452,9 @@
 ::android::status_t BnStringConstants::onTransact(uint32_t _aidl_code, const ::android::Parcel& _aidl_data, ::android::Parcel* _aidl_reply, uint32_t _aidl_flags) {
   ::android::status_t _aidl_ret_status = ::android::OK;
   switch (_aidl_code) {
-  case 1599489362 /* getInterfaceVersion */:
+  case 16777214 /* getInterfaceVersion */:
   {
+    _aidl_data.checkInterface(this);
     _aidl_reply->writeInt32(IStringConstants::VERSION);
   }
   break;