mirror of
				https://github.com/subsurface/subsurface.git
				synced 2025-02-19 22:16:15 +00:00 
			
		
		
		
	android usb serial: Prepare device / driver select
This commit contains the serial_android_usb part of the changes proposed in issue #2657. What's implemented: - A data structure that contains all the data that can be used to describe an usb device (including user-facing string). - A function to get a list of all attached usb devices (optionally with selectable driver class). - Changes in the serial_android_usb_open-function and in the Java part to use the information about the usb device and optionally selected driver when connecting. This commit keeps compatibility with the current UI-Code in the case that only one USB-Device is connected. If two devices are connected, only the first one is tried. There are still some small things to do: - Change the user-facing string to something more descriptive. - Parts which aren't uesd anymore when the UI-Part is implemented are simply marked as obsolete (to keep compatibility for now). But generally it seems to work. [Dirk Hohndel: some white space / coding style adjustments] Signed-off-by: Christof Arnosti <charno@charno.ch> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
This commit is contained in:
		
							parent
							
								
									822b05bec4
								
							
						
					
					
						commit
						a34a81d120
					
				
					 4 changed files with 147 additions and 25 deletions
				
			
		| 
						 | 
				
			
			@ -14,6 +14,8 @@ import android.content.Intent;
 | 
			
		|||
import org.subsurfacedivelog.mobile.SubsurfaceMobileActivity;
 | 
			
		||||
 | 
			
		||||
import java.lang.System;
 | 
			
		||||
import java.lang.Class;
 | 
			
		||||
import java.lang.reflect.Constructor;
 | 
			
		||||
import java.lang.Thread;
 | 
			
		||||
import java.util.Queue;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
| 
						 | 
				
			
			@ -83,38 +85,68 @@ public class AndroidSerial {
 | 
			
		|||
		this.usbSerialPort = usbSerialPort;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public static AndroidSerial open_android_serial()
 | 
			
		||||
	public static AndroidSerial open_android_serial(UsbDevice usbDevice, String driverClassName)
 | 
			
		||||
	{
 | 
			
		||||
		try {
 | 
			
		||||
			Log.d(TAG, "in " + Thread.currentThread().getStackTrace()[2].getMethodName());
 | 
			
		||||
			// Find all available drivers from attached devices.
 | 
			
		||||
			Context context = SubsurfaceMobileActivity.getAppContext();
 | 
			
		||||
			UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
 | 
			
		||||
			ProbeTable usbSerialProbetable = UsbSerialProber.getDefaultProbeTable();
 | 
			
		||||
 | 
			
		||||
			usbSerialProbetable.addProduct(0x0403, 0xf460, FtdiSerialDriver.class); // Oceanic Custom PID
 | 
			
		||||
			usbSerialProbetable.addProduct(0x0403, 0xf680, FtdiSerialDriver.class); // Suunto Custom PID
 | 
			
		||||
			usbSerialProbetable.addProduct(0x0403, 0x87d0, FtdiSerialDriver.class); // Cressi (Leonardo) Custom PID
 | 
			
		||||
			if (usbDevice == null) {
 | 
			
		||||
					Log.e(TAG, "usbDevice == null");
 | 
			
		||||
					return null;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			usbSerialProbetable.addProduct(0x04B8, 0x0521, ProlificSerialDriver.class); // Mares (Nemo Sport) / Cressi Custom PID
 | 
			
		||||
			usbSerialProbetable.addProduct(0x04B8, 0x0521, ProlificSerialDriver.class); // Zeagle Custom PID
 | 
			
		||||
			usbSerialProbetable.addProduct(0xFFFF, 0x0005, CdcAcmSerialDriver.class); // Mares Icon HD Custom PID
 | 
			
		||||
			UsbSerialDriver driver = null;
 | 
			
		||||
 | 
			
		||||
			UsbSerialProber usbSerialProber = new UsbSerialProber(usbSerialProbetable);
 | 
			
		||||
			if (driverClassName.length() == 0) {
 | 
			
		||||
				ProbeTable usbSerialProbetable = UsbSerialProber.getDefaultProbeTable();
 | 
			
		||||
 | 
			
		||||
			List<UsbSerialDriver> availableDrivers = usbSerialProber.findAllDrivers(manager);
 | 
			
		||||
			if (availableDrivers.isEmpty()) {
 | 
			
		||||
				Log.w(TAG, "no usb-to-serial devices found!");
 | 
			
		||||
				return null;
 | 
			
		||||
				usbSerialProbetable.addProduct(0x0403, 0xf460, FtdiSerialDriver.class); // Oceanic Custom PID
 | 
			
		||||
				usbSerialProbetable.addProduct(0x0403, 0xf680, FtdiSerialDriver.class); // Suunto Custom PID
 | 
			
		||||
				usbSerialProbetable.addProduct(0x0403, 0x87d0, FtdiSerialDriver.class); // Cressi (Leonardo) Custom PID
 | 
			
		||||
 | 
			
		||||
				usbSerialProbetable.addProduct(0x04B8, 0x0521, ProlificSerialDriver.class); // Mares (Nemo Sport) / Cressi Custom PID
 | 
			
		||||
				usbSerialProbetable.addProduct(0x04B8, 0x0521, ProlificSerialDriver.class); // Zeagle Custom PID
 | 
			
		||||
				usbSerialProbetable.addProduct(0xFFFF, 0x0005, CdcAcmSerialDriver.class); // Mares Icon HD Custom PID
 | 
			
		||||
 | 
			
		||||
				UsbSerialProber usbSerialProber = new UsbSerialProber(usbSerialProbetable);
 | 
			
		||||
 | 
			
		||||
				driver = usbSerialProber.probeDevice(usbDevice);
 | 
			
		||||
 | 
			
		||||
				if (driver == null) {
 | 
			
		||||
					Log.w(TAG, "Could not find a driver for the usb device " + usbDevice);
 | 
			
		||||
					return null;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Log.i(TAG, "Using autodetected driver class " + driver.getClass().getSimpleName());
 | 
			
		||||
 | 
			
		||||
			} else {
 | 
			
		||||
				final Class<? extends UsbSerialDriver> driverClass = Class.forName("com.hoho.android.usbserial.driver." + driverClassName).asSubclass(UsbSerialDriver.class);
 | 
			
		||||
 | 
			
		||||
				if (driverClass == null) {
 | 
			
		||||
					Log.w(TAG, "Could not find driver class " + driverClassName);
 | 
			
		||||
					return null;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				try {
 | 
			
		||||
					final Constructor<? extends UsbSerialDriver> ctor =
 | 
			
		||||
							driverClass.getConstructor(UsbDevice.class);
 | 
			
		||||
					driver = ctor.newInstance(usbDevice);
 | 
			
		||||
				} catch (Exception e) {
 | 
			
		||||
					Log.w(TAG, "Could not load user-specified driver class " + driverClassName, e);
 | 
			
		||||
					return null;
 | 
			
		||||
				}
 | 
			
		||||
				Log.i(TAG, "Using user-specified driver class " + driver.getClass().getSimpleName());
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Open a connection to the first available driver.
 | 
			
		||||
			UsbSerialDriver driver = availableDrivers.get(0);
 | 
			
		||||
			UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
 | 
			
		||||
			UsbDeviceConnection connection = manager.openDevice(usbDevice);
 | 
			
		||||
 | 
			
		||||
			if (connection == null) {
 | 
			
		||||
				manager.requestPermission(driver.getDevice(), PendingIntent.getBroadcast(context, 0, new Intent("org.subsurfacedivelog.mobile.USB_PERMISSION"), 0));
 | 
			
		||||
				Log.w(TAG, "Could not open device!");
 | 
			
		||||
				manager.requestPermission(usbDevice, PendingIntent.getBroadcast(context, 0, new Intent("org.subsurfacedivelog.mobile.USB_PERMISSION"), 0));
 | 
			
		||||
				Log.w(TAG, "Could not open device. Requesting permission.");
 | 
			
		||||
				return null;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,11 +6,14 @@
 | 
			
		|||
 | 
			
		||||
#include <QAndroidJniObject>
 | 
			
		||||
#include <QAndroidJniEnvironment>
 | 
			
		||||
#include <QtAndroid>
 | 
			
		||||
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
#include <android/log.h>
 | 
			
		||||
 | 
			
		||||
#include "serial_usb_android.h"
 | 
			
		||||
 | 
			
		||||
#define INFO(context, fmt, ...)	 __android_log_print(ANDROID_LOG_DEBUG, __FILE__, "INFO: " fmt "\n", ##__VA_ARGS__)
 | 
			
		||||
#define ERROR(context, fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, __FILE__, "ERROR: " fmt "\n", ##__VA_ARGS__)
 | 
			
		||||
#define TRACE INFO
 | 
			
		||||
| 
						 | 
				
			
			@ -84,8 +87,7 @@ static dc_status_t serial_usb_android_purge(void *io, dc_direction_t direction)
 | 
			
		|||
	return static_cast<dc_status_t>(device->callMethod<jint>("purge", "(I)I", direction));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static dc_status_t
 | 
			
		||||
serial_usb_android_configure(void *io, unsigned int baudrate, unsigned int databits, dc_parity_t parity,
 | 
			
		||||
static dc_status_t serial_usb_android_configure(void *io, unsigned int baudrate, unsigned int databits, dc_parity_t parity,
 | 
			
		||||
			     dc_stopbits_t stopbits, dc_flowcontrol_t flowcontrol)
 | 
			
		||||
{
 | 
			
		||||
	TRACE (device->context, "%s: baudrate=%i, databits=%i, parity=%i, stopbits=%i, flowcontrol=%i", __FUNCTION__,
 | 
			
		||||
| 
						 | 
				
			
			@ -148,7 +150,6 @@ static dc_status_t serial_usb_android_read(void *io, void *data, size_t size, si
 | 
			
		|||
		return DC_STATUS_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static dc_status_t serial_usb_android_write(void *io, const void *data, size_t size, size_t *actual)
 | 
			
		||||
{
 | 
			
		||||
	TRACE (device->context, "%s: size: %i", __FUNCTION__, size);
 | 
			
		||||
| 
						 | 
				
			
			@ -172,7 +173,7 @@ static dc_status_t serial_usb_android_write(void *io, const void *data, size_t s
 | 
			
		|||
	return DC_STATUS_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dc_status_t serial_usb_android_open(dc_iostream_t **iostream, dc_context_t *context)
 | 
			
		||||
dc_status_t serial_usb_android_open(dc_iostream_t **iostream, dc_context_t *context, QAndroidJniObject usbDevice, std::string driverClassName)
 | 
			
		||||
{
 | 
			
		||||
	TRACE(device->contxt, "%s", __FUNCTION__);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -191,11 +192,84 @@ dc_status_t serial_usb_android_open(dc_iostream_t **iostream, dc_context_t *cont
 | 
			
		|||
 | 
			
		||||
	QAndroidJniObject localdevice = QAndroidJniObject::callStaticObjectMethod("org/subsurfacedivelog/mobile/AndroidSerial",
 | 
			
		||||
										  "open_android_serial",
 | 
			
		||||
										  "()Lorg/subsurfacedivelog/mobile/AndroidSerial;");
 | 
			
		||||
	if (localdevice == nullptr) {
 | 
			
		||||
										  "(Landroid/hardware/usb/UsbDevice;Ljava/lang/String;)Lorg/subsurfacedivelog/mobile/AndroidSerial;",
 | 
			
		||||
										  usbDevice.object<jobject>(),
 | 
			
		||||
										  QAndroidJniObject::fromString(driverClassName.c_str()).object());
 | 
			
		||||
	if (localdevice == nullptr)
 | 
			
		||||
		return DC_STATUS_IO;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	QAndroidJniObject *device = new QAndroidJniObject(localdevice);
 | 
			
		||||
	TRACE(device->contxt, "%s", "calling dc_custom_open())");
 | 
			
		||||
	return dc_custom_open(iostream, context, DC_TRANSPORT_SERIAL, &callbacks, device);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<android_usb_serial_device_descriptor> serial_usb_android_get_devices(bool driverSelection)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<std::string> driverNames;
 | 
			
		||||
	if (driverSelection)
 | 
			
		||||
		driverNames = { "", "CdcAcmSerialDriver", "Ch34xSerialDriver", "Cp21xxSerialDriver", "FtdiSerialDriver", "ProlificSerialDriver" };
 | 
			
		||||
	else
 | 
			
		||||
		driverNames = {""};
 | 
			
		||||
 | 
			
		||||
	// Get the current main activity of the application.
 | 
			
		||||
	QAndroidJniObject activity = QtAndroid::androidActivity();
 | 
			
		||||
	QAndroidJniObject usb_service = QAndroidJniObject::fromString("usb");
 | 
			
		||||
	QAndroidJniEnvironment env;
 | 
			
		||||
 | 
			
		||||
	// Get UsbManager from activity
 | 
			
		||||
	QAndroidJniObject usbManager = activity.callObjectMethod("getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", QAndroidJniObject::fromString("usb").object());
 | 
			
		||||
 | 
			
		||||
	//UsbDevice[] arrayOfDevices = usbManager.getDeviceList().values().toArray();
 | 
			
		||||
	QAndroidJniObject deviceListHashMap = usbManager.callObjectMethod("getDeviceList","()Ljava/util/HashMap;");
 | 
			
		||||
	QAndroidJniObject deviceListCollection = deviceListHashMap.callObjectMethod("values", "()Ljava/util/Collection;");
 | 
			
		||||
	jint numDevices = deviceListCollection.callMethod<jint>("size");
 | 
			
		||||
	QAndroidJniObject arrayOfDevices = deviceListCollection.callObjectMethod("toArray", "()[Ljava/lang/Object;");
 | 
			
		||||
 | 
			
		||||
	// Special case to keep a generic user-facing name if only one device is present.
 | 
			
		||||
	if (numDevices == 1 && !driverSelection) {
 | 
			
		||||
		// UsbDevice usbDevice = arrayOfDevices[0]
 | 
			
		||||
		jobject value = env->GetObjectArrayElement(arrayOfDevices.object<jobjectArray>(), 0);
 | 
			
		||||
		QAndroidJniObject usbDevice(value);
 | 
			
		||||
		return std::vector<android_usb_serial_device_descriptor> { {QAndroidJniObject(usbDevice), "", "USB Connection"} };
 | 
			
		||||
	} else {
 | 
			
		||||
		std::vector<android_usb_serial_device_descriptor> retval;
 | 
			
		||||
		for (int i = 0; i < numDevices ; i++) {
 | 
			
		||||
			// UsbDevice usbDevice = arrayOfDevices[i]
 | 
			
		||||
			jobject value = env->GetObjectArrayElement(arrayOfDevices.object<jobjectArray>(), i);
 | 
			
		||||
			QAndroidJniObject usbDevice(value);
 | 
			
		||||
 | 
			
		||||
			// std::string deviceName = usbDevice.getDeviceName()
 | 
			
		||||
			QAndroidJniObject usbDeviceNameString = usbDevice.callObjectMethod<jstring>("getDeviceName");
 | 
			
		||||
			const char *charArray = env->GetStringUTFChars(usbDeviceNameString.object<jstring>(), nullptr);
 | 
			
		||||
			std::string deviceName(charArray);
 | 
			
		||||
			env->ReleaseStringUTFChars(usbDeviceNameString.object<jstring>(), charArray);
 | 
			
		||||
 | 
			
		||||
			// TODO the deviceName should probably be something better... Currently it's the /dev-filename.
 | 
			
		||||
 | 
			
		||||
			for (std::string driverName : driverNames) {
 | 
			
		||||
				std::string uiDeviceName;
 | 
			
		||||
				if (driverName != "")
 | 
			
		||||
					uiDeviceName = deviceName + " (" + driverName + ")";
 | 
			
		||||
				else
 | 
			
		||||
					uiDeviceName = deviceName + " (autoselect driver)";
 | 
			
		||||
				retval.push_back({QAndroidJniObject(usbDevice), driverName, uiDeviceName});
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return retval;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * For testing and compatibility only, can be removed after the UI changes. Behaves exactly like the "old"
 | 
			
		||||
 * implementation if only one device is attached.
 | 
			
		||||
 */
 | 
			
		||||
dc_status_t serial_usb_android_open(dc_iostream_t **iostream, dc_context_t *context)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<android_usb_serial_device_descriptor> devices = serial_usb_android_get_devices(false);
 | 
			
		||||
 | 
			
		||||
	if(devices.empty())
 | 
			
		||||
		return DC_STATUS_NODEVICE;
 | 
			
		||||
 | 
			
		||||
	return serial_usb_android_open(iostream, context, devices[0].usbDevice, devices[0].className);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										16
									
								
								core/serial_usb_android.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								core/serial_usb_android.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
#ifndef SERIAL_USB_ANDROID_H
 | 
			
		||||
#define SERIAL_USB_ANDROID_H
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
/* USB Device Information */
 | 
			
		||||
struct android_usb_serial_device_descriptor {
 | 
			
		||||
	QAndroidJniObject usbDevice; /* the UsbDevice */
 | 
			
		||||
	std::string className; /* the driver class name. If empty, then "autodetect" */
 | 
			
		||||
	std::string uiRepresentation; /* The string that can be used for the user interface. */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
std::vector<android_usb_serial_device_descriptor> serial_usb_android_get_devices(bool driverSelection);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -89,7 +89,7 @@ int main(int argc, char **argv)
 | 
			
		|||
void set_non_bt_addresses()
 | 
			
		||||
{
 | 
			
		||||
#if defined(Q_OS_ANDROID)
 | 
			
		||||
	connectionListModel.addAddress("usb-serial");
 | 
			
		||||
	connectionListModel.addAddress("usb-serial"); /* obsolete, can be removed when the new USB device selection is implemented. */
 | 
			
		||||
#elif defined(Q_OS_LINUX) // since this is in the else, it does NOT include Android
 | 
			
		||||
	connectionListModel.addAddress("/dev/ttyS0");
 | 
			
		||||
	connectionListModel.addAddress("/dev/ttyS1");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue