summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-12 08:23:48 +0000
committeryhirano@chromium.org <yhirano@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-12 08:23:48 +0000
commit3e17c7e42f11398b1e0387bd551379b13b7b4538 (patch)
tree6e42087202a32b33a388b588ac43a5cfb2a1c0e3
parent5fb6f94f70432acc9b5c995e3cc6f1f85fc767bd (diff)
downloadchromium_src-3e17c7e42f11398b1e0387bd551379b13b7b4538.zip
chromium_src-3e17c7e42f11398b1e0387bd551379b13b7b4538.tar.gz
chromium_src-3e17c7e42f11398b1e0387bd551379b13b7b4538.tar.bz2
Implement WebMIDI for android, output side.
Implement WebMIDI for android. This CL implements the output function only. BUG=303596 R=toyoshim@chromium.org Review URL: https://codereview.chromium.org/133163006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@250648 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--media/base/android/java/src/org/chromium/media/UsbMidiDeviceAndroid.java126
-rw-r--r--media/base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java164
-rw-r--r--media/base/android/media_jni_registrar.cc6
-rw-r--r--media/media.gyp7
-rw-r--r--media/midi/midi_manager.cc3
-rw-r--r--media/midi/midi_manager_android.cc16
-rw-r--r--media/midi/usb_midi_device.h1
-rw-r--r--media/midi/usb_midi_device_android.cc49
-rw-r--r--media/midi/usb_midi_device_android.h45
-rw-r--r--media/midi/usb_midi_device_factory_android.cc78
-rw-r--r--media/midi/usb_midi_device_factory_android.h48
11 files changed, 542 insertions, 1 deletions
diff --git a/media/base/android/java/src/org/chromium/media/UsbMidiDeviceAndroid.java b/media/base/android/java/src/org/chromium/media/UsbMidiDeviceAndroid.java
new file mode 100644
index 0000000..84c35ea
--- /dev/null
+++ b/media/base/android/java/src/org/chromium/media/UsbMidiDeviceAndroid.java
@@ -0,0 +1,126 @@
+// 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.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbEndpoint;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbRequest;
+
+import org.chromium.base.CalledByNative;
+import org.chromium.base.JNINamespace;
+
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Owned by its native counterpart declared in usb_midi_device_android.h.
+ * Refer to that class for general comments.
+ */
+@JNINamespace("media")
+class UsbMidiDeviceAndroid {
+ /**
+ * A connection handle for this device.
+ */
+ private UsbDeviceConnection mConnection;
+
+ /**
+ * A map from endpoint number to UsbEndpoint.
+ */
+ private final Map<Integer, UsbEndpoint> mEndpointMap;
+
+ /**
+ * A map from UsbEndpoint to UsbRequest associated to it.
+ */
+ private final Map<UsbEndpoint, UsbRequest> mRequestMap;
+
+ /**
+ * Audio interface subclass code for MIDI.
+ */
+ static final int MIDI_SUBCLASS = 3;
+
+ /**
+ * Constructs a UsbMidiDeviceAndroid.
+ * @param manager
+ * @param device The USB device which this object is assocated with.
+ */
+ UsbMidiDeviceAndroid(UsbManager manager, UsbDevice device) {
+ mConnection = manager.openDevice(device);
+ mEndpointMap = new HashMap<Integer, UsbEndpoint>();
+ mRequestMap = new HashMap<UsbEndpoint, UsbRequest>();
+
+ for (int i = 0; i < device.getInterfaceCount(); ++i) {
+ UsbInterface iface = device.getInterface(i);
+ if (iface.getInterfaceClass() != UsbConstants.USB_CLASS_AUDIO ||
+ iface.getInterfaceSubclass() != MIDI_SUBCLASS) {
+ continue;
+ }
+ mConnection.claimInterface(iface, true);
+ for (int j = 0; j < iface.getEndpointCount(); ++j) {
+ UsbEndpoint endpoint = iface.getEndpoint(j);
+ if (endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
+ mEndpointMap.put(endpoint.getEndpointNumber(), endpoint);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sends a USB-MIDI data to the device.
+ * @param endpointNumber The endpoint number of the destination endpoint.
+ * @param bs The data to be sent.
+ */
+ @CalledByNative
+ void send(int endpointNumber, byte[] bs) {
+ if (mConnection == null) {
+ return;
+ }
+ if (!mEndpointMap.containsKey(endpointNumber)) {
+ return;
+ }
+ UsbEndpoint endpoint = mEndpointMap.get(endpointNumber);
+ UsbRequest request;
+ if (mRequestMap.containsKey(endpoint)) {
+ request = mRequestMap.get(endpoint);
+ } else {
+ request = new UsbRequest();
+ request.initialize(mConnection, endpoint);
+ mRequestMap.put(endpoint, request);
+ }
+ request.queue(ByteBuffer.wrap(bs), bs.length);
+ }
+
+ /**
+ * Returns the descriptors bytes of this device.
+ * @return The descriptors bytes of this device.
+ */
+ @CalledByNative
+ byte[] getDescriptors() {
+ if (mConnection == null) {
+ return new byte[0];
+ }
+ return mConnection.getRawDescriptors();
+ }
+
+ /**
+ * Closes the device connection.
+ */
+ @CalledByNative
+ void close() {
+ mEndpointMap.clear();
+ for (UsbRequest request : mRequestMap.values()) {
+ request.close();
+ }
+ mRequestMap.clear();
+ if (mConnection != null) {
+ mConnection.close();
+ mConnection = null;
+ }
+ }
+}
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 0000000..7c0953f
--- /dev/null
+++ b/media/base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java
@@ -0,0 +1,164 @@
+// 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);
+}
diff --git a/media/base/android/media_jni_registrar.cc b/media/base/android/media_jni_registrar.cc
index d26c71e..fe0ff67 100644
--- a/media/base/android/media_jni_registrar.cc
+++ b/media/base/android/media_jni_registrar.cc
@@ -15,6 +15,8 @@
#include "media/base/android/media_player_bridge.h"
#include "media/base/android/media_player_listener.h"
#include "media/base/android/webaudio_media_codec_bridge.h"
+#include "media/midi/usb_midi_device_android.h"
+#include "media/midi/usb_midi_device_factory_android.h"
#include "media/video/capture/android/video_capture_device_android.h"
namespace media {
@@ -32,6 +34,10 @@ static base::android::RegistrationMethod kMediaRegisteredMethods[] = {
MediaPlayerBridge::RegisterMediaPlayerBridge },
{ "MediaPlayerListener",
MediaPlayerListener::RegisterMediaPlayerListener },
+ { "UsbMidiDevice",
+ UsbMidiDeviceAndroid::RegisterUsbMidiDevice },
+ { "UsbMidiDeviceFactory",
+ UsbMidiDeviceFactoryAndroid::RegisterUsbMidiDeviceFactory },
{ "VideoCaptureDevice",
VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice },
{ "WebAudioMediaCodecBridge",
diff --git a/media/media.gyp b/media/media.gyp
index baeb378..2fc8898 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -417,6 +417,7 @@
'midi/midi_manager.h',
'midi/midi_manager_alsa.cc',
'midi/midi_manager_alsa.h',
+ 'midi/midi_manager_android.cc',
'midi/midi_manager_mac.cc',
'midi/midi_manager_mac.h',
'midi/midi_manager_usb.cc',
@@ -432,6 +433,10 @@
'midi/usb_midi_descriptor_parser.cc',
'midi/usb_midi_descriptor_parser.h',
'midi/usb_midi_device.h',
+ 'midi/usb_midi_device_android.cc',
+ 'midi/usb_midi_device_android.h',
+ 'midi/usb_midi_device_factory_android.cc',
+ 'midi/usb_midi_device_factory_android.h',
'midi/usb_midi_input_stream.cc',
'midi/usb_midi_input_stream.h',
'midi/usb_midi_jack.h',
@@ -1516,6 +1521,8 @@
'base/android/java/src/org/chromium/media/MediaDrmBridge.java',
'base/android/java/src/org/chromium/media/MediaPlayerBridge.java',
'base/android/java/src/org/chromium/media/MediaPlayerListener.java',
+ 'base/android/java/src/org/chromium/media/UsbMidiDeviceAndroid.java',
+ 'base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java',
'base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java',
],
'variables': {
diff --git a/media/midi/midi_manager.cc b/media/midi/midi_manager.cc
index 49a51b3..4447a05 100644
--- a/media/midi/midi_manager.cc
+++ b/media/midi/midi_manager.cc
@@ -9,7 +9,8 @@
namespace media {
-#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(USE_ALSA)
+#if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(USE_ALSA) && \
+ !defined(OS_ANDROID)
// TODO(toyoshim): implement MidiManager for other platforms.
MidiManager* MidiManager::Create() {
return new MidiManager;
diff --git a/media/midi/midi_manager_android.cc b/media/midi/midi_manager_android.cc
new file mode 100644
index 0000000..b8385fb
--- /dev/null
+++ b/media/midi/midi_manager_android.cc
@@ -0,0 +1,16 @@
+// 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.
+
+#include "base/memory/scoped_ptr.h"
+#include "media/midi/midi_manager_usb.h"
+#include "media/midi/usb_midi_device_factory_android.h"
+
+namespace media {
+
+MidiManager* MidiManager::Create() {
+ return new MidiManagerUsb(
+ scoped_ptr<UsbMidiDevice::Factory>(new UsbMidiDeviceFactoryAndroid));
+}
+
+} // namespace media
diff --git a/media/midi/usb_midi_device.h b/media/midi/usb_midi_device.h
index 59f129d..28b32bc 100644
--- a/media/midi/usb_midi_device.h
+++ b/media/midi/usb_midi_device.h
@@ -51,6 +51,7 @@ class MEDIA_EXPORT UsbMidiDevice {
// Otherwise |callback| will be called with |false| and empty devices.
// When this factory is destroyed during the operation, the operation
// will be canceled silently (i.e. |callback| will not be called).
+ // This function can be called at most once per instance.
virtual void EnumerateDevices(UsbMidiDeviceDelegate* delegate,
Callback callback) = 0;
};
diff --git a/media/midi/usb_midi_device_android.cc b/media/midi/usb_midi_device_android.cc
new file mode 100644
index 0000000..f3ccc43
--- /dev/null
+++ b/media/midi/usb_midi_device_android.cc
@@ -0,0 +1,49 @@
+// 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.
+
+#include "media/midi/usb_midi_device_android.h"
+
+#include <jni.h>
+#include <vector>
+
+#include "base/android/jni_array.h"
+#include "jni/UsbMidiDeviceAndroid_jni.h"
+
+namespace media {
+
+UsbMidiDeviceAndroid::UsbMidiDeviceAndroid(ObjectRef raw_device,
+ UsbMidiDeviceDelegate* delegate)
+ : raw_device_(raw_device), delegate_(delegate) {}
+
+UsbMidiDeviceAndroid::~UsbMidiDeviceAndroid() {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_UsbMidiDeviceAndroid_close(env, raw_device_.obj());
+}
+
+std::vector<uint8> UsbMidiDeviceAndroid::GetDescriptor() {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jbyteArray> descriptors =
+ Java_UsbMidiDeviceAndroid_getDescriptors(env, raw_device_.obj());
+
+ std::vector<uint8> ret;
+ base::android::JavaByteArrayToByteVector(env, descriptors.obj(), &ret);
+ return ret;
+}
+
+void UsbMidiDeviceAndroid::Send(int endpoint_number,
+ const std::vector<uint8>& data) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ const uint8* head = data.size() ? &data[0] : NULL;
+ ScopedJavaLocalRef<jbyteArray> data_to_pass =
+ base::android::ToJavaByteArray(env, head, data.size());
+
+ Java_UsbMidiDeviceAndroid_send(
+ env, raw_device_.obj(), endpoint_number, data_to_pass.obj());
+}
+
+bool UsbMidiDeviceAndroid::RegisterUsbMidiDevice(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace media
diff --git a/media/midi/usb_midi_device_android.h b/media/midi/usb_midi_device_android.h
new file mode 100644
index 0000000..ed0a414
--- /dev/null
+++ b/media/midi/usb_midi_device_android.h
@@ -0,0 +1,45 @@
+// 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.
+
+#ifndef MEDIA_MIDI_USB_MIDI_DEVICE_ANDROID_H_
+#define MEDIA_MIDI_USB_MIDI_DEVICE_ANDROID_H_
+
+#include <jni.h>
+#include <vector>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "media/base/media_export.h"
+#include "media/midi/usb_midi_device.h"
+
+namespace media {
+
+class MEDIA_EXPORT UsbMidiDeviceAndroid : public UsbMidiDevice {
+ public:
+ typedef base::android::ScopedJavaLocalRef<jobject> ObjectRef;
+
+ static scoped_ptr<Factory> CreateFactory();
+
+ UsbMidiDeviceAndroid(ObjectRef raw_device, UsbMidiDeviceDelegate* delegate);
+ virtual ~UsbMidiDeviceAndroid();
+
+ // UsbMidiDevice implementation.
+ virtual std::vector<uint8> GetDescriptor() OVERRIDE;
+ virtual void Send(int endpoint_number,
+ const std::vector<uint8>& data) OVERRIDE;
+
+ static bool RegisterUsbMidiDevice(JNIEnv* env);
+
+ private:
+ // The actual device object.
+ base::android::ScopedJavaGlobalRef<jobject> raw_device_;
+ UsbMidiDeviceDelegate* delegate_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(UsbMidiDeviceAndroid);
+};
+
+} // namespace media
+
+#endif // MEDIA_MIDI_USB_MIDI_DEVICE_ANDROID_H_
diff --git a/media/midi/usb_midi_device_factory_android.cc b/media/midi/usb_midi_device_factory_android.cc
new file mode 100644
index 0000000..11385c26
--- /dev/null
+++ b/media/midi/usb_midi_device_factory_android.cc
@@ -0,0 +1,78 @@
+// 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.
+
+#include "media/midi/usb_midi_device_factory_android.h"
+
+#include <jni.h>
+#include <vector>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
+#include "base/containers/hash_tables.h"
+#include "base/lazy_instance.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/synchronization/lock.h"
+#include "jni/UsbMidiDeviceFactoryAndroid_jni.h"
+#include "media/midi/usb_midi_device_android.h"
+
+namespace media {
+
+namespace {
+
+typedef UsbMidiDevice::Factory::Callback Callback;
+
+} // namespace
+
+UsbMidiDeviceFactoryAndroid::UsbMidiDeviceFactoryAndroid() : delegate_(NULL) {}
+
+UsbMidiDeviceFactoryAndroid::~UsbMidiDeviceFactoryAndroid() {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ if (!raw_factory_.is_null())
+ Java_UsbMidiDeviceFactoryAndroid_close(env, raw_factory_.obj());
+}
+
+void UsbMidiDeviceFactoryAndroid::EnumerateDevices(
+ UsbMidiDeviceDelegate* delegate,
+ Callback callback) {
+ DCHECK(!delegate_);
+ JNIEnv* env = base::android::AttachCurrentThread();
+ uintptr_t pointer = reinterpret_cast<uintptr_t>(this);
+ raw_factory_.Reset(Java_UsbMidiDeviceFactoryAndroid_create(env, pointer));
+
+ delegate_ = delegate;
+ callback_ = callback;
+
+ if (Java_UsbMidiDeviceFactoryAndroid_enumerateDevices(
+ env, raw_factory_.obj(), base::android::GetApplicationContext())) {
+ // Asynchronous operation.
+ return;
+ }
+ // No devices are found.
+ ScopedVector<UsbMidiDevice> devices;
+ callback.Run(true, &devices);
+}
+
+// Called from the Java world.
+void UsbMidiDeviceFactoryAndroid::OnUsbMidiDeviceRequestDone(
+ JNIEnv* env,
+ jobject caller,
+ jobjectArray devices) {
+ size_t size = env->GetArrayLength(devices);
+ ScopedVector<UsbMidiDevice> devices_to_pass;
+ for (size_t i = 0; i < size; ++i) {
+ UsbMidiDeviceAndroid::ObjectRef raw_device(
+ env, env->GetObjectArrayElement(devices, i));
+ devices_to_pass.push_back(new UsbMidiDeviceAndroid(raw_device, delegate_));
+ }
+
+ callback_.Run(true, &devices_to_pass);
+}
+
+bool UsbMidiDeviceFactoryAndroid::RegisterUsbMidiDeviceFactory(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace media
diff --git a/media/midi/usb_midi_device_factory_android.h b/media/midi/usb_midi_device_factory_android.h
new file mode 100644
index 0000000..63a9eb3
--- /dev/null
+++ b/media/midi/usb_midi_device_factory_android.h
@@ -0,0 +1,48 @@
+// 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.
+
+#ifndef MEDIA_MIDI_USB_MIDI_DEVICE_FACTORY_ANDROID_H_
+#define MEDIA_MIDI_USB_MIDI_DEVICE_FACTORY_ANDROID_H_
+
+#include <jni.h>
+#include <vector>
+
+#include "base/android/scoped_java_ref.h"
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "media/base/media_export.h"
+#include "media/midi/usb_midi_device.h"
+
+namespace media {
+
+// This class enumerates UsbMidiDevices.
+class MEDIA_EXPORT UsbMidiDeviceFactoryAndroid : public UsbMidiDevice::Factory {
+ public:
+ UsbMidiDeviceFactoryAndroid();
+ virtual ~UsbMidiDeviceFactoryAndroid();
+
+ // UsbMidiDevice::Factory implementation.
+ virtual void EnumerateDevices(UsbMidiDeviceDelegate* delegate,
+ Callback callback) OVERRIDE;
+
+ void OnUsbMidiDeviceRequestDone(JNIEnv* env,
+ jobject caller,
+ jobjectArray devices);
+
+ static bool RegisterUsbMidiDeviceFactory(JNIEnv* env);
+
+ private:
+ base::android::ScopedJavaGlobalRef<jobject> raw_factory_;
+ // Not owned.
+ UsbMidiDeviceDelegate* delegate_;
+ Callback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbMidiDeviceFactoryAndroid);
+};
+
+} // namespace media
+
+#endif // MEDIA_MIDI_USB_MIDI_DEVICE_FACTORY_ANDROID_H_