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