Merge "libbinder_ndk: Expose UTF-8 interface descriptor string"
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index 5e2e1bd..350c658 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -307,7 +307,8 @@
     : onCreate(onCreate),
       onDestroy(onDestroy),
       onTransact(onTransact),
-      mInterfaceDescriptor(interfaceDescriptor) {}
+      mInterfaceDescriptor(interfaceDescriptor),
+      mWideInterfaceDescriptor(interfaceDescriptor) {}
 
 AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor,
                                       AIBinder_Class_onCreate onCreate,
@@ -335,6 +336,12 @@
     clazz->handleShellCommand = handleShellCommand;
 }
 
+const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) {
+    CHECK(clazz != nullptr) << "getDescriptor requires non-null clazz";
+
+    return clazz->getInterfaceDescriptorUtf8();
+}
+
 void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
     CHECK(who == mWho);
 
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 6236e81..6824306 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -112,7 +112,8 @@
     AIBinder_Class(const char* interfaceDescriptor, AIBinder_Class_onCreate onCreate,
                    AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact);
 
-    const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; }
+    const ::android::String16& getInterfaceDescriptor() const { return mWideInterfaceDescriptor; }
+    const char* getInterfaceDescriptorUtf8() const { return mInterfaceDescriptor.c_str(); }
 
     // required to be non-null, implemented for every class
     const AIBinder_Class_onCreate onCreate = nullptr;
@@ -124,9 +125,11 @@
     AIBinder_handleShellCommand handleShellCommand = nullptr;
 
    private:
+    // Copy of the raw char string for when we don't have to return UTF-16
+    const std::string mInterfaceDescriptor;
     // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
     // one.
-    const ::android::String16 mInterfaceDescriptor;
+    const ::android::String16 mWideInterfaceDescriptor;
 };
 
 // Ownership is like this (when linked to death):
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index ce3d1db..5e1ed46 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -642,6 +642,23 @@
 
 #endif  //__ANDROID_API__ >= 30
 
+#if __ANDROID_API__ >= 31
+
+/**
+ * Retrieve the class descriptor for the class.
+ *
+ * Available since API level 31.
+ *
+ * \param clazz the class to fetch the descriptor from
+ *
+ * \return the class descriptor string. This pointer will never be null; a
+ * descriptor is required to define a class. The pointer is owned by the class
+ * and will remain valid as long as the class does.
+ */
+const char* AIBinder_Class_getDescriptor(const AIBinder_Class* clazz) __INTRODUCED_IN(31);
+
+#endif  //__ANDROID_API__ >= 31
+
 __END_DECLS
 
 /** @} */
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 6962f86..e233ffd 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -121,6 +121,7 @@
     AServiceManager_registerLazyService; # llndk
     AServiceManager_waitForService; # apex llndk
 
+    AIBinder_Class_getDescriptor;
     AParcel_appendFrom;
     AParcel_create;
     AParcel_getDataSize;
diff --git a/libs/binder/ndk/tests/iface.cpp b/libs/binder/ndk/tests/iface.cpp
index a588985..2afe5d2 100644
--- a/libs/binder/ndk/tests/iface.cpp
+++ b/libs/binder/ndk/tests/iface.cpp
@@ -25,7 +25,7 @@
 
 const char* IFoo::kSomeInstanceName = "libbinder_ndk-test-IFoo";
 const char* IFoo::kInstanceNameToDieFor = "libbinder_ndk-test-IFoo-to-die";
-const char* kIFooDescriptor = "my-special-IFoo-class";
+const char* IFoo::kIFooDescriptor = "my-special-IFoo-class";
 
 struct IFoo_Class_Data {
     sp<IFoo> foo;
diff --git a/libs/binder/ndk/tests/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h
index d9dd64b..7408d0c 100644
--- a/libs/binder/ndk/tests/include/iface/iface.h
+++ b/libs/binder/ndk/tests/include/iface/iface.h
@@ -27,6 +27,7 @@
    public:
     static const char* kSomeInstanceName;
     static const char* kInstanceNameToDieFor;
+    static const char* kIFooDescriptor;
 
     static AIBinder_Class* kClass;
 
diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
index f84d9d3..b7df115 100644
--- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
+++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp
@@ -39,6 +39,7 @@
 #include <condition_variable>
 #include <iostream>
 #include <mutex>
+#include "android/binder_ibinder.h"
 
 using namespace android;
 
@@ -543,6 +544,10 @@
     EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"}));
 }
 
+TEST(NdkBinder, GetClassInterfaceDescriptor) {
+    ASSERT_STREQ(IFoo::kIFooDescriptor, AIBinder_Class_getDescriptor(IFoo::kClass));
+}
+
 int main(int argc, char* argv[]) {
     ::testing::InitGoogleTest(&argc, argv);