UsbMidiDevice.java revision b7ce094c9e546c4a802bd8ce3a43592979a5e3df
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions an 14 * limitations under the License. 15 */ 16 17package com.android.server.usb; 18 19import android.content.Context; 20import android.media.midi.MidiDeviceInfo; 21import android.media.midi.MidiDeviceServer; 22import android.media.midi.MidiDispatcher; 23import android.media.midi.MidiManager; 24import android.media.midi.MidiReceiver; 25import android.media.midi.MidiSender; 26import android.os.Bundle; 27import android.system.ErrnoException; 28import android.system.Os; 29import android.system.OsConstants; 30import android.system.StructPollfd; 31import android.util.Log; 32 33import libcore.io.IoUtils; 34 35import java.io.Closeable; 36import java.io.FileDescriptor; 37import java.io.FileInputStream; 38import java.io.FileOutputStream; 39import java.io.IOException; 40 41public final class UsbMidiDevice implements Closeable { 42 private static final String TAG = "UsbMidiDevice"; 43 44 private MidiDeviceServer mServer; 45 46 private final MidiReceiver[] mInputPortReceivers; 47 48 private static final int BUFFER_SIZE = 512; 49 50 private final FileDescriptor[] mFileDescriptors; 51 52 // for polling multiple FileDescriptors for MIDI events 53 private final StructPollfd[] mPollFDs; 54 // streams for reading from ALSA driver 55 private final FileInputStream[] mInputStreams; 56 // streams for writing to ALSA driver 57 private final FileOutputStream[] mOutputStreams; 58 59 public static UsbMidiDevice create(Context context, Bundle properties, int card, int device) { 60 // FIXME - support devices with different number of input and output ports 61 int subDevices = nativeGetSubdeviceCount(card, device); 62 if (subDevices <= 0) { 63 Log.e(TAG, "nativeGetSubdeviceCount failed"); 64 return null; 65 } 66 67 // FIXME - support devices with different number of input and output ports 68 FileDescriptor[] fileDescriptors = nativeOpen(card, device, subDevices); 69 if (fileDescriptors == null) { 70 Log.e(TAG, "nativeOpen failed"); 71 return null; 72 } 73 74 UsbMidiDevice midiDevice = new UsbMidiDevice(fileDescriptors); 75 if (!midiDevice.register(context, properties)) { 76 IoUtils.closeQuietly(midiDevice); 77 Log.e(TAG, "createDeviceServer failed"); 78 return null; 79 } 80 return midiDevice; 81 } 82 83 private UsbMidiDevice(FileDescriptor[] fileDescriptors) { 84 mFileDescriptors = fileDescriptors; 85 int inputCount = fileDescriptors.length; 86 int outputCount = fileDescriptors.length; 87 88 mPollFDs = new StructPollfd[inputCount]; 89 mInputStreams = new FileInputStream[inputCount]; 90 for (int i = 0; i < inputCount; i++) { 91 FileDescriptor fd = fileDescriptors[i]; 92 StructPollfd pollfd = new StructPollfd(); 93 pollfd.fd = fd; 94 pollfd.events = (short)OsConstants.POLLIN; 95 mPollFDs[i] = pollfd; 96 mInputStreams[i] = new FileInputStream(fd); 97 } 98 99 mOutputStreams = new FileOutputStream[outputCount]; 100 for (int i = 0; i < outputCount; i++) { 101 mOutputStreams[i] = new FileOutputStream(fileDescriptors[i]); 102 } 103 104 mInputPortReceivers = new MidiReceiver[inputCount]; 105 for (int port = 0; port < inputCount; port++) { 106 final int portF = port; 107 mInputPortReceivers[port] = new MidiReceiver() { 108 @Override 109 public void onReceive(byte[] data, int offset, int count, long timestamp) 110 throws IOException { 111 // FIXME - timestamps are ignored, future posting not supported yet. 112 mOutputStreams[portF].write(data, offset, count); 113 } 114 }; 115 } 116 } 117 118 private boolean register(Context context, Bundle properties) { 119 MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE); 120 if (midiManager == null) { 121 Log.e(TAG, "No MidiManager in UsbMidiDevice.create()"); 122 return false; 123 } 124 125 int outputCount = mOutputStreams.length; 126 mServer = midiManager.createDeviceServer(mInputPortReceivers, outputCount, 127 null, null, properties, MidiDeviceInfo.TYPE_USB, null); 128 if (mServer == null) { 129 return false; 130 } 131 final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers(); 132 133 // FIXME can we only run this when we have a dispatcher that has listeners? 134 new Thread() { 135 @Override 136 public void run() { 137 byte[] buffer = new byte[BUFFER_SIZE]; 138 try { 139 boolean done = false; 140 while (!done) { 141 // look for a readable FileDescriptor 142 for (int index = 0; index < mPollFDs.length; index++) { 143 StructPollfd pfd = mPollFDs[index]; 144 if ((pfd.revents & OsConstants.POLLIN) != 0) { 145 // clear readable flag 146 pfd.revents = 0; 147 148 int count = mInputStreams[index].read(buffer); 149 outputReceivers[index].send(buffer, 0, count); 150 } else if ((pfd.revents & (OsConstants.POLLERR 151 | OsConstants.POLLHUP)) != 0) { 152 done = true; 153 } 154 } 155 156 // wait until we have a readable port 157 Os.poll(mPollFDs, -1 /* infinite timeout */); 158 } 159 } catch (IOException e) { 160 Log.d(TAG, "reader thread exiting"); 161 } catch (ErrnoException e) { 162 Log.d(TAG, "reader thread exiting"); 163 } 164 } 165 }.start(); 166 167 return true; 168 } 169 170 @Override 171 public void close() throws IOException { 172 if (mServer != null) { 173 mServer.close(); 174 } 175 176 for (int i = 0; i < mInputStreams.length; i++) { 177 mInputStreams[i].close(); 178 } 179 for (int i = 0; i < mOutputStreams.length; i++) { 180 mOutputStreams[i].close(); 181 } 182 nativeClose(mFileDescriptors); 183 } 184 185 private static native int nativeGetSubdeviceCount(int card, int device); 186 private static native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount); 187 private static native void nativeClose(FileDescriptor[] fileDescriptors); 188} 189