diff --git a/app/src/main/java/com/termux/api/apis/UsbAPI.java b/app/src/main/java/com/termux/api/apis/UsbAPI.java index 667ba1793..42a6293bc 100644 --- a/app/src/main/java/com/termux/api/apis/UsbAPI.java +++ b/app/src/main/java/com/termux/api/apis/UsbAPI.java @@ -5,11 +5,13 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; import android.os.Looper; import android.util.JsonWriter; +import android.util.Log; import android.util.SparseArray; import com.termux.api.TermuxApiReceiver; @@ -47,6 +49,24 @@ public void writeJson(JsonWriter out) throws Exception { } }); break; + case "open": + device = getDevice(apiReceiver, context, intent); + if (device == null) return; + ResultReturner.returnData(apiReceiver, intent, new ResultReturner.WithAncillaryFd() { + @Override + public void writeResult(PrintWriter out) { + if (getPermission(device, context, intent)) { + int result = open(device, context); + if (result < 0) { + out.append("Failed to open device\n"); + } else { + this.setFd(result); + out.append("@"); // has to be non-empty + } + } else out.append("No permission\n"); + } + }); + break; case "permission": device = getDevice(apiReceiver, context, intent); if (device == null) return; @@ -54,51 +74,105 @@ public void writeJson(JsonWriter out) throws Exception { boolean result = getPermission(device, context, intent); out.append(result ? "yes\n" : "no\n"); }); - break; - case "open": - device = getDevice(apiReceiver, context, intent); - if (device == null) return; - ResultReturner.returnData(apiReceiver, intent, new ResultReturner.WithAncillaryFd() { - @Override - public void writeResult(PrintWriter out) { - if (getPermission(device, context, intent)) { - int result = open(device, context); - if (result < 0) { - out.append("Failed to open device\n"); - } else { - this.setFd(result); - out.append("@"); // has to be non-empty - } - } else out.append("No permission\n"); - } - }); - break; default: ResultReturner.returnData(apiReceiver, intent, out -> out.append("Invalid action\n")); } } - } private static void listDevices(final Context context, JsonWriter out) throws IOException { final UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); HashMap deviceList = usbManager.getDeviceList(); - Iterator deviceIterator = deviceList.keySet().iterator(); out.beginArray(); - while (deviceIterator.hasNext()) { - out.value(deviceIterator.next()); + for (UsbDevice device : deviceList.values()) { + out.beginObject(); + out.name("device_name").value(device.getDeviceName()); + out.name("device_id").value(device.getDeviceId()); + out.name("vendor_id").value(String.format("0x%04x", device.getVendorId())); + out.name("product_id").value(String.format("0x%04x", device.getProductId())); + out.name("device_class").value(device.getDeviceClass()+" - "+translateDeviceClass(device.getDeviceClass())); + out.name("device_sub_class").value(device.getDeviceSubclass()); + out.name("manufacturer_name").value(device.getManufacturerName().replace("\u0000", "")); + out.name("device_protocol").value(device.getDeviceProtocol()); + out.name("product_name").value(device.getProductName().replace("\u0000", "")); + out.name("serial_number").value(device.getSerialNumber()); + out.name("configurations").value(device.getConfigurationCount()); + out.name("descriptor_type").value(device.describeContents()); + out.name("access_granted").value(usbManager.hasPermission(device)); + out.endObject(); } out.endArray(); } + private static String translateDeviceClass(int usbClass){ + switch(usbClass){ + case UsbConstants.USB_CLASS_APP_SPEC: + return "App specific USB class"; + case UsbConstants.USB_CLASS_AUDIO: + return "Audio device"; + case UsbConstants.USB_CLASS_CDC_DATA: + return "CDC device (communications device class)"; + case UsbConstants.USB_CLASS_COMM: + return "Communication device"; + case UsbConstants.USB_CLASS_CONTENT_SEC: + return "Content security device"; + case UsbConstants.USB_CLASS_CSCID: + return "Content smart card device"; + case UsbConstants.USB_CLASS_HID: + return "Human interface device (for example a keyboard)"; + case UsbConstants.USB_CLASS_HUB: + return "USB hub"; + case UsbConstants.USB_CLASS_MASS_STORAGE: + return "Mass storage device"; + case UsbConstants.USB_CLASS_MISC: + return "Wireless miscellaneous devices"; + case UsbConstants.USB_CLASS_PER_INTERFACE: + return "Usb class is determined on a per-interface basis"; + case UsbConstants.USB_CLASS_PHYSICA: + return "Physical device"; + case UsbConstants.USB_CLASS_PRINTER: + return "Printer"; + case UsbConstants.USB_CLASS_STILL_IMAGE: + return "Still image devices (digital cameras)"; + case UsbConstants.USB_CLASS_VENDOR_SPEC: + return "Vendor specific USB class"; + case UsbConstants.USB_CLASS_VIDEO: + return "Video device"; + case UsbConstants.USB_CLASS_WIRELESS_CONTROLLER: + return "Wireless controller device"; + default: return "Unknown USB class!"; + } + } private static UsbDevice getDevice(final TermuxApiReceiver apiReceiver, final Context context, final Intent intent) { String deviceName = intent.getStringExtra("device"); + String vendorId = intent.getStringExtra("vendorId"); + String productId = intent.getStringExtra("productId"); + if (deviceName == null && (vendorId == null || productId == null)) { + Log.e(LOG_TAG, "Missing usb device info in open()"); + ResultReturner.returnData(apiReceiver, intent, out -> out.append("Need either usbfs path or vendorId+productId\n")); + return null; + } + final UsbManager usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); HashMap deviceList = usbManager.getDeviceList(); - UsbDevice device = deviceList.get(deviceName); + UsbDevice device = null; + if (deviceName != null) { + device = deviceList.get(deviceName); + } else { + for (UsbDevice dev : deviceList.values()) { + Log.d(LOG_TAG, "Comparing "+dev.getDeviceName()+" with given vendorId and productId"); + if (String.format("0x%04x", dev.getVendorId()).equalsIgnoreCase(vendorId) && + String.format("0x%04x", dev.getProductId()).equalsIgnoreCase(productId)) { + device = dev; + break; + } + } + } if (device == null) { ResultReturner.returnData(apiReceiver, intent, out -> out.append("No such device\n")); + } else { + Log.i(LOG_TAG, "Found matching device at "+device.getDeviceName()); } return device; } @@ -131,7 +205,6 @@ public void onReceive(final Context usbContext, final Intent usbIntent) { if (looper != null) looper.quit(); } } - } } }; @@ -148,7 +221,7 @@ public void onReceive(final Context usbContext, final Intent usbIntent) { private static boolean getPermission(final @NonNull UsbDevice device, final Context context, final Intent intent) { boolean request = intent.getBooleanExtra("request", false); - if(request) { + if (request) { return requestPermission(device, context); } else { return hasPermission(device, context); diff --git a/app/src/main/java/com/termux/api/util/ResultReturner.java b/app/src/main/java/com/termux/api/util/ResultReturner.java index 4ae93f097..8600950cd 100644 --- a/app/src/main/java/com/termux/api/util/ResultReturner.java +++ b/app/src/main/java/com/termux/api/util/ResultReturner.java @@ -57,19 +57,19 @@ public void setInput(InputStream inputStream) throws Exception { this.in = inputStream; } } - + /** * Possible subclass of {@link ResultWriter} when the output is binary data instead of text. */ public static abstract class BinaryOutput implements ResultWriter { private OutputStream out; - + public void setOutput(OutputStream outputStream) { this.out = outputStream; } - + public abstract void writeResult(OutputStream out) throws Exception; - + /** * writeResult with a PrintWriter is marked as final and overwritten, so you don't accidentally use it */ @@ -178,7 +178,7 @@ public static void returnData(Object context, final Intent intent, final ResultW } else { resultWriter.writeResult(writer); } - if(resultWriter instanceof WithAncillaryFd) { + if (resultWriter instanceof WithAncillaryFd) { int fd = ((WithAncillaryFd) resultWriter).getFd(); if (fd >= 0) { pfds[0] = ParcelFileDescriptor.adoptFd(fd); @@ -188,7 +188,7 @@ public static void returnData(Object context, final Intent intent, final ResultW } } - if(pfds[0] != null) { + if (pfds[0] != null) { pfds[0].close(); }