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