diff --git a/android.pri b/android.pri index 8179a76f155b1cb3b9b46c595f8e2001a4309fb6..f655709bc50e8b137bf808938fba18a5bc075f6c 100644 --- a/android.pri +++ b/android.pri @@ -15,7 +15,8 @@ OTHER_FILES += \ $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java \ $$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java \ $$PWD/android/src/org/mavlink/qgroundcontrol/QGCActivity.java \ - $$PWD/android/src/org/mavlink/qgroundcontrol/UsbIoManager.java + $$PWD/android/src/org/mavlink/qgroundcontrol/UsbIoManager.java \ + $$PWD/android/src/org/mavlink/qgroundcontrol/TaiSync.java DISTFILES += \ $$PWD/android/gradle/wrapper/gradle-wrapper.jar \ diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 7143aa2a4970df8292496c271219ff246bef8988..beddd03b6cc1420a604282d0812968789c0eb244 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -7,10 +7,12 @@ + + @@ -60,13 +62,14 @@ - - + + - + - - + + + diff --git a/android/res/xml/device_filter.xml b/android/res/xml/device_filter.xml index 782fae8dd7e17709718b824ace9fa92e9479b52b..31474575daf185cb8eafd71bbd15022deb3c27d0 100644 --- a/android/res/xml/device_filter.xml +++ b/android/res/xml/device_filter.xml @@ -2,5 +2,6 @@ + diff --git a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java index 2f99cd20607072c7d5c4202b93ea7656a0b67e22..756597b29dc0b521e7047f5021e3bd72489bf894 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java @@ -29,10 +29,13 @@ package org.mavlink.qgroundcontrol; // //////////////////////////////////////////////////////////////////////////////////////////// +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.Timer; +import java.util.TimerTask; import java.io.IOException; import android.app.Activity; @@ -40,12 +43,16 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.hardware.usb.*; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbDeviceConnection; +import android.hardware.usb.UsbManager; import android.widget.Toast; import android.util.Log; import android.os.PowerManager; -import android.view.WindowManager; import android.os.Bundle; +import android.app.PendingIntent; +import android.view.WindowManager; import com.hoho.android.usbserial.driver.*; import org.qtproject.qt5.android.bindings.QtActivity; @@ -65,6 +72,8 @@ public class QGCActivity extends QtActivity private final static ExecutorService m_Executor = Executors.newSingleThreadExecutor(); private static final String TAG = "QGC_QGCActivity"; private static PowerManager.WakeLock m_wl; + private static String USB_ACTION = "org.mavlink.qgroundcontrol.action.USB_PERMISSION"; + private TaiSync taiSync = null; public static Context m_context; @@ -85,6 +94,26 @@ public class QGCActivity extends QtActivity } }; + private final BroadcastReceiver mOpenAccessoryReceiver = + new BroadcastReceiver() + { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (USB_ACTION.equals(action)) { + UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); + if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { + openAccessory(accessory); + } + } else if( UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) { + UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY); + if (accessory != null) { + closeAccessory(accessory); + } + } + } + }; + // NATIVE C++ FUNCTION THAT WILL BE CALLED IF THE DEVICE IS UNPLUGGED private static native void nativeDeviceHasDisconnected(int userDataA); private static native void nativeDeviceException(int userDataA, String messageA); @@ -120,10 +149,27 @@ public class QGCActivity extends QtActivity Log.i(TAG, "SCREEN_BRIGHT_WAKE_LOCK not acquired!!!"); } m_instance.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + if (m_manager == null) { + try { + m_manager = (UsbManager)m_instance.getSystemService(Context.USB_SERVICE); + taiSync = new TaiSync(); + + IntentFilter filter = new IntentFilter(USB_ACTION); + filter.addAction( UsbManager.ACTION_USB_ACCESSORY_DETACHED); + registerReceiver(mOpenAccessoryReceiver, filter); + + probeAccessories(); + } catch(Exception e) { + Log.e(TAG, "Exception getCurrentDevices(): " + e); + } + } } @Override - protected void onDestroy() { + protected void onDestroy() + { + unregisterReceiver(mOpenAccessoryReceiver); try { if(m_wl != null) { m_wl.release(); @@ -149,9 +195,6 @@ public class QGCActivity extends QtActivity if (m_instance == null) return false; - if (m_manager == null) - m_manager = (UsbManager)m_instance.getSystemService(Context.USB_SERVICE); - if (m_devices != null) m_devices.clear(); @@ -193,7 +236,7 @@ public class QGCActivity extends QtActivity int countL = 0; int iL; - // CHECK FOR ALREADY OPENED DEVICES AND DON"T INCLUDE THEM IN THE COUNT + // CHECK FOR ALREADY OPENED DEVICES AND DON'T INCLUDE THEM IN THE COUNT for (iL=0; iL= 0) { + synchronized (this) + { + if (!running) { + break; + } + } + + bytesRead = mFileInputStream.read(mBytes); + + if (bytesRead > 0) + { + if (mBytes[3] == PROTOCOL_VERSION) + { + vMaj = mBytes[19]; + Log.i("QGC_TaiSync", "Got protocol version message vMaj = " + mBytes[19]); + byte[] data = { 0x00, 0x00, 0x00, 0x01, // uint32 - protocol + 0x00, 0x00, 0x00, 0x00, // uint32 - dport + 0x00, 0x00, 0x00, 0x1C, // uint32 - length + 0x00, 0x00, 0x00, 0x00, // uint32 - magic + 0x00, 0x00, 0x00, vMaj, // uint32 - version major + 0x00, 0x00, 0x00, 0x00, // uint32 - version minor + 0x00, 0x00, 0x00, 0x00 }; // uint32 - padding + mFileOutputStream.write(data); + } + else if (mBytes[3] == PROTOCOL_CHANNEL) { + int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff); + int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff); + Log.i("QGC_TaiSync", "Read 2 port = " + dPort + " length = " + dLength); + byte[] data = { 0x00, 0x00, 0x00, 0x02, // uint32 - protocol + 0x00, 0x00, mBytes[6], mBytes[7], // uint32 - dport + 0x00, 0x00, 0x00, 0x1C, // uint32 - length + 0x00, 0x00, 0x00, 0x00, // uint32 - magic + 0x00, 0x00, 0x00, vMaj, // uint32 - version major + 0x00, 0x00, 0x00, 0x00, // uint32 - version minor + 0x00, 0x00, 0x00, 0x00 }; // uint32 - padding + mFileOutputStream.write(data); + } + else if (mBytes[3] == PROTOCOL_DATA) { + int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff); + int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff); + + int payloadOffset = 28; + int payloadLength = bytesRead - payloadOffset; + + if (dPort == 8000) { // Video + byte[] sBytes = new byte[payloadLength]; + System.arraycopy(mBytes, payloadOffset, sBytes, 0, payloadLength); + DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, VIDEO_PORT); + socket.send(packet); + + } else if (dPort == 8200) { // Command +/* + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < dLength; i++) { + sb.append(String.format("%02X ", mBytes[i])); + } + Log.i("QGC_TaiSync", "Read 3 port = " + dPort + " length = " + dLength + " - " + sb.toString()); +*/ + } else if (dPort == 8400) { // Telemetry + byte[] sBytes = new byte[payloadLength]; + System.arraycopy(mBytes, payloadOffset, sBytes, 0, payloadLength); + DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, TELEM_PORT); + socket.send(packet); + } + } + } + } + } catch (IOException e) { + Log.e("QGC_TaiSync", "Exception: " + e); + e.printStackTrace(); + } finally { + close(); + } + } + }); + + mThreadPool.execute(new Runnable() { + @Override + public void run() + { + byte[] inbuf = new byte[256]; + + try { + while (true) { + synchronized (this) + { + if (!running) { + break; + } + } + + DatagramPacket packet = new DatagramPacket(inbuf, inbuf.length); + socket.receive(packet); + + int dataPort = 8400; + byte portMSB = (byte)((dataPort >> 8) & 0xFF); + byte portLSB = (byte)(dataPort & 0xFF); + + byte[] lA = new byte[4]; + int len = 28 + packet.getLength(); + byte[] buffer = new byte[len]; + + for (int i = 3; i >= 0; i--) { + lA[i] = (byte)(len & 0xFF); + len >>= 8; + } + + byte[] header = { 0x00, 0x00, 0x00, PROTOCOL_DATA, // uint32 - protocol + 0x00, 0x00, portMSB, portLSB, // uint32 - dport + lA[0], lA[1], lA[2], lA[3], // uint32 - length + 0x00, 0x00, 0x00, 0x00, // uint32 - magic + 0x00, 0x00, 0x00, vMaj, // uint32 - version major + 0x00, 0x00, 0x00, 0x00, // uint32 - version minor + 0x00, 0x00, 0x00, 0x00 }; // uint32 - padding + + System.arraycopy(header, 0, buffer, 0, header.length); + System.arraycopy(packet.getData(), 0, buffer, header.length, packet.getLength()); + mFileOutputStream.write(buffer); + } + } catch (IOException e) { + Log.e("QGC_TaiSync", "Exception: " + e); + e.printStackTrace(); + } finally { + close(); + } + } + }); + } + + public void close() + { +// Log.i("QGC_TaiSync", "Close"); + synchronized (this) + { + running = false; + } + try { + if (socket != null) { + socket.close(); + socket = null; + } + if (mParcelFileDescriptor != null) { + mParcelFileDescriptor.close(); + mParcelFileDescriptor = null; + } + } catch (Exception e) { + } + socket = null; + } +}