/* UVCDeviceManager.java Copyright (c) 2015 NTT DOCOMO,INC. Released under the MIT license http://opensource.org/licenses/mit-license.php */ package org.deviceconnect.android.deviceplugin.uvc.core; import android.content.Context; import android.hardware.usb.UsbDevice; import android.os.Build; import com.serenegiant.usb.DeviceFilter; import com.serenegiant.usb.USBMonitor; import org.deviceconnect.android.deviceplugin.uvc.R; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Logger; public class UVCDeviceManager { private static final long INTERVAL_MONITORING = 3000; // 3 seconds private final Logger mLogger = Logger.getLogger("uvc.dplugin"); private final USBMonitor mUSBMonitor; private final List<UVCDevice> mAttachedDevices = new ArrayList<UVCDevice>(); private final List<DeviceListener> mDeviceListeners = new ArrayList<DeviceListener>(); private final List<ConnectionListener> mConnectionListeners = new ArrayList<ConnectionListener>(); private final List<DiscoveryListener> mDiscoveryListeners = new ArrayList<DiscoveryListener>(); private final List<PreviewListener> mPreviewListeners = new ArrayList<PreviewListener>(); private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); private final UVCDevice.PreviewListener mPreviewListener = new UVCDevice.PreviewListener() { @Override public void onFrame(final UVCDevice device, final byte[] frame, final int frameFormat, final int width, final int height) { notifyPreviewFrame(device, frame, frameFormat, width, height); } }; private boolean mIsStarted; private Thread mScanThread; private int mScanNum; public UVCDeviceManager(final Context context) { mUSBMonitor = new USBMonitor(context, new USBMonitor.OnDeviceConnectListener() { @Override public void onAttach(final UsbDevice usbDevice) { if (!supportedAttachEvent()) { return; } if (usbDevice == null) { return; } mLogger.info("onAttach: " + usbDevice.getDeviceName()); UVCDevice device = getDevice(usbDevice); if (device == null) { device = new UVCDevice(usbDevice, UVCDeviceManager.this); device.addPreviewListener(mPreviewListener); pushDevice(device); notifyEventOnFound(device); } } @Override public void onDettach(final UsbDevice usbDevice) { mLogger.info("onDettach: " + usbDevice.getDeviceName()); pullDevice(usbDevice); } @Override public void onConnect(final UsbDevice usbDevice, final USBMonitor.UsbControlBlock ctrlBlock, final boolean createNew) { mLogger.info("onConnect: device = " + usbDevice.getDeviceName() + ", ctrlBlock = " + ctrlBlock + ", createNew = " + createNew); UVCDevice device = getDevice(usbDevice); if (device != null) { device.notifyPermission(ctrlBlock); } } @Override public void onDisconnect(final UsbDevice usbDevice, final USBMonitor.UsbControlBlock ctrlBlock) { mLogger.info("onDisconnect: " + usbDevice.getDeviceName()); final UVCDevice device = getDevice(usbDevice); mExecutor.execute(new Runnable() { @Override public void run() { if (device != null && device.disconnect()) { notifyEventOnDisconnect(device); } } }); } @Override public void onCancel(final UsbDevice usbDevice) { mLogger.info("onCancel"); UVCDevice device = getDevice(usbDevice); if (device != null) { device.notifyPermission(null); } } }); List<DeviceFilter> filters = DeviceFilter.getDeviceFilters(context, R.xml.device_filter); mUSBMonitor.setDeviceFilter(filters); } void requestPermission(final UsbDevice usbDevice) { mUSBMonitor.requestPermission(usbDevice); } public boolean connectDevice(final UVCDevice device) { mLogger.info("Connecting Device... : " + device.getName()); if (device.connect()) { notifyEventOnConnect(device); return true; } else { notifyEventOnConnectionFailed(device); return false; } } public boolean connectDevice(final String id) { UVCDevice device = getDevice(id); if (device != null) { return connectDevice(device); } else { mLogger.warning("connectDevice: unknown device: " + device.getName()); return false; } } public void disconnectDevice(final UVCDevice device) { if (device.disconnect()) { mLogger.info("disconnectDevice: closed " + device.getName()); notifyEventOnDisconnect(device); } else { mLogger.info("disconnectDevice: already closed " + device.getName()); } } public void disconnectDevice(final String id) { UVCDevice device = getDevice(id); if (device != null) { disconnectDevice(device); } else { mLogger.warning("disconnectDevice: unknown device: " + device.getName()); } } private void pushDevice(final UVCDevice device) { synchronized (mAttachedDevices) { mAttachedDevices.add(device); } } private UVCDevice pullDevice(final UsbDevice usbDevice) { synchronized (mAttachedDevices) { for (Iterator<UVCDevice> it = mAttachedDevices.iterator(); it.hasNext(); ) { UVCDevice device = it.next(); if (device.isSameDevice(usbDevice)) { it.remove(); return device; } } } return null; } private UVCDevice getDevice(final UsbDevice usbDevice) { synchronized (mAttachedDevices) { for (Iterator<UVCDevice> it = mAttachedDevices.iterator(); it.hasNext(); ) { UVCDevice device = it.next(); if (device.isSameDevice(usbDevice)) { return device; } } } return null; } public void addDeviceListener(final DeviceListener listener) { synchronized (mDeviceListeners) { mDeviceListeners.add(listener); } } public void removeDeviceListener(final DeviceListener listener) { synchronized (mDeviceListeners) { for (Iterator<DeviceListener> it = mDeviceListeners.iterator(); it.hasNext(); ) { DeviceListener l = it.next(); if (l == listener) { it.remove(); return; } } } } private void notifyEventOnFound(final UVCDevice device) { synchronized (mDeviceListeners) { for (Iterator<DeviceListener> it = mDeviceListeners.iterator(); it.hasNext(); ) { final DeviceListener l = it.next(); mExecutor.execute(new Runnable() { @Override public void run() { l.onFound(device); } }); } } } public void addConnectionListener(final ConnectionListener listener) { synchronized (mConnectionListeners) { mConnectionListeners.add(listener); } } public void removeConnectionListener(final ConnectionListener listener) { synchronized (mConnectionListeners) { for (Iterator<ConnectionListener> it = mConnectionListeners.iterator(); it.hasNext(); ) { ConnectionListener l = it.next(); if (l == listener) { it.remove(); return; } } } } private void notifyEventOnConnect(final UVCDevice device) { synchronized (mConnectionListeners) { for (Iterator<ConnectionListener> it = mConnectionListeners.iterator(); it.hasNext(); ) { final ConnectionListener l = it.next(); mExecutor.execute(new Runnable() { @Override public void run() { l.onConnect(device); } }); } } } private void notifyEventOnConnectionFailed(final UVCDevice device) { synchronized (mConnectionListeners) { for (Iterator<ConnectionListener> it = mConnectionListeners.iterator(); it.hasNext(); ) { final ConnectionListener l = it.next(); mExecutor.execute(new Runnable() { @Override public void run() { l.onConnectionFailed(device); } }); } } } private void notifyEventOnDisconnect(final UVCDevice device) { synchronized (mConnectionListeners) { for (Iterator<ConnectionListener> it = mConnectionListeners.iterator(); it.hasNext(); ) { final ConnectionListener l = it.next(); mExecutor.execute(new Runnable() { @Override public void run() { l.onDisconnect(device); } }); } } } public void addDiscoveryListener(final DiscoveryListener listener) { synchronized (mDiscoveryListeners) { mDiscoveryListeners.add(listener); } } public void removeDiscoveryListener(final DiscoveryListener listener) { synchronized (mDiscoveryListeners) { for (Iterator<DiscoveryListener> it = mDiscoveryListeners.iterator(); it.hasNext(); ) { DiscoveryListener l = it.next(); if (l == listener) { it.remove(); return; } } } } private void notifyEventOnDiscovery(final List<UVCDevice> devices) { synchronized (mDiscoveryListeners) { for (Iterator<DiscoveryListener> it = mDiscoveryListeners.iterator(); it.hasNext(); ) { final DiscoveryListener l = it.next(); mExecutor.execute(new Runnable() { @Override public void run() { l.onDiscovery(devices); } }); } } } public void addPreviewListener(final PreviewListener listener) { synchronized (mPreviewListeners) { for (Iterator<PreviewListener> it = mPreviewListeners.iterator(); it.hasNext(); ) { if (it.next() == listener) { return; } } mPreviewListeners.add(listener); } } private void clearPreviewListeners() { synchronized (mPreviewListeners) { mPreviewListeners.clear(); } } private void notifyPreviewFrame(final UVCDevice device, final byte[] frame, final int frameFormat, final int width, final int height) { synchronized (mPreviewListeners) { for (Iterator<PreviewListener> it = mPreviewListeners.iterator(); it.hasNext(); ) { final PreviewListener l = it.next(); mExecutor.execute(new Runnable() { @Override public void run() { l.onFrame(device, frame, frameFormat, width, height); } }); } } } public synchronized void start() { if (mIsStarted) { return; } mIsStarted = true; mUSBMonitor.register(); if (supportedAttachEvent()) { doScanOnce(); } else { startScan(); } } public synchronized void startScan() { if (mScanThread == null) { mScanThread = new Thread(new Runnable() { @Override public void run() { mLogger.info("Started UVC device monitoring: "); // Find UVC devices connected to Host device already. try { do { doScanOnce(); Thread.sleep(INTERVAL_MONITORING); } while (mIsStarted); } catch (InterruptedException e) { // Nothing to do. } mLogger.info("Stopped UVC device monitoring."); } }); mScanThread.start(); } ++mScanNum; } private void doScanOnce() { List<UsbDevice> usbDevices = mUSBMonitor.getDeviceList(); notifyEventOnDiscovery(getDeviceList()); for (UsbDevice usbDevice : usbDevices) { if (getDevice(usbDevice) != null) { continue; } UVCDevice device = new UVCDevice(usbDevice, UVCDeviceManager.this); device.addPreviewListener(mPreviewListener); pushDevice(device); notifyEventOnFound(device); } } public synchronized void stopScan() { if (mScanThread != null) { --mScanNum; if (mScanNum == 0) { mScanThread.interrupt(); mScanThread = null; } } } private boolean supportedAttachEvent() { return Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1; } public synchronized void stop() { if (!mIsStarted) { return; } stopScan(); mUSBMonitor.unregister(); mIsStarted = false; } public List<UVCDevice> getDeviceList() { return mAttachedDevices; } public UVCDevice getDevice(final String id) { synchronized (mAttachedDevices) { for (UVCDevice device : mAttachedDevices) { if (device.getId().equals(id)) { return device; } } } return null; } public USBMonitor getUSBMonitor() { return mUSBMonitor; } public interface DeviceListener { void onFound(UVCDevice device); } public interface ConnectionListener { void onConnect(UVCDevice device); void onConnectionFailed(UVCDevice device); void onDisconnect(UVCDevice device); } public interface DiscoveryListener { void onDiscovery(List<UVCDevice> devices); } public interface PreviewListener { void onFrame(UVCDevice device, byte[] frame, int frameFormat, int width, int height); } }