diff options
author | Mike Lockwood <lockwood@google.com> | 2014-12-11 13:44:20 -0800 |
---|---|---|
committer | Mike Lockwood <lockwood@google.com> | 2015-01-14 16:51:54 -0800 |
commit | 10024b3dc12a8552c1547b67810c77b865045cc8 (patch) | |
tree | ad7f453999f8387077c40251355781e0d15cb4d1 /services/usb/java/com | |
parent | 34b064a1406ebb2eb10b5b1f24c845891748c238 (diff) | |
download | frameworks_base-10024b3dc12a8552c1547b67810c77b865045cc8.zip frameworks_base-10024b3dc12a8552c1547b67810c77b865045cc8.tar.gz frameworks_base-10024b3dc12a8552c1547b67810c77b865045cc8.tar.bz2 |
MidiManager updates:
MIDI ports are now implemented as file descriptors directly between the sender
and receiver, so the MidiService is no longer in the message path.
To facilitate the above, each port has its own file descriptor, rather than multiplexing
all ports on a device through a single socket.
Added a new class MidiDeviceServer, which is used by implementors of MIDI devices.
This replaces the MidiVirtualDevice class (which only was included in changes that were reviewed but never submitted).
The USB MIDI implementation has moved from the MIDI service to the USB service.
The USB MIDI implementation uses MidiDeviceServer as its interface, so we now have a common
interface for all MIDI device implementations.
Change-Id: I8effd1583f344beb6c940c3a24dbf20b477a6436
Diffstat (limited to 'services/usb/java/com')
3 files changed, 235 insertions, 43 deletions
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java index 32ca723..6589135 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java @@ -24,15 +24,15 @@ import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbInterface; import android.media.AudioManager; -import android.midi.IMidiManager; import android.os.FileObserver; import android.os.IBinder; -import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.util.Slog; +import libcore.io.IoUtils; + import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -42,14 +42,13 @@ import java.util.ArrayList; /** * UsbAlsaManager manages USB audio and MIDI devices. */ -public class UsbAlsaManager { +public final class UsbAlsaManager { private static final String TAG = UsbAlsaManager.class.getSimpleName(); private static final boolean DEBUG = true; private static final String ALSA_DIRECTORY = "/dev/snd/"; private final Context mContext; - private IMidiManager mMidiManager; private final AlsaCardsParser mCardsParser = new AlsaCardsParser(); private final AlsaDevicesParser mDevicesParser = new AlsaDevicesParser(); @@ -60,6 +59,9 @@ public class UsbAlsaManager { private final HashMap<UsbDevice,UsbAudioDevice> mAudioDevices = new HashMap<UsbDevice,UsbAudioDevice>(); + private final HashMap<UsbDevice,UsbMidiDevice> + mMidiDevices = new HashMap<UsbDevice,UsbMidiDevice>(); + private final HashMap<String,AlsaDevice> mAlsaDevices = new HashMap<String,AlsaDevice>(); @@ -121,8 +123,6 @@ public class UsbAlsaManager { } public void systemReady() { - final IBinder b = ServiceManager.getService(Context.MIDI_SERVICE); - mMidiManager = IMidiManager.Stub.asInterface(b); mAlsaObserver.startWatching(); // add existing alsa devices @@ -151,7 +151,6 @@ public class UsbAlsaManager { intent.putExtra("device", audioDevice.mDevice); intent.putExtra("hasPlayback", audioDevice.mHasPlayback); intent.putExtra("hasCapture", audioDevice.mHasCapture); - intent.putExtra("hasMIDI", audioDevice.mHasMIDI); intent.putExtra("class", audioDevice.mDeviceClass); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } @@ -235,9 +234,9 @@ public class UsbAlsaManager { /* * Select the default device of the specified card. */ - /* package */ boolean selectCard(int card) { + /* package */ boolean selectAudioCard(int card) { if (DEBUG) { - Slog.d(TAG, "selectCard() card:" + card); + Slog.d(TAG, "selectAudioCard() card:" + card); } if (!mCardsParser.isCardUsb(card)) { // Don't. AudioPolicyManager has logic for falling back to internal devices. @@ -259,7 +258,6 @@ public class UsbAlsaManager { boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card); boolean hasCapture = mDevicesParser.hasCaptureDevices(card); - boolean hasMidi = mDevicesParser.hasMIDIDevices(card); int deviceClass = (mCardsParser.isCardUsb(card) ? UsbAudioDevice.kAudioDeviceClass_External @@ -275,21 +273,13 @@ public class UsbAlsaManager { if (hasCapture && (waitForAlsaDevice(card, device, AlsaDevice.TYPE_CAPTURE) == null)) { return false; } - //TODO - seems to me that we need to decouple the above tests for audio - // from the one below for MIDI. - - // MIDI device file needed/present? - AlsaDevice midiDevice = null; - if (hasMidi) { - midiDevice = waitForAlsaDevice(card, device, AlsaDevice.TYPE_MIDI); - } if (DEBUG) { Slog.d(TAG, "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture); } mSelectedAudioDevice = - new UsbAudioDevice(card, device, hasPlayback, hasCapture, hasMidi, deviceClass); + new UsbAudioDevice(card, device, hasPlayback, hasCapture, deviceClass); mSelectedAudioDevice.mDeviceName = mCardsParser.getCardRecordFor(card).mCardName; mSelectedAudioDevice.mDeviceDescription = mCardsParser.getCardRecordFor(card).mCardDescription; @@ -304,7 +294,7 @@ public class UsbAlsaManager { Slog.d(TAG, "UsbAudioManager.selectDefaultDevice()"); } mCardsParser.scan(); - return selectCard(mCardsParser.getDefaultCard()); + return selectAudioCard(mCardsParser.getDefaultCard()); } /* package */ void deviceAdded(UsbDevice usbDevice) { @@ -314,7 +304,6 @@ public class UsbAlsaManager { // Is there an audio interface in there? boolean isAudioDevice = false; - AlsaDevice midiDevice = null; // FIXME - handle multiple configurations? int interfaceCount = usbDevice.getInterfaceCount(); @@ -347,36 +336,44 @@ public class UsbAlsaManager { // If the default isn't a USB device, let the existing "select internal mechanism" // handle the selection. if (mCardsParser.isCardUsb(addedCard)) { - selectCard(addedCard); + selectAudioCard(addedCard); mAudioDevices.put(usbDevice, mSelectedAudioDevice); - } - if (midiDevice != null && mMidiManager != null) { - try { - mMidiManager.alsaDeviceAdded(midiDevice.mCard, midiDevice.mDevice, usbDevice); - } catch (RemoteException e) { - Slog.e(TAG, "MIDI Manager dead", e); + // look for MIDI devices + + // Don't need to call mDevicesParser.scan() because selectAudioCard() does this above. + // Uncomment this next line if that behavior changes in the fugure. + // mDevicesParser.scan() + + boolean hasMidi = mDevicesParser.hasMIDIDevices(addedCard); + if (hasMidi) { + int device = mDevicesParser.getDefaultDeviceNum(addedCard); + AlsaDevice alsaDevice = waitForAlsaDevice(addedCard, device, AlsaDevice.TYPE_MIDI); + if (alsaDevice != null) { + UsbMidiDevice usbMidiDevice = UsbMidiDevice.create(mContext, usbDevice, + alsaDevice.mCard, alsaDevice.mDevice); + if (usbMidiDevice != null) { + mMidiDevices.put(usbDevice, usbMidiDevice); + } + } } } } - /* package */ void deviceRemoved(UsbDevice device) { + /* package */ void deviceRemoved(UsbDevice usbDevice) { if (DEBUG) { - Slog.d(TAG, "deviceRemoved(): " + device); + Slog.d(TAG, "deviceRemoved(): " + usbDevice); } - UsbAudioDevice audioDevice = mAudioDevices.remove(device); + UsbAudioDevice audioDevice = mAudioDevices.remove(usbDevice); if (audioDevice != null) { if (audioDevice.mHasPlayback || audioDevice.mHasPlayback) { sendDeviceNotification(audioDevice, false); } - if (audioDevice.mHasMIDI) { - try { - mMidiManager.alsaDeviceRemoved(device); - } catch (RemoteException e) { - Slog.e(TAG, "MIDI Manager dead", e); - } - } + } + UsbMidiDevice usbMidiDevice = mMidiDevices.remove(usbDevice); + if (usbMidiDevice != null) { + IoUtils.closeQuietly(usbMidiDevice); } mSelectedAudioDevice = null; @@ -400,10 +397,14 @@ public class UsbAlsaManager { // Logging // public void dump(FileDescriptor fd, PrintWriter pw) { - pw.println(" USB AudioDevices:"); + pw.println(" USB Audio Devices:"); for (UsbDevice device : mAudioDevices.keySet()) { pw.println(" " + device.getDeviceName() + ": " + mAudioDevices.get(device)); } + pw.println(" USB MIDI Devices:"); + for (UsbDevice device : mMidiDevices.keySet()) { + pw.println(" " + device.getDeviceName() + ": " + mMidiDevices.get(device)); + } } public void logDevicesList(String title) { diff --git a/services/usb/java/com/android/server/usb/UsbAudioDevice.java b/services/usb/java/com/android/server/usb/UsbAudioDevice.java index b7b9563..069d917 100644 --- a/services/usb/java/com/android/server/usb/UsbAudioDevice.java +++ b/services/usb/java/com/android/server/usb/UsbAudioDevice.java @@ -24,7 +24,6 @@ public final class UsbAudioDevice { public int mDevice; public boolean mHasPlayback; public boolean mHasCapture; - public boolean mHasMIDI; // Device "class" flags public static final int kAudioDeviceClassMask = 0x00FFFFFF; @@ -41,12 +40,11 @@ public final class UsbAudioDevice { public String mDeviceDescription = ""; public UsbAudioDevice(int card, int device, - boolean hasPlayback, boolean hasCapture, boolean hasMidi, int deviceClass) { + boolean hasPlayback, boolean hasCapture, int deviceClass) { mCard = card; mDevice = device; mHasPlayback = hasPlayback; mHasCapture = hasCapture; - mHasMIDI = hasMidi; mDeviceClass = deviceClass; } @@ -58,7 +56,6 @@ public final class UsbAudioDevice { sb.append(", description: " + mDeviceDescription); sb.append(", hasPlayback: " + mHasPlayback); sb.append(", hasCapture: " + mHasCapture); - sb.append(", hasMidi: " + mHasMIDI); sb.append(", class: 0x" + Integer.toHexString(mDeviceClass) + "]"); return sb.toString(); } diff --git a/services/usb/java/com/android/server/usb/UsbMidiDevice.java b/services/usb/java/com/android/server/usb/UsbMidiDevice.java new file mode 100644 index 0000000..aaac331 --- /dev/null +++ b/services/usb/java/com/android/server/usb/UsbMidiDevice.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions an + * limitations under the License. + */ + +package com.android.server.usb; + +import android.content.Context; +import android.hardware.usb.UsbDevice; +import android.midi.MidiDeviceInfo; +import android.midi.MidiDeviceServer; +import android.midi.MidiManager; +import android.midi.MidiReceiver; +import android.midi.MidiSender; +import android.midi.MidiUtils; +import android.os.Bundle; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; +import android.system.StructPollfd; +import android.util.Log; + +import java.io.Closeable; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; + +public final class UsbMidiDevice implements Closeable { + private static final String TAG = "UsbMidiDevice"; + + private final MidiDeviceServer mServer; + private final MidiReceiver[] mOutputPortReceivers; + + // for polling multiple FileDescriptors for MIDI events + private final StructPollfd[] mPollFDs; + private final FileInputStream[] mInputStreams; + private final FileOutputStream[] mOutputStreams; + + public static UsbMidiDevice create(Context context, UsbDevice usbDevice, int card, int device) { + MidiManager midiManager = (MidiManager)context.getSystemService(Context.MIDI_SERVICE); + if (midiManager == null) { + Log.e(TAG, "No MidiManager in UsbMidiDevice.create()"); + return null; + } + + // FIXME - support devices with different number of input and output ports + int subDevices = nativeGetSubdeviceCount(card, device); + if (subDevices <= 0) { + Log.e(TAG, "nativeGetSubdeviceCount failed"); + return null; + } + + // FIXME - support devices with different number of input and output ports + FileDescriptor[] fileDescriptors = nativeOpen(card, device, subDevices); + if (fileDescriptors == null) { + Log.e(TAG, "nativeOpen failed"); + return null; + } + + Bundle properties = new Bundle(); + properties.putString(MidiDeviceInfo.PROPERTY_MANUFACTURER, usbDevice.getManufacturerName()); + properties.putString(MidiDeviceInfo.PROPERTY_MODEL, usbDevice.getProductName()); + properties.putString(MidiDeviceInfo.PROPERTY_SERIAL_NUMBER, usbDevice.getSerialNumber()); + properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_CARD, card); + properties.putInt(MidiDeviceInfo.PROPERTY_ALSA_DEVICE, device); + properties.putParcelable(MidiDeviceInfo.PROPERTY_USB_DEVICE, usbDevice); + MidiDeviceServer server = midiManager.createDeviceServer(subDevices, subDevices, properties, + false, MidiDeviceInfo.TYPE_USB); + if (server == null) { + Log.e(TAG, "createDeviceServer failed"); + return null; + } + + return new UsbMidiDevice(server, fileDescriptors, fileDescriptors); + } + + private UsbMidiDevice(MidiDeviceServer server, FileDescriptor[] inputFiles, + FileDescriptor[] outputFiles) { + mServer = server; + int inputCount = inputFiles.length; + int outputCount = outputFiles.length; + + mPollFDs = new StructPollfd[inputCount]; + mInputStreams = new FileInputStream[inputCount]; + for (int i = 0; i < inputCount; i++) { + FileDescriptor fd = inputFiles[i]; + StructPollfd pollfd = new StructPollfd(); + pollfd.fd = fd; + pollfd.events = (short)OsConstants.POLLIN; + mPollFDs[i] = pollfd; + mInputStreams[i] = new FileInputStream(fd); + } + + mOutputStreams = new FileOutputStream[outputCount]; + for (int i = 0; i < outputCount; i++) { + mOutputStreams[i] = new FileOutputStream(outputFiles[i]); + } + + mOutputPortReceivers = new MidiReceiver[outputCount]; + for (int port = 0; port < outputCount; port++) { + mOutputPortReceivers[port] = server.openOutputPortReceiver(port); + } + + for (int port = 0; port < inputCount; port++) { + final int portNumberF = port; + MidiReceiver receiver = new MidiReceiver() { + + @Override + public void onPost(byte[] data, int offset, int count, long timestamp) + throws IOException { + // FIXME - timestamps are ignored, future posting not supported yet. + mOutputStreams[portNumberF].write(data, offset, count); + } + }; + MidiSender sender = server.openInputPortSender(port); + sender.connect(receiver); + } + + new Thread() { + @Override + public void run() { + byte[] buffer = new byte[3]; + try { + while (true) { + // look for a readable FileDescriptor + for (int index = 0; index < mPollFDs.length; index++) { + StructPollfd pfd = mPollFDs[index]; + if ((pfd.revents & OsConstants.POLLIN) != 0) { + // clear readable flag + pfd.revents = 0; + int count = readMessage(buffer, index); + mOutputPortReceivers[index].onPost(buffer, 0, count, System.nanoTime()); + } + } + + // poll if none are readable + Os.poll(mPollFDs, -1 /* infinite timeout */); + } + } catch (IOException e) { + Log.d(TAG, "reader thread exiting"); + } catch (ErrnoException e) { + Log.d(TAG, "reader thread exiting"); + } + } + }.start(); + } + + @Override + public void close() throws IOException { + mServer.close(); + + for (int i = 0; i < mInputStreams.length; i++) { + mInputStreams[i].close(); + } + for (int i = 0; i < mOutputStreams.length; i++) { + mOutputStreams[i].close(); + } + } + + private int readMessage(byte[] buffer, int index) throws IOException { + FileInputStream inputStream = mInputStreams[index]; + + if (inputStream.read(buffer, 0, 1) != 1) { + Log.e(TAG, "could not read command byte"); + return -1; + } + int dataSize = MidiUtils.getMessageDataSize(buffer[0]); + if (dataSize < 0) { + return -1; + } + if (dataSize > 0) { + if (inputStream.read(buffer, 1, dataSize) != dataSize) { + Log.e(TAG, "could not read command data"); + return -1; + } + } + return dataSize + 1; + } + + private static native int nativeGetSubdeviceCount(int card, int device); + private static native FileDescriptor[] nativeOpen(int card, int device, int subdeviceCount); +} |