// 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 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 permission requests.
*/
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;
/**
* The identifier of this factory.
*/
private long mNativePointer;
private static final String ACTION_USB_PERMISSION =
"org.chromium.media.USB_PERMISSION";
/**
* Constructs a UsbMidiDeviceAndroid.
* @param natviePointer The native pointer to which the created factory is associated.
*/
UsbMidiDeviceFactoryAndroid(long nativePointer) {
mNativePointer = nativePointer;
}
/**
* Constructs a UsbMidiDeviceAndroid.
* @param nativePointer The native pointer to which the created factory is associated.
*/
@CalledByNative
static UsbMidiDeviceFactoryAndroid create(long nativePointer) {
return new UsbMidiDeviceFactoryAndroid(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 onRequestDone will be called.
*
* If there are no USB-MIDI interfaces, this function returns false.
* @return true if some permission requests are in progress.
*/
@CalledByNative
boolean enumerateDevices(Context context) {
mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
Map<String, UsbDevice> devices = mUsbManager.getDeviceList();
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, new Intent(ACTION_USB_PERMISSION), 0);
mRequestedDevices = new HashSet<UsbDevice>();
for (UsbDevice device : devices.values()) {
boolean found = false;
for (int i = 0; i < device.getInterfaceCount() && !found; ++i) {
UsbInterface iface = device.getInterface(i);
if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO &&
iface.getInterfaceSubclass() == UsbMidiDeviceAndroid.MIDI_SUBCLASS) {
found = true;
}
}
if (found) {
mUsbManager.requestPermission(device, pendingIntent);
mRequestedDevices.add(device);
}
}
if (mRequestedDevices.isEmpty()) {
// No USB-MIDI devices are found.
return false;
}
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (ACTION_USB_PERMISSION.equals(intent.getAction())) {
onRequestDone(context, intent);
}
}
};
context.registerReceiver(mReceiver, filter);
return true;
}
/**
* Called when the user accepts or rejects the permission request requested by
* EnumerateDevices.
* If all permission requests are responded, this function calls
* nativeOnUsbMidiDeviceRequestDone with the accessible USB-MIDI devices.
*/
private void onRequestDone(Context context, Intent intent) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (!mRequestedDevices.contains(device)) {
// We are not interested in the device.
return;
}
mRequestedDevices.remove(device);
if (!intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
// The request was rejected.
device = null;
}
if (device != null) {
// Now we can add the device.
mDevices.add(new UsbMidiDeviceAndroid(mUsbManager, device));
}
if (mRequestedDevices.isEmpty()) {
// All requests are done.
context.unregisterReceiver(mReceiver);
if (mNativePointer != 0) {
nativeOnUsbMidiDeviceRequestDone(mNativePointer, mDevices.toArray());
}
}
}
/**
* Disconnects the native object.
*/
@CalledByNative
void close() {
mNativePointer = 0;
}
private static native void nativeOnUsbMidiDeviceRequestDone(
long nativeUsbMidiDeviceFactoryAndroid, Object[] devices);
}