[BinderTransport] Avoid depending on NdkBinder at compile time (#27912)

* [BinderTransport] Avoid depending on NdkBinder at compile time

We would like to make it possible to use BinderTransport in a APK that
has min sdk version lower than 29 (NdkBinder was introduced at 29)

We copies constants and type definitions from Ndk headers, creates a
same name wrapper for every NdkBinder API we use in
grpc_binder::ndk_util namespace.

We will try to load libbinder_ndk.so and resolve the symbol when the
NdkBinder API wrappers are invoked.

* regenerate projects

* Add GRPC_NO_BINDER guard
diff --git a/BUILD b/BUILD
index a1080ac..0c23c65 100644
--- a/BUILD
+++ b/BUILD
@@ -557,6 +557,7 @@
         "src/core/ext/transport/binder/server/binder_server.cc",
         "src/core/ext/transport/binder/server/binder_server_credentials.cc",
         "src/core/ext/transport/binder/transport/binder_transport.cc",
+        "src/core/ext/transport/binder/utils/ndk_binder.cc",
         "src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc",
         "src/core/ext/transport/binder/wire_format/binder_android.cc",
         "src/core/ext/transport/binder/wire_format/binder_constants.cc",
@@ -574,6 +575,8 @@
         "src/core/ext/transport/binder/server/binder_server.h",
         "src/core/ext/transport/binder/transport/binder_stream.h",
         "src/core/ext/transport/binder/transport/binder_transport.h",
+        "src/core/ext/transport/binder/utils/binder_auto_utils.h",
+        "src/core/ext/transport/binder/utils/ndk_binder.h",
         "src/core/ext/transport/binder/utils/transport_stream_receiver.h",
         "src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h",
         "src/core/ext/transport/binder/wire_format/binder.h",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eb87405..ee41b91 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2785,6 +2785,7 @@
   src/core/ext/transport/binder/server/binder_server.cc
   src/core/ext/transport/binder/server/binder_server_credentials.cc
   src/core/ext/transport/binder/transport/binder_transport.cc
+  src/core/ext/transport/binder/utils/ndk_binder.cc
   src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   src/core/ext/transport/binder/wire_format/binder_android.cc
   src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -8389,6 +8390,7 @@
   src/core/ext/transport/binder/server/binder_server.cc
   src/core/ext/transport/binder/server/binder_server_credentials.cc
   src/core/ext/transport/binder/transport/binder_transport.cc
+  src/core/ext/transport/binder/utils/ndk_binder.cc
   src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   src/core/ext/transport/binder/wire_format/binder_android.cc
   src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -10124,6 +10126,7 @@
   src/core/ext/transport/binder/server/binder_server.cc
   src/core/ext/transport/binder/server/binder_server_credentials.cc
   src/core/ext/transport/binder/transport/binder_transport.cc
+  src/core/ext/transport/binder/utils/ndk_binder.cc
   src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   src/core/ext/transport/binder/wire_format/binder_android.cc
   src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -10531,6 +10534,7 @@
   src/core/ext/transport/binder/server/binder_server.cc
   src/core/ext/transport/binder/server/binder_server_credentials.cc
   src/core/ext/transport/binder/transport/binder_transport.cc
+  src/core/ext/transport/binder/utils/ndk_binder.cc
   src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   src/core/ext/transport/binder/wire_format/binder_android.cc
   src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -16156,6 +16160,7 @@
   src/core/ext/transport/binder/server/binder_server.cc
   src/core/ext/transport/binder/server/binder_server_credentials.cc
   src/core/ext/transport/binder/transport/binder_transport.cc
+  src/core/ext/transport/binder/utils/ndk_binder.cc
   src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   src/core/ext/transport/binder/wire_format/binder_android.cc
   src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -16462,6 +16467,7 @@
   src/core/ext/transport/binder/server/binder_server.cc
   src/core/ext/transport/binder/server/binder_server_credentials.cc
   src/core/ext/transport/binder/transport/binder_transport.cc
+  src/core/ext/transport/binder/utils/ndk_binder.cc
   src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   src/core/ext/transport/binder/wire_format/binder_android.cc
   src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -16552,6 +16558,7 @@
   src/core/ext/transport/binder/server/binder_server.cc
   src/core/ext/transport/binder/server/binder_server_credentials.cc
   src/core/ext/transport/binder/transport/binder_transport.cc
+  src/core/ext/transport/binder/utils/ndk_binder.cc
   src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   src/core/ext/transport/binder/wire_format/binder_android.cc
   src/core/ext/transport/binder/wire_format/binder_constants.cc
diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index 9881310..4c5ada8 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -2454,6 +2454,8 @@
   - src/core/ext/transport/binder/server/binder_server.h
   - src/core/ext/transport/binder/transport/binder_stream.h
   - src/core/ext/transport/binder/transport/binder_transport.h
+  - src/core/ext/transport/binder/utils/binder_auto_utils.h
+  - src/core/ext/transport/binder/utils/ndk_binder.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h
   - src/core/ext/transport/binder/wire_format/binder.h
@@ -2486,6 +2488,7 @@
   - src/core/ext/transport/binder/server/binder_server.cc
   - src/core/ext/transport/binder/server/binder_server_credentials.cc
   - src/core/ext/transport/binder/transport/binder_transport.cc
+  - src/core/ext/transport/binder/utils/ndk_binder.cc
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   - src/core/ext/transport/binder/wire_format/binder_android.cc
   - src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -4683,6 +4686,8 @@
   - src/core/ext/transport/binder/server/binder_server.h
   - src/core/ext/transport/binder/transport/binder_stream.h
   - src/core/ext/transport/binder/transport/binder_transport.h
+  - src/core/ext/transport/binder/utils/binder_auto_utils.h
+  - src/core/ext/transport/binder/utils/ndk_binder.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h
   - src/core/ext/transport/binder/wire_format/binder.h
@@ -4712,6 +4717,7 @@
   - src/core/ext/transport/binder/server/binder_server.cc
   - src/core/ext/transport/binder/server/binder_server_credentials.cc
   - src/core/ext/transport/binder/transport/binder_transport.cc
+  - src/core/ext/transport/binder/utils/ndk_binder.cc
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   - src/core/ext/transport/binder/wire_format/binder_android.cc
   - src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -5487,6 +5493,8 @@
   - src/core/ext/transport/binder/server/binder_server.h
   - src/core/ext/transport/binder/transport/binder_stream.h
   - src/core/ext/transport/binder/transport/binder_transport.h
+  - src/core/ext/transport/binder/utils/binder_auto_utils.h
+  - src/core/ext/transport/binder/utils/ndk_binder.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h
   - src/core/ext/transport/binder/wire_format/binder.h
@@ -5516,6 +5524,7 @@
   - src/core/ext/transport/binder/server/binder_server.cc
   - src/core/ext/transport/binder/server/binder_server_credentials.cc
   - src/core/ext/transport/binder/transport/binder_transport.cc
+  - src/core/ext/transport/binder/utils/ndk_binder.cc
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   - src/core/ext/transport/binder/wire_format/binder_android.cc
   - src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -5702,6 +5711,8 @@
   - src/core/ext/transport/binder/server/binder_server.h
   - src/core/ext/transport/binder/transport/binder_stream.h
   - src/core/ext/transport/binder/transport/binder_transport.h
+  - src/core/ext/transport/binder/utils/binder_auto_utils.h
+  - src/core/ext/transport/binder/utils/ndk_binder.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h
   - src/core/ext/transport/binder/wire_format/binder.h
@@ -5731,6 +5742,7 @@
   - src/core/ext/transport/binder/server/binder_server.cc
   - src/core/ext/transport/binder/server/binder_server_credentials.cc
   - src/core/ext/transport/binder/transport/binder_transport.cc
+  - src/core/ext/transport/binder/utils/ndk_binder.cc
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   - src/core/ext/transport/binder/wire_format/binder_android.cc
   - src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -8070,6 +8082,8 @@
   - src/core/ext/transport/binder/server/binder_server.h
   - src/core/ext/transport/binder/transport/binder_stream.h
   - src/core/ext/transport/binder/transport/binder_transport.h
+  - src/core/ext/transport/binder/utils/binder_auto_utils.h
+  - src/core/ext/transport/binder/utils/ndk_binder.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h
   - src/core/ext/transport/binder/wire_format/binder.h
@@ -8098,6 +8112,7 @@
   - src/core/ext/transport/binder/server/binder_server.cc
   - src/core/ext/transport/binder/server/binder_server_credentials.cc
   - src/core/ext/transport/binder/transport/binder_transport.cc
+  - src/core/ext/transport/binder/utils/ndk_binder.cc
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   - src/core/ext/transport/binder/wire_format/binder_android.cc
   - src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -8245,6 +8260,8 @@
   - src/core/ext/transport/binder/server/binder_server.h
   - src/core/ext/transport/binder/transport/binder_stream.h
   - src/core/ext/transport/binder/transport/binder_transport.h
+  - src/core/ext/transport/binder/utils/binder_auto_utils.h
+  - src/core/ext/transport/binder/utils/ndk_binder.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h
   - src/core/ext/transport/binder/wire_format/binder.h
@@ -8274,6 +8291,7 @@
   - src/core/ext/transport/binder/server/binder_server.cc
   - src/core/ext/transport/binder/server/binder_server_credentials.cc
   - src/core/ext/transport/binder/transport/binder_transport.cc
+  - src/core/ext/transport/binder/utils/ndk_binder.cc
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   - src/core/ext/transport/binder/wire_format/binder_android.cc
   - src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -8336,6 +8354,8 @@
   - src/core/ext/transport/binder/server/binder_server.h
   - src/core/ext/transport/binder/transport/binder_stream.h
   - src/core/ext/transport/binder/transport/binder_transport.h
+  - src/core/ext/transport/binder/utils/binder_auto_utils.h
+  - src/core/ext/transport/binder/utils/ndk_binder.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver.h
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h
   - src/core/ext/transport/binder/wire_format/binder.h
@@ -8365,6 +8385,7 @@
   - src/core/ext/transport/binder/server/binder_server.cc
   - src/core/ext/transport/binder/server/binder_server_credentials.cc
   - src/core/ext/transport/binder/transport/binder_transport.cc
+  - src/core/ext/transport/binder/utils/ndk_binder.cc
   - src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc
   - src/core/ext/transport/binder/wire_format/binder_android.cc
   - src/core/ext/transport/binder/wire_format/binder_constants.cc
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleclient/BUILD b/examples/android/binder/java/io/grpc/binder/cpp/exampleclient/BUILD
index 3efffcd..c21ea99 100644
--- a/examples/android/binder/java/io/grpc/binder/cpp/exampleclient/BUILD
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleclient/BUILD
@@ -21,7 +21,6 @@
         "-ldl",
         "-llog",
         "-lm",
-        "-lbinder_ndk",
         "-Wl,--no-undefined",
     ],
     deps = [
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/BUILD b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/BUILD
index df302fe..3c11762 100644
--- a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/BUILD
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/BUILD
@@ -21,7 +21,6 @@
         "-ldl",
         "-llog",
         "-lm",
-        "-lbinder_ndk",
         "-Wl,--no-undefined",
     ],
     deps = [
diff --git a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/native.cc b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/native.cc
index ef77d60..6868063 100644
--- a/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/native.cc
+++ b/examples/android/binder/java/io/grpc/binder/cpp/exampleserver/native.cc
@@ -12,10 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <android/binder_auto_utils.h>
-#include <android/binder_ibinder.h>
-#include <android/binder_ibinder_jni.h>
-#include <android/binder_interface_utils.h>
 #include <android/log.h>
 #include <jni.h>
 
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index dfab7a7..896884f 100644
--- a/gRPC-C++.podspec
+++ b/gRPC-C++.podspec
@@ -292,6 +292,9 @@
                       'src/core/ext/transport/binder/transport/binder_stream.h',
                       'src/core/ext/transport/binder/transport/binder_transport.cc',
                       'src/core/ext/transport/binder/transport/binder_transport.h',
+                      'src/core/ext/transport/binder/utils/binder_auto_utils.h',
+                      'src/core/ext/transport/binder/utils/ndk_binder.cc',
+                      'src/core/ext/transport/binder/utils/ndk_binder.h',
                       'src/core/ext/transport/binder/utils/transport_stream_receiver.h',
                       'src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc',
                       'src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h',
@@ -989,6 +992,8 @@
                               'src/core/ext/transport/binder/server/binder_server.h',
                               'src/core/ext/transport/binder/transport/binder_stream.h',
                               'src/core/ext/transport/binder/transport/binder_transport.h',
+                              'src/core/ext/transport/binder/utils/binder_auto_utils.h',
+                              'src/core/ext/transport/binder/utils/ndk_binder.h',
                               'src/core/ext/transport/binder/utils/transport_stream_receiver.h',
                               'src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h',
                               'src/core/ext/transport/binder/wire_format/binder.h',
diff --git a/grpc.gyp b/grpc.gyp
index 8dd6cd0..1c5540f 100644
--- a/grpc.gyp
+++ b/grpc.gyp
@@ -1468,6 +1468,7 @@
         'src/core/ext/transport/binder/server/binder_server.cc',
         'src/core/ext/transport/binder/server/binder_server_credentials.cc',
         'src/core/ext/transport/binder/transport/binder_transport.cc',
+        'src/core/ext/transport/binder/utils/ndk_binder.cc',
         'src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc',
         'src/core/ext/transport/binder/wire_format/binder_android.cc',
         'src/core/ext/transport/binder/wire_format/binder_constants.cc',
diff --git a/include/grpc/impl/codegen/port_platform.h b/include/grpc/impl/codegen/port_platform.h
index d9fdeed..96ef901 100644
--- a/include/grpc/impl/codegen/port_platform.h
+++ b/include/grpc/impl/codegen/port_platform.h
@@ -127,7 +127,7 @@
 #define GPR_PLATFORM_STRING "android"
 #define GPR_ANDROID 1
 #ifdef __ANDROID_API__
-#if (__ANDROID_API__) >= 29
+#if (__ANDROID_API__) >= 23
 #define GPR_SUPPORT_BINDER_TRANSPORT 1
 #endif
 #endif
diff --git a/src/core/ext/transport/binder/client/channel_create.cc b/src/core/ext/transport/binder/client/channel_create.cc
index 482efa6..eb5d800 100644
--- a/src/core/ext/transport/binder/client/channel_create.cc
+++ b/src/core/ext/transport/binder/client/channel_create.cc
@@ -33,11 +33,6 @@
 
 #include <grpc/support/port_platform.h>
 
-#include <android/binder_auto_utils.h>
-#include <android/binder_ibinder.h>
-#include <android/binder_ibinder_jni.h>
-#include <android/binder_interface_utils.h>
-
 #include "absl/memory/memory.h"
 #include "absl/time/clock.h"
 #include "absl/time/time.h"
diff --git a/src/core/ext/transport/binder/client/endpoint_binder_pool.cc b/src/core/ext/transport/binder/client/endpoint_binder_pool.cc
index 7ae7818..638c93c 100644
--- a/src/core/ext/transport/binder/client/endpoint_binder_pool.cc
+++ b/src/core/ext/transport/binder/client/endpoint_binder_pool.cc
@@ -36,7 +36,8 @@
   const char* conn_id = jni_env->GetStringUTFChars(conn_id_jstring, &isCopy);
   gpr_log(GPR_ERROR, "%s called with conn_id = %s", __func__, conn_id);
   GPR_ASSERT(ibinder != nullptr);
-  ndk::SpAIBinder aibinder = grpc_binder::FromJavaBinder(jni_env, ibinder);
+  grpc_binder::ndk_util::SpAIBinder aibinder =
+      grpc_binder::FromJavaBinder(jni_env, ibinder);
   gpr_log(GPR_ERROR, "aibinder = %p", aibinder.get());
   auto b = absl::make_unique<grpc_binder::BinderAndroid>(aibinder);
   GPR_ASSERT(b != nullptr);
diff --git a/src/core/ext/transport/binder/server/binder_server.cc b/src/core/ext/transport/binder/server/binder_server.cc
index 7907437..bbcf078 100644
--- a/src/core/ext/transport/binder/server/binder_server.cc
+++ b/src/core/ext/transport/binder/server/binder_server.cc
@@ -27,6 +27,7 @@
 #include <grpc/grpc.h>
 
 #include "src/core/ext/transport/binder/transport/binder_transport.h"
+#include "src/core/ext/transport/binder/utils/ndk_binder.h"
 #include "src/core/ext/transport/binder/wire_format/binder_android.h"
 #include "src/core/lib/iomgr/exec_ctx.h"
 #include "src/core/lib/surface/server.h"
@@ -34,8 +35,6 @@
 
 #ifdef GPR_SUPPORT_BINDER_TRANSPORT
 
-#include <android/binder_ibinder.h>
-#include <android/binder_ibinder_jni.h>
 #include <jni.h>
 
 extern "C" {
@@ -45,14 +44,14 @@
 JNIEXPORT jobject JNICALL
 Java_io_grpc_binder_cpp_GrpcCppServerBuilder_GetEndpointBinderInternal__Ljava_lang_String_2(
     JNIEnv* jni_env, jobject, jstring conn_id_jstring) {
-  AIBinder* ai_binder = nullptr;
+  grpc_binder::ndk_util::AIBinder* ai_binder = nullptr;
 
   {
     // This block is the scope of conn_id c-string
     jboolean isCopy;
     const char* conn_id = jni_env->GetStringUTFChars(conn_id_jstring, &isCopy);
-    ai_binder =
-        static_cast<AIBinder*>(grpc_get_endpoint_binder(std::string(conn_id)));
+    ai_binder = static_cast<grpc_binder::ndk_util::AIBinder*>(
+        grpc_get_endpoint_binder(std::string(conn_id)));
     if (ai_binder == nullptr) {
       gpr_log(GPR_ERROR, "Cannot find endpoint binder with connection id = %s",
               conn_id);
@@ -66,7 +65,7 @@
     return nullptr;
   }
 
-  return AIBinder_toJavaBinder(jni_env, ai_binder);
+  return grpc_binder::ndk_util::AIBinder_toJavaBinder(jni_env, ai_binder);
 }
 }
 
diff --git a/src/core/ext/transport/binder/utils/binder_auto_utils.h b/src/core/ext/transport/binder/utils/binder_auto_utils.h
new file mode 100644
index 0000000..2317b5b
--- /dev/null
+++ b/src/core/ext/transport/binder/utils/binder_auto_utils.h
@@ -0,0 +1,76 @@
+// Copyright 2021 gRPC authors.
+//
+// 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.
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_BINDER_UTILS_BINDER_AUTO_UTILS_H
+#define GRPC_CORE_EXT_TRANSPORT_BINDER_UTILS_BINDER_AUTO_UTILS_H
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_SUPPORT_BINDER_TRANSPORT
+
+#include "src/core/ext/transport/binder/utils/ndk_binder.h"
+
+namespace grpc_binder {
+namespace ndk_util {
+
+/**
+ * Represents one strong pointer to an AIBinder object.
+ * Copied from binder/ndk/include_cpp/android/binder_auto_utils.h
+ */
+class SpAIBinder {
+ public:
+  SpAIBinder() : mBinder(nullptr) {}
+  explicit SpAIBinder(AIBinder* binder) : mBinder(binder) {}
+  SpAIBinder(std::nullptr_t)
+      : SpAIBinder() {}  // NOLINT(google-explicit-constructor)
+  SpAIBinder(const SpAIBinder& other) { *this = other; }
+
+  ~SpAIBinder() { set(nullptr); }
+  SpAIBinder& operator=(const SpAIBinder& other) {
+    if (this == &other) {
+      return *this;
+    }
+    AIBinder_incStrong(other.mBinder);
+    set(other.mBinder);
+    return *this;
+  }
+
+  void set(AIBinder* binder) {
+    AIBinder* old = *const_cast<AIBinder* volatile*>(&mBinder);
+    if (old != nullptr) AIBinder_decStrong(old);
+    if (old != *const_cast<AIBinder* volatile*>(&mBinder)) {
+      __assert(__FILE__, __LINE__, "Race detected.");
+    }
+    mBinder = binder;
+  }
+
+  AIBinder* get() const { return mBinder; }
+  AIBinder** getR() { return &mBinder; }
+
+  bool operator!=(const SpAIBinder& rhs) const { return get() != rhs.get(); }
+  bool operator<(const SpAIBinder& rhs) const { return get() < rhs.get(); }
+  bool operator<=(const SpAIBinder& rhs) const { return get() <= rhs.get(); }
+  bool operator==(const SpAIBinder& rhs) const { return get() == rhs.get(); }
+  bool operator>(const SpAIBinder& rhs) const { return get() > rhs.get(); }
+  bool operator>=(const SpAIBinder& rhs) const { return get() >= rhs.get(); }
+
+ private:
+  AIBinder* mBinder = nullptr;
+};
+}  // namespace ndk_util
+}  // namespace grpc_binder
+
+#endif
+
+#endif  // GRPC_CORE_EXT_TRANSPORT_BINDER_UTILS_BINDER_AUTO_UTILS_H
diff --git a/src/core/ext/transport/binder/utils/ndk_binder.cc b/src/core/ext/transport/binder/utils/ndk_binder.cc
new file mode 100644
index 0000000..5c0aedd
--- /dev/null
+++ b/src/core/ext/transport/binder/utils/ndk_binder.cc
@@ -0,0 +1,165 @@
+// Copyright 2021 gRPC authors.
+//
+// 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 <grpc/support/port_platform.h>
+
+#include "src/core/ext/transport/binder/utils/ndk_binder.h"
+
+#ifndef GRPC_NO_BINDER
+
+#ifdef GPR_SUPPORT_BINDER_TRANSPORT
+
+#include <dlfcn.h>
+
+#include <grpc/support/log.h>
+
+namespace {
+void* GetNdkBinderHandle() {
+  // TODO(mingcl): Consider using RTLD_NOLOAD to check if it is already loaded
+  // first
+  static void* handle = dlopen("libbinder_ndk.so", RTLD_LAZY);
+  if (handle == nullptr) {
+    gpr_log(
+        GPR_ERROR,
+        "Cannot open libbinder_ndk.so. Does this device support API level 29?");
+    GPR_ASSERT(0);
+  }
+  return handle;
+}
+}  // namespace
+
+namespace grpc_binder {
+namespace ndk_util {
+
+// Helper macro to obtain the function pointer corresponding to the name
+#define FORWARD(name)                                                  \
+  typedef decltype(&name) func_type;                                   \
+  static func_type ptr =                                               \
+      reinterpret_cast<func_type>(dlsym(GetNdkBinderHandle(), #name)); \
+  if (ptr == nullptr) {                                                \
+    gpr_log(GPR_ERROR,                                                 \
+            "dlsym failed. Cannot find %s in libbinder_ndk.so. "       \
+            "BinderTransport requires API level >= 33",                \
+            #name);                                                    \
+    GPR_ASSERT(0);                                                     \
+  }                                                                    \
+  return ptr
+
+void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) {
+  FORWARD(AIBinder_Class_disableInterfaceTokenHeader)(clazz);
+}
+
+void* AIBinder_getUserData(AIBinder* binder) {
+  FORWARD(AIBinder_getUserData)(binder);
+}
+
+uid_t AIBinder_getCallingUid() { FORWARD(AIBinder_getCallingUid)(); }
+
+AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) {
+  FORWARD(AIBinder_fromJavaBinder)(env, binder);
+}
+
+AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor,
+                                      AIBinder_Class_onCreate onCreate,
+                                      AIBinder_Class_onDestroy onDestroy,
+                                      AIBinder_Class_onTransact onTransact) {
+  FORWARD(AIBinder_Class_define)
+  (interfaceDescriptor, onCreate, onDestroy, onTransact);
+}
+
+AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args) {
+  FORWARD(AIBinder_new)(clazz, args);
+}
+
+bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz) {
+  FORWARD(AIBinder_associateClass)(binder, clazz);
+}
+
+void AIBinder_incStrong(AIBinder* binder) {
+  FORWARD(AIBinder_incStrong)(binder);
+}
+
+void AIBinder_decStrong(AIBinder* binder) {
+  FORWARD(AIBinder_decStrong)(binder);
+}
+
+binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code,
+                                  AParcel** in, AParcel** out,
+                                  binder_flags_t flags) {
+  FORWARD(AIBinder_transact)(binder, code, in, out, flags);
+}
+
+binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData,
+                                      AParcel_byteArrayAllocator allocator) {
+  FORWARD(AParcel_readByteArray)(parcel, arrayData, allocator);
+}
+
+void AParcel_delete(AParcel* parcel) { FORWARD(AParcel_delete)(parcel); }
+int32_t AParcel_getDataSize(const AParcel* parcel) {
+  FORWARD(AParcel_getDataSize)(parcel);
+}
+
+binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value) {
+  FORWARD(AParcel_writeInt32)(parcel, value);
+}
+
+binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value) {
+  FORWARD(AParcel_writeInt64)(parcel, value);
+}
+
+binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder) {
+  FORWARD(AParcel_writeStrongBinder)(parcel, binder);
+}
+
+binder_status_t AParcel_writeString(AParcel* parcel, const char* string,
+                                    int32_t length) {
+  FORWARD(AParcel_writeString)(parcel, string, length);
+}
+
+binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value) {
+  FORWARD(AParcel_readInt32)(parcel, value);
+}
+
+binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value) {
+  FORWARD(AParcel_readInt64)(parcel, value);
+}
+
+binder_status_t AParcel_readString(const AParcel* parcel, void* stringData,
+                                   AParcel_stringAllocator allocator) {
+  FORWARD(AParcel_readString)(parcel, stringData, allocator);
+}
+
+binder_status_t AParcel_readStrongBinder(const AParcel* parcel,
+                                         AIBinder** binder) {
+  FORWARD(AParcel_readStrongBinder)(parcel, binder);
+}
+
+binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData,
+                                       int32_t length) {
+  FORWARD(AParcel_writeByteArray)(parcel, arrayData, length);
+}
+
+binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in) {
+  FORWARD(AIBinder_prepareTransaction)(binder, in);
+}
+
+jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder) {
+  FORWARD(AIBinder_toJavaBinder)(env, binder);
+}
+
+}  // namespace ndk_util
+}  // namespace grpc_binder
+
+#endif
+#endif
diff --git a/src/core/ext/transport/binder/utils/ndk_binder.h b/src/core/ext/transport/binder/utils/ndk_binder.h
new file mode 100644
index 0000000..7cf181e
--- /dev/null
+++ b/src/core/ext/transport/binder/utils/ndk_binder.h
@@ -0,0 +1,107 @@
+// Copyright 2021 gRPC authors.
+//
+// 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.
+
+#ifndef GRPC_CORE_EXT_TRANSPORT_BINDER_UTILS_NDK_BINDER_H
+#define GRPC_CORE_EXT_TRANSPORT_BINDER_UTILS_NDK_BINDER_H
+
+#include <grpc/support/port_platform.h>
+
+#ifdef GPR_SUPPORT_BINDER_TRANSPORT
+
+#include <assert.h>
+#include <jni.h>
+
+#include <memory>
+
+// This file defines NdkBinder functions, variables, and types in
+// ::grpc_binder::ndk_util namespace. This allows us to dynamically load
+// libbinder_ndk at runtime, and make it possible to compile the code without
+// the library present at compile time.
+
+// TODO(mingcl): Consider if we want to check API level and include NDK headers
+// normally if the level is high enough
+
+namespace grpc_binder {
+namespace ndk_util {
+
+struct AIBinder;
+struct AParcel;
+struct AIBinder_Class;
+
+// Only enum values used by the project is defined here
+enum {
+  FLAG_ONEWAY = 0x01,
+};
+enum {
+  STATUS_OK = 0,
+  STATUS_UNKNOWN_ERROR = (-2147483647 - 1),
+};
+
+typedef int32_t binder_status_t;
+typedef uint32_t binder_flags_t;
+typedef uint32_t transaction_code_t;
+
+typedef bool (*AParcel_byteArrayAllocator)(void* arrayData, int32_t length,
+                                           int8_t** outBuffer);
+typedef bool (*AParcel_stringAllocator)(void* stringData, int32_t length,
+                                        char** buffer);
+typedef void* (*AIBinder_Class_onCreate)(void* args);
+typedef void (*AIBinder_Class_onDestroy)(void* userData);
+typedef binder_status_t (*AIBinder_Class_onTransact)(AIBinder* binder,
+                                                     transaction_code_t code,
+                                                     const AParcel* in,
+                                                     AParcel* out);
+
+void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz);
+void* AIBinder_getUserData(AIBinder* binder);
+uid_t AIBinder_getCallingUid();
+AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder);
+AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor,
+                                      AIBinder_Class_onCreate onCreate,
+                                      AIBinder_Class_onDestroy onDestroy,
+                                      AIBinder_Class_onTransact onTransact);
+AIBinder* AIBinder_new(const AIBinder_Class* clazz, void* args);
+bool AIBinder_associateClass(AIBinder* binder, const AIBinder_Class* clazz);
+void AIBinder_incStrong(AIBinder* binder);
+void AIBinder_decStrong(AIBinder* binder);
+binder_status_t AIBinder_transact(AIBinder* binder, transaction_code_t code,
+                                  AParcel** in, AParcel** out,
+                                  binder_flags_t flags);
+binder_status_t AParcel_readByteArray(const AParcel* parcel, void* arrayData,
+                                      AParcel_byteArrayAllocator allocator);
+void AParcel_delete(AParcel* parcel);
+int32_t AParcel_getDataSize(const AParcel* parcel);
+binder_status_t AParcel_writeInt32(AParcel* parcel, int32_t value);
+binder_status_t AParcel_writeInt64(AParcel* parcel, int64_t value);
+binder_status_t AParcel_writeStrongBinder(AParcel* parcel, AIBinder* binder);
+binder_status_t AParcel_writeString(AParcel* parcel, const char* string,
+                                    int32_t length);
+binder_status_t AParcel_readInt32(const AParcel* parcel, int32_t* value);
+binder_status_t AParcel_readInt64(const AParcel* parcel, int64_t* value);
+binder_status_t AParcel_readString(const AParcel* parcel, void* stringData,
+                                   AParcel_stringAllocator allocator);
+binder_status_t AParcel_readStrongBinder(const AParcel* parcel,
+                                         AIBinder** binder);
+binder_status_t AParcel_writeByteArray(AParcel* parcel, const int8_t* arrayData,
+                                       int32_t length);
+binder_status_t AIBinder_prepareTransaction(AIBinder* binder, AParcel** in);
+jobject AIBinder_toJavaBinder(JNIEnv* env, AIBinder* binder);
+
+}  // namespace ndk_util
+
+}  // namespace grpc_binder
+
+#endif /*GPR_SUPPORT_BINDER_TRANSPORT*/
+
+#endif  // GRPC_CORE_EXT_TRANSPORT_BINDER_UTILS_NDK_BINDER_H
diff --git a/src/core/ext/transport/binder/wire_format/binder_android.cc b/src/core/ext/transport/binder/wire_format/binder_android.cc
index 1683682..bba8616 100644
--- a/src/core/ext/transport/binder/wire_format/binder_android.cc
+++ b/src/core/ext/transport/binder/wire_format/binder_android.cc
@@ -28,17 +28,6 @@
 #include "src/core/ext/transport/binder/wire_format/binder_android.h"
 #include "src/core/lib/gprpp/sync.h"
 
-extern "C" {
-// TODO(mingcl): This function is introduced at API level 32 and is not
-// available in any NDK release yet. So we export it weakly so that we can use
-// it without triggering undefined reference error. Its purpose is to disable
-// header in Parcel to conform to the BinderChannel wire format.
-extern void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz)
-    __attribute__((weak));
-// This is released in API level 31.
-extern int32_t AParcel_getDataSize(const AParcel* parcel) __attribute__((weak));
-}
-
 namespace grpc_binder {
 namespace {
 
@@ -69,24 +58,27 @@
 void f_onDestroy_noop(void* /*userData*/) {}
 
 // TODO(mingcl): Consider if thread safety is a requirement here
-binder_status_t f_onTransact(AIBinder* binder, transaction_code_t code,
-                             const AParcel* in, AParcel* /*out*/) {
+ndk_util::binder_status_t f_onTransact(ndk_util::AIBinder* binder,
+                                       transaction_code_t code,
+                                       const ndk_util::AParcel* in,
+                                       ndk_util::AParcel* /*out*/) {
   gpr_log(GPR_INFO, __func__);
   gpr_log(GPR_INFO, "tx code = %u", code);
 
-  auto* user_data = static_cast<BinderUserData*>(AIBinder_getUserData(binder));
+  auto* user_data =
+      static_cast<BinderUserData*>(ndk_util::AIBinder_getUserData(binder));
   TransactionReceiver::OnTransactCb* callback = user_data->callback;
   // Wrap the parcel in a ReadableParcel.
   std::unique_ptr<ReadableParcel> output =
       absl::make_unique<ReadableParcelAndroid>(in);
   // The lock should be released "after" the callback finishes.
   absl::Status status =
-      (*callback)(code, output.get(), AIBinder_getCallingUid());
+      (*callback)(code, output.get(), ndk_util::AIBinder_getCallingUid());
   if (status.ok()) {
-    return STATUS_OK;
+    return ndk_util::STATUS_OK;
   } else {
     gpr_log(GPR_ERROR, "Callback failed: %s", status.ToString().c_str());
-    return STATUS_UNKNOWN_ERROR;
+    return ndk_util::STATUS_UNKNOWN_ERROR;
   }
 }
 
@@ -103,9 +95,10 @@
   return true;
 }
 
-binder_status_t AParcelReadString(const AParcel* parcel, std::string* str) {
+ndk_util::binder_status_t AParcelReadString(const ndk_util::AParcel* parcel,
+                                            std::string* str) {
   void* stringData = static_cast<void*>(str);
-  return AParcel_readString(parcel, stringData, StdStringAllocator);
+  return ndk_util::AParcel_readString(parcel, stringData, StdStringAllocator);
 }
 
 template <typename T>
@@ -120,16 +113,18 @@
   return true;
 }
 
-binder_status_t AParcelReadVector(const AParcel* parcel,
-                                  std::vector<uint8_t>* vec) {
+ndk_util::binder_status_t AParcelReadVector(const ndk_util::AParcel* parcel,
+                                            std::vector<uint8_t>* vec) {
   void* vectorData = static_cast<void*>(vec);
-  return AParcel_readByteArray(parcel, vectorData, StdVectorAllocator<int8_t>);
+  return ndk_util::AParcel_readByteArray(parcel, vectorData,
+                                         StdVectorAllocator<int8_t>);
 }
 
 }  // namespace
 
-ndk::SpAIBinder FromJavaBinder(JNIEnv* jni_env, jobject binder) {
-  return ndk::SpAIBinder(AIBinder_fromJavaBinder(jni_env, binder));
+ndk_util::SpAIBinder FromJavaBinder(JNIEnv* jni_env, jobject binder) {
+  return ndk_util::SpAIBinder(
+      ndk_util::AIBinder_fromJavaBinder(jni_env, binder));
 }
 
 TransactionReceiverAndroid::TransactionReceiverAndroid(
@@ -140,19 +135,11 @@
   // we want it to be something more meaningful (we can probably manually change
   // interface descriptor by modifying Java code's reply to
   // os.IBinder.INTERFACE_TRANSACTION)
-  AIBinder_Class* aibinder_class = AIBinder_Class_define(
+  ndk_util::AIBinder_Class* aibinder_class = ndk_util::AIBinder_Class_define(
       /*interfaceDescriptor=*/"", f_onCreate_userdata, f_onDestroy_delete,
       f_onTransact);
 
-  if (AIBinder_Class_disableInterfaceTokenHeader) {
-    AIBinder_Class_disableInterfaceTokenHeader(aibinder_class);
-  } else {
-    // TODO(mingcl): Make this a fatal error
-    gpr_log(GPR_ERROR,
-            "AIBinder_Class_disableInterfaceTokenHeader remain unresolved. "
-            "This BinderTransport implementation contains header and is not "
-            "compatible with Java's implementation");
-  }
+  ndk_util::AIBinder_Class_disableInterfaceTokenHeader(aibinder_class);
 
   // Pass the on-transact callback to the on-create function of the binder. The
   // on-create function equips the callback with a mutex and gives it to the
@@ -163,70 +150,67 @@
   OnCreateArgs args;
   args.wire_reader_ref = wire_reader_ref;
   args.callback = &transact_cb_;
-  binder_ = AIBinder_new(aibinder_class, &args);
+  binder_ = ndk_util::AIBinder_new(aibinder_class, &args);
   GPR_ASSERT(binder_);
-  gpr_log(GPR_INFO, "AIBinder_associateClass = %d",
-          static_cast<int>(AIBinder_associateClass(binder_, aibinder_class)));
+  gpr_log(GPR_INFO, "ndk_util::AIBinder_associateClass = %d",
+          static_cast<int>(
+              ndk_util::AIBinder_associateClass(binder_, aibinder_class)));
 }
 
 TransactionReceiverAndroid::~TransactionReceiverAndroid() {
   // Release the binder.
-  AIBinder_decStrong(binder_);
+  ndk_util::AIBinder_decStrong(binder_);
 }
 
 namespace {
 
-binder_status_t f_onTransact_noop(AIBinder* /*binder*/,
-                                  transaction_code_t /*code*/,
-                                  const AParcel* /*in*/, AParcel* /*out*/) {
+ndk_util::binder_status_t f_onTransact_noop(ndk_util::AIBinder* /*binder*/,
+                                            transaction_code_t /*code*/,
+                                            const ndk_util::AParcel* /*in*/,
+                                            ndk_util::AParcel* /*out*/) {
   return {};
 }
 
-void AssociateWithNoopClass(AIBinder* binder) {
+void AssociateWithNoopClass(ndk_util::AIBinder* binder) {
   // Need to associate class before using it
-  AIBinder_Class* aibinder_class = AIBinder_Class_define(
+  ndk_util::AIBinder_Class* aibinder_class = ndk_util::AIBinder_Class_define(
       "", f_onCreate_noop, f_onDestroy_noop, f_onTransact_noop);
 
-  if (AIBinder_Class_disableInterfaceTokenHeader) {
-    AIBinder_Class_disableInterfaceTokenHeader(aibinder_class);
-  } else {
-    // TODO(mingcl): Make this a fatal error
-    gpr_log(GPR_ERROR,
-            "AIBinder_Class_disableInterfaceTokenHeader remain unresolved. "
-            "This BinderTransport implementation contains header and is not "
-            "compatible with Java's implementation");
-  }
+  ndk_util::AIBinder_Class_disableInterfaceTokenHeader(aibinder_class);
 
-  gpr_log(GPR_INFO, "AIBinder_associateClass = %d",
-          static_cast<int>(AIBinder_associateClass(binder, aibinder_class)));
+  gpr_log(GPR_INFO, "ndk_util::AIBinder_associateClass = %d",
+          static_cast<int>(
+              ndk_util::AIBinder_associateClass(binder, aibinder_class)));
 }
 
 }  // namespace
 
 void BinderAndroid::Initialize() {
-  AIBinder* binder = binder_.get();
+  ndk_util::AIBinder* binder = binder_.get();
   AssociateWithNoopClass(binder);
 }
 
 absl::Status BinderAndroid::PrepareTransaction() {
-  AIBinder* binder = binder_.get();
-  return AIBinder_prepareTransaction(binder, &input_parcel_->parcel_) ==
-                 STATUS_OK
+  ndk_util::AIBinder* binder = binder_.get();
+  return ndk_util::AIBinder_prepareTransaction(
+             binder, &input_parcel_->parcel_) == ndk_util::STATUS_OK
              ? absl::OkStatus()
-             : absl::InternalError("AIBinder_prepareTransaction failed");
+             : absl::InternalError(
+                   "ndk_util::AIBinder_prepareTransaction failed");
 }
 
 absl::Status BinderAndroid::Transact(BinderTransportTxCode tx_code) {
-  AIBinder* binder = binder_.get();
+  ndk_util::AIBinder* binder = binder_.get();
   // We only do one-way transaction and thus the output parcel is never used.
-  AParcel* unused_output_parcel;
+  ndk_util::AParcel* unused_output_parcel;
   absl::Status result =
-      (AIBinder_transact(binder, static_cast<transaction_code_t>(tx_code),
-                         &input_parcel_->parcel_, &unused_output_parcel,
-                         FLAG_ONEWAY) == STATUS_OK)
+      (ndk_util::AIBinder_transact(
+           binder, static_cast<transaction_code_t>(tx_code),
+           &input_parcel_->parcel_, &unused_output_parcel,
+           ndk_util::FLAG_ONEWAY) == ndk_util::STATUS_OK)
           ? absl::OkStatus()
-          : absl::InternalError("AIBinder_transact failed");
-  AParcel_delete(unused_output_parcel);
+          : absl::InternalError("ndk_util::AIBinder_transact failed");
+  ndk_util::AParcel_delete(unused_output_parcel);
   return result;
 }
 
@@ -238,81 +222,73 @@
 }
 
 int32_t WritableParcelAndroid::GetDataSize() const {
-  if (AParcel_getDataSize) {
-    return AParcel_getDataSize(parcel_);
-  } else {
-    gpr_log(GPR_INFO, "[Warning] AParcel_getDataSize is not available");
-    return 0;
-  }
+  return ndk_util::AParcel_getDataSize(parcel_);
 }
 
 absl::Status WritableParcelAndroid::WriteInt32(int32_t data) {
-  return AParcel_writeInt32(parcel_, data) == STATUS_OK
+  return ndk_util::AParcel_writeInt32(parcel_, data) == ndk_util::STATUS_OK
              ? absl::OkStatus()
              : absl::InternalError("AParcel_writeInt32 failed");
 }
 
 absl::Status WritableParcelAndroid::WriteInt64(int64_t data) {
-  return AParcel_writeInt64(parcel_, data) == STATUS_OK
+  return ndk_util::AParcel_writeInt64(parcel_, data) == ndk_util::STATUS_OK
              ? absl::OkStatus()
              : absl::InternalError("AParcel_writeInt64 failed");
 }
 
 absl::Status WritableParcelAndroid::WriteBinder(HasRawBinder* binder) {
-  return AParcel_writeStrongBinder(
-             parcel_, reinterpret_cast<AIBinder*>(binder->GetRawBinder())) ==
-                 STATUS_OK
+  return ndk_util::AParcel_writeStrongBinder(
+             parcel_, reinterpret_cast<ndk_util::AIBinder*>(
+                          binder->GetRawBinder())) == ndk_util::STATUS_OK
              ? absl::OkStatus()
              : absl::InternalError("AParcel_writeStrongBinder failed");
 }
 
 absl::Status WritableParcelAndroid::WriteString(absl::string_view s) {
-  return AParcel_writeString(parcel_, s.data(), s.length()) == STATUS_OK
+  return ndk_util::AParcel_writeString(parcel_, s.data(), s.length()) ==
+                 ndk_util::STATUS_OK
              ? absl::OkStatus()
              : absl::InternalError("AParcel_writeString failed");
 }
 
 absl::Status WritableParcelAndroid::WriteByteArray(const int8_t* buffer,
                                                    int32_t length) {
-  return AParcel_writeByteArray(parcel_, buffer, length) == STATUS_OK
+  return ndk_util::AParcel_writeByteArray(parcel_, buffer, length) ==
+                 ndk_util::STATUS_OK
              ? absl::OkStatus()
              : absl::InternalError("AParcel_writeByteArray failed");
 }
 
 int32_t ReadableParcelAndroid::GetDataSize() const {
-  if (AParcel_getDataSize) {
-    return AParcel_getDataSize(parcel_);
-  } else {
-    gpr_log(GPR_INFO, "[Warning] AParcel_getDataSize is not available");
-    return 0;
-  }
+  return ndk_util::AParcel_getDataSize(parcel_);
 }
 
 absl::Status ReadableParcelAndroid::ReadInt32(int32_t* data) {
-  return AParcel_readInt32(parcel_, data) == STATUS_OK
+  return ndk_util::AParcel_readInt32(parcel_, data) == ndk_util::STATUS_OK
              ? absl::OkStatus()
              : absl::InternalError("AParcel_readInt32 failed");
 }
 
 absl::Status ReadableParcelAndroid::ReadInt64(int64_t* data) {
-  return AParcel_readInt64(parcel_, data) == STATUS_OK
+  return ndk_util::AParcel_readInt64(parcel_, data) == ndk_util::STATUS_OK
              ? absl::OkStatus()
              : absl::InternalError("AParcel_readInt64 failed");
 }
 
 absl::Status ReadableParcelAndroid::ReadBinder(std::unique_ptr<Binder>* data) {
-  AIBinder* binder;
-  if (AParcel_readStrongBinder(parcel_, &binder) != STATUS_OK) {
+  ndk_util::AIBinder* binder;
+  if (AParcel_readStrongBinder(parcel_, &binder) != ndk_util::STATUS_OK) {
     *data = nullptr;
     return absl::InternalError("AParcel_readStrongBinder failed");
   }
-  *data = absl::make_unique<BinderAndroid>(ndk::SpAIBinder(binder));
+  *data = absl::make_unique<BinderAndroid>(ndk_util::SpAIBinder(binder));
   return absl::OkStatus();
 }
 
 absl::Status ReadableParcelAndroid::ReadByteArray(std::string* data) {
   std::vector<uint8_t> vec;
-  if (AParcelReadVector(parcel_, &vec) == STATUS_OK) {
+  if (AParcelReadVector(parcel_, &vec) == ndk_util::STATUS_OK) {
     data->resize(vec.size());
     if (!vec.empty()) {
       memcpy(&((*data)[0]), vec.data(), vec.size());
@@ -323,7 +299,7 @@
 }
 
 absl::Status ReadableParcelAndroid::ReadString(std::string* str) {
-  return AParcelReadString(parcel_, str) == STATUS_OK
+  return AParcelReadString(parcel_, str) == ndk_util::STATUS_OK
              ? absl::OkStatus()
              : absl::InternalError("AParcel_readString failed");
 }
diff --git a/src/core/ext/transport/binder/wire_format/binder_android.h b/src/core/ext/transport/binder/wire_format/binder_android.h
index 3d4f63e..774bc09 100644
--- a/src/core/ext/transport/binder/wire_format/binder_android.h
+++ b/src/core/ext/transport/binder/wire_format/binder_android.h
@@ -19,29 +19,27 @@
 
 #ifdef GPR_SUPPORT_BINDER_TRANSPORT
 
-#include <android/binder_auto_utils.h>
-#include <android/binder_ibinder.h>
-#include <android/binder_ibinder_jni.h>
-#include <android/binder_interface_utils.h>
 #include <jni.h>
 
 #include <memory>
 
 #include "absl/memory/memory.h"
 
+#include "src/core/ext/transport/binder/utils/binder_auto_utils.h"
+#include "src/core/ext/transport/binder/utils/ndk_binder.h"
 #include "src/core/ext/transport/binder/wire_format/binder.h"
 #include "src/core/ext/transport/binder/wire_format/wire_reader.h"
 
 namespace grpc_binder {
 
-ndk::SpAIBinder FromJavaBinder(JNIEnv* jni_env, jobject binder);
+ndk_util::SpAIBinder FromJavaBinder(JNIEnv* jni_env, jobject binder);
 
 class BinderAndroid;
 
 class WritableParcelAndroid final : public WritableParcel {
  public:
   WritableParcelAndroid() = default;
-  explicit WritableParcelAndroid(AParcel* parcel) : parcel_(parcel) {}
+  explicit WritableParcelAndroid(ndk_util::AParcel* parcel) : parcel_(parcel) {}
   ~WritableParcelAndroid() override = default;
 
   int32_t GetDataSize() const override;
@@ -52,7 +50,7 @@
   absl::Status WriteByteArray(const int8_t* buffer, int32_t length) override;
 
  private:
-  AParcel* parcel_ = nullptr;
+  ndk_util::AParcel* parcel_ = nullptr;
 
   friend class BinderAndroid;
 };
@@ -61,7 +59,8 @@
  public:
   ReadableParcelAndroid() = default;
   // TODO(waynetu): Get rid of the const_cast.
-  explicit ReadableParcelAndroid(const AParcel* parcel) : parcel_(parcel) {}
+  explicit ReadableParcelAndroid(const ndk_util::AParcel* parcel)
+      : parcel_(parcel) {}
   ~ReadableParcelAndroid() override = default;
 
   int32_t GetDataSize() const override;
@@ -72,14 +71,14 @@
   absl::Status ReadString(std::string* str) override;
 
  private:
-  const AParcel* parcel_ = nullptr;
+  const ndk_util::AParcel* parcel_ = nullptr;
 
   friend class BinderAndroid;
 };
 
 class BinderAndroid final : public Binder {
  public:
-  explicit BinderAndroid(ndk::SpAIBinder binder)
+  explicit BinderAndroid(ndk_util::SpAIBinder binder)
       : binder_(binder),
         input_parcel_(absl::make_unique<WritableParcelAndroid>()) {}
   ~BinderAndroid() override = default;
@@ -99,7 +98,7 @@
       TransactionReceiver::OnTransactCb transact_cb) const override;
 
  private:
-  ndk::SpAIBinder binder_;
+  ndk_util::SpAIBinder binder_;
   std::unique_ptr<WritableParcelAndroid> input_parcel_;
 };
 
@@ -112,7 +111,7 @@
   void* GetRawBinder() override { return binder_; }
 
  private:
-  AIBinder* binder_;
+  ndk_util::AIBinder* binder_;
   OnTransactCb transact_cb_;
 };
 
diff --git a/src/core/ext/transport/binder/wire_format/binder_constants.cc b/src/core/ext/transport/binder/wire_format/binder_constants.cc
index a92f39b..42048bd 100644
--- a/src/core/ext/transport/binder/wire_format/binder_constants.cc
+++ b/src/core/ext/transport/binder/wire_format/binder_constants.cc
@@ -18,13 +18,9 @@
 
 #include "src/core/ext/transport/binder/wire_format/binder_constants.h"
 
-#ifndef GPR_SUPPORT_BINDER_TRANSPORT
-
 const int FIRST_CALL_TRANSACTION = 0x00000001;
 const int LAST_CALL_TRANSACTION = 0x00FFFFFF;
 
-#endif  // GPR_SUPPORT_BINDER_TRANSPORT
-
 namespace grpc_binder {
 
 const int kFirstCallId = FIRST_CALL_TRANSACTION + 1000;
diff --git a/src/core/ext/transport/binder/wire_format/binder_constants.h b/src/core/ext/transport/binder/wire_format/binder_constants.h
index 030dff0..5479f1a 100644
--- a/src/core/ext/transport/binder/wire_format/binder_constants.h
+++ b/src/core/ext/transport/binder/wire_format/binder_constants.h
@@ -17,24 +17,15 @@
 
 #include <grpc/support/port_platform.h>
 
-#include "absl/base/attributes.h"
-
-#ifdef GPR_SUPPORT_BINDER_TRANSPORT
-
-#include <android/binder_auto_utils.h>
-#include <android/binder_ibinder.h>
-
-#else
-
 #include <cstdint>
 
+#include "absl/base/attributes.h"
+
 using transaction_code_t = uint32_t;
 
 ABSL_CONST_INIT extern const int FIRST_CALL_TRANSACTION;
 ABSL_CONST_INIT extern const int LAST_CALL_TRANSACTION;
 
-#endif  // GPR_SUPPORT_BINDER_TRANSPORT
-
 namespace grpc_binder {
 
 enum class BinderTransportTxCode {
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index e854b5e..e3694ba 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -1205,6 +1205,9 @@
 src/core/ext/transport/binder/transport/binder_stream.h \
 src/core/ext/transport/binder/transport/binder_transport.cc \
 src/core/ext/transport/binder/transport/binder_transport.h \
+src/core/ext/transport/binder/utils/binder_auto_utils.h \
+src/core/ext/transport/binder/utils/ndk_binder.cc \
+src/core/ext/transport/binder/utils/ndk_binder.h \
 src/core/ext/transport/binder/utils/transport_stream_receiver.h \
 src/core/ext/transport/binder/utils/transport_stream_receiver_impl.cc \
 src/core/ext/transport/binder/utils/transport_stream_receiver_impl.h \