From 6243dbac76e95143e9ae8697dcf33030929e6385 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Tue, 22 Jan 2019 10:36:00 -0800 Subject: [PATCH] Fix android USB permissions --- .../usbserial/driver/CdcAcmSerialDriver.java | 4 +- .../driver/CommonUsbSerialDriver.java | 28 +- .../usbserial/driver/Cp2102SerialDriver.java | 4 +- .../usbserial/driver/FtdiSerialDriver.java | 4 +- .../driver/ProlificSerialDriver.java | 4 +- .../usbserial/driver/UsbSerialDriver.java | 10 + .../usbserial/driver/UsbSerialProber.java | 24 +- .../mavlink/qgroundcontrol/QGCActivity.java | 509 ++++++++---------- .../src/qserialport_android.cpp | 2 +- 9 files changed, 271 insertions(+), 318 deletions(-) diff --git a/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java b/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java index a596870fb..880df09b0 100644 --- a/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java +++ b/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java @@ -41,8 +41,8 @@ public class CdcAcmSerialDriver extends CommonUsbSerialDriver { private static final int SET_CONTROL_LINE_STATE = 0x22; private static final int SEND_BREAK = 0x23; - public CdcAcmSerialDriver(UsbDevice device, UsbDeviceConnection connection) { - super(device, connection); + public CdcAcmSerialDriver(UsbDevice device) { + super(device); } @Override diff --git a/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java b/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java index 734933a22..0facbdf1c 100644 --- a/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java +++ b/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java @@ -35,11 +35,13 @@ abstract class CommonUsbSerialDriver implements UsbSerialDriver { public static final int DEFAULT_READ_BUFFER_SIZE = 16 * 1024; public static final int DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024; - protected final UsbDevice mDevice; - protected final UsbDeviceConnection mConnection; + protected final UsbDevice mDevice; + protected final Object mReadBufferLock = new Object(); + protected final Object mWriteBufferLock = new Object(); - protected final Object mReadBufferLock = new Object(); - protected final Object mWriteBufferLock = new Object(); + protected UsbDeviceConnection mConnection = null; + + private int _permissionStatus = permissionStatusRequestRequired; /** Internal read buffer. Guarded by {@link #mReadBufferLock}. */ protected byte[] mReadBuffer; @@ -47,14 +49,28 @@ abstract class CommonUsbSerialDriver implements UsbSerialDriver { /** Internal write buffer. Guarded by {@link #mWriteBufferLock}. */ protected byte[] mWriteBuffer; - public CommonUsbSerialDriver(UsbDevice device, UsbDeviceConnection connection) { + public CommonUsbSerialDriver(UsbDevice device) { mDevice = device; - mConnection = connection; mReadBuffer = new byte[DEFAULT_READ_BUFFER_SIZE]; mWriteBuffer = new byte[DEFAULT_WRITE_BUFFER_SIZE]; } + @Override + public void setConnection(UsbDeviceConnection connection) { + mConnection = connection; + } + + @Override + public int permissionStatus() { + return _permissionStatus; + } + + @Override + public void setPermissionStatus(int permissionStatus) { + _permissionStatus = permissionStatus; + } + /** * Returns the currently-bound USB device. * diff --git a/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java b/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java index aa151ba1a..679b719c9 100644 --- a/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java +++ b/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java @@ -61,8 +61,8 @@ public class Cp2102SerialDriver extends CommonUsbSerialDriver { private UsbEndpoint mReadEndpoint; private UsbEndpoint mWriteEndpoint; - public Cp2102SerialDriver(UsbDevice device, UsbDeviceConnection connection) { - super(device, connection); + public Cp2102SerialDriver(UsbDevice device) { + super(device); } private int setConfigSingle(int request, int value) { diff --git a/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java b/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java index 4d13b5cd8..549bdf8e2 100644 --- a/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java +++ b/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java @@ -211,8 +211,8 @@ public class FtdiSerialDriver extends CommonUsbSerialDriver { * @throws UsbSerialRuntimeException if the given device is incompatible * with this driver */ - public FtdiSerialDriver(UsbDevice usbDevice, UsbDeviceConnection usbConnection) { - super(usbDevice, usbConnection); + public FtdiSerialDriver(UsbDevice usbDevice) { + super(usbDevice); mType = null; } diff --git a/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java b/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java index 7c4c0a7f3..bde3c3807 100644 --- a/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java +++ b/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java @@ -231,8 +231,8 @@ public class ProlificSerialDriver extends CommonUsbSerialDriver { return ((getStatus() & flag) == flag); } - public ProlificSerialDriver(UsbDevice device, UsbDeviceConnection connection) { - super(device, connection); + public ProlificSerialDriver(UsbDevice device) { + super(device); } @Override diff --git a/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java b/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java index ed4426fc4..48ea197e1 100644 --- a/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java +++ b/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java @@ -83,6 +83,16 @@ public interface UsbSerialDriver { /** 2 stop bits. */ public static final int STOPBITS_2 = 2; + public static final int permissionStatusSuccess = 0; + public static final int permissionStatusDenied = 1; + public static final int permissionStatusRequested = 2; + public static final int permissionStatusRequestRequired = 3; + public static final int permissionStatusOpen = 4; + public int permissionStatus(); + public void setPermissionStatus(int permissionStatus); + + public void setConnection(UsbDeviceConnection connection); + /** * Returns the currently-bound USB device. * diff --git a/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java b/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java index b689ccc7c..d01755a4f 100644 --- a/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java +++ b/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java @@ -65,11 +65,7 @@ public enum UsbSerialProber { if (!testIfSupported(usbDevice, FtdiSerialDriver.getSupportedDevices())) { return Collections.emptyList(); } - final UsbDeviceConnection connection = manager.openDevice(usbDevice); - if (connection == null) { - return Collections.emptyList(); - } - final UsbSerialDriver driver = new FtdiSerialDriver(usbDevice, connection); + final UsbSerialDriver driver = new FtdiSerialDriver(usbDevice); return Collections.singletonList(driver); } }, @@ -80,11 +76,7 @@ public enum UsbSerialProber { if (!testIfSupported(usbDevice, CdcAcmSerialDriver.getSupportedDevices())) { return Collections.emptyList(); } - final UsbDeviceConnection connection = manager.openDevice(usbDevice); - if (connection == null) { - return Collections.emptyList(); - } - final UsbSerialDriver driver = new CdcAcmSerialDriver(usbDevice, connection); + final UsbSerialDriver driver = new CdcAcmSerialDriver(usbDevice); return Collections.singletonList(driver); } }, @@ -95,11 +87,7 @@ public enum UsbSerialProber { if (!testIfSupported(usbDevice, Cp2102SerialDriver.getSupportedDevices())) { return Collections.emptyList(); } - final UsbDeviceConnection connection = manager.openDevice(usbDevice); - if (connection == null) { - return Collections.emptyList(); - } - final UsbSerialDriver driver = new Cp2102SerialDriver(usbDevice, connection); + final UsbSerialDriver driver = new Cp2102SerialDriver(usbDevice); return Collections.singletonList(driver); } }, @@ -110,11 +98,7 @@ public enum UsbSerialProber { if (!testIfSupported(usbDevice, ProlificSerialDriver.getSupportedDevices())) { return Collections.emptyList(); } - final UsbDeviceConnection connection = manager.openDevice(usbDevice); - if (connection == null) { - return Collections.emptyList(); - } - final UsbSerialDriver driver = new ProlificSerialDriver(usbDevice, connection); + final UsbSerialDriver driver = new ProlificSerialDriver(usbDevice); return Collections.singletonList(driver); } }; diff --git a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java index 2f99cd206..a716392e6 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java @@ -31,11 +31,13 @@ package org.mavlink.qgroundcontrol; import java.util.HashMap; import java.util.List; +import java.util.ArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.io.IOException; import android.app.Activity; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -53,81 +55,136 @@ 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 PendingIntent _usbPermissionIntent = 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 userDataA) + public void onRunError(Exception eA, int userData) { Log.e(TAG, "onRunError Exception"); - nativeDeviceException(userDataA, eA.getMessage()); + nativeDeviceException(userData, eA.getMessage()); } @Override - public void onNewData(final byte[] dataA, int userDataA) + public void onNewData(final byte[] dataA, int userData) { - nativeDeviceNewData(userDataA, dataA); + 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 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); - //////////////////////////////////////////////////////////////////////////////////////////////// - // - // Constructor. Only used once to create the initial instance for the static functions. - // - //////////////////////////////////////////////////////////////////////////////////////////////// + // QGCActivity singleton public QGCActivity() { - m_instance = this; - m_openedDevices = new HashMap(); - m_userData = new HashMap(); - m_ioManager = new HashMap(); - Log.i(TAG, "Instance created"); + _instance = this; + _drivers = new ArrayList(); + _userDataHashByDeviceId = new HashMap(); + m_ioManager = new HashMap(); } @Override - public void onCreate(Bundle savedInstanceState) { + 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."); + 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); } @Override - protected void onDestroy() { + protected void onDestroy() + { try { - if(m_wl != null) { - m_wl.release(); - Log.i(TAG, "SCREEN_BRIGHT_WAKE_LOCK released."); + if(_wakeLock != null) { + _wakeLock.release(); } } catch(Exception e) { Log.e(TAG, "Exception onDestroy()"); @@ -138,225 +195,156 @@ public class QGCActivity extends QtActivity 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() + /// Incrementally updates the list of drivers connected to the device + private static void updateCurrentDrivers() { - if (m_instance == null) - return false; - - if (m_manager == null) - m_manager = (UsbManager)m_instance.getSystemService(Context.USB_SERVICE); + List currentDrivers = UsbSerialProber.findAllDevices(_usbManager); + + // 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 (m_devices != null) - m_devices.clear(); + if (!found) { + qgcLogDebug("Remove stale driver " + _drivers.get(i).getDevice().getDeviceName()); + _drivers.remove(i); + } + } - m_devices = UsbSerialProber.findAllDevices(m_manager); + // Add new drivers + for (int i=0; i deviceInfoList = new ArrayList(); - int countL = 0; - int iL; + 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; iLsetError(QSerialPort::DeviceNotFoundError); return false; } -- 2.22.0