Coco Coco - 2 months ago 17
Java Question

Android - dlopen failed: file offset for the library

I am making a remote controlling app for my arduino car. The code that is used has first been tested in Eclipse using Java and now I'm trying to use the same code for the Android application.

I have used the jSerialComm library, I have no errors in my code according to Android Studio, but when I run it, it can't find the library? I recieve the following error:


FATAL EXCEPTION: main
Process: com.sahragard.avengrecontroller, PID: 11728
java.lang.UnsatisfiedLinkError: dlopen failed: file offset for the
library
"/data/user/0/com.sahragard.avengrecontroller/cache/1454627726168-libjSerialComm.so"


= file size: 0 >= 0
at java.lang.Runtime.load(Runtime.java:332)
at java.lang.System.load(System.java:1069)
at com.fazecast.jSerialComm.SerialPort.(SerialPort.java:181)
at com.sahragard.avengrecontroller.Conn.getPorts(Conn.java:19)
at
com.sahragard.avengrecontroller.MainActivity.onCreate(MainActivity.java:25)
at android.app.Activity.performCreate(Activity.java:6237)
at
android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
at
android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
at
android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)



I googled for many hours and followed the tips that I have found, but whatever I do, it's always the same error. I have added the library according to this post:
http://stackoverflow.com/a/16628496/4582696

I appreciate any help I can get! Thanks in advance.

EDIT:

MainActivity class is an adaptation of a class containing Swing interface, its code is added below this code

public class MainActivity extends AppCompatActivity {



@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

final Spinner conSpinner =(Spinner) findViewById(R.id.spinner);

String[] a = new String[Conn.getPorts().length];
for(int i=0; i<Conn.getPorts().length; i++){
a[i] = Conn.getPorts()[i].getSystemPortName();
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, a);
conSpinner.setAdapter(adapter);


final Button disconnectButton = (Button) findViewById(R.id.disconnect);
disconnectButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
Conn.disconnect();
}
});


final Button connectButton = (Button) findViewById(R.id.connect);
connectButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
Conn.connect(conSpinner.getSelectedItemPosition());
Runnable run = new Runnable() {
public void run() {
Conn.listen();
}
};
Conn.listen = new Thread(run);
Conn.listen.start();
}
});



final Button up = (Button) findViewById(R.id.upButton);
up.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
Conn.sendMsg("w");
}
});

final Button down = (Button) findViewById(R.id.downButton);
down.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
Conn.sendMsg("s");
}
});

final Button left = (Button) findViewById(R.id.leftButton);
left.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
Conn.sendMsg("a");
}
});

final Button right = (Button) findViewById(R.id.rightButton);
right.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
Conn.sendMsg("d");
}
});




}
}


Original Remote_Interface class

import java.awt.BorderLayout;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;

import com.fazecast.jSerialComm.SerialPort;

import javax.swing.JComboBox;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JTextPane;
import java.awt.SystemColor;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class Remote_Interface extends JFrame {

private JPanel contentPane;
private JTextField textField;

/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
Remote_Interface frame = new Remote_Interface();
frame.setVisible(true);
// Conn.listen();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}

/**
* Create the frame.
*/
public Remote_Interface() {
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 270, 268);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);

JComboBox<String> comboBox = new JComboBox<String> ();
comboBox.setBounds(20, 46, 214, 20);
for(int i=0; i<Conn.getPorts().length; i++){
comboBox.addItem(Conn.getPorts()[i].getSystemPortName());
}
contentPane.add(comboBox);

textField = new JTextField();
textField.setBounds(20, 131, 214, 20);
contentPane.add(textField);
textField.setColumns(10);

textField.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
Conn.sendMsg(e.getActionCommand());
textField.setText("");
}
});

JButton btnNewButton = new JButton("Send");
btnNewButton.setBounds(20, 162, 89, 23);
contentPane.add(btnNewButton);

JTextPane txtpnPleaseSelectA = new JTextPane();
txtpnPleaseSelectA.setBackground(SystemColor.control);
txtpnPleaseSelectA.setText("Please select a port to connect");
txtpnPleaseSelectA.setEditable(false);
txtpnPleaseSelectA.setBounds(10, 11, 214, 20);
contentPane.add(txtpnPleaseSelectA);

btnNewButton.setEnabled(false);
textField.setEnabled(false);

JButton btnNewButton_1 = new JButton("Connect");
btnNewButton_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Conn.connect(comboBox.getSelectedIndex());
btnNewButton.setEnabled(true);
textField.setEnabled(true);
Runnable run = new Runnable(){
public void run(){
Conn.listen();
}
};
Conn.listen = new Thread(run);
Conn.listen.start();
}
});
btnNewButton_1.setBounds(143, 77, 89, 23);
contentPane.add(btnNewButton_1);

JButton btnD = new JButton("Disconnect");
btnD.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Conn.disconnect();
btnNewButton.setEnabled(false);
textField.setEnabled(false);
}
});
btnD.setBounds(20, 77, 89, 23);
contentPane.add(btnD);
}
}


And the Conn class

import com.fazecast.jSerialComm.*;

import java.io.PrintWriter;
import java.util.Scanner;

public class Conn {

static PrintWriter out;
static SerialPort[] ports;
static SerialPort port;
static Scanner in;
static Thread listen;

public static SerialPort[] getPorts(){
ports = SerialPort.getCommPorts();
return ports;
}

public static void sendMsg(String s){
out.println(s);
out.flush();
}

public static void connect(int i){
port = ports[i];
port.openPort();
port.setComPortTimeouts(SerialPort.TIMEOUT_SCANNER, 0, 0);
out = new PrintWriter(port.getOutputStream());
in = new Scanner(port.getInputStream());
}

public static void disconnect(){
port.closePort();

}

public static void listen(){// handle the input from the Arduino chip
while(in.hasNextLine()){
System.out.println(in.nextLine());// just print it out to the console
}
}



}

Answer

I had the same problem, the only way how I was able to fix it was:

  1. Copied files from https://github.com/Fazecast/jSerialComm/tree/master/src/main/java/com/fazecast/jSerialComm into my project keeping the same package name as in original.
  2. Downloaded jar from https://mvnrepository.com/artifact/com.fazecast/jSerialComm/1.3.11 and extracted it, copied all folders inside \jSerialComm-1.3.11\Android\
  3. Pasted all folders inside project src/main/jniLibs
  4. Call System.loadLibrary("jSerialComm"); before doing any method calls.

After that

SerialPort myPort = SerialPort.getCommPort("/dev/ttyMT2");

gave the corresponding object, without any UnsatisfiedLinkError.

*Another thing, if you are using the /dev folder, it could be necessary to make a android build, where the SeLinux permission mode is set to Disabled or Permissive. https://www.centos.org/docs/5/html/5.1/Deployment_Guide/sec-sel-enable-disable.html