From: Marcus Comstedt Date: Sun, 27 Nov 2016 14:56:24 +0000 (+0100) Subject: UsbHelper: Add additional methods for device enumeration and monitoring X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=054991d3ec685bf80bd7ca5c2f4c54963f797952;p=sigrok-androidutils.git UsbHelper: Add additional methods for device enumeration and monitoring In newer Android versions, the native enumeration code use by libusb does not work anymore due to the necessary virtual file system nodes not being available to user processes. Therefore, Java helper code is now needed for enumeration as well. This fixes parts of bug #701. --- diff --git a/src/org/sigrok/androidutils/UsbEventListener.java b/src/org/sigrok/androidutils/UsbEventListener.java new file mode 100644 index 0000000..18876cd --- /dev/null +++ b/src/org/sigrok/androidutils/UsbEventListener.java @@ -0,0 +1,25 @@ +/* + * This file is part of the sigrok-androidutils project. + * + * Copyright (C) 2016 Marcus Comstedt + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sigrok.androidutils; + +public interface UsbEventListener +{ + public void onUsbDeviceAction(String name, boolean removed); +} diff --git a/src/org/sigrok/androidutils/UsbEventListenerStub.java b/src/org/sigrok/androidutils/UsbEventListenerStub.java new file mode 100644 index 0000000..d677c86 --- /dev/null +++ b/src/org/sigrok/androidutils/UsbEventListenerStub.java @@ -0,0 +1,25 @@ +/* + * This file is part of the sigrok-androidutils project. + * + * Copyright (C) 2016 Marcus Comstedt + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sigrok.androidutils; + +final class UsbEventListenerStub implements UsbEventListener +{ + public native void onUsbDeviceAction(String name, boolean removed); +} diff --git a/src/org/sigrok/androidutils/UsbEventMonitor.java b/src/org/sigrok/androidutils/UsbEventMonitor.java new file mode 100644 index 0000000..2ca5d63 --- /dev/null +++ b/src/org/sigrok/androidutils/UsbEventMonitor.java @@ -0,0 +1,107 @@ +/* + * This file is part of the sigrok-androidutils project. + * + * Copyright (C) 2016 Marcus Comstedt + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sigrok.androidutils; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbManager; +import android.util.Log; + +final class UsbEventMonitor +{ + private final Context context; + private final UsbManager manager; + private final UsbEventListener listener; + private final BroadcastReceiver permReceiver; + private final BroadcastReceiver hotplugReceiver; + private final IntentFilter permFilter; + private final IntentFilter hotplugFilter; + + UsbEventMonitor(Context context, UsbManager manager, UsbEventListener listener) + { + this.context = context; + this.manager = manager; + this.listener = listener; + permReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (UsbSupplicant.ACTION_USB_PERMISSION.equals(action)) { + permissionCallback((UsbDevice)intent.getParcelableExtra( + UsbManager.EXTRA_DEVICE), intent.getBooleanExtra( + UsbManager.EXTRA_PERMISSION_GRANTED, false)); + } + } + }; + hotplugReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent != null && UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { + attachCallback((UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)); + } else if (intent != null && UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) { + detachCallback((UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)); + } + } + }; + permFilter = new IntentFilter(UsbSupplicant.ACTION_USB_PERMISSION); + hotplugFilter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_ATTACHED); + hotplugFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); + } + + synchronized void start() + { + context.registerReceiver(permReceiver, permFilter); + context.registerReceiver(hotplugReceiver, hotplugFilter); + } + + synchronized void stop() + { + context.unregisterReceiver(hotplugReceiver); + context.unregisterReceiver(permReceiver); + } + + private void permissionCallback(UsbDevice dev, boolean granted) + { + Log.d("UsbEventMonitor", "permission " + + (granted ? "granted" : "denied") + " for device " + dev); + addRemoveDevice(dev, !granted); + } + + private void attachCallback(UsbDevice dev) + { + Log.d("UsbEventMonitor", "device " + dev + "added"); + if (manager.hasPermission(dev)) + addRemoveDevice(dev, false); + } + + private void detachCallback(UsbDevice dev) + { + Log.d("UsbEventMonitor", "device " + dev + "removed"); + addRemoveDevice(dev, true); + } + + private synchronized void addRemoveDevice(UsbDevice dev, boolean removed) + { + listener.onUsbDeviceAction(dev.getDeviceName(), removed); + } +} diff --git a/src/org/sigrok/androidutils/UsbHelper.java b/src/org/sigrok/androidutils/UsbHelper.java index e16b33d..cfa2aa9 100644 --- a/src/org/sigrok/androidutils/UsbHelper.java +++ b/src/org/sigrok/androidutils/UsbHelper.java @@ -29,9 +29,12 @@ import java.util.HashMap; public final class UsbHelper { private static UsbManager manager; + private static Context context; + private static UsbEventMonitor eventMonitor; public static void setContext(Context ctx) { + context = ctx; if (ctx == null) manager = null; else @@ -54,6 +57,45 @@ public final class UsbHelper return (conn == null ? -1 : conn.getFileDescriptor()); } + private static synchronized void startEventMonitor(Context context, UsbManager manager, UsbEventListener listener) + { + if (eventMonitor != null) { + eventMonitor.stop(); + eventMonitor = null; + } + if (context == null) { + Log.w("UsbHelper", "no context"); + return; + } + if (manager == null) { + Log.w("UsbHelper", "no manager"); + return; + } + eventMonitor = new UsbEventMonitor(context, manager, listener); + eventMonitor.start(); + } + + private static synchronized void stopEventMonitor(Context context) + { + if (eventMonitor != null) { + eventMonitor.stop(); + eventMonitor = null; + } + } + + private static String[] scanDevices(UsbManager manager) + { + if (manager == null) { + Log.w("UsbHelper", "no manager"); + return null; + } + HashMap devlist = manager.getDeviceList(); + if (devlist == null) + return null; + String[] list = devlist.keySet().toArray(new String[devlist.size()]); + return list; + } + public static int open(String name, int mode) { try { @@ -63,5 +105,33 @@ public final class UsbHelper return -1; } } + + public static void startEventMonitor(UsbEventListener listener) + { + try { + startEventMonitor(context, manager, listener); + } catch (Exception e) { + Log.w("UsbHelper", "caught exception " + e); + } + } + + public static void stopEventMonitor() + { + try { + stopEventMonitor(context); + } catch (Exception e) { + Log.w("UsbHelper", "caught exception " + e); + } + } + + public static String[] scanDevices() + { + try { + return scanDevices(manager); + } catch (Exception e) { + Log.w("UsbHelper", "caught exception " + e); + return null; + } + } } diff --git a/src/org/sigrok/androidutils/UsbSupplicant.java b/src/org/sigrok/androidutils/UsbSupplicant.java index 8a13146..0f219a8 100644 --- a/src/org/sigrok/androidutils/UsbSupplicant.java +++ b/src/org/sigrok/androidutils/UsbSupplicant.java @@ -53,7 +53,7 @@ import org.xmlpull.v1.XmlPullParserException; public class UsbSupplicant { - private static final String ACTION_USB_PERMISSION = + static final String ACTION_USB_PERMISSION = "org.sigrok.androidutils.USB_PERMISSION"; protected final Context context;