| 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);
|
| +}
|
|
|