Search code examples
javaandroidc++java-native-interfacejavah

JNI: javah mangles arguments that are inner classes


I have a JNI function that passes android.graphics.Bitmap$Config as an argument. Config is an inner class of Bitmap. When I run javah I get the incorrect header signature of (truncating to just the single argument):

Landroid_graphics_Bitmap_Config

which is the equivalent of:

Landroid/graphics/Bitmap/Config

instead of:

Landroid_graphics_Bitmap_00024Config

which is the equivalent

Landroid/graphics/Bitmap$Config

What javah generates is wrong as JNI will throw an error looking for the _00024 representation of the $ for the inner class. The man for javah doesn't seem to imply any setting to correct this. Is this just a limitation of javah?


Solution

  • It looks like, there is a bug (or inconsistency, at least) in JDK when parameters of inner class type are involved.

    Here is a sample class that reproduces the problem:

    public class A {
    
        public native void a(android.graphics.Bitmap.Config b);
        public native void a(android.graphics.Bitmap.Config b, int c);
        static {
            System.loadLibrary("hello-libs");
            a(null);
        }
    }
    

    If you use javah to generate the native header, you will get

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class com_example_hellolibs_MainActivity */
    
    #ifndef _Included_com_example_hellolibs_MainActivity
    #define _Included_com_example_hellolibs_MainActivity
    #ifdef __cplusplus
    extern "C" {
    #endif
    /*
     * Class:     A
     * Method:    a
     * Signature: (Landroid/graphics/Bitmap/Config;)V
     */
    JNIEXPORT void JNICALL A_a__Landroid_graphics_Bitmap_Config_2
      (JNIEnv *, jobject, jobject);
    
    /*
     * Class:     A
     * Method:    a
     * Signature: (Landroid/graphics/Bitmap/Config;I)V
     */
    JNIEXPORT void JNICALL Java_A_a__Landroid_graphics_Bitmap_Config_2I
      (JNIEnv *, jobject, jobject, jint);
    
    #ifdef __cplusplus
    }
    #endif
    #endif
    

    and -

    java.lang.UnsatisfiedLinkError: No implementation found for void A.a(android.graphics.Bitmap$Config) (tried Java_A_a and Java_A_a__Landroid_graphics_Bitmap_00024Config_2)

    But this bug rarely effects the headers generated by javah or javac -h dir, because usually the native methods are generated with 'short' names, e.g. Java_A_a which does not care about the type of parameter.

    The solution is to manually change the method signatures, as suggested in https://bugs.openjdk.java.net/browse/JDK-8145897.