/* 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.ArrayList; 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.app.PendingIntent; 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; public static int BAD_DEVICE_ID = 0; private static QGCActivity _instance = null; private static UsbManager _usbManager = null; private static List _drivers; private static HashMap m_ioManager; private static HashMap _userDataHashByDeviceId; private static final String TAG = "QGC_QGCActivity"; private static PowerManager.WakeLock _wakeLock; // private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"; private static final String ACTION_USB_PERMISSION = "org.mavlink.qgroundcontrol.action.USB_PERMISSION"; private static PendingIntent _usbPermissionIntent = null; private TaiSync taiSync = null; public static Context m_context; private final static ExecutorService m_Executor = Executors.newSingleThreadExecutor(); private final static UsbIoManager.Listener m_Listener = new UsbIoManager.Listener() { @Override public void onRunError(Exception eA, int userData) { Log.e(TAG, "onRunError Exception"); nativeDeviceException(userData, eA.getMessage()); } @Override public void onNewData(final byte[] dataA, int userData) { nativeDeviceNewData(userData, dataA); } }; // 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); private final BroadcastReceiver mOpenAccessoryReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.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); } } } }; private static UsbSerialDriver _findDriverByDeviceId(int deviceId) { for (UsbSerialDriver driver: _drivers) { if (driver.getDevice().getDeviceId() == deviceId) { return driver; } } return null; } private static UsbSerialDriver _findDriverByDeviceName(String deviceName) { for (UsbSerialDriver driver: _drivers) { if (driver.getDevice().getDeviceName().equals(deviceName)) { return driver; } } return null; } private final static BroadcastReceiver _usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.i(TAG, "BroadcastReceiver USB action " + action); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (_instance) { UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (device != null) { UsbSerialDriver driver = _findDriverByDeviceId(device.getDeviceId()); if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { qgcLogDebug("Permission granted to " + device.getDeviceName()); driver.setPermissionStatus(UsbSerialDriver.permissionStatusSuccess); } else { qgcLogDebug("Permission denied for " + device.getDeviceName()); driver.setPermissionStatus(UsbSerialDriver.permissionStatusDenied); } } } } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (device != null) { if (_userDataHashByDeviceId.containsKey(device.getDeviceId())) { nativeDeviceHasDisconnected(_userDataHashByDeviceId.get(device.getDeviceId())); } } } } }; // Native C++ functions which connect back to QSerialPort code private static native void nativeDeviceHasDisconnected(int userData); private static native void nativeDeviceException(int userData, String messageA); private static native void nativeDeviceNewData(int userData, byte[] dataA); // Native C++ functions called to log output public static native void qgcLogDebug(String message); public static native void qgcLogWarning(String message); // QGCActivity singleton public QGCActivity() { _instance = this; _drivers = new ArrayList(); _userDataHashByDeviceId = new HashMap(); m_ioManager = new HashMap(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); PowerManager pm = (PowerManager)_instance.getSystemService(Context.POWER_SERVICE); _wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "QGroundControl"); if(_wakeLock != null) { _wakeLock.acquire(); } else { Log.i(TAG, "SCREEN_BRIGHT_WAKE_LOCK not acquired!!!"); } m_instance.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); _instance.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); _usbManager = (UsbManager)_instance.getSystemService(Context.USB_SERVICE); // Register for USB Detach and USB Permission intent IntentFilter filter = new IntentFilter(); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); filter.addAction(ACTION_USB_PERMISSION); _instance.registerReceiver(_instance._usbReceiver, filter); // Create intent for usb permission request _usbPermissionIntent = PendingIntent.getBroadcast(_instance, 0, new Intent(ACTION_USB_PERMISSION), 0); if (m_manager == null) { try { m_manager = (UsbManager)m_instance.getSystemService(Context.USB_SERVICE); taiSync = new TaiSync(); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); 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(_wakeLock != null) { _wakeLock.release(); } } catch(Exception e) { Log.e(TAG, "Exception onDestroy()"); } super.onDestroy(); } public void onInit(int status) { } /// Incrementally updates the list of drivers connected to the device private static void updateCurrentDrivers() { List currentDrivers = UsbSerialProber.findAllDevices(_usbManager); if (m_manager == null) m_manager = (UsbManager)m_instance.getSystemService(Context.USB_SERVICE); if (m_devices != null) m_devices.clear(); // Remove stale drivers for (int i=_drivers.size()-1; i>=0; i--) { boolean found = false; for (UsbSerialDriver currentDriver: currentDrivers) { if (_drivers.get(i).getDevice().getDeviceId() == currentDriver.getDevice().getDeviceId()) { found = true; break; } } if (!found) { qgcLogDebug("Remove stale driver " + _drivers.get(i).getDevice().getDeviceName()); _drivers.remove(i); } } // Add new drivers for (int i=0; i deviceInfoList = new ArrayList(); for (int i=0; i<_drivers.size(); i++) { String deviceInfo; UsbSerialDriver driver = _drivers.get(i); // CHECK FOR ALREADY OPENED DEVICES AND DON"T INCLUDE THEM IN THE COUNT for (iL=0; iL