4

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?

Anthony
  • 7,638
  • 3
  • 38
  • 71
  • Why would JNI be looking for the 0024 representation if javah doesn't generate it? – user207421 May 15 '16 at 00:21
  • See https://bugs.openjdk.java.net/browse/JDK-8145897 – Alex Cohn May 15 '16 at 08:06
  • @EJP An error is thrown loading the library in Android Studio, which defined that it was looking for the 00024 signature which I didn't have as I used the one directly from javah. I imagine this is a java error, so its odd that javah doesn't generate the right signature. The error message is what actually sent me on this wild goose chase. Otherwise I would have had absolutely no idea about 00024/$. – Anthony May 15 '16 at 09:12
  • @AlexCohn thanks, that confirms my suspicions I guess. What is the relationship between OpenJDK and Oracle JDK? – Anthony May 15 '16 at 09:14
  • OpenJDK is an open-source clone of Oracle (historically Sun) JDK. But the truth is that the same issue happens with Oracle JDK 1.8.0_77 on Mac. Note that if you run `javap -s` on your class, you will see the correct parameter descriptor. – Alex Cohn May 15 '16 at 09:56
  • 1
    @AlexCohn Thanks for all those details. If you want to add that as an answer I'll select it. – Anthony May 15 '16 at 11:40

1 Answers1

5

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.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307