package org.mavlink.qgroundcontrol; /* Copyright 2013 Google Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. * * Project home page: http://code.google.com/p/usb-serial-for-android/ */ /////////////////////////////////////////////////////////////////////////////////////////// // Written by: Mike Goza April 2014 // // These routines interface with the Android USB Host devices for serial port communication. // The code uses the usb-serial-for-android software library. The QGCActivity class is the // interface to the C++ routines through jni calls. Do not change the functions without also // changing the corresponding calls in the C++ routines or you will break the interface. // //////////////////////////////////////////////////////////////////////////////////////////// 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; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; 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.os.Bundle; import android.app.PendingIntent; import android.view.WindowManager; import com.hoho.android.usbserial.driver.*; import org.qtproject.qt5.android.bindings.QtActivity; import org.qtproject.qt5.android.bindings.QtApplication; public class QGCActivity extends QtActivity { public static int BAD_PORT = 0; private static QGCActivity m_instance; private static UsbManager m_manager; // ANDROID USB HOST CLASS private static List m_devices; // LIST OF CURRENT DEVICES private static HashMap m_openedDevices; // LIST OF OPENED DEVICES private static HashMap m_ioManager; // THREADS FOR LISTENING FOR INCOMING DATA private static HashMap m_userData; // CORRESPONDING USER DATA FOR OPENED DEVICES. USED IN DISCONNECT CALLBACK // USED TO DETECT WHEN A DEVICE HAS BEEN UNPLUGGED private BroadcastReceiver m_UsbReceiver = null; 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; private final static UsbIoManager.Listener m_Listener = new UsbIoManager.Listener() { @Override public void onRunError(Exception eA, int userDataA) { Log.e(TAG, "onRunError Exception"); nativeDeviceException(userDataA, eA.getMessage()); } @Override public void onNewData(final byte[] dataA, int userDataA) { nativeDeviceNewData(userDataA, dataA); } }; 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); private static native void nativeDeviceNewData(int userDataA, byte[] dataA); // Native C++ functions called to log output public static native void qgcLogDebug(String message); public static native void qgcLogWarning(String message); //////////////////////////////////////////////////////////////////////////////////////////////// // // Constructor. Only used once to create the initial instance for the static functions. // //////////////////////////////////////////////////////////////////////////////////////////////// public QGCActivity() { m_instance = this; m_openedDevices = new HashMap(); m_userData = new HashMap(); m_ioManager = new HashMap(); Log.i(TAG, "Instance created"); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); PowerManager pm = (PowerManager)m_instance.getSystemService(Context.POWER_SERVICE); m_wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "QGroundControl"); if(m_wl != null) { m_wl.acquire(); Log.i(TAG, "SCREEN_BRIGHT_WAKE_LOCK acquired."); } else { 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() { unregisterReceiver(mOpenAccessoryReceiver); try { if(m_wl != null) { m_wl.release(); Log.i(TAG, "SCREEN_BRIGHT_WAKE_LOCK released."); } } catch(Exception e) { Log.e(TAG, "Exception onDestroy()"); } super.onDestroy(); } public void onInit(int status) { } ///////////////////////////////////////////////////////////////////////////////////////////////////////// // // Find all current devices that match the device filter described in the androidmanifest.xml and the // device_filter.xml // ///////////////////////////////////////////////////////////////////////////////////////////////////////// private static boolean getCurrentDevices() { if (m_instance == null) return false; if (m_devices != null) m_devices.clear(); m_devices = UsbSerialProber.findAllDevices(m_manager); return true; } ///////////////////////////////////////////////////////////////////////////////////////////////////////// // // List all available devices that are not already open. It returns the serial port info // in a : separated string array. Each string entry consists of the following: // // DeviceName:Company:ProductId:VendorId // ///////////////////////////////////////////////////////////////////////////////////////////////////////// public static String[] availableDevicesInfo() { // GET THE LIST OF CURRENT DEVICES if (!getCurrentDevices()) { Log.e(TAG, "QGCActivity instance not present"); return null; } // MAKE SURE WE HAVE ENTRIES if (m_devices.size() <= 0) { //Log.e(TAG, "No USB devices found"); return null; } if (m_openedDevices == null) { Log.e(TAG, "m_openedDevices is null"); return null; } int countL = 0; int iL; // CHECK FOR ALREADY OPENED DEVICES AND DON'T INCLUDE THEM IN THE COUNT for (iL=0; iL