Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android Library Compatibility #133

Open
hedgecrw opened this issue Jul 27, 2018 · 31 comments
Open

Android Library Compatibility #133

hedgecrw opened this issue Jul 27, 2018 · 31 comments
Assignees

Comments

@hedgecrw
Copy link
Contributor

Placeholder issue to track the status of Android compatibility within this library. Right now, rooting is required, and there appear to be a number of issues with including the library in Android Studio. Investigation forthcoming.

References #36 and #45.

@hedgecrw hedgecrw self-assigned this Jul 27, 2018
@jfichtner
Copy link

I'm not sure if the issue I'm having should be posted here, or in a new issue (or in the jSerialComm issues at all), but I've used a previous version (1.4.0) of jSerialComm in a Xamarin.Android project for a couple of years now, and it's been working great. However, when trying to use the new 2.x.x jars, I'm getting a compile error from the Xamarin build system that says:

2>COMPILETODALVIK : PARSE error :
2> unknown tag byte: 13
2> ...while preparsing cst 0005 at offset 00000036
2> ...while parsing module-info.class
2> 1 error; aborting

I cannot find any info regarding this error, and it very well could just be a bug in Xamarin. But I was hoping someone could maybe tell me what might have changed since 1.4.0 that could cause such an error. I also wonder if this could point to an actual issue with the jar files, themselves. I attempted to compile myself using Android Studio (which I used successfully to build 1.4.0), but failed, as I am a novice when it comes to Android Studio and the gradle build system. Any help would be very much appreciated!

@jfichtner
Copy link

Okay, I followed the instructions in issue #128 and created a test project in Android Studio to strip the Java9 stuff from the jar file, then I located the repackaged jar file in the build artifacts and used that file in my Xamarin project. This works! However, now I'm getting a "ClassNotFoundException" when I try to open a port, and it traces back to the cfmakeraw call in the OpenNativePort call in the .so file. Any thoughts on why this might happen?

@mtotaro
Copy link

mtotaro commented Feb 8, 2019

Having the same issue:

02-08 12:58:10.996 14495-14495/com.hasar.hasarmposdriver W/System.err: Could not locate or access the native jSerialComm shared library.
If you are using multiple projects with interdependencies, you may need to fix your build settings to ensure that library resources are copied properly.
02-08 12:58:12.518 14495-14495/com.hasar.hasarmposdriver W/dalvikvm: No implementation found for native Lcom/fazecast/jSerialComm/SerialPort;.openPortNative:()J
02-08 12:58:20.942 14495-14495/com.hasar.hasarmposdriver D/AndroidRuntime: Shutting down VM
02-08 12:58:20.942 14495-14495/com.hasar.hasarmposdriver W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x41682ba8)
02-08 12:58:20.962 14495-14495/com.hasar.hasarmposdriver E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.hasar.hasarmposdriver, PID: 14495
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)

@hedgecrw
Copy link
Contributor Author

The issue with the library not finding native function calls (like cmakeraw) in Android has now been resolved (available in 2.4.1); however, it still requires a rooted device to function since Android does not allow direct access to device ports on a non-rooted device.

@NvrBst
Copy link

NvrBst commented Mar 14, 2019

I looked around and couldn't find a solution. I'm able to use the library in my android studio 3 projects by including the gradle dependency, and, manually taking the "jSerialComm.jar/Android/*" files out, and putting them into the ".../app/src/main/jniLibs/" folder. If I don't manually copy the native libs then I get "can't find native method" errors at run-time.

I was wondering if there was some way around this just so I can be more sure the .so's are always synced with the gradle dependency jar after updating it. Maybe some known gradle rule/repackage tasks or such for android?

I do see that the jar contains "/Android, /Linux, /OSX, /Solaris, /Windows", however, the resulting Android Studio generated apk only has "/OSX, /Windows", and "/lib" if I manually copy to "jniLibs". If there is a gradle rule\hackary for android to fix the "manually copy to jniLibs" step, then, maybe it can even be edited to exclude "/OSX, /Windows" directories too since they are not that useful on android apk builds.

Many thanks, sorry if this has already been asked/requested/etc, I checked the wiki as best as I could.

@NvrBst
Copy link

NvrBst commented Apr 4, 2019

Also, if it hasn't been suggested, jCenter/Maven allows owners to deploy "aar" packages to the project with the jar, and then users just reference the aar like "compile 'com.fazecast:jSerialComm:2.5.0@aar'".

If this was done it'd be a lot easier for android users, and also woudln't affect non-android users in anyway. Also, packaging an aar will let you remove the "OSX/Windows/etc" binaries, include a proguard file for the library, and let android studio auto include any native library dependencies in the aar.

It would be a great addition to the library since jSerialComm is basically the best native serial access library for android already. Thanks.

@Horkyze
Copy link

Horkyze commented Aug 5, 2020

Im getting this error on Android 7.1

No implementation found for long com.fazecast.jSerialComm.SerialPort.openPortNative() (tried Java_com_fazecast_jSerialComm_SerialPort_openPortNative and Java_com_fazecast_jSerialComm_SerialPort_openPortNative__)
FATAL EXCEPTION: main
    Process: com.xxx, PID: 27692
    java.lang.UnsatisfiedLinkError: No implementation found for long com.fazecast.jSerialComm.SerialPort.openPortNative() (tried Java_com_fazecast_jSerialComm_SerialPort_openPortNative and Java_com_fazecast_jSerialComm_SerialPort_openPortNative__)
        at com.fazecast.jSerialComm.SerialPort.openPortNative(Native Method)
        at com.fazecast.jSerialComm.SerialPort.openPort(SerialPort.java:448)
        at com.fazecast.jSerialComm.SerialPort.openPort(SerialPort.java:466)
        at com.fazecast.jSerialComm.SerialPort.openPort(SerialPort.java:479)

Is there a way to use it on android?

@timon-langlotz
Copy link

I would like to work on the AAR delivery. Easiest would be to convert the project to a multi-module project and include an android or jSerialComm-android module. @hedgecrw, I would like to work on this as well!

@HAIWWH
Copy link

HAIWWH commented May 6, 2021

我在Android 7.1上收到此错误

No implementation found for long com.fazecast.jSerialComm.SerialPort.openPortNative() (tried Java_com_fazecast_jSerialComm_SerialPort_openPortNative and Java_com_fazecast_jSerialComm_SerialPort_openPortNative__)
FATAL EXCEPTION: main
    Process: com.xxx, PID: 27692
    java.lang.UnsatisfiedLinkError: No implementation found for long com.fazecast.jSerialComm.SerialPort.openPortNative() (tried Java_com_fazecast_jSerialComm_SerialPort_openPortNative and Java_com_fazecast_jSerialComm_SerialPort_openPortNative__)
        at com.fazecast.jSerialComm.SerialPort.openPortNative(Native Method)
        at com.fazecast.jSerialComm.SerialPort.openPort(SerialPort.java:448)
        at com.fazecast.jSerialComm.SerialPort.openPort(SerialPort.java:466)
        at com.fazecast.jSerialComm.SerialPort.openPort(SerialPort.java:479)

有没有办法在Android上使用它?

I need that question too. How did you solve it?

@gresolio
Copy link

gresolio commented Oct 5, 2021

Android supports USB serial hardware in host mode (OTG), no root access required. IMHO, currently this is the only way that can be used to implement Android compatibility for jSerialComm in a user-friendly manner. Bluetooth serial communication can also be taken into account, but I think it's better to start with USB.

As a result, jSerialComm users will be able to use almost all USB serial converters, e.g. FTDI, Prolific, Silabs and other devices implementing the USB CDC protocol.

References:
usb-serial-for-android https://github.com/mik3y/usb-serial-for-android
SimpleUsbTerminal (usage example) https://github.com/kai-morich/SimpleUsbTerminal

@orensharon
Copy link

I have a rooted Android device.
I'm using version 2.5.8 and it's working great!
Now I'm trying to upgrade to one of the latest versions (3.0.0+).

This is the first error I encountered -
(similar to: #283)

Exception in thread "main" java.lang.NoSuchFieldError: isOpened
   at com.fazecast.jSerialComm.SerialPort.initializeLibrary(Native Method)
   at com.fazecast.jSerialComm.SerialPort.<clinit>(SerialPort.java:249)

Given solution didn't work.

I remember of having some issues with the jniLibs deps injection so this is the area I decided to investigate.
I'm not sure if the following info is related to the error I get, but it's a start.

I ran getprop on the device and the output is:

    ...
    [ro.product.cpu.abi2]: [armeabi]
    [ro.product.cpu.abi]: [armeabi-v7a]
    ...

You can see that I have 2 ABI types and the ro.product.cpu.abi2 key- value pair is prior to ro.product.cpu.abi
Then I started to debug -

When getprop is executed by the code, in SerialPort class, the selected libraryPath is set to the first match (i.e. - ro.product.cpu.abi2) but armeabi folder is not exists (armeabi was deprecated and removed in version 2.6.1) and exception is thrown.

There is a work around you can think of so it would pick the armeabi-v7a instead of armeabi?
Any suggestions?

Many thanks..

@hedgecrw
Copy link
Contributor Author

@orensharon, good catch! I missed that some of the Android architectures had become deprecated (and also that some of them no longer use the ro.product.cpu.abi props in favor of ro.product.cpu.abilist).

I've updated the library to correctly use the Android props now. If you have the ability to manually add a library to your project, you can test the following beta version of 2.9.0:

https://www.dropbox.com/t/05S9zYWyedbs7RAN

If not, this update will be in the next release. Thanks for bringing it to my attention!

@orensharon
Copy link

orensharon commented Jan 28, 2022

@hedgecrw, thank you for the response.

I tried to load the JAR.
I copied the Android folder content from the JAR into the project src/main/jniLibs folder.

Now the libraryPath is set to armeabi-7va and it's being copy to the cache folder of my app as expected.
The next thing is to load the library using:

 try {
    System.load(tempNativeLibrary.getAbsolutePath());
 } catch (UnsatisfiedLinkError var18) {
     libraryLoaded = false;
     if (backupFileContents == null && attempt > 0) {
         throw new UnsatisfiedLinkError("Cannot load native library " + tempNativeLibrary.getAbsolutePath() + " with expected architecture: " + libraryPath);
     }
 }

UnsatisfiedLinkError has caught (but not throwing yet) later it throws -

D/dalvikvm: Trying to load lib /data/data/com.comp.app.stage/cache/jSerialComm/1643366882893-libjSerialComm.so 0x41cbce38
E/dalvikvm: dlopen("/data/data/com.comp.app.stage/cache/jSerialComm/1643366882893-libjSerialComm.so") failed: dlopen failed: cannot locate symbol "sigemptyset" referenced by "1643366882893-libjSerialComm.so"...
No implementation found for native Lcom/fazecast/jSerialComm/SerialPort;.retrievePortDetails:()V
threadid=17: thread exiting with uncaught exception (group=0x41a01ba8)
java.lang.UnsatisfiedLinkError: Native method not found: com.fazecast.jSerialComm.SerialPort.retrievePortDetails:()
    at com.fazecast.jSerialComm.SerialPort.retrievePortDetails(Native Method)
    at com.fazecast.jSerialComm.SerialPort.getCommPort(SerialPort.java:504)

BTW I also noticed that backupLibraryPath is still set to Android/armeabi

@hedgecrw
Copy link
Contributor Author

Ah yes, that's a new method in the upcoming release that's only halfway implemented right now. Apologies about that. The important thing is that the native library was chosen and (attempted to) load correctly. It should work fine in the next release. I'll post back here when that is made. Thanks for testing!

@hedgecrw
Copy link
Contributor Author

This should be fully functional with today's release of v2.9.0. Thanks!

@orensharon
Copy link

orensharon commented Feb 3, 2022

Hello again,

So I now I tried with the official v2.9.0 and I'm getting the same errors (more or less..)
This fails in SerialPort.java:

try { System.load(tempNativeLibrary.getAbsolutePath()); }

Log output:

....
Trying to load lib /data/data/com.comp.app/cache/jSerialComm/1643892038782-libjSerialComm.so 0x41b00aa8
dlopen("/data/data/com.comp.app/cache/jSerialComm/1643892038782-libjSerialComm.so") failed: dlopen failed: cannot locate symbol "__read_chk" referenced by "1643892038782-libjSerialComm.so"...
....
No implementation found for native Lcom/fazecast/jSerialComm/SerialPort;.retrievePortDetails:()V
threadid=18: thread exiting with uncaught exception (group=0x4184cba8)
....
java.lang.UnsatisfiedLinkError: Native method not found: com.fazecast.jSerialComm.SerialPort.retrievePortDetails:()V
    at com.fazecast.jSerialComm.SerialPort.retrievePortDetails(Native Method)
    at com.fazecast.jSerialComm.SerialPort.getCommPort(SerialPort.java:504)

@hedgecrw
Copy link
Contributor Author

Will do. I've been trying to get my own Android setup recreated so I can test this further, but it's become a bit of a nightmare because it looks like they've locked down their devices even more (even from a rooting perspective) in the long interim since this was initially working.

With regards to the error you're seeing, my suspicion is that the Android NDK has been updated over the years so that it now adds a bunch of new runtime checks by default (like the __read_chk() call you mentioned) that are not present on the Android OS you are using. I'm trying to get those added functions removed so that the DLL can properly load for you. Please test this version and see what errors it gives you:

https://www.dropbox.com/t/6hoybASQYqpJOtPY

Ultimately though, I'm working to move away from using any of the native libraries on Android so that users can make use of this library without having to root their devices.

@orensharon
Copy link

Sounds great. Thank you for the efforts.

Unfortunately it still fails on loading the native library file:
try { System.load(tempNativeLibrary.getAbsolutePath()); }

The error:

dlopen("/data/data/com.app.example/cache/jSerialComm/1645033325623-libjSerialComm.so") failed: dlopen failed: cannot locate symbol "pthread_condattr_setclock" referenced by "1645033325623-libjSerialComm.so"...

@hedgecrw
Copy link
Contributor Author

Gotcha, at least it made it past the runtime checks Android was adding to an actual library function call. Here is a new version with that call removed for Android:

https://www.dropbox.com/t/QfrfYjy9LCzYUJnB

@orensharon
Copy link

Now we getting some good progress....
The native library seems to load successfully!!
I made some simple test to determine the connection .. and it passed!

The only thing I notice at this point is a strange string in the logs:

Trying to load lib /data/data/com.example.app/cache/jSerialComm/1645036217492-libjSerialComm.so 0x419edb10
Added shared lib /data/data/com.example.app/cache/jSerialComm/1645036217492-libjSerialComm.so 0x419edb10
No JNI_OnLoad found in /data/data/com.example.app/cache/jSerialComm/1645036217492-libjSerialComm.so 0x419edb10, skipping init
JNI WARNING: NewStringUTF input is not valid Modified UTF-8: illegal continuation byte 0x60
          string: '0-0@0��@��Q�`��@2����f'
          in Lcom/fazecast/jSerialComm/SerialPort;.retrievePortDetails:()V (NewStringUTF)

@hedgecrw
Copy link
Contributor Author

Great! And thanks for the info from the log file - that's definitely a bug, and I see where it's coming from in the native code. I've patched it here:

https://www.dropbox.com/t/75SarQc7JO5C92Wm

@orensharon
Copy link

Now the logs looks good!
The only output regarding the native lib is:

Trying to load lib /data/data/com.example.app/cache/jSerialComm/1645085708887-libjSerialComm.so 0x41ab83d8
Added shared lib /data/data/com.example.app/cache/jSerialComm/1645085708887-libjSerialComm.so 0x41ab83d8
No JNI_OnLoad found in /data/data/com.example.app/cache/jSerialComm/1645085708887-libjSerialComm.so 0x41ab83d8, skipping init

I will let you know if I will find anything else

@hedgecrw
Copy link
Contributor Author

@orensharon, great, thanks for testing!

As one more final test before releasing, could you please test this final beta4 version:

https://www.dropbox.com/t/ywoycnP7qRGjZDCG

I've moved native library initialization into JNI_OnLoad and want to make sure it still works for you under Android.

@orensharon
Copy link

Sure!

I did the test, seems like everything is working well and the message No JNI_OnLoad removed from the log.
This is the current output of beta-4:

Trying to load lib /data/data/com.example.app/cache/jSerialComm/1645261676952-libjSerialComm.so 0x41bbdea8
D/dalvikvm: Added shared lib /data/data/com.example.app/cache/jSerialComm/1645261676952-libjSerialComm.so 0x41bbdea8

@gresolio
Copy link

@orensharon
I'm curious what Android hardware are you using? Is this a regular smartphone with an easily accessible/soldereble serial port on the motherboard? Or maybe some kind of single board computer?

@gresolio
Copy link

I have a rooted Android device.

Hey @orensharon, can you please say what Android hardware are you using?
I'm interested in a smartphone with easily accessible UART on the PCB.

@orensharon
Copy link

@gresolio
Yes, sorry for the delay.
I'm using the Freescale sabresd-MX6DQ.
This isn't a smartphone but an industrial device.

@queal
Copy link

queal commented Jun 27, 2022

I manually copied the so file to the Android libs directory; And I can also see the libjserialComm.so file in apk.armeabi-v7a

but also got exceptions like this:
Caused by: java.lang.UnsatisfiedLinkError: No implementation found for com.fazecast.jSerialComm.SerialPort[] com.fazecast.jSerialComm.SerialPort.getCommPortsNative() (tried Java_com_fazecast_jSerialComm_SerialPort_getCommPortsNative and Java_com_fazecast_jSerialComm_SerialPort_getCommPortsNative__)

i debug found this path:
tempFileDirectory=/data/user/0/com.queal.test/cache/jSerialComm/2.9.2/

but cacheDir is empty, if i manual copy the libjSerialComm.so to jniLibs, it will be found in /data/user/0/com.queal.test/lib, or /data/user/0/com.queal.test/lib-main

i try to set
System.setProperty("jSerialComm.library.path", "/data/user/0/com.queal.test/lib-main/");
but it does not work, please help, @hedgecrw

@danielesergio
Copy link

I had the same issue of @queal, adding the libjSerialComm.so file in the jniLibs/arm64-v8a folder seems to be not enough.
I fixed it by manually loading the library:
static { System.loadLibrary("jSerialComm"); }

@r00li
Copy link

r00li commented Feb 11, 2023

I tried what has been written here, and I am getting the library to load. And when I run the app it does ask for root permission. So that seems to all work fine. But when I try to actually open the serial port SELinux just denies it. I am running this on a rooted Sony Xperia Hello - running Android 8.0.

This is basically what I am trying:

val port = SerialPort.getCommPort("/dev/ttyHS1")
port.setComPortParameters(115200, 8, 1, 0)
port.openPort()
if (port.openPort()) {
    println("Connection established to \"${port.descriptivePortName}\"")
} else {
    println("Connection failed")
}

And the resulting error:

W/iapotatocontrol: type=1400 audit(0.0:71): avc: denied { read write } for name="ttyHS1" dev="tmpfs" ino=7829 scontext=u:r:untrusted_app:s0:c512,c768 tcontext=u:object_r:serial_device:s0 tclass=chr_file permissive=0 ppid=1456 pcomm="main" pgid=1456 pgcomm="main"
I/System.out: Connection failed

Any ideas on what could be wrong?

@leobert-lan
Copy link

If somebody got UnsatisfiedLinkError, it's caused by the structure of the JAR package, so the .so library cannot be loaded correctly. A simple solution is to unzip the JAR package and manually copy the contents under the Android directory to the standard directory src/main/jniLibs (no need to copy the manifest file).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests