110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood/* 210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Copyright (C) 2014 The Android Open Source Project 310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * 410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Licensed under the Apache License, Version 2.0 (the "License"); 510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * you may not use this file except in compliance with the License. 610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * You may obtain a copy of the License at 710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * 810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * http://www.apache.org/licenses/LICENSE-2.0 910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * 1010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * Unless required by applicable law or agreed to in writing, software 1110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * distributed under the License is distributed on an "AS IS" BASIS, 1210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * See the License for the specific language governing permissions an 1410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood * limitations under the License. 1510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood */ 1610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 1710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodpackage com.android.server.usb; 1810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 1910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.content.Context; 20b673770f7172d4fca9bc05de1f36bc53e93eb247Mike Lockwoodimport android.media.midi.MidiDeviceInfo; 21b673770f7172d4fca9bc05de1f36bc53e93eb247Mike Lockwoodimport android.media.midi.MidiDeviceServer; 226d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwoodimport android.media.midi.MidiDeviceStatus; 23b673770f7172d4fca9bc05de1f36bc53e93eb247Mike Lockwoodimport android.media.midi.MidiManager; 24b673770f7172d4fca9bc05de1f36bc53e93eb247Mike Lockwoodimport android.media.midi.MidiReceiver; 25b673770f7172d4fca9bc05de1f36bc53e93eb247Mike Lockwoodimport android.media.midi.MidiSender; 2610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.os.Bundle; 2710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.system.ErrnoException; 2810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.system.Os; 2910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.system.OsConstants; 3010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.system.StructPollfd; 3110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.util.Log; 3210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 332776133be7ac60dc8d6aea5b12e35449ca331836Mike Lockwoodimport com.android.internal.midi.MidiEventScheduler; 342776133be7ac60dc8d6aea5b12e35449ca331836Mike Lockwoodimport com.android.internal.midi.MidiEventScheduler.MidiEvent; 352776133be7ac60dc8d6aea5b12e35449ca331836Mike Lockwood 3611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwoodimport libcore.io.IoUtils; 3711fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 3810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.Closeable; 3910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.FileDescriptor; 4010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.FileInputStream; 4110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.FileOutputStream; 4210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.IOException; 4310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 4410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodpublic final class UsbMidiDevice implements Closeable { 4510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static final String TAG = "UsbMidiDevice"; 4610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 476d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private final int mAlsaCard; 486d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private final int mAlsaDevice; 496d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private final int mSubdeviceCount; 506d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private final InputReceiverProxy[] mInputPortReceivers; 516d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 5211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood private MidiDeviceServer mServer; 5311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 54aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov // event schedulers for each input port of the physical device 556d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private MidiEventScheduler[] mEventSchedulers; 5610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5720821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood private static final int BUFFER_SIZE = 512; 5820821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood 596d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private FileDescriptor[] mFileDescriptors; 60b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood 6110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // for polling multiple FileDescriptors for MIDI events 626d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private StructPollfd[] mPollFDs; 63cb096273734d96f4676014fa9030b57ea48b58d8Mike Lockwood // streams for reading from ALSA driver 646d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private FileInputStream[] mInputStreams; 65cb096273734d96f4676014fa9030b57ea48b58d8Mike Lockwood // streams for writing to ALSA driver 666d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private FileOutputStream[] mOutputStreams; 6710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 686d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private final Object mLock = new Object(); 696d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private boolean mIsOpen; 706d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 716d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // pipe file descriptor for signalling input thread to exit 726d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // only accessed from JNI code 736d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private int mPipeFD = -1; 746d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 756d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() { 766d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 776d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood @Override 786d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood public void onDeviceStatusChanged(MidiDeviceServer server, MidiDeviceStatus status) { 796d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood MidiDeviceInfo deviceInfo = status.getDeviceInfo(); 806d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood int inputPorts = deviceInfo.getInputPortCount(); 816d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood int outputPorts = deviceInfo.getOutputPortCount(); 826d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood boolean hasOpenPorts = false; 836d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 846d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood for (int i = 0; i < inputPorts; i++) { 856d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (status.isInputPortOpen(i)) { 866d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood hasOpenPorts = true; 876d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood break; 886d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 896d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 906d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 916d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (!hasOpenPorts) { 926d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood for (int i = 0; i < outputPorts; i++) { 936d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (status.getOutputPortOpenCount(i) > 0) { 946d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood hasOpenPorts = true; 956d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood break; 966d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 976d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 986d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 996d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 1006d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood synchronized (mLock) { 1016d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (hasOpenPorts && !mIsOpen) { 1026d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood openLocked(); 1036d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } else if (!hasOpenPorts && mIsOpen) { 1046d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood closeLocked(); 1056d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 1066d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 1076d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 1086d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 1096d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood @Override 1106d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood public void onClose() { 1116d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 1126d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood }; 1136d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 1146d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // This class acts as a proxy for our MidiEventScheduler receivers, which do not exist 1156d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // until the device has active clients 1166d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private final class InputReceiverProxy extends MidiReceiver { 1176d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private MidiReceiver mReceiver; 1186d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 1196d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood @Override 1206d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood public void onSend(byte[] msg, int offset, int count, long timestamp) throws IOException { 1216d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood MidiReceiver receiver = mReceiver; 1226d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (receiver != null) { 1236d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood receiver.send(msg, offset, count, timestamp); 1246d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 12510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 12610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 1276d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood public void setReceiver(MidiReceiver receiver) { 1286d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mReceiver = receiver; 1296d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 130de2922468be26ab649ee44e7af291d97aebf0701Phil Burk 131de2922468be26ab649ee44e7af291d97aebf0701Phil Burk @Override 132de2922468be26ab649ee44e7af291d97aebf0701Phil Burk public void onFlush() throws IOException { 133de2922468be26ab649ee44e7af291d97aebf0701Phil Burk MidiReceiver receiver = mReceiver; 134de2922468be26ab649ee44e7af291d97aebf0701Phil Burk if (receiver != null) { 135de2922468be26ab649ee44e7af291d97aebf0701Phil Burk receiver.flush(); 136de2922468be26ab649ee44e7af291d97aebf0701Phil Burk } 137de2922468be26ab649ee44e7af291d97aebf0701Phil Burk } 1386d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 1396456a65afed66819b73df7eb605037402b91fc3fPhil Burk 1406d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) { 14110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // FIXME - support devices with different number of input and output ports 1426d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood int subDeviceCount = nativeGetSubdeviceCount(card, device); 1436d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (subDeviceCount <= 0) { 1446d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood Log.e(TAG, "nativeGetSubdeviceCount failed"); 14510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 14610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 14710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 1486d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood UsbMidiDevice midiDevice = new UsbMidiDevice(card, device, subDeviceCount); 14911fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (!midiDevice.register(context, properties)) { 15011fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood IoUtils.closeQuietly(midiDevice); 15110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "createDeviceServer failed"); 15210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 15310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 15411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return midiDevice; 15510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 15610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 1576d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private UsbMidiDevice(int card, int device, int subdeviceCount) { 1586d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mAlsaCard = card; 1596d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mAlsaDevice = device; 1606d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mSubdeviceCount = subdeviceCount; 1616d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 1626d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // FIXME - support devices with different number of input and output ports 163aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov int inputPortCount = subdeviceCount; 164aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov mInputPortReceivers = new InputReceiverProxy[inputPortCount]; 165aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov for (int port = 0; port < inputPortCount; port++) { 1666d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mInputPortReceivers[port] = new InputReceiverProxy(); 1676d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 1686d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 1696d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 1706d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private boolean openLocked() { 1716d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // FIXME - support devices with different number of input and output ports 1726d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood FileDescriptor[] fileDescriptors = nativeOpen(mAlsaCard, mAlsaDevice, mSubdeviceCount); 1736d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (fileDescriptors == null) { 1746d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood Log.e(TAG, "nativeOpen failed"); 1756d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood return false; 1766d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 1776d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 178b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood mFileDescriptors = fileDescriptors; 179aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov int inputStreamCount = fileDescriptors.length; 1806d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // last file descriptor returned from nativeOpen() is only used for unblocking Os.poll() 1816d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // in our input thread 182aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov int outputStreamCount = fileDescriptors.length - 1; 18310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 184aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov mPollFDs = new StructPollfd[inputStreamCount]; 185aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov mInputStreams = new FileInputStream[inputStreamCount]; 186aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov for (int i = 0; i < inputStreamCount; i++) { 187b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood FileDescriptor fd = fileDescriptors[i]; 18810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood StructPollfd pollfd = new StructPollfd(); 18910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood pollfd.fd = fd; 19010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood pollfd.events = (short)OsConstants.POLLIN; 19110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mPollFDs[i] = pollfd; 19210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mInputStreams[i] = new FileInputStream(fd); 19310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 19410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 195aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov mOutputStreams = new FileOutputStream[outputStreamCount]; 196aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov mEventSchedulers = new MidiEventScheduler[outputStreamCount]; 197aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov for (int i = 0; i < outputStreamCount; i++) { 198b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]); 19910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 2006d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood MidiEventScheduler scheduler = new MidiEventScheduler(); 2016d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mEventSchedulers[i] = scheduler; 2026d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mInputPortReceivers[i].setReceiver(scheduler.getReceiver()); 20311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 20411fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 20511fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers(); 20611fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 207aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov // Create input thread which will read from all output ports of the physical device 2082776133be7ac60dc8d6aea5b12e35449ca331836Mike Lockwood new Thread("UsbMidiDevice input thread") { 20910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 21010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public void run() { 21120821ecbe81ba52b260ae232096bc2bfb3e92ad0Mike Lockwood byte[] buffer = new byte[BUFFER_SIZE]; 21210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood try { 2136d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood while (true) { 2146456a65afed66819b73df7eb605037402b91fc3fPhil Burk // Record time of event immediately after waking. 2156456a65afed66819b73df7eb605037402b91fc3fPhil Burk long timestamp = System.nanoTime(); 2166d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood synchronized (mLock) { 2176d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (!mIsOpen) break; 2186d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 2196d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // look for a readable FileDescriptor 2206d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood for (int index = 0; index < mPollFDs.length; index++) { 2216d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood StructPollfd pfd = mPollFDs[index]; 2226d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if ((pfd.revents & (OsConstants.POLLERR 2236d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood | OsConstants.POLLHUP)) != 0) { 2246d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood break; 2256d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } else if ((pfd.revents & OsConstants.POLLIN) != 0) { 2266d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // clear readable flag 2276d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood pfd.revents = 0; 2286456a65afed66819b73df7eb605037402b91fc3fPhil Burk 2296d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (index == mInputStreams.length - 1) { 2306d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // last file descriptor is used only for unblocking Os.poll() 2316d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood break; 2326d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 2336d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 2346d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood int count = mInputStreams[index].read(buffer); 2356456a65afed66819b73df7eb605037402b91fc3fPhil Burk outputReceivers[index].send(buffer, 0, count, timestamp); 2366d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 23710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 23810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 23910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 2406d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // wait until we have a readable port or we are signalled to close 24110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Os.poll(mPollFDs, -1 /* infinite timeout */); 24210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 24310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } catch (IOException e) { 24410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.d(TAG, "reader thread exiting"); 24510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } catch (ErrnoException e) { 24610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.d(TAG, "reader thread exiting"); 24710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 2482776133be7ac60dc8d6aea5b12e35449ca331836Mike Lockwood Log.d(TAG, "input thread exit"); 2492776133be7ac60dc8d6aea5b12e35449ca331836Mike Lockwood } 2502776133be7ac60dc8d6aea5b12e35449ca331836Mike Lockwood }.start(); 2512776133be7ac60dc8d6aea5b12e35449ca331836Mike Lockwood 252aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov // Create output thread for each input port of the physical device 253aab4c1ad489a8b62186215d826293e9cbcbaeb85Mikhail Naganov for (int port = 0; port < outputStreamCount; port++) { 254b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood final MidiEventScheduler eventSchedulerF = mEventSchedulers[port]; 255b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood final FileOutputStream outputStreamF = mOutputStreams[port]; 256b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood final int portF = port; 257b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood 258b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood new Thread("UsbMidiDevice output thread " + port) { 259b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood @Override 260b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood public void run() { 261b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood while (true) { 262b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood MidiEvent event; 263b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood try { 264b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood event = (MidiEvent)eventSchedulerF.waitNextEvent(); 265b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood } catch (InterruptedException e) { 266b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood // try again 267b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood continue; 268b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood } 269b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood if (event == null) { 270b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood break; 271b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood } 272b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood try { 273b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood outputStreamF.write(event.data, 0, event.count); 274b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood } catch (IOException e) { 275b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood Log.e(TAG, "write failed for port " + portF); 276b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood } 277b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood eventSchedulerF.addEventToPool(event); 2782776133be7ac60dc8d6aea5b12e35449ca331836Mike Lockwood } 279b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood Log.d(TAG, "output thread exit"); 2802776133be7ac60dc8d6aea5b12e35449ca331836Mike Lockwood } 281b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood }.start(); 282b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood } 28311fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood 2846d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mIsOpen = true; 2856d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood return true; 2866d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 2876d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 2886d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private boolean register(Context context, Bundle properties) { 2896d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE); 2906d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (midiManager == null) { 2916d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood Log.e(TAG, "No MidiManager in UsbMidiDevice.create()"); 2926d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood return false; 2936d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 2946d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 2956d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mServer = midiManager.createDeviceServer(mInputPortReceivers, mSubdeviceCount, 2966d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood null, null, properties, MidiDeviceInfo.TYPE_USB, mCallback); 2976d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (mServer == null) { 2986d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood return false; 2996d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 3006d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 30111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood return true; 30210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 30310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 30410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 30510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public void close() throws IOException { 3066d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood synchronized (mLock) { 3076d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood if (mIsOpen) { 3086d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood closeLocked(); 3096d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 310b6f50d357bd3d4d296be6bb047f5ce93a79cbca1Mike Lockwood } 3112776133be7ac60dc8d6aea5b12e35449ca331836Mike Lockwood 31211fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood if (mServer != null) { 3136d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood IoUtils.closeQuietly(mServer); 3146d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 3156d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood } 3166d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 3176d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private void closeLocked() { 3186d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood for (int i = 0; i < mEventSchedulers.length; i++) { 3196d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mInputPortReceivers[i].setReceiver(null); 3206d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mEventSchedulers[i].close(); 32111fd96d6ff25bc1d710448eab545fe09da55a5f5Mike Lockwood } 3226d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mEventSchedulers = null; 32310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 32410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int i = 0; i < mInputStreams.length; i++) { 3256d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood IoUtils.closeQuietly(mInputStreams[i]); 32610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 3276d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mInputStreams = null; 3286d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 32910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int i = 0; i < mOutputStreams.length; i++) { 3306d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood IoUtils.closeQuietly(mOutputStreams[i]); 33110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 3326d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mOutputStreams = null; 3336d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 3346d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood // nativeClose will close the file descriptors and signal the input thread to exit 335b7ce094c9e546c4a802bd8ce3a43592979a5e3dfMike Lockwood nativeClose(mFileDescriptors); 3366d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mFileDescriptors = null; 3376d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood 3386d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood mIsOpen = false; 33910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 34010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 34110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static native int nativeGetSubdeviceCount(int card, int device); 3426d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount); 3436d5a0f916499a69f28b860fd66d09b5025c30450Mike Lockwood private native void nativeClose(FileDescriptor[] fileDescriptors); 34410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood} 345