Fazecast / jSerialComm

Platform-independent serial port access for Java
GNU Lesser General Public License v3.0
1.35k stars 287 forks source link

Native method not found: com.fazecast.jSerialComm.SerialPort.openPortNative:()J #187

Closed mtotaro closed 5 years ago

mtotaro commented 5 years ago

Hi,

Opening ports, or reading bytes. The issue is that it couldnt locate the native jSerialComm shared library.

02-08 16:57:09.164 26277-26277/com.hasar.hasarmposdriver E/AndroidRuntime: FATAL EXCEPTION: main Process: com.hasar.hasarmposdriver, PID: 26277 java.lang.UnsatisfiedLinkError: Native method not found: com.fazecast.jSerialComm.SerialPort.openPortNative:()J at com.fazecast.jSerialComm.SerialPort.openPortNative(Native Method) at com.fazecast.jSerialComm.SerialPort.openPort(SerialPort.java:426) at com.fazecast.jSerialComm.SerialPort.openPort(SerialPort.java:446) at com.fazecast.jSerialComm.SerialPort.openPort(SerialPort.java:459) at com.hasar.icard.pinpad.serial.SerialConnectorJSerialComm.open(SerialConnectorJSerialComm.java:43) at com.hasar.icard.pinpad.serial.SerialConnectorJSerialComm.(SerialConnectorJSerialComm.java:30) at com.hasar.hasarmposdriver.MainActivity$1.onClick(MainActivity.java:52) at android.view.View.performClick(View.java:4438) at android.view.View$PerformClick.run(View.java:18422) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5020) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) at dalvik.system.NativeStart.main(Native Method)


This is my dependecies gradle:

configurations { repackage }

dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' implementation project(':libs:usbSerialForAndroid') compile 'org.slf4j:slf4j-api:1.7.25' repackage 'com.googlecode.jarjar:jarjar:1.3' repackage 'com.fazecast:jSerialComm:[2.0.0,3.0.0)' }

task repackageJavaModuleJars() { project.ant { taskdef name: 'jarjar', classname: 'com.tonicsystems.jarjar.JarJarTask', classpath: configurations.repackage.asPath jarjar(jarfile: 'libs/repackagedLibs.jar', filesetmanifest: 'merge') { configurations.repackage.each { if (!it.getName().find('jarjar-')) { zipfileset(src: it, excludes: 'module-info.class') } } } }


This is in my main activity:

package com.hasar.icard.pinpad.serial;

import java.io.IOException;

import org.slf4j.Logger; import org.slf4j.LoggerFactory;

import com.fazecast.jSerialComm.SerialPort;

import com.hasar.icard.pinpad.LogHelper;

public class SerialConnectorJSerialComm implements SerialConnector {

protected Logger logger = LoggerFactory.getLogger(SerialConnectorJSerialComm.class);

private SerialPort serialPort;

protected String serialPortName;

public SerialConnectorJSerialComm( final String serialPortName, final Integer baudRate, final Integer dataBits, final StopBits stopBits, final Parity parity, final Integer timeout) {

this.serialPortName = serialPortName;

open(baudRate, dataBits, stopBits, parity, timeout);

}

@Override public void open(Integer baudRate, Integer dataBits, StopBits stopBits, Parity parity, Integer timeout) { serialPort = SerialPort.getCommPort(serialPortName);

serialPort.setComPortParameters(baudRate, dataBits, toSerialCommStopBits(stopBits),
    toSerialCommParity(parity));

serialPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, timeout, 0);

logger.debug("connecting blocking to port name: " + serialPortName);
serialPort.openPort();

logger.debug("connected blocking to port " + serialPortName);

}

@Override public void close() { serialPort.closePort(); logger.info("closed"); }

@Override public int sendData(final byte[] data) throws IOException { logger.debug("sending data " + LogHelper.hexDump(data, data.length)); serialPort.writeBytes(data, data.length); return data.length; }

@Override public int readData(final byte[] readBuffer) { int numRead = serialPort.readBytes(readBuffer, readBuffer.length); if (numRead != 0) { logger.debug("readData " + numRead + " bytes."); logger.debug("dataReceived " + LogHelper.hexDump(readBuffer, numRead)); } return numRead; }

@Override public void initRead() { int numBytes = serialPort.bytesAvailable(); if (numBytes >0){ byte [] buff = new byte[numBytes]; serialPort.readBytes(buff, numBytes); } }

private int toSerialCommStopBits(final StopBits stopBits) { switch (stopBits) { case ONE_STOP_BIT: return SerialPort.ONE_STOP_BIT; case ONE_POINT_FIVE_STOP_BITS: return SerialPort.ONE_POINT_FIVE_STOP_BITS; case TWO_STOP_BITS: return SerialPort.TWO_STOP_BITS; } return SerialPort.ONE_STOP_BIT; }

private int toSerialCommParity(final Parity parity) { switch (parity) { case EVEN_PARITY: return SerialPort.EVEN_PARITY; case MARK_PARITY: return SerialPort.MARK_PARITY; case NO_PARITY: return SerialPort.NO_PARITY; case ODD_PARITY: return SerialPort.ODD_PARITY; case SPACE_PARITY: return SerialPort.SPACE_PARITY; } return SerialPort.ONE_STOP_BIT; }

@Override public String toString() { return "SerialTransmissorConnector [serialPortName=" + serialPortName + "]"; } }

This is where its failing

try { InputStream fileContents = SerialPort.class.getResourceAsStream("/" + libraryPath + "/" + fileName); if (fileContents == null && isAndroid) { libraryPath = libraryPath.replace("Android/", "lib/"); ### fileContents = SerialPort.class.getResourceAsStream("/" + libraryPath + "/" + fileName); }

        if (fileContents == null) {
            System.err.println("Could not locate or access the native jSerialComm shared library.");
            System.err.println("If you are using multiple projects with interdependencies, you may need to fix your build settings to ensure that library resources are copied properly.");
        } else {
            try {
                FileOutputStream destinationFileContents = new FileOutputStream(tempNativeLibrary);
                byte[] transferBuffer = new byte[4096];

                int numBytesRead;
                while((numBytesRead = fileContents.read(transferBuffer)) > 0) {
                    destinationFileContents.write(transferBuffer, 0, numBytesRead);
                }

                destinationFileContents.close();
            } catch (FileNotFoundException var12) {
                ;
            }

            fileContents.close();
            System.load(tempNativeLibrary.getAbsolutePath());
            initializeLibrary();
        }
    } catch (Exception var13) {
        var13.printStackTrace();
    }
mtotaro commented 5 years ago

Another error

02-11 10:20:29.206 13890-13890/com.hasar.hasarmposdriver E/AndroidRuntime: FATAL EXCEPTION: main Process: com.hasar.hasarmposdriver, PID: 13890 java.lang.UnsatisfiedLinkError: Native method not found: com.fazecast.jSerialComm.SerialPort.bytesAvailable:(J)I at com.fazecast.jSerialComm.SerialPort.bytesAvailable(Native Method) at com.fazecast.jSerialComm.SerialPort.bytesAvailable(SerialPort.java:529) at com.hasar.icard.pinpad.serial.SerialConnectorJSerialComm.initRead(SerialConnectorJSerialComm.java:73) at com.hasar.icard.pinpad.protocol.SimpleProtocol.executeCommand(SimpleProtocol.java:59) at com.hasar.icard.pinpad.commands.Command.send(Command.java:31) at com.hasar.hasarmposdriver.MainActivity$1.onClick(MainActivity.java:56) at android.view.View.performClick(View.java:4438) at android.view.View$PerformClick.run(View.java:18422) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5020) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) at dalvik.system.NativeStart.main(Native Method)

mtotaro commented 5 years ago

In SerialPort.class is always failing in this line:

line 707: if (fileContents == null) { System.err.println("Could not locate or access the native jSerialComm shared library."); System.err.println("If you are using multiple projects with interdependencies, you may need to fix your build settings to ensure that library resources are copied properly."); }

and before that

try { InputStream fileContents = SerialPort.class.getResourceAsStream("/" + libraryPath + "/" + fileName); if (fileContents == null && isAndroid) { libraryPath = libraryPath.replace("Android/", "lib/"); fileContents = SerialPort.class.getResourceAsStream("/" + libraryPath + "/" + fileName); }

this line : fileContents = SerialPort.class.getResourceAsStream("/" + libraryPath + "/" + fileName);

is returning always null.

libraryPath is lib/armeabi, and filename is libjSerialComm.so

Can somebody help me please?

hedgecrw commented 5 years ago

It sounds like somehow the jSerialComm native binaries aren't getting copied into your Android installation (APK) file. Can you open that file and verify that the binaries are contained in there? If they are missing, it's some sort of Android build configuration issue, but I'm not an expert on building/configuring for Android, nor is this library completely supported on Android any more since they keep tightening down security for filesystem access of third-party libraries.

mtotaro commented 5 years ago

Yes they are there!. I am trying to configure Gradle, in order to load the files. Apparently now he is loading them, but he throws the following error:

02-11 12: 33: 53.927 25312-25312 / com.hasar.hasarmposdriver E / AndroidRuntime: FATAL EXCEPTION: main Process: com.hasar.hasarmposdriver, PID: 25312 java.lang.UnsatisfiedLinkError: dlopen failed: can not locate symbol "cfmakeraw" referenced by "1549899212417-libjSerialComm.so" ... at java.lang.Runtime.load (Runtime.java:333) at java.lang.System.load (System.java:512) at com.fazecast.jSerialComm.SerialPort. (SerialPort.java:244) at com.hasar.icard.pinpad.serial.SerialConnectorJSerialComm.open (SerialConnectorJSerialComm.java:35) at com.hasar.icard.pinpad.serial.SerialConnectorJSerialComm. (SerialConnectorJSerialComm.java:30) at com.hasar.hasarmposdriver.MainActivity $ 1.onClick (MainActivity.java:54) at android.view.View.performClick (View.java:4438) at android.view.View $ PerformClick.run (View.java:18422) at android.os.Handler.handleCallback (Handler.java:733) at android.os.Handler.dispatchMessage (Handler.java:95) at android.os.Looper.loop (Looper.java:136) at android.app.ActivityThread.main (ActivityThread.java:5020) at java.lang.reflect.Method.invokeNative (Native Method) at java.lang.reflect.Method.invoke (Method.java:515) at com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:785) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:601) at dalvik.system.NativeStart.main (Native Method)

I suppose he charges it but he charges it badly.

Any ideas on how to properly compile the files?

This is my gradle:

`apply plugin: 'com.android.application'

android { compileSdkVersion 28 defaultConfig { applicationId "com.hasar.hasarmposdriver" minSdkVersion 19 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" }

sourceSets {
    main {
        jniLibs.srcDirs = ['lib']
    }
}

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}
compileOptions {
    targetCompatibility JavaVersion.VERSION_1_8
    sourceCompatibility JavaVersion.VERSION_1_8
}

} configurations { repackage } dependencies {

implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//implementation project(':libs:usbSerialForAndroid')
implementation 'org.slf4j:slf4j-api:1.7.25'
repackage 'com.googlecode.jarjar:jarjar:1.3'
repackage 'com.fazecast:jSerialComm:[2.0.0,3.0.0)'
implementation fileTree(include: ['*.jar'], dir: 'lib')

}

task repackageJavaModuleJars() { project.ant { taskdef name: 'jarjar', classname: 'com.tonicsystems.jarjar.JarJarTask', classpath: configurations.repackage.asPath jarjar(jarfile: 'lib/repackagedLibs.jar', filesetmanifest: 'merge') { configurations.repackage.each { if (!it.getName().find('jarjar-')) { zipfileset(src: it, excludes: 'module-info.class') } } } } } `

hedgecrw commented 5 years ago

Hmmm the cmakeraw error is a native OS system call, so give me a minute to look into why that could possibly be throwing an error.

mtotaro commented 5 years ago

I had to create a jniLibs Folder and extracted all the folders of .jar and put it there. that's why in my gradle is

sourceSets { main { jniLibs.srcDirs = ['lib'] } }

But well im trying all things to get it work.

title

hedgecrw commented 5 years ago

OK, so it looks like more recent Android systems removed cmakeraw from their libC implementations. I inserted my own version, so please test the following code and let me know if it works:

https://www.dropbox.com/s/b0ojhbwf82qnmlc/jSerialComm-2.4.1.jar?dl=0

You may need to extract its contents to overwrite what you have in jniLibs. Please post back if that works.

mtotaro commented 5 years ago

I got this error:

02-11 13:54:26.534 29672-29672/com.hasar.hasarmposdriver E/AndroidRuntime: FATAL EXCEPTION: main Process: com.hasar.hasarmposdriver, PID: 29672 java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "tcdrain" referenced by "1549904061115-libjSerialComm.so"... at java.lang.Runtime.load(Runtime.java:333) at java.lang.System.load(System.java:512) at com.fazecast.jSerialComm.SerialPort.<clinit>(SerialPort.java:245) at com.hasar.icard.pinpad.serial.SerialConnectorJSerialComm.open(SerialConnectorJSerialComm.java:35) at com.hasar.icard.pinpad.serial.SerialConnectorJSerialComm.<init>(SerialConnectorJSerialComm.java:30) at com.hasar.hasarmposdriver.MainActivity$1.onClick(MainActivity.java:54) at android.view.View.performClick(View.java:4438) at android.view.View$PerformClick.run(View.java:18422) at android.os.Handler.handleCallback(Handler.java:733) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:136) at android.app.ActivityThread.main(ActivityThread.java:5020) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) at dalvik.system.NativeStart.main(Native Method)

hedgecrw commented 5 years ago

Perfect, that's basically the same problem as cmakeraw - looks like Android removed all tc* functions.

Try this one: https://www.dropbox.com/s/so22e6t4nnw7noc/jSerialComm-2.4.1-beta.jar?dl=0

mtotaro commented 5 years ago

Its seems to be working now, but when i do the following:

SerialPort test[]= SerialPort.getCommPorts();

I got nothing.

The min target is API 24. Could that be the error or making some trouble?

hedgecrw commented 5 years ago

Hmmm...probably not, but no idea - Like I said, Android is a moving target when it comes to device libraries. 1) Is your device rooted, 2) I don't think anything enumerates on Android without the device you're trying to talk to already physically connected, and 3) if the device you're trying to talk to doesn't natively show up as a serial port in Posix/Linux (like a USB-to-Serial converter), then it won't show up in this library either.

mtotaro commented 5 years ago

I understand, i tried with a targetversion 17, and im getting /dev/ttyACM0. But im having issues with sending commands to my device attached.

mtotaro commented 5 years ago

1) Is not rooted 2) Its connected 3) Have you tried to get this work on android 8.1?

mtotaro commented 5 years ago

I was wondering if this is a issue of permission? Could it be that i need to know if my device is attached or not?

mtotaro commented 5 years ago

There is something i want to ask you. Is there by any chance that i can add like my own list of USBProber? Something like this:

probeTable.addProduct(0x11CA, 0x0220,Ch34xSerialDriver.class);

Because your driver dont recognize my device.

hedgecrw commented 5 years ago

The problem is most likely that your device is not rooted. More recent Android devices do not allow direct access to their serial ports (emulated or otherwise) on non-rooted devices, but instead require that you go through the Android USB API. This has been a sticking point with providing support for Android in this library, since it is a serial port library and not a USB one. If you are trying to communicate with a USB-based device using non-rooted Android, you will most likely have to use a library designed to be used specifically for that purpose, unfortunately.

hedgecrw commented 5 years ago

Closing this issue. The bug portion of the issue has been fixed, and the non-bug portion (using the library on a non-rooted Android device) is not supported at this time.