15d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
25d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// found in the LICENSE file.
45d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
55d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)package org.chromium.media;
65d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import android.annotation.TargetApi;
85d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.hardware.usb.UsbConstants;
95d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.hardware.usb.UsbDevice;
105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.hardware.usb.UsbDeviceConnection;
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.hardware.usb.UsbEndpoint;
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.hardware.usb.UsbInterface;
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.hardware.usb.UsbManager;
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.hardware.usb.UsbRequest;
15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import android.os.Build;
16effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochimport android.os.Handler;
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.util.SparseArray;
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import org.chromium.base.CalledByNative;
205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import org.chromium.base.JNINamespace;
215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.nio.ByteBuffer;
235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.util.HashMap;
245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.util.Map;
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)/**
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Owned by its native counterpart declared in usb_midi_device_android.h.
285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) * Refer to that class for general comments.
295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) */
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)@JNINamespace("media")
315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class UsbMidiDeviceAndroid {
325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * A connection handle for this device.
345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
35effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private final UsbDeviceConnection mConnection;
365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * A map from endpoint number to UsbEndpoint.
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private final SparseArray<UsbEndpoint> mEndpointMap;
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * A map from UsbEndpoint to UsbRequest associated to it.
445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private final Map<UsbEndpoint, UsbRequest> mRequestMap;
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
48effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * The handler used for posting events on the main thread.
49effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     */
50effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private final Handler mHandler;
51effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
52effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /**
53effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * True if this device is closed.
54effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     */
55effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private boolean mIsClosed;
56effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
57effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /**
58010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)     * True if there is a thread processing input data.
59010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)     */
60010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    private boolean mHasInputThread;
61010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
62010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    /**
63effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * The identifier of this device.
64effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     */
65effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private long mNativePointer;
66effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
67effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /**
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Audio interface subclass code for MIDI.
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    static final int MIDI_SUBCLASS = 3;
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Constructs a UsbMidiDeviceAndroid.
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @param manager
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @param device The USB device which this object is assocated with.
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    UsbMidiDeviceAndroid(UsbManager manager, UsbDevice device) {
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mConnection = manager.openDevice(device);
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        mEndpointMap = new SparseArray<UsbEndpoint>();
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mRequestMap = new HashMap<UsbEndpoint, UsbRequest>();
81effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        mHandler = new Handler();
82effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        mIsClosed = false;
83010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        mHasInputThread = false;
84effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        mNativePointer = 0;
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        for (int i = 0; i < device.getInterfaceCount(); ++i) {
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            UsbInterface iface = device.getInterface(i);
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            if (iface.getInterfaceClass() != UsbConstants.USB_CLASS_AUDIO ||
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                iface.getInterfaceSubclass() != MIDI_SUBCLASS) {
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                continue;
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            }
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            mConnection.claimInterface(iface, true);
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            for (int j = 0; j < iface.getEndpointCount(); ++j) {
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                UsbEndpoint endpoint = iface.getEndpoint(j);
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                if (endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                    mEndpointMap.put(endpoint.getEndpointNumber(), endpoint);
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                }
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            }
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
100effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        // Start listening for input endpoints.
101effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        // This function will create and run a thread if there is USB-MIDI endpoints in the
102effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        // device. Note that because UsbMidiDevice is shared among all tabs and the thread
103effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        // will be terminated when the device is disconnected, at most one thread can be created
104effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        // for each connected USB-MIDI device.
105effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        startListen(device);
106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
107effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
108effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /**
109effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * Starts listening for input endpoints.
110effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     */
111effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private void startListen(final UsbDevice device) {
112effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        final Map<UsbEndpoint, ByteBuffer> bufferForEndpoints =
113effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            new HashMap<UsbEndpoint, ByteBuffer>();
114effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
115effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        for (int i = 0; i < device.getInterfaceCount(); ++i) {
116effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            UsbInterface iface = device.getInterface(i);
117effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            if (iface.getInterfaceClass() != UsbConstants.USB_CLASS_AUDIO ||
118effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                iface.getInterfaceSubclass() != MIDI_SUBCLASS) {
119effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                continue;
120effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            }
121effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            for (int j = 0; j < iface.getEndpointCount(); ++j) {
122effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                UsbEndpoint endpoint = iface.getEndpoint(j);
123effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                if (endpoint.getDirection() == UsbConstants.USB_DIR_IN) {
124effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    ByteBuffer buffer = ByteBuffer.allocate(endpoint.getMaxPacketSize());
125effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    UsbRequest request = new UsbRequest();
126effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    request.initialize(mConnection, endpoint);
127effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    request.queue(buffer, buffer.remaining());
128effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    bufferForEndpoints.put(endpoint, buffer);
129effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                }
130effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            }
131effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        }
132effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        if (bufferForEndpoints.isEmpty()) {
133effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            return;
134effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        }
135010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        mHasInputThread = true;
136effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        // bufferForEndpoints must not be accessed hereafter on this thread.
137effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        new Thread() {
138effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            public void run() {
139effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                while (true) {
140effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    UsbRequest request = mConnection.requestWait();
141effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    if (request == null) {
142effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                        // When the device is closed requestWait will fail.
143effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                        break;
144effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    }
145effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    UsbEndpoint endpoint = request.getEndpoint();
146effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    if (endpoint.getDirection() != UsbConstants.USB_DIR_IN) {
147effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                        continue;
148effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    }
149effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    ByteBuffer buffer = bufferForEndpoints.get(endpoint);
150effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    int length = getInputDataLength(buffer);
151effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    if (length > 0) {
152effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                        buffer.rewind();
153effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                        final byte[] bs = new byte[length];
154effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                        buffer.get(bs, 0, length);
155effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                        postOnDataEvent(endpoint.getEndpointNumber(), bs);
156effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    }
157effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    buffer.rewind();
158effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    request.queue(buffer, buffer.capacity());
159effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                }
160effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            }
161effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        }.start();
162effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
163effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
164effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /**
165effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * Posts a data input event to the main thread.
166effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     */
167effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private void postOnDataEvent(final int endpointNumber, final byte[] bs) {
168effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        mHandler.post(new Runnable() {
169effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                public void run() {
170effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    if (mIsClosed) {
171effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                        return;
172effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    }
173effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                    nativeOnData(mNativePointer, endpointNumber, bs);
174effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                }
175effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            });
176effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
177effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
178effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /**
179effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * Register the own native pointer.
180effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     */
181effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    @CalledByNative
182effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    void registerSelf(long nativePointer) {
183effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        mNativePointer = nativePointer;
1845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
1875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Sends a USB-MIDI data to the device.
1885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @param endpointNumber The endpoint number of the destination endpoint.
1895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @param bs The data to be sent.
1905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
1925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @CalledByNative
1935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    void send(int endpointNumber, byte[] bs) {
194effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        if (mIsClosed) {
1955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            return;
1965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        UsbEndpoint endpoint = mEndpointMap.get(endpointNumber);
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (endpoint == null) {
1995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            return;
2005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
201010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        if (shouldUseBulkTransfer()) {
202010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            // We use bulkTransfer instead of UsbRequest.queue because queueing
203010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            // a UsbRequest is currently not thread safe.
204010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            // Note that this is not exactly correct because we don't care
205010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            // about the transfer attribute (bmAttribute) of the endpoint.
206010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            // See also:
207010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            //  http://stackoverflow.com/questions/9644415/
208010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            //  https://code.google.com/p/android/issues/detail?id=59467
209010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            //
210010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            // TODO(yhirano): Delete this block once the problem is fixed.
2111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            final int timeout = 100;
2121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            mConnection.bulkTransfer(endpoint, bs, 0, bs.length, timeout);
213010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        } else {
214010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            UsbRequest request = mRequestMap.get(endpoint);
215010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            if (request == null) {
216010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                request = new UsbRequest();
217010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                request.initialize(mConnection, endpoint);
218010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)                mRequestMap.put(endpoint, request);
219010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            }
220010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)            request.queue(ByteBuffer.wrap(bs), bs.length);
2215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
222010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    }
223010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)
224010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    /**
225010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)     * Returns true if |bulkTransfer| should be used in |send|.
226010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)     * See comments in |send|.
227010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)     */
228010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    private boolean shouldUseBulkTransfer() {
229010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)        return mHasInputThread;
2305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
2315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Returns the descriptors bytes of this device.
2345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * @return The descriptors bytes of this device.
2355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
2365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @CalledByNative
2375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    byte[] getDescriptors() {
2385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (mConnection == null) {
2395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            return new byte[0];
2405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
2415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        return mConnection.getRawDescriptors();
2425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
2435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
2445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
2455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Closes the device connection.
2465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
2475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @CalledByNative
2485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    void close() {
2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mEndpointMap.clear();
2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        for (UsbRequest request : mRequestMap.values()) {
2515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            request.close();
2525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
2535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mRequestMap.clear();
254effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        mConnection.close();
255effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        mNativePointer = 0;
256effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        mIsClosed = true;
257effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
258effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
259effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /**
260effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * Returns the length of a USB-MIDI input.
261effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * Since the Android API doesn't provide us the length,
262effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * we calculate it manually.
263effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     */
264effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private static int getInputDataLength(ByteBuffer buffer) {
265effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        int position = buffer.position();
266effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        // We assume that the data length is always divisable by 4.
267effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        for (int i = 0; i < position; i += 4) {
268effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            // Since Code Index Number 0 is reserved, it is not a valid USB-MIDI data.
269effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            if (buffer.get(i) == 0) {
270effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                return i;
271effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            }
2725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
273effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        return position;
2745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
275effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
276effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private static native void nativeOnData(long nativeUsbMidiDeviceAndroid,
277effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                                            int endpointNumber,
278effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                                            byte[] data);
2795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
280