UsbMidiDevice.java revision b673770f7172d4fca9bc05de1f36bc53e93eb247
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; 22b673770f7172d4fca9bc05de1f36bc53e93eb247Mike Lockwoodimport android.media.midi.MidiManager; 23b673770f7172d4fca9bc05de1f36bc53e93eb247Mike Lockwoodimport android.media.midi.MidiPort; 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 3310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.Closeable; 3410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.FileDescriptor; 3510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.FileInputStream; 3610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.FileOutputStream; 3710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.IOException; 3810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 3910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodpublic final class UsbMidiDevice implements Closeable { 4010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static final String TAG = "UsbMidiDevice"; 4110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 4210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final MidiDeviceServer mServer; 4310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final MidiReceiver[] mOutputPortReceivers; 4410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 4510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // for polling multiple FileDescriptors for MIDI events 4610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final StructPollfd[] mPollFDs; 47cb096273734d96f4676014fa9030b57ea48b58d8Mike Lockwood // streams for reading from ALSA driver 4810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final FileInputStream[] mInputStreams; 49cb096273734d96f4676014fa9030b57ea48b58d8Mike Lockwood // streams for writing to ALSA driver 5010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final FileOutputStream[] mOutputStreams; 5110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 522a57bc7fd602853dc1a22dcee1ff50f92cc29060Mike Lockwood public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) { 5310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE); 5410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (midiManager == null) { 5510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "No MidiManager in UsbMidiDevice.create()"); 5610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 5710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 5810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // FIXME - support devices with different number of input and output ports 6010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood int subDevices = nativeGetSubdeviceCount(card, device); 6110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (subDevices <= 0) { 6210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "nativeGetSubdeviceCount failed"); 6310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 6410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 6510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 6610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // FIXME - support devices with different number of input and output ports 6710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood FileDescriptor[] fileDescriptors = nativeOpen(card, device, subDevices); 6810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (fileDescriptors == null) { 6910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "nativeOpen failed"); 7010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 7110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 7210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 7310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood MidiDeviceServer server = midiManager.createDeviceServer(subDevices, subDevices, properties, 7410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood false, MidiDeviceInfo.TYPE_USB); 7510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (server == null) { 7610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "createDeviceServer failed"); 7710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 7810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 7910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 8010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return new UsbMidiDevice(server, fileDescriptors, fileDescriptors); 8110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 8210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 8310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private UsbMidiDevice(MidiDeviceServer server, FileDescriptor[] inputFiles, 8410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood FileDescriptor[] outputFiles) { 8510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mServer = server; 8610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood int inputCount = inputFiles.length; 8710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood int outputCount = outputFiles.length; 8810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 8910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mPollFDs = new StructPollfd[inputCount]; 9010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mInputStreams = new FileInputStream[inputCount]; 9110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int i = 0; i < inputCount; i++) { 9210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood FileDescriptor fd = inputFiles[i]; 9310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood StructPollfd pollfd = new StructPollfd(); 9410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood pollfd.fd = fd; 9510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood pollfd.events = (short)OsConstants.POLLIN; 9610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mPollFDs[i] = pollfd; 9710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mInputStreams[i] = new FileInputStream(fd); 9810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 9910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 10010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputStreams = new FileOutputStream[outputCount]; 10110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int i = 0; i < outputCount; i++) { 10210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputStreams[i] = new FileOutputStream(outputFiles[i]); 10310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 10410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 10510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputPortReceivers = new MidiReceiver[outputCount]; 10610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int port = 0; port < outputCount; port++) { 10710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputPortReceivers[port] = server.openOutputPortReceiver(port); 10810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 10910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 11010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int port = 0; port < inputCount; port++) { 11110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood final int portNumberF = port; 11210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood MidiReceiver receiver = new MidiReceiver() { 11310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 11410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 11590b9a6a4ab30fc162aee71b4dc484b3839534369Mike Lockwood public void post(byte[] data, int offset, int count, long timestamp) 11610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood throws IOException { 11710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // FIXME - timestamps are ignored, future posting not supported yet. 11810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputStreams[portNumberF].write(data, offset, count); 11910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 12010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood }; 12110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood MidiSender sender = server.openInputPortSender(port); 12210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood sender.connect(receiver); 12310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 12410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 12510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood new Thread() { 12610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 12710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public void run() { 128cb096273734d96f4676014fa9030b57ea48b58d8Mike Lockwood byte[] buffer = new byte[MidiPort.MAX_PACKET_DATA_SIZE]; 12910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood try { 13098cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood boolean done = false; 13198cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood while (!done) { 13210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // look for a readable FileDescriptor 13310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int index = 0; index < mPollFDs.length; index++) { 13410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood StructPollfd pfd = mPollFDs[index]; 13510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if ((pfd.revents & OsConstants.POLLIN) != 0) { 13610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // clear readable flag 13710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood pfd.revents = 0; 138cb096273734d96f4676014fa9030b57ea48b58d8Mike Lockwood 139cb096273734d96f4676014fa9030b57ea48b58d8Mike Lockwood int count = mInputStreams[index].read(buffer); 14090b9a6a4ab30fc162aee71b4dc484b3839534369Mike Lockwood mOutputPortReceivers[index].post(buffer, 0, count, 14198cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood System.nanoTime()); 14298cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood } else if ((pfd.revents & (OsConstants.POLLERR 14398cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood | OsConstants.POLLHUP)) != 0) { 14498cc8e5a6473b3a5802d97cc81020ec4e3cd23f3Mike Lockwood done = true; 14510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 14610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 14710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 148cb096273734d96f4676014fa9030b57ea48b58d8Mike Lockwood // wait until we have a readable port 14910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Os.poll(mPollFDs, -1 /* infinite timeout */); 15010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 15110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } catch (IOException e) { 15210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.d(TAG, "reader thread exiting"); 15310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } catch (ErrnoException e) { 15410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.d(TAG, "reader thread exiting"); 15510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 15610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 15710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood }.start(); 15810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 15910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 16010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 16110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public void close() throws IOException { 16210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mServer.close(); 16310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 16410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int i = 0; i < mInputStreams.length; i++) { 16510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mInputStreams[i].close(); 16610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 16710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int i = 0; i < mOutputStreams.length; i++) { 16810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputStreams[i].close(); 16910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 17010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 17110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 17210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static native int nativeGetSubdeviceCount(int card, int device); 17310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount); 17410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood} 175