libbinder_ndk: allow disabling interface token

For compatibility with Java interfaces which don't use interface tokens.

Test: CtsNdkBinderTestCases
Bug: 188022639
Change-Id: I77642427993f37971b4b5b5928fc4a3688f17e42
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 11e9fc5..23db59e 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -172,7 +172,7 @@
 status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parcel* reply,
                               binder_flags_t flags) {
     if (isUserCommand(code)) {
-        if (!data.checkInterface(this)) {
+        if (getClass()->writeHeader && !data.checkInterface(this)) {
             return STATUS_BAD_TYPE;
         }
 
@@ -354,6 +354,12 @@
     clazz->onDump = onDump;
 }
 
+void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) {
+    CHECK(clazz != nullptr) << "disableInterfaceTokenHeader requires non-null clazz";
+
+    clazz->writeHeader = false;
+}
+
 void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz,
                                           AIBinder_handleShellCommand handleShellCommand) {
     CHECK(clazz != nullptr) << "setHandleShellCommand requires non-null clazz";
@@ -606,7 +612,10 @@
     *in = new AParcel(binder);
     (*in)->get()->markForBinder(binder->getBinder());
 
-    status_t status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor());
+    status_t status = android::OK;
+    if (clazz->writeHeader) {
+        status = (*in)->get()->writeInterfaceToken(clazz->getInterfaceDescriptor());
+    }
     binder_status_t ret = PruneStatusT(status);
 
     if (ret != STATUS_OK) {
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index f2c69b3..6509545 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -116,6 +116,9 @@
     const ::android::String16& getInterfaceDescriptor() const { return mWideInterfaceDescriptor; }
     const char* getInterfaceDescriptorUtf8() const { return mInterfaceDescriptor.c_str(); }
 
+    // whether a transaction header should be written
+    bool writeHeader = true;
+
     // required to be non-null, implemented for every class
     const AIBinder_Class_onCreate onCreate;
     const AIBinder_Class_onDestroy onDestroy;
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 78f2d3a..c82df83 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -219,6 +219,21 @@
 void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29);
 
 /**
+ * This tells users of this class not to use a transaction header. By default, libbinder_ndk users
+ * read/write transaction headers implicitly (in the SDK, this must be manually written by
+ * android.os.Parcel#writeInterfaceToken, and it is read/checked with
+ * android.os.Parcel#enforceInterface). This method is provided in order to talk to legacy code
+ * which does not write an interface token. When this is disabled, type safety is reduced, so you
+ * must have a separate way of determining the binder you are talking to is the right type. Must
+ * be called before any instance of the class is created.
+ *
+ * Available since API level 32.
+ *
+ * \param clazz class to disable interface header on.
+ */
+void AIBinder_Class_disableInterfaceTokenHeader(AIBinder_Class* clazz) __INTRODUCED_IN(32);
+
+/**
  * Creates a new binder object of the appropriate class.
  *
  * Ownership of args is passed to this object. The lifecycle is implemented with AIBinder_incStrong
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 685ebb5..1975bdc 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -141,6 +141,11 @@
     AParcel_reset;
 };
 
+LIBBINDER_NDK32 { # introduced=32
+  global:
+    AIBinder_Class_disableInterfaceTokenHeader;
+};
+
 LIBBINDER_NDK_PLATFORM {
   global:
     AParcel_getAllowFds;