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