MidiDeviceServer.java revision 3b7664589be22ddad34b72e11ced937d48660ebb
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 and 14 * limitations under the License. 15 */ 16 17package android.media.midi; 18 19import android.os.IBinder; 20import android.os.Binder; 21import android.os.ParcelFileDescriptor; 22import android.os.Process; 23import android.os.RemoteException; 24import android.system.OsConstants; 25import android.util.Log; 26 27import libcore.io.IoUtils; 28 29import java.io.Closeable; 30import java.io.IOException; 31 32/** 33 * Internal class used for providing an implementation for a MIDI device. 34 * 35 * @hide 36 */ 37public final class MidiDeviceServer implements Closeable { 38 private static final String TAG = "MidiDeviceServer"; 39 40 private final IMidiManager mMidiManager; 41 42 // MidiDeviceInfo for the device implemented by this server 43 private MidiDeviceInfo mDeviceInfo; 44 private final int mInputPortCount; 45 private final int mOutputPortCount; 46 47 // MidiReceivers for receiving data on our input ports 48 private final MidiReceiver[] mInputPortReceivers; 49 50 // MidiDispatchers for sending data on our output ports 51 private MidiDispatcher[] mOutputPortDispatchers; 52 53 // MidiOutputPorts for clients connected to our input ports 54 private final MidiOutputPort[] mInputPortOutputPorts; 55 56 // Binder interface stub for receiving connection requests from clients 57 private final IMidiDeviceServer mServer = new IMidiDeviceServer.Stub() { 58 59 @Override 60 public ParcelFileDescriptor openInputPort(int portNumber) { 61 if (mDeviceInfo.isPrivate()) { 62 if (Binder.getCallingUid() != Process.myUid()) { 63 throw new SecurityException("Can't access private device from different UID"); 64 } 65 } 66 67 if (portNumber < 0 || portNumber >= mInputPortCount) { 68 Log.e(TAG, "portNumber out of range in openInputPort: " + portNumber); 69 return null; 70 } 71 72 synchronized (mInputPortOutputPorts) { 73 if (mInputPortOutputPorts[portNumber] != null) { 74 Log.d(TAG, "port " + portNumber + " already open"); 75 return null; 76 } 77 78 try { 79 ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair( 80 OsConstants.SOCK_SEQPACKET); 81 final MidiOutputPort outputPort = new MidiOutputPort(pair[0], portNumber); 82 mInputPortOutputPorts[portNumber] = outputPort; 83 final int portNumberF = portNumber; 84 final MidiReceiver inputPortReceviver = mInputPortReceivers[portNumber]; 85 86 outputPort.connect(new MidiReceiver() { 87 @Override 88 public void receive(byte[] msg, int offset, int count, long timestamp) 89 throws IOException { 90 try { 91 inputPortReceviver.receive(msg, offset, count, timestamp); 92 } catch (IOException e) { 93 IoUtils.closeQuietly(mInputPortOutputPorts[portNumberF]); 94 mInputPortOutputPorts[portNumberF] = null; 95 // FIXME also flush the receiver 96 } 97 } 98 }); 99 100 return pair[1]; 101 } catch (IOException e) { 102 Log.e(TAG, "unable to create ParcelFileDescriptors in openInputPort"); 103 return null; 104 } 105 } 106 } 107 108 @Override 109 public ParcelFileDescriptor openOutputPort(int portNumber) { 110 if (mDeviceInfo.isPrivate()) { 111 if (Binder.getCallingUid() != Process.myUid()) { 112 throw new SecurityException("Can't access private device from different UID"); 113 } 114 } 115 116 if (portNumber < 0 || portNumber >= mOutputPortCount) { 117 Log.e(TAG, "portNumber out of range in openOutputPort: " + portNumber); 118 return null; 119 } 120 121 try { 122 ParcelFileDescriptor[] pair = ParcelFileDescriptor.createSocketPair( 123 OsConstants.SOCK_SEQPACKET); 124 final MidiInputPort inputPort = new MidiInputPort(pair[0], portNumber); 125 final MidiSender sender = mOutputPortDispatchers[portNumber].getSender(); 126 sender.connect(new MidiReceiver() { 127 @Override 128 public void receive(byte[] msg, int offset, int count, long timestamp) 129 throws IOException { 130 try { 131 inputPort.receive(msg, offset, count, timestamp); 132 } catch (IOException e) { 133 IoUtils.closeQuietly(inputPort); 134 sender.disconnect(this); 135 // FIXME also flush the receiver? 136 } 137 } 138 }); 139 140 return pair[1]; 141 } catch (IOException e) { 142 Log.e(TAG, "unable to create ParcelFileDescriptors in openOutputPort"); 143 return null; 144 } 145 } 146 }; 147 148 /* package */ MidiDeviceServer(IMidiManager midiManager, MidiReceiver[] inputPortReceivers, 149 int numOutputPorts) { 150 mMidiManager = midiManager; 151 mInputPortReceivers = inputPortReceivers; 152 mInputPortCount = inputPortReceivers.length; 153 mOutputPortCount = numOutputPorts; 154 155 mInputPortOutputPorts = new MidiOutputPort[mInputPortCount]; 156 157 mOutputPortDispatchers = new MidiDispatcher[numOutputPorts]; 158 for (int i = 0; i < numOutputPorts; i++) { 159 mOutputPortDispatchers[i] = new MidiDispatcher(); 160 } 161 } 162 163 /* package */ IMidiDeviceServer getBinderInterface() { 164 return mServer; 165 } 166 167 /* package */ void setDeviceInfo(MidiDeviceInfo deviceInfo) { 168 if (mDeviceInfo != null) { 169 throw new IllegalStateException("setDeviceInfo should only be called once"); 170 } 171 mDeviceInfo = deviceInfo; 172 } 173 174 @Override 175 public void close() throws IOException { 176 try { 177 // FIXME - close input and output ports too? 178 mMidiManager.unregisterDeviceServer(mServer); 179 } catch (RemoteException e) { 180 Log.e(TAG, "RemoteException in unregisterDeviceServer"); 181 } 182 } 183 184 /** 185 * Returns an array of {@link MidiReceiver} for the device's output ports. 186 * Clients can use these receivers to send data out the device's output ports. 187 * @return array of MidiReceivers 188 */ 189 public MidiReceiver[] getOutputPortReceivers() { 190 MidiReceiver[] receivers = new MidiReceiver[mOutputPortCount]; 191 System.arraycopy(mOutputPortDispatchers, 0, receivers, 0, mOutputPortCount); 192 return receivers; 193 } 194} 195