mavlink / MAVSDK-Java

MAVSDK client for Java.
73 stars 42 forks source link

Permission Denied with MAVSDK server with Android tablet over serial interface #134

Closed mfran89 closed 1 year ago

mfran89 commented 1 year ago

My goal is to connect a Ardupilot Pixhawk device that uses a microusb to a usbc on a Samsung tablet. From reading the readme documentation here and on androids (https://developer.android.com/guide/topics/connectivity/usb/host#java) I am confident that I went through the right steps to get permission granted to interface to the USB device. However when I use MavsdkEventQueue.executor().execute(() -> _mavsdkServer.run("serial://"+usbDevice.getDeviceName()+":57600")); I get a "open failed: Permission denied" and "Connection failed" error from the underlying MAVSDK service. I also get this strange one from the audit service running in the background implying the same thing type=1400 audit(1688669663.762:1569): avc: denied { search } for pid=31507 comm="mavsdk-event-qu" name="usb" dev="tmpfs" ino=7050 scontext=u:r:untrusted_app_30:s0:c19,c257,c512,c768 tcontext=u:object_r:usb_device:s0 tclass=dir permissive=0 SEPF_SM-X200_12_0001 unfiltered To me in it looks like the SeLinux OS is not giving me permission for MAVSDK server to interface to it but cannot confirm. I have also used another repo to confirm I can interface to the autopilot (https://github.com/mik3y/usb-serial-for-android) to open up a connection from my android app to the autopilot. But I was unable to use this along with MAVSDK server to interface to the autopilot, I really would like to use the MAVSDK library to interface to the autopilot as opposed to using the other repo I mentioned earlier.

From looking at the issues section I saw that there was an issue #23 that talks about this but I couldnt figure out if there was a solution that relied on just using the MAVSDK java library. Looking forward to hearing back soon.

akirahrkw commented 1 year ago

@mfran89 I was also exploring Mavsdk on android app over serial and ended up with #23
you might want to try the tcp server approach with SerialInputOutputManager on this comment. (I tried the approach recently and it worked)

https://github.com/mavlink/MAVSDK-Java/issues/23#issuecomment-718102218

mfran89 commented 1 year ago

Hey thanks ! I think this might have done the trick too. Will test and give more feedback . I do have a couple of questions though.

  1. For SerialInputOutputManager obj that is imported from the 'com.github.mik3y:usb-serial-for-android:3.2.0' in the DroneRepository.java class, I see that the solution references this obj and executes a couple of methods with it SerialInputOutputManager serialManager = new SerialInputOutputManager(usbSerialPort); serialManager.setReadTimeout(IO_TIMEOUT); serialManager.setReadBufferSize(BUFFER_SIZE); serialManager.setWriteTimeout(IO_TIMEOUT); serialManager.setWriteBufferSize(BUFFER_SIZE); I am not able to execute those same methods (they do not exist) and wasnt sure why the sample code included it. For me I just created an instance of the manager and it looks like it's working. This is strange since I thought I would need to run its run() but that causes issues. Is there a way to set those values like mentioned in the code above or am I doing something wrong?

  2. In the MAVSDK-Java documentation it says to use MavsdkEventQueue.executor().execute() however in this sample code ExecutorService executorService is used instead. I was wondering if anyone found any issues by not using the MavSDKEventQueue obj.

Thanks for reaching out ! Hope to keep fleshing this solution out for myself :)

akirahrkw commented 1 year ago

@mfran89 hi

  1. I am not able to execute those same methods

Does it mean you can't call the setter methods?

2

Not sure the issues you are facing, but As MavsdkEventQueue.executor() calls Executors.newSingleThreadExecutor in the class, ExecutorService is used in the end

public class MavsdkEventQueue {

  private MavsdkEventQueue() {
  }

  private static final class ExecutorHolder {
    private static final Executor EXECUTOR = Executors.newSingleThreadExecutor(runnable ->
        new Thread(runnable, "mavsdk-event-queue")
    );
  }
...
...
...

Edit: 3 backticks around code.

mfran89 commented 1 year ago
  1. Correct I cannot use those setter commands because in version 3.2 and in the updated version of this repo 3.5.1 those setters do not exist.
  2. So with mavsdk event queue I see what is happening now, thanks

On a separate issue I am able to connect to the device, but when I try to disconnect I dont think my mavsdk server actually closes , and it makes it hard to reconnect without restarting the app altogether. I see that there is a destroy function in the dronerepository.java class that sets the tcpserver and serial manager to stop if they are not null public void destroy() { mCompositeDisposable.dispose(); mDrone.dispose(); mMavsdkServer.stop(); if (mSerialManager != null) { mSerialManager.stop(); } if (mTcpManager != null) { mTcpManager.stop(); } } I am not sure this code works because when I try to reconnect using initializeUsbAndTcp(); initializeServerAndDrone("tcp://:" + TCP_SERVER_PORT);

I get a warning Run ending due to exception: Attempt to invoke virtual method 'void java.io.DataOutputStream.write(byte[])' on a null object reference java.lang.NullPointerException: Attempt to invoke virtual method 'void java.io.DataOutputStream.write(byte[])' on a null object reference at TCPServer.step(TCPServer.java:189) at TCPServer.run(TCPServer.java:151) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:463) at java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) at java.lang.Thread.run(Thread.java:1012 This makes me think that I am not properly destroying the serial manager and tcp manager that the executor class is using.

divyanshupundir commented 1 year ago

Hello @mfran89.

Regarding the reconnection issue. The Android example is quite a simple one. The InputOutputManager classes aren't designed to handle reconnections.

Based on the usage of this library, the users need to either change the InputOutputManager implementation or create new managers whenever you want to connect and destroy the old ones.

akirahrkw commented 1 year ago

@mfran89 I also use v3.5.1 of the library and can call the setter methods

implementation 'com.github.mik3y:usb-serial-for-android:3.5.1'
mfran89 commented 1 year ago

Thanks @divyanshupundir for the heads up will work on that.

@akirahrkw I am looking at the code now that my project pulled when using implementation 'com.github.mik3y:usb-serial-for-android:3.5.1' in the build.gradle file.

and my SerialInputOutputManager class does not include the start function like it does on the online repo.

Not sure how this happened and I am looking into it now. SerialInputOutputManager.txt

mfran89 commented 1 year ago

Ok so I figured out how to resolve the connect / reconnect issue. The problem was in the tcp server class. When the stop function gets called the mServerSocket was not getting closed causing a binding issue for the address was already in use. So once I added that line into the stop() everything was okay.

For now I will use the Serialinputoutmanager class as is without some of the setters. @akirahrkw Thanks again for reaching out and giving me advice for this solution and @divyanshupundir for putting it together hopefully more people will see it and use it too!