Index: media/base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java |
diff --git a/media/base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java b/media/base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..814bb17c46ea1542c21c65978ab064363507f83b |
--- /dev/null |
+++ b/media/base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java |
@@ -0,0 +1,264 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.media; |
+ |
+import android.app.PendingIntent; |
+import android.content.BroadcastReceiver; |
+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.UsbInterface; |
+import android.hardware.usb.UsbManager; |
+import android.os.Parcelable; |
+ |
+import org.chromium.base.CalledByNative; |
+import org.chromium.base.JNINamespace; |
+ |
+import java.util.ArrayList; |
+import java.util.HashSet; |
+import java.util.List; |
+import java.util.Map; |
+import java.util.Set; |
+ |
+/** |
+ * Owned by its native counterpart declared in |
+ * usb_midi_device_factory_android.h. Refer to that class for general comments. |
+ */ |
+@JNINamespace("media") |
+class UsbMidiDeviceFactoryAndroid { |
+ /** |
+ * The UsbManager of this system. |
+ */ |
+ private UsbManager mUsbManager; |
+ |
+ /** |
+ * A BroadcastReceiver for USB device events. |
+ */ |
+ private BroadcastReceiver mReceiver; |
+ |
+ /** |
+ * Accessible USB-MIDI devices got so far. |
+ */ |
+ private final List<UsbMidiDeviceAndroid> mDevices = new ArrayList<UsbMidiDeviceAndroid>(); |
+ |
+ /** |
+ * Devices whose access permission requested but not resolved so far. |
+ */ |
+ private Set<UsbDevice> mRequestedDevices; |
+ |
+ /** |
+ * True when the enumeration is in progress. |
+ */ |
+ private boolean mIsEnumeratingDevices; |
+ |
+ /** |
+ * The identifier of this factory. |
+ */ |
+ private long mNativePointer; |
+ |
+ private static final String ACTION_USB_PERMISSION = |
+ "org.chromium.media.USB_PERMISSION"; |
+ |
+ /** |
+ * Constructs a UsbMidiDeviceAndroid. |
+ * @param context |
+ * @param nativePointer The native pointer to which the created factory is associated. |
+ */ |
+ UsbMidiDeviceFactoryAndroid(Context context, long nativePointer) { |
+ mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); |
+ mNativePointer = nativePointer; |
+ mReceiver = new BroadcastReceiver() { |
+ @Override |
+ public void onReceive(Context context, Intent intent) { |
+ Parcelable extra = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); |
+ if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { |
+ requestDevicePermissionIfNecessary(context, (UsbDevice) extra); |
+ } |
+ if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) { |
+ onUsbDeviceDetached((UsbDevice) extra); |
+ } |
+ if (ACTION_USB_PERMISSION.equals(intent.getAction())) { |
+ onUsbDevicePermissionRequestDone(context, intent); |
+ } |
+ } |
+ }; |
+ IntentFilter filter = new IntentFilter(); |
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); |
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); |
+ filter.addAction(ACTION_USB_PERMISSION); |
+ context.registerReceiver(mReceiver, filter); |
+ mRequestedDevices = new HashSet<UsbDevice>(); |
+ } |
+ |
+ /** |
+ * Constructs a UsbMidiDeviceAndroid. |
+ * @param context |
+ * @param nativePointer The native pointer to which the created factory is associated. |
+ */ |
+ @CalledByNative |
+ static UsbMidiDeviceFactoryAndroid create(Context context, long nativePointer) { |
+ return new UsbMidiDeviceFactoryAndroid(context, nativePointer); |
+ } |
+ |
+ /** |
+ * Enumerates USB-MIDI devices. |
+ * If there are devices having USB-MIDI interfaces, this function requests permission for |
+ * accessing the device to the user. |
+ * When the permission request is accepted or rejected, nativeOnUsbMidiDeviceRequestDone |
+ * will be called. |
+ * |
+ * If there are no USB-MIDI interfaces, this function returns false. |
+ * @param context |
+ * @return true if some permission requests are in progress. |
+ */ |
+ @CalledByNative |
+ boolean enumerateDevices(Context context) { |
+ assert !mIsEnumeratingDevices; |
+ mIsEnumeratingDevices = true; |
+ Map<String, UsbDevice> devices = mUsbManager.getDeviceList(); |
+ if (devices.isEmpty()) { |
+ // No USB-MIDI devices are found. |
+ mIsEnumeratingDevices = false; |
+ return false; |
+ } |
+ for (UsbDevice device: devices.values()) { |
+ requestDevicePermissionIfNecessary(context, device); |
+ } |
+ return true; |
+ } |
+ |
+ /** |
+ * Request a device access permission if there is a MIDI interface in the device. |
+ * |
+ * @param context |
+ * @param device a USB device |
+ */ |
+ private void requestDevicePermissionIfNecessary(Context context, UsbDevice device) { |
+ for (UsbDevice d: mRequestedDevices) { |
+ if (d.getDeviceId() == device.getDeviceId()) { |
+ // It is already requested. |
+ return; |
+ } |
+ } |
+ |
+ for (int i = 0; i < device.getInterfaceCount(); ++i) { |
+ UsbInterface iface = device.getInterface(i); |
+ if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO |
+ && iface.getInterfaceSubclass() == UsbMidiDeviceAndroid.MIDI_SUBCLASS) { |
+ // There is at least one interface supporting MIDI. |
+ mUsbManager.requestPermission(device, PendingIntent.getBroadcast( |
+ context, 0, new Intent(ACTION_USB_PERMISSION), 0)); |
+ mRequestedDevices.add(device); |
+ break; |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Called when a USB device is detached. |
+ * |
+ * @param device a USB device |
+ */ |
+ private void onUsbDeviceDetached(UsbDevice device) { |
+ for (UsbDevice usbDevice: mRequestedDevices) { |
+ if (usbDevice.getDeviceId() == device.getDeviceId()) { |
+ mRequestedDevices.remove(usbDevice); |
+ break; |
+ } |
+ } |
+ for (int i = 0; i < mDevices.size(); ++i) { |
+ UsbMidiDeviceAndroid midiDevice = mDevices.get(i); |
+ if (midiDevice.isClosed()) { |
+ // Once a device is disconnected, the system may reassign its device ID to |
+ // another device. So we should ignore disconnected ones. |
+ continue; |
+ } |
+ if (midiDevice.getUsbDevice().getDeviceId() == device.getDeviceId()) { |
+ midiDevice.close(); |
+ if (mIsEnumeratingDevices) { |
+ // In this case, we don't have to keep mDevices sync with the devices list |
+ // in MidiManagerUsb. |
+ mDevices.remove(i); |
+ return; |
+ } |
+ if (mNativePointer != 0) { |
+ nativeOnUsbMidiDeviceDetached(mNativePointer, i); |
+ } |
+ return; |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Called when the user accepts or rejects the permission request requested by |
+ * EnumerateDevices. |
+ * |
+ * @param context |
+ * @param intent |
+ */ |
+ private void onUsbDevicePermissionRequestDone(Context context, Intent intent) { |
+ UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); |
+ UsbMidiDeviceAndroid midiDevice = null; |
+ if (mRequestedDevices.contains(device)) { |
+ mRequestedDevices.remove(device); |
+ if (!intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { |
+ // The request was rejected. |
+ device = null; |
+ } |
+ } else { |
+ device = null; |
+ } |
+ |
+ if (device != null) { |
+ for (UsbMidiDeviceAndroid registered: mDevices) { |
+ if (!registered.isClosed() |
+ && registered.getUsbDevice().getDeviceId() == device.getDeviceId()) { |
+ // The device is already registered. |
+ device = null; |
+ break; |
+ } |
+ } |
+ } |
+ |
+ if (device != null) { |
+ // Now we can add the device. |
+ midiDevice = new UsbMidiDeviceAndroid(mUsbManager, device); |
+ mDevices.add(midiDevice); |
+ } |
+ |
+ if (!mRequestedDevices.isEmpty()) { |
+ return; |
+ } |
+ if (mNativePointer == 0) { |
+ return; |
+ } |
+ |
+ if (mIsEnumeratingDevices) { |
+ nativeOnUsbMidiDeviceRequestDone(mNativePointer, mDevices.toArray()); |
+ mIsEnumeratingDevices = false; |
+ } else if (midiDevice != null) { |
+ nativeOnUsbMidiDeviceAttached(mNativePointer, midiDevice); |
+ } |
+ } |
+ |
+ /** |
+ * Disconnects the native object. |
+ * @param context |
+ */ |
+ @CalledByNative |
+ void close(Context context) { |
+ mNativePointer = 0; |
+ context.unregisterReceiver(mReceiver); |
+ } |
+ |
+ private static native void nativeOnUsbMidiDeviceRequestDone( |
+ long nativeUsbMidiDeviceFactoryAndroid, Object[] devices); |
+ private static native void nativeOnUsbMidiDeviceAttached( |
+ long nativeUsbMidiDeviceFactoryAndroid, Object device); |
+ private static native void nativeOnUsbMidiDeviceDetached( |
+ long nativeUsbMidiDeviceFactoryAndroid, int index); |
+} |