UsbMidiDevice.java revision 10024b3dc12a8552c1547b67810c77b865045cc8
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; 2010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.hardware.usb.UsbDevice; 2110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.midi.MidiDeviceInfo; 2210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.midi.MidiDeviceServer; 2310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.midi.MidiManager; 2410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.midi.MidiReceiver; 2510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.midi.MidiSender; 2610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.midi.MidiUtils; 2710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.os.Bundle; 2810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.system.ErrnoException; 2910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.system.Os; 3010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.system.OsConstants; 3110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.system.StructPollfd; 3210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport android.util.Log; 3310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 3410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.Closeable; 3510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.FileDescriptor; 3610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.FileInputStream; 3710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.FileOutputStream; 3810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodimport java.io.IOException; 3910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 4010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwoodpublic final class UsbMidiDevice implements Closeable { 4110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static final String TAG = "UsbMidiDevice"; 4210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 4310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final MidiDeviceServer mServer; 4410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final MidiReceiver[] mOutputPortReceivers; 4510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 4610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // for polling multiple FileDescriptors for MIDI events 4710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final StructPollfd[] mPollFDs; 4810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final FileInputStream[] mInputStreams; 4910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private final FileOutputStream[] mOutputStreams; 5010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public static UsbMidiDevice create(Context context, UsbDevice usbDevice, int card, int device) { 5210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE); 5310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (midiManager == null) { 5410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "No MidiManager in UsbMidiDevice.create()"); 5510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 5610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 5710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 5810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // FIXME - support devices with different number of input and output ports 5910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood int subDevices = nativeGetSubdeviceCount(card, device); 6010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (subDevices <= 0) { 6110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "nativeGetSubdeviceCount failed"); 6210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 6310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 6410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 6510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // FIXME - support devices with different number of input and output ports 6610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood FileDescriptor[] fileDescriptors = nativeOpen(card, device, subDevices); 6710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (fileDescriptors == null) { 6810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "nativeOpen failed"); 6910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 7010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 7110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 7210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Bundle properties = new Bundle(); 7310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, usbDevice.getManufacturerName()); 7410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood properties.putString(MidiDeviceInfo.PROPERTY_MODEL, usbDevice.getProductName()); 7510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER, usbDevice.getSerialNumber()); 7610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card); 7710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device); 7810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice); 7910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood MidiDeviceServer server = midiManager.createDeviceServer(subDevices, subDevices, properties, 8010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood false, MidiDeviceInfo.TYPE_USB); 8110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (server == null) { 8210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "createDeviceServer failed"); 8310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return null; 8410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 8510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 8610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return new UsbMidiDevice(server, fileDescriptors, fileDescriptors); 8710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 8810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 8910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private UsbMidiDevice(MidiDeviceServer server, FileDescriptor[] inputFiles, 9010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood FileDescriptor[] outputFiles) { 9110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mServer = server; 9210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood int inputCount = inputFiles.length; 9310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood int outputCount = outputFiles.length; 9410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 9510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mPollFDs = new StructPollfd[inputCount]; 9610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mInputStreams = new FileInputStream[inputCount]; 9710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int i = 0; i < inputCount; i++) { 9810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood FileDescriptor fd = inputFiles[i]; 9910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood StructPollfd pollfd = new StructPollfd(); 10010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood pollfd.fd = fd; 10110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood pollfd.events = (short)OsConstants.POLLIN; 10210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mPollFDs[i] = pollfd; 10310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mInputStreams[i] = new FileInputStream(fd); 10410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 10510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 10610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputStreams = new FileOutputStream[outputCount]; 10710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int i = 0; i < outputCount; i++) { 10810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputStreams[i] = new FileOutputStream(outputFiles[i]); 10910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 11010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 11110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputPortReceivers = new MidiReceiver[outputCount]; 11210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int port = 0; port < outputCount; port++) { 11310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputPortReceivers[port] = server.openOutputPortReceiver(port); 11410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 11510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 11610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int port = 0; port < inputCount; port++) { 11710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood final int portNumberF = port; 11810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood MidiReceiver receiver = new MidiReceiver() { 11910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 12010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 12110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public void onPost(byte[] data, int offset, int count, long timestamp) 12210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood throws IOException { 12310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // FIXME - timestamps are ignored, future posting not supported yet. 12410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputStreams[portNumberF].write(data, offset, count); 12510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 12610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood }; 12710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood MidiSender sender = server.openInputPortSender(port); 12810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood sender.connect(receiver); 12910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 13010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 13110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood new Thread() { 13210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood @Override 13310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood public void run() { 13410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood byte[] buffer = new byte[3]; 13510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood try { 13610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood while (true) { 13710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // look for a readable FileDescriptor 13810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood for (int index = 0; index < mPollFDs.length; index++) { 13910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood StructPollfd pfd = mPollFDs[index]; 14010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if ((pfd.revents & OsConstants.POLLIN) != 0) { 14110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // clear readable flag 14210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood pfd.revents = 0; 14310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood int count = readMessage(buffer, index); 14410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood mOutputPortReceivers[index].onPost(buffer, 0, count, System.nanoTime()); 14510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 14610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 14710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 14810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood // poll if none are readable 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 int readMessage(byte[] buffer, int index) throws IOException { 17310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood FileInputStream inputStream = mInputStreams[index]; 17410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 17510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (inputStream.read(buffer, 0, 1) != 1) { 17610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "could not read command byte"); 17710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return -1; 17810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 17910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood int dataSize = MidiUtils.getMessageDataSize(buffer[0]); 18010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (dataSize < 0) { 18110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return -1; 18210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 18310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (dataSize > 0) { 18410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood if (inputStream.read(buffer, 1, dataSize) != dataSize) { 18510024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood Log.e(TAG, "could not read command data"); 18610024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return -1; 18710024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 18810024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 18910024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood return dataSize + 1; 19010024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood } 19110024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood 19210024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static native int nativeGetSubdeviceCount(int card, int device); 19310024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood private static native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount); 19410024b3dc12a8552c1547b67810c77b865045cc8Mike Lockwood} 195