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