Rnld
Rnld

Reputation: 68

Cannot resolve corresponding jni fuction

I am making an app to send data through serial port. this requires to call methos from a native library I have two native methods "open" "close" I have generated to .so libraries using ndk and put them in jnilibs folder but still it gives an error saying "cannot reolve corresponding jni function"

SerialPort.c

#include <termios.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>

#include "android/log.h"
static const char *TAG="serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO,  TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)

static speed_t getBaudrate(jint baudrate)
{
    switch(baudrate) {
    case 0: return B0;
    case 50: return B50;
    case 75: return B75;
    case 110: return B110;
    case 134: return B134;
    case 150: return B150;
    case 200: return B200;
    case 300: return B300;
    case 600: return B600;
    case 1200: return B1200;
    case 1800: return B1800;
    case 2400: return B2400;
    case 4800: return B4800;
    case 9600: return B9600;
    case 19200: return B19200;
    case 38400: return B38400;
    case 57600: return B57600;
    case 115200: return B115200;
    case 230400: return B230400;
    case 460800: return B460800;
    case 500000: return B500000;
    case 576000: return B576000;
    case 921600: return B921600;
    case 1000000: return B1000000;
    case 1152000: return B1152000;
    case 1500000: return B1500000;
    case 2000000: return B2000000;
    case 2500000: return B2500000;
    case 3000000: return B3000000;
    case 3500000: return B3500000;
    case 4000000: return B4000000;
    default: return -1;
    }
}

JNIEXPORT jobject JNICALL Java_com_example_richyrony_serial485_SerialPort_open(JNIEnv *env, jobject thiz, jstring path, jint baudrate)
{
    int fd;
    speed_t speed;
    jobject mFileDescriptor;

    /* Check arguments */
    {
        speed = getBaudrate(baudrate);
        if (speed == -1) {
            /* TODO: throw an exception */
            LOGE("Invalid baudrate");
            return NULL;
        }
    }

    /* Opening device */
    {
        jboolean iscopy;
        const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy);
        LOGD("Opening serial port %s", path_utf);
        LOGD(" serial port buadrate %s", speed);
//      fd = open(path_utf, O_RDWR | O_DIRECT | O_SYNC);
        fd = open(path_utf,O_RDWR | O_SYNC);               //2011.8.26 
        LOGD("open() fd = %d", fd);
        (*env)->ReleaseStringUTFChars(env, path, path_utf);
        if (fd == -1)
        {
            /* Throw an exception */
            LOGE("Cannot open port");
            /* TODO: throw an exception */
            return NULL;
        }
    }

    /* Configure device */
    {
        struct termios cfg;
        LOGD("Configuring serial port");
        if (tcgetattr(fd, &cfg))
        {
            LOGE("tcgetattr() failed");
            close(fd);
            /* TODO: throw an exception */
            return NULL;
        }

        cfmakeraw(&cfg);
        cfsetispeed(&cfg, speed);
        cfsetospeed(&cfg, speed);

        if (tcsetattr(fd, TCSANOW, &cfg))
        {
            LOGE("tcsetattr() failed");
            close(fd);
            /* TODO: throw an exception */
            return NULL;
        }
    }

    /* Create a corresponding file descriptor */
    {
        jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
        jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V");
        jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
        mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
        (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
    }

    return mFileDescriptor;
}

void  Java_com_example_richyrony_serial485_SerialPort_close
  (JNIEnv *env, jobject thiz)
{
    jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
    jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");

    jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
    jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");

    jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
    jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);

    LOGD("close(fd = %d)", descriptor);
    close(descriptor);
}

SerialPort.Java

package com.example.richyrony.serial485;


import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.util.Log;

public class SerialPort {

    private static final String TAG = "SerialPort";

    /*
     * Do not 

remove or rename the field mFd: it is used by native method close();
     */
    private FileDescriptor mFd;
    private FileInputStream mFileInputStream;
    private FileOutputStream mFileOutputStream;
public SerialPort(File device, int baudrate) throws SecurityException, IOException {

    /* Check access permission */
    if (!device.canRead() || !device.canWrite()) {
        try {

            if (!device.canRead() || !device.canWrite()) {
                throw new SecurityException();
            } 
        }catch (Exception e) {
                e.printStackTrace();
                throw new SecurityException();
        }
    }



    mFd = open(device.getAbsolutePath(), baudrate);
    if (mFd == null) {
        Log.e(TAG, "native open returns null");
        throw new IOException();
    }
    mFileInputStream = new FileInputStream(mFd);
    mFileOutputStream = new FileOutputStream(mFd);
}

// Getters and setters
public InputStream getInputStream() {
    return mFileInputStream;
}

public OutputStream getOutputStream() {
    return mFileOutputStream;
}

// JNI
static {
    System.loadLibrary("serial_port");

}

private native static FileDescriptor open(String path, int baudrate);
public native void close();

}

These are my java class and native file. Please do help

Upvotes: 3

Views: 1815

Answers (1)

Alex Cohn
Alex Cohn

Reputation: 57203

As I explained elsewhere, don't expect Android Studio to resolve magically the native method declarations into a prebuilt library (even if it is correctly copied into src/main/jnLibs).

You can simply ignore this error message: your APK will still install the prebuilt library, and the native method will be resolved at run time.

You can add @SuppressWarnings("JniMissingFunction") annotation for these method, or for the entire class:

@SuppressWarnings("JniMissingFunction")
private native static FileDescriptor open(String path, int baudrate);

@SuppressWarnings("JniMissingFunction")
public native void close();

If you can install the APK on any device, or even on emulator, and the SerialPort class is loaded, then your JNI wrapper is configured correctly. When the system fails to load a native library, it writes helpful error messages to logcat.

Let me expand a bit on "the SerialPort class is loaded". In Java, the classloader may (rather 'should') defer loading a class until it is really necessary. So, simply having the class in your APK will not trigger its static constructor. But if you have a field

private SerialPort m_serialPort = new SerialPort();

in your MainActivity, then the class will be loaded, and the JNI will be initialized even if you don't actually touch this m_serialPort at all. Note that I added an do-nothing default constructor to SerialPort class:

public SerialPort() {}

This does not test the JNI code itself, like conversion of the parameters and such. If you don't have a real device that can be used to test your code, you should design some mock interfaces that will play the role of the actual serial port.

Upvotes: 2

Related Questions