MidiOutputPort.java revision b6f50d357bd3d4d296be6bb047f5ce93a79cbca1
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.ParcelFileDescriptor; 21import android.os.RemoteException; 22import android.util.Log; 23 24import com.android.internal.midi.MidiDispatcher; 25 26import dalvik.system.CloseGuard; 27 28import libcore.io.IoUtils; 29 30import java.io.Closeable; 31import java.io.FileInputStream; 32import java.io.IOException; 33 34/** 35 * This class is used for receiving data from a port on a MIDI device 36 */ 37public final class MidiOutputPort extends MidiSender implements Closeable { 38 private static final String TAG = "MidiOutputPort"; 39 40 private IMidiDeviceServer mDeviceServer; 41 private final IBinder mToken; 42 private final int mPortNumber; 43 private final FileInputStream mInputStream; 44 private final MidiDispatcher mDispatcher = new MidiDispatcher(); 45 46 private final CloseGuard mGuard = CloseGuard.get(); 47 private boolean mIsClosed; 48 49 // This thread reads MIDI events from a socket and distributes them to the list of 50 // MidiReceivers attached to this device. 51 private final Thread mThread = new Thread() { 52 @Override 53 public void run() { 54 byte[] buffer = new byte[MidiPortImpl.MAX_PACKET_SIZE]; 55 56 try { 57 while (true) { 58 // read next event 59 int count = mInputStream.read(buffer); 60 if (count < 0) { 61 break; 62 // FIXME - inform receivers here? 63 } 64 65 int packetType = MidiPortImpl.getPacketType(buffer, count); 66 switch (packetType) { 67 case MidiPortImpl.PACKET_TYPE_DATA: { 68 int offset = MidiPortImpl.getDataOffset(buffer, count); 69 int size = MidiPortImpl.getDataSize(buffer, count); 70 long timestamp = MidiPortImpl.getPacketTimestamp(buffer, count); 71 72 // dispatch to all our receivers 73 mDispatcher.sendWithTimestamp(buffer, offset, size, timestamp); 74 break; 75 } 76 case MidiPortImpl.PACKET_TYPE_FLUSH: 77 mDispatcher.flush(); 78 break; 79 default: 80 Log.e(TAG, "Unknown packet type " + packetType); 81 break; 82 } 83 } 84 } catch (IOException e) { 85 // FIXME report I/O failure? 86 Log.e(TAG, "read failed"); 87 } finally { 88 IoUtils.closeQuietly(mInputStream); 89 } 90 } 91 }; 92 93 /* package */ MidiOutputPort(IMidiDeviceServer server, IBinder token, 94 ParcelFileDescriptor pfd, int portNumber) { 95 mDeviceServer = server; 96 mToken = token; 97 mPortNumber = portNumber; 98 mInputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd); 99 mThread.start(); 100 mGuard.open("close"); 101 } 102 103 /* package */ MidiOutputPort(ParcelFileDescriptor pfd, int portNumber) { 104 this(null, null, pfd, portNumber); 105 } 106 107 /** 108 * Returns the port number of this port 109 * 110 * @return the port's port number 111 */ 112 public final int getPortNumber() { 113 return mPortNumber; 114 } 115 116 @Override 117 public void connect(MidiReceiver receiver) { 118 mDispatcher.getSender().connect(receiver); 119 } 120 121 @Override 122 public void disconnect(MidiReceiver receiver) { 123 mDispatcher.getSender().disconnect(receiver); 124 } 125 126 @Override 127 public void close() throws IOException { 128 synchronized (mGuard) { 129 if (mIsClosed) return; 130 131 mGuard.close(); 132 mInputStream.close(); 133 if (mDeviceServer != null) { 134 try { 135 mDeviceServer.closePort(mToken); 136 } catch (RemoteException e) { 137 Log.e(TAG, "RemoteException in MidiOutputPort.close()"); 138 } 139 } 140 mIsClosed = true; 141 } 142 } 143 144 @Override 145 protected void finalize() throws Throwable { 146 try { 147 mGuard.warnIfOpen(); 148 // not safe to make binder calls from finalize() 149 mDeviceServer = null; 150 close(); 151 } finally { 152 super.finalize(); 153 } 154 } 155} 156